├── src
├── sgpsdp
│ ├── TR
│ │ ├── README
│ │ ├── Makefile.am
│ │ ├── test-001-01.res
│ │ └── test-002-01.res
│ ├── README
│ ├── 1_COPYING
│ ├── Makefile.am
│ ├── solar.c
│ └── sgp_math.c
├── math
│ └── fast_atan2f.h
├── dsp
│ ├── lpf_taps.h
│ ├── gaussian_taps.h
│ ├── dc_blocker.h
│ ├── lpf.h
│ ├── clock_recovery_mm.h
│ ├── quadrature_demod.h
│ ├── mmse_fir_interpolator.h
│ ├── interp_fir_filter.h
│ ├── frequency_modulator.h
│ ├── fsk_demod.h
│ ├── gfsk_mod.h
│ ├── sig_source.h
│ ├── doppler.h
│ ├── gaussian_taps.c
│ ├── fir_filter.h
│ ├── lpf.c
│ ├── frequency_modulator.c
│ ├── sig_source.c
│ ├── lpf_taps.c
│ ├── quadrature_demod.c
│ ├── dc_blocker.c
│ ├── fsk_demod.c
│ ├── gfsk_mod.c
│ ├── clock_recovery_mm.c
│ └── interp_fir_filter.c
├── resources
│ ├── sdr-modem.service
│ └── config.conf
├── tcp_server.h
├── tcp_utils.h
├── queue.h
├── sdr
│ ├── file_source.h
│ ├── sdr_device.h
│ ├── sdr_server_client.h
│ ├── plutosdr.h
│ ├── sdr_server_api.h
│ ├── iio_lib.h
│ ├── file_source.c
│ └── iio_lib.c
├── sdr_worker.h
├── dsp_worker.h
├── api.h
├── api_utils.h
├── linked_list.h
├── server_config.h
├── main.c
├── tcp_utils.c
├── api_utils.c
├── linked_list.c
└── sdr_worker.c
├── test
├── resources
│ ├── invalid.format.conf
│ ├── invalid.rx_sdr_type.conf
│ ├── invalid.timeout.conf
│ ├── invalid.tx_sdr_type.conf
│ ├── tx.cf32
│ ├── lucky7.cf32
│ ├── nusat.cf32
│ ├── inputnan.cf32
│ ├── pluto_enabled.conf
│ ├── processed.s8
│ ├── lucky7.expected.s8
│ ├── lucky7.expected.cf32
│ ├── lucky7.expected.nodc.s8
│ ├── lucky7.expected.47000.cf32
│ ├── lucky7.expected.95000.cf32
│ ├── minimal.conf
│ ├── nan.s8
│ ├── test-001.tle
│ ├── test-002.tle
│ ├── run_tests.sh
│ └── full.conf
├── iio_lib_mock.h
├── sdr_server_mock.h
├── utils.h
├── sdr_modem_client.h
├── test_sig_source.c
├── test_gaussian_taps.c
├── test_mmse_fir_interpolator.c
├── test_lpf_taps.c
├── perf_fsk_modem.c
├── test_dc_blocker.c
├── test_server_config.c
├── test_clock_recovery_mm.c
├── test_dsp_worker.c
├── test_quadrature_demod.c
├── test_file_source.c
├── test_fsk_demod.c
├── test_queue.c
├── test_linked_list.c
├── test_sgp4_002.c
├── test_doppler.c
├── test_frequency_modulator.c
└── sdr_server_mock.c
├── docs
└── design.png
├── .idea
├── sdr-modem.iml
├── codeStyles
│ └── codeStyleConfig.xml
├── vcs.xml
├── .gitignore
├── modules.xml
└── misc.xml
├── sonar-project.properties
├── .gitignore
├── .project
├── .github
└── workflows
│ └── cmake.yml
├── api.proto
├── README.md
└── CMakeLists.txt
/src/sgpsdp/TR/README:
--------------------------------------------------------------------------------
1 | Numerical test result for sgpsdp.
2 |
--------------------------------------------------------------------------------
/test/resources/invalid.format.conf:
--------------------------------------------------------------------------------
1 | bind_address=127.0.0.1
--------------------------------------------------------------------------------
/test/resources/invalid.rx_sdr_type.conf:
--------------------------------------------------------------------------------
1 | rx_sdr_type="unknown"
--------------------------------------------------------------------------------
/test/resources/invalid.timeout.conf:
--------------------------------------------------------------------------------
1 | read_timeout_seconds=-10
2 |
--------------------------------------------------------------------------------
/test/resources/invalid.tx_sdr_type.conf:
--------------------------------------------------------------------------------
1 | tx_sdr_type="unknown"
2 |
--------------------------------------------------------------------------------
/docs/design.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/docs/design.png
--------------------------------------------------------------------------------
/test/resources/tx.cf32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/tx.cf32
--------------------------------------------------------------------------------
/src/sgpsdp/TR/Makefile.am:
--------------------------------------------------------------------------------
1 | EXTRA_DIST = \
2 | README \
3 | test-001-01.res \
4 | test-002-01.res
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test/resources/lucky7.cf32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/lucky7.cf32
--------------------------------------------------------------------------------
/test/resources/nusat.cf32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/nusat.cf32
--------------------------------------------------------------------------------
/test/resources/inputnan.cf32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/inputnan.cf32
--------------------------------------------------------------------------------
/test/resources/pluto_enabled.conf:
--------------------------------------------------------------------------------
1 | tx_sdr_type="plutosdr"
2 | tx_plutosdr_gain=10.0
3 | tx_plutosdr_timeout_millis=20000
--------------------------------------------------------------------------------
/test/resources/processed.s8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/processed.s8
--------------------------------------------------------------------------------
/.idea/sdr-modem.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/resources/lucky7.expected.s8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/lucky7.expected.s8
--------------------------------------------------------------------------------
/test/resources/lucky7.expected.cf32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/lucky7.expected.cf32
--------------------------------------------------------------------------------
/test/resources/lucky7.expected.nodc.s8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/lucky7.expected.nodc.s8
--------------------------------------------------------------------------------
/test/resources/lucky7.expected.47000.cf32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/lucky7.expected.47000.cf32
--------------------------------------------------------------------------------
/test/resources/lucky7.expected.95000.cf32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dernasherbrezon/sdr-modem/HEAD/test/resources/lucky7.expected.95000.cf32
--------------------------------------------------------------------------------
/src/math/fast_atan2f.h:
--------------------------------------------------------------------------------
1 | #ifndef MATH_FAST_ATAN2F_H_
2 | #define MATH_FAST_ATAN2F_H_
3 |
4 | float fast_atan2f(float y, float x);
5 |
6 | #endif /* MATH_FAST_ATAN2F_H_ */
7 |
--------------------------------------------------------------------------------
/test/resources/minimal.conf:
--------------------------------------------------------------------------------
1 | # minimal config has reasonable defaults
2 | # putting random config because libconfig fails with "syntax error"
3 | # if file simply empty
4 | A=1
--------------------------------------------------------------------------------
/test/resources/nan.s8:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/resources/test-001.tle:
--------------------------------------------------------------------------------
1 | TEST SAT SGP 001
2 | 1 88888U 80275.98708465 .00073094 13844-3 66816-4 0 9
3 | 2 88888 72.8435 115.9689 0086731 52.6988 110.5714 16.05824518 103
4 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test/resources/test-002.tle:
--------------------------------------------------------------------------------
1 | TEST SAT SDP 001
2 | 1 11801U 80230.29629788 .01431103 00000-0 14311-1 0 2
3 | 2 11801 46.7916 230.4354 7318036 47.4722 10.4117 2.28537848 2
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/src/dsp/lpf_taps.h:
--------------------------------------------------------------------------------
1 | #ifndef SRC_LPF_H_
2 | #define SRC_LPF_H_
3 |
4 | #include
5 |
6 | int create_low_pass_filter(float gain, uint64_t sampling_freq, uint64_t cutoff_freq, uint32_t transition_width, float **taps, size_t *len);
7 |
8 | #endif /* SRC_LPF_H_ */
9 |
--------------------------------------------------------------------------------
/src/dsp/gaussian_taps.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_GAUSSIAN_TAPS_H
2 | #define SDR_MODEM_GAUSSIAN_TAPS_H
3 |
4 | #include
5 |
6 | int gaussian_taps_create(double gain, double samples_per_symbol, double bt, size_t taps_len, float **taps);
7 |
8 | #endif //SDR_MODEM_GAUSSIAN_TAPS_H
9 |
--------------------------------------------------------------------------------
/src/sgpsdp/README:
--------------------------------------------------------------------------------
1 | This library is based on the sgp4sdp4-0.3 by Neoklis Kyriazis
2 | (http://leonardo.spidernet.net/Copernicus/22420). I made some minor
3 | modifications to the code in order to create a library that works on
4 | multiple platforms.
5 |
6 | Hari Nair
7 | hari@alumni.caltech.edu
8 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/resources/sdr-modem.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=sdr-modem Service
3 |
4 | [Service]
5 | WorkingDirectory=/etc/sdr-modem
6 | Environment="VOLK_CONFIGPATH=/etc/sdr-modem"
7 | ExecStart=/usr/bin/sdr_modem /etc/sdr-modem/config.conf
8 | Restart=always
9 |
10 | [Install]
11 | Alias=sdr-modem.service
12 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/test/iio_lib_mock.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_IIO_LIB_MOCK_H
2 | #define SDR_MODEM_IIO_LIB_MOCK_H
3 |
4 | #include "../src/sdr/iio_lib.h"
5 |
6 | int iio_lib_mock_create(int16_t *expected_rx, size_t expected_rx_len, int16_t *expected_tx, iio_lib **lib);
7 |
8 | void iio_lib_mock_get_tx(int16_t **output, size_t *output_len);
9 |
10 | #endif //SDR_MODEM_IIO_LIB_MOCK_H
11 |
--------------------------------------------------------------------------------
/src/tcp_server.h:
--------------------------------------------------------------------------------
1 | #ifndef TCP_SERVER_H_
2 | #define TCP_SERVER_H_
3 |
4 | #include "server_config.h"
5 |
6 | typedef struct tcp_server_t tcp_server;
7 |
8 | int tcp_server_create(struct server_config *config, tcp_server **server);
9 |
10 | void tcp_server_join_thread(tcp_server *server);
11 |
12 | void tcp_server_destroy(tcp_server *server);
13 |
14 |
15 | #endif /* TCP_SERVER_H_ */
16 |
--------------------------------------------------------------------------------
/src/dsp/dc_blocker.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef DSP_DC_BLOCKER_H_
3 | #define DSP_DC_BLOCKER_H_
4 |
5 | typedef struct dc_blocker_t dc_blocker;
6 |
7 | int dc_blocker_create(int length, dc_blocker **blocker);
8 |
9 | void dc_blocker_process(float *input, size_t input_len, float **output, size_t *output_len, dc_blocker *blocker);
10 |
11 | void dc_blocker_destroy(dc_blocker *blocker);
12 |
13 | #endif /* DSP_DC_BLOCKER_H_ */
14 |
--------------------------------------------------------------------------------
/test/resources/run_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set +e
4 |
5 | EXIT_CODE=0
6 | for file in test_*; do
7 | [[ ${file} == *.dSYM ]] && continue
8 | export VOLK_GENERIC=1
9 | export VOLK_ALIGNMENT=16
10 | valgrind -v --error-exitcode=0 -q --tool=memcheck --leak-check=yes --show-reachable=yes ./${file}
11 | CURRENT_EXIT_CODE=$?
12 | if [ ${CURRENT_EXIT_CODE} != 0 ]; then
13 | EXIT_CODE=${CURRENT_EXIT_CODE}
14 | fi
15 | done
16 |
17 | exit ${EXIT_CODE}
18 |
--------------------------------------------------------------------------------
/src/tcp_utils.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_TCP_UTILS_H
2 | #define SDR_MODEM_TCP_UTILS_H
3 |
4 | #include
5 | #include
6 |
7 | int tcp_utils_write_data(uint8_t *buffer, size_t total_len_bytes, int client_socket);
8 |
9 | int tcp_utils_read_data(void *result, size_t len_bytes, int client_socket);
10 |
11 | int tcp_utils_read_data_partially(void *result, size_t len_bytes, size_t *actually_read, int client_socket);
12 |
13 | #endif //SDR_MODEM_TCP_UTILS_H
14 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/dsp/lpf.h:
--------------------------------------------------------------------------------
1 | #ifndef DSP_LPF_H_
2 | #define DSP_LPF_H_
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef struct lpf_t lpf;
9 |
10 | int lpf_create(uint8_t decimation, uint64_t sampling_freq, uint64_t cutoff_freq, uint32_t transition_width, size_t output_len, size_t num_bytes, lpf **filter);
11 |
12 | void lpf_process(const void *input, size_t input_len, void **output, size_t *output_len, lpf *filter);
13 |
14 | void lpf_destroy(lpf *filter);
15 |
16 | #endif /* DSP_LPF_H_ */
17 |
--------------------------------------------------------------------------------
/src/dsp/clock_recovery_mm.h:
--------------------------------------------------------------------------------
1 | #ifndef DSP_CLOCK_RECOVERY_MM_H_
2 | #define DSP_CLOCK_RECOVERY_MM_H_
3 |
4 | #include
5 |
6 | typedef struct clock_mm_t clock_mm;
7 |
8 | int clock_mm_create(float omega, float gain_omega, float mu, float gain_mu, float omega_relative_limit, size_t output_len, clock_mm **clock);
9 |
10 | void clock_mm_process(const float *input, size_t input_len, float **output, size_t *output_len, clock_mm *clock);
11 |
12 | void clock_mm_destroy(clock_mm *clock);
13 |
14 | #endif /* DSP_CLOCK_RECOVERY_MM_H_ */
15 |
--------------------------------------------------------------------------------
/src/dsp/quadrature_demod.h:
--------------------------------------------------------------------------------
1 | #ifndef DSP_QUADRATURE_DEMOD_H_
2 | #define DSP_QUADRATURE_DEMOD_H_
3 |
4 | #include
5 | #include
6 |
7 | typedef struct quadrature_demod_t quadrature_demod;
8 |
9 | int quadrature_demod_create(float gain, uint32_t max_input_buffer_length, quadrature_demod **demod);
10 |
11 | void quadrature_demod_process(float complex *input, size_t input_len, float **output, size_t *output_len, quadrature_demod *demod);
12 |
13 | void quadrature_demod_destroy(quadrature_demod *demod);
14 |
15 | #endif /* DSP_QUADRATURE_DEMOD_H_ */
16 |
--------------------------------------------------------------------------------
/src/dsp/mmse_fir_interpolator.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_MMSE_FIR_INTERPOLATOR_H
2 | #define SDR_MODEM_MMSE_FIR_INTERPOLATOR_H
3 |
4 | #include
5 |
6 | typedef struct mmse_fir_interpolator_t mmse_fir_interpolator;
7 |
8 | int mmse_fir_interpolator_create(mmse_fir_interpolator **interp);
9 |
10 | float mmse_fir_interpolator_process(const float *input, float mu, mmse_fir_interpolator *interp);
11 |
12 | void mmse_fir_interpolator_destroy(mmse_fir_interpolator *interp);
13 |
14 | int mmse_fir_interpolator_taps(mmse_fir_interpolator *interp);
15 |
16 | #endif //SDR_MODEM_MMSE_FIR_INTERPOLATOR_H
17 |
--------------------------------------------------------------------------------
/src/queue.h:
--------------------------------------------------------------------------------
1 | #ifndef QUEUE_H_
2 | #define QUEUE_H_
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef struct queue_t queue;
9 |
10 | int create_queue(uint32_t buffer_size, uint16_t queue_size, bool blocking, queue **queue);
11 |
12 | int queue_put(const float complex *buffer, size_t len, queue *queue);
13 | void take_buffer_for_processing(float complex **buffer, size_t *len, queue *queue);
14 | void complete_buffer_processing(queue *queue);
15 |
16 | void interrupt_waiting_the_data(queue *queue);
17 | void destroy_queue(queue *queue);
18 |
19 | #endif /* QUEUE_H_ */
20 |
--------------------------------------------------------------------------------
/src/dsp/interp_fir_filter.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_INTERP_FIR_FILTER_H
2 | #define SDR_MODEM_INTERP_FIR_FILTER_H
3 |
4 | #include
5 | #include
6 |
7 | typedef struct interp_fir_filter_t interp_fir_filter;
8 |
9 | int interp_fir_filter_create(float *taps, size_t taps_len, uint8_t interpolation, uint32_t max_input_buffer_length, interp_fir_filter **filter);
10 |
11 | void interp_fir_filter_process(float *input, size_t input_len, float **output, size_t *output_len, interp_fir_filter *filter);
12 |
13 | void interp_fir_filter_destroy(interp_fir_filter *filter);
14 |
15 | #endif //SDR_MODEM_INTERP_FIR_FILTER_H
16 |
--------------------------------------------------------------------------------
/src/dsp/frequency_modulator.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_FREQUENCY_MODULATOR_H
2 | #define SDR_MODEM_FREQUENCY_MODULATOR_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef struct frequency_modulator_t frequency_modulator;
9 |
10 | int frequency_modulator_create(float sensitivity, uint32_t max_input_buffer_length, frequency_modulator **mod);
11 |
12 | void frequency_modulator_process(float *input, size_t input_len, float complex **output, size_t *output_len, frequency_modulator *mod);
13 |
14 | void frequency_modulator_destroy(frequency_modulator *mod);
15 |
16 | #endif //SDR_MODEM_FREQUENCY_MODULATOR_H
17 |
--------------------------------------------------------------------------------
/src/sdr/file_source.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_FILE_SOURCE_H
2 | #define SDR_MODEM_FILE_SOURCE_H
3 |
4 | #include "sdr_device.h"
5 |
6 | typedef struct file_device_t file_device;
7 |
8 | int file_source_create(uint32_t id, const char *rx_filename, const char *tx_filename, uint64_t sampling_freq, int64_t freq_offset, uint32_t max_output_buffer_length, sdr_device **result);
9 |
10 | int file_source_process_rx(float complex **output, size_t *output_len, void *plugin);
11 |
12 | int file_source_process_tx(float complex *input, size_t input_len, void *plugin);
13 |
14 | void file_source_destroy(void *plugin);
15 |
16 | #endif //SDR_MODEM_FILE_SOURCE_H
17 |
--------------------------------------------------------------------------------
/src/dsp/fsk_demod.h:
--------------------------------------------------------------------------------
1 | #ifndef DSP_FSK_DEMOD_H_
2 | #define DSP_FSK_DEMOD_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | typedef struct fsk_demod_t fsk_demod;
10 |
11 | int fsk_demod_create(uint64_t sampling_freq, uint32_t baud_rate, int64_t deviation, uint8_t decimation, uint32_t transition_width, bool use_dc_block, uint32_t max_input_buffer_length, fsk_demod **demod);
12 |
13 | void fsk_demod_process(const float complex *input, size_t input_len, int8_t **output, size_t *output_len, fsk_demod *demod);
14 |
15 | void fsk_demod_destroy(fsk_demod *demod);
16 |
17 | #endif /* DSP_FSK_DEMOD_H_ */
18 |
--------------------------------------------------------------------------------
/src/sdr_worker.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_SDR_WORKER_H
2 | #define SDR_MODEM_SDR_WORKER_H
3 |
4 | #include
5 | #include
6 | #include "dsp_worker.h"
7 | #include "sdr/sdr_device.h"
8 |
9 | typedef struct sdr_worker_t sdr_worker;
10 |
11 | void sdr_worker_destroy(void *data);
12 |
13 | bool sdr_worker_find_closest(void *id, void *data);
14 |
15 | void sdr_worker_destroy_by_dsp_worker_id(uint32_t id, sdr_worker *sdr);
16 |
17 | int sdr_worker_add_dsp_worker(dsp_worker *worker, sdr_worker *sdr);
18 |
19 | int sdr_worker_create(uint32_t id, struct sdr_rx *rx, sdr_device *rx_device, sdr_worker **result);
20 |
21 | #endif //SDR_MODEM_SDR_WORKER_H
22 |
--------------------------------------------------------------------------------
/src/dsp/gfsk_mod.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_GFSK_MOD_H
2 | #define SDR_MODEM_GFSK_MOD_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef struct gfsk_mod_t gfsk_mod;
9 |
10 | int gfsk_mod_create(float samplesPerSymbol, float sensitivity, float bt, uint32_t max_input_buffer_length, gfsk_mod **mod);
11 |
12 | void gfsk_mod_process(const uint8_t *input, size_t input_len, float complex **output, size_t *output_len, gfsk_mod *mod);
13 |
14 | //used in tests
15 | int gfsk_mod_convolve(float *x, size_t x_len, float *y, size_t y_len, float **out, size_t *out_len);
16 |
17 | void gfsk_mod_destroy(gfsk_mod *mod);
18 |
19 | #endif //SDR_MODEM_GFSK_MOD_H
20 |
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | sonar.projectKey=dernasherbrezon_sdr-modem
2 | sonar.organization=dernasherbrezon-github
3 |
4 | # This is the name and version displayed in the SonarCloud UI.
5 | sonar.projectName=sdr-modem
6 | #sonar.projectVersion=1.0
7 |
8 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
9 | sonar.sources=./src/
10 | sonar.tests=./test/
11 |
12 | # Encoding of the source code. Default is default system encoding
13 | sonar.sourceEncoding=UTF-8
14 |
15 | sonar.cfamily.gcov.reportsPath=build
16 |
17 | sonar.cpp.file.suffixes=-
18 | sonar.objc.file.suffixes=-
19 |
20 | #sonar.cfamily.build-wrapper-output=debug/bw-output
21 | sonar.cfamily.threads=2
--------------------------------------------------------------------------------
/src/sdr/sdr_device.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_SDR_DEVICE_H
2 | #define SDR_MODEM_SDR_DEVICE_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef struct sdr_device_t sdr_device;
9 |
10 | struct sdr_rx {
11 | uint64_t rx_center_freq;
12 | uint64_t rx_sampling_freq;
13 | int64_t rx_offset;
14 | };
15 |
16 | struct sdr_device_t {
17 | void *plugin;
18 |
19 | int (*sdr_process_rx)(float complex **output, size_t *output_len, void *plugin);
20 | int (*sdr_process_tx)(float complex *input, size_t input_len, void *plugin);
21 | void (*destroy)(void *plugin);
22 | void (*stop_rx)(void *plugin);
23 | };
24 |
25 | #endif //SDR_MODEM_SDR_DEVICE_H
26 |
--------------------------------------------------------------------------------
/test/sdr_server_mock.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_SDR_SERVER_MOCK_H
2 | #define SDR_MODEM_SDR_SERVER_MOCK_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef struct sdr_server_mock_t sdr_server_mock;
9 |
10 | int sdr_server_mock_create(const char *addr, int port, void (*handler)(int client_socket, sdr_server_mock *server), uint32_t max_output_buffer_length, sdr_server_mock **server);
11 |
12 | void mock_response_success(int client_socket, sdr_server_mock *server);
13 |
14 | int sdr_server_mock_send(float complex *input, size_t input_len, sdr_server_mock *server);
15 |
16 | void sdr_server_mock_destroy(sdr_server_mock *server);
17 |
18 | #endif //SDR_MODEM_SDR_SERVER_MOCK_H
19 |
--------------------------------------------------------------------------------
/src/dsp/sig_source.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_SIG_SOURCE_H
2 | #define SDR_MODEM_SIG_SOURCE_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef struct sig_source_t sig_source;
9 |
10 | int sig_source_create(float amplitude, uint64_t rx_sampling_freq, uint32_t max_output_buffer_length, sig_source **source);
11 |
12 | void sig_source_process(int64_t freq, size_t expected_output_len, float complex **output, size_t *output_len, sig_source *source);
13 |
14 | void sig_source_multiply(int64_t freq, const float complex *input, size_t input_len, float complex **output, size_t *output_len, sig_source *source);
15 |
16 | void sig_source_destroy(sig_source *source);
17 |
18 | #endif //SDR_MODEM_SIG_SOURCE_H
19 |
--------------------------------------------------------------------------------
/src/dsp_worker.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_DSP_WORKER_H
2 | #define SDR_MODEM_DSP_WORKER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "api.pb-c.h"
10 | #include "server_config.h"
11 |
12 | typedef struct dsp_worker_t dsp_worker;
13 |
14 | void dsp_worker_destroy(void *data);
15 |
16 | bool dsp_worker_find_by_id(void *id, void *data);
17 |
18 | void dsp_worker_shutdown(void *arg, void *data);
19 |
20 | void dsp_worker_put(float complex *output, size_t output_len, dsp_worker *worker);
21 |
22 | int dsp_worker_create(uint32_t id, int client_socket, struct server_config *config, struct RxRequest *req, dsp_worker **result);
23 |
24 | #endif //SDR_MODEM_DSP_WORKER_H
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.fsk_mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 | /.settings/
54 | /build/
55 | /Debug/
56 | /cmake-build-debug/
57 |
--------------------------------------------------------------------------------
/src/api.h:
--------------------------------------------------------------------------------
1 | #ifndef API_H_
2 | #define API_H_
3 |
4 | #include
5 |
6 | #define PROTOCOL_VERSION 0
7 |
8 | // client to server
9 | #define TYPE_RX_REQUEST 0
10 | #define TYPE_SHUTDOWN 1
11 | #define TYPE_PING 3
12 | #define TYPE_TX_DATA 4
13 | #define TYPE_TX_REQUEST 5
14 | //server to client
15 | #define TYPE_RESPONSE 2
16 |
17 | #define RESPONSE_NO_DETAILS 0
18 | #define RESPONSE_DETAILS_INVALID_REQUEST 1
19 | #define RESPONSE_DETAILS_INTERNAL_ERROR 3
20 | #define RESPONSE_DETAILS_TX_IS_BEING_USED 4
21 | #define RESPONSE_DETAILS_RX_IS_BEING_USED 5
22 |
23 | struct message_header {
24 | uint8_t protocol_version;
25 | uint8_t type;
26 | uint32_t message_length;
27 | } __attribute__((packed));
28 |
29 | #endif /* API_H_ */
30 |
--------------------------------------------------------------------------------
/src/api_utils.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef SDR_MODEM_API_UTILS_H
3 | #define SDR_MODEM_API_UTILS_H
4 |
5 | #include "api.h"
6 | #include "api.pb-c.h"
7 |
8 | int api_utils_read_header(int socket, struct message_header *header);
9 |
10 | int api_utils_read_rx_request(int socket, const struct message_header *header, struct RxRequest **request);
11 |
12 | int api_utils_read_tx_request(int socket, const struct message_header *header, struct TxRequest **request);
13 |
14 | int api_utils_read_tx_data(int socket, const struct message_header *header, struct TxData **request);
15 |
16 | int api_utils_write_response(int socket, ResponseStatus status, uint32_t details);
17 |
18 | void api_utils_convert_tle(char **tle, char (*output)[80]);
19 |
20 | #endif //SDR_MODEM_API_UTILS_H
21 |
--------------------------------------------------------------------------------
/src/sdr/sdr_server_client.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_SERVER_CLIENT_H_
2 | #define SDR_SERVER_CLIENT_H_
3 |
4 | #include
5 | #include
6 | #include "sdr_server_api.h"
7 | #include "sdr_device.h"
8 |
9 | typedef struct sdr_server_client_t sdr_server_client;
10 |
11 | int sdr_server_client_create(uint32_t id, struct sdr_rx *rx, char *addr, int port, int read_timeout_seconds, uint32_t max_output_buffer_length, sdr_device **result);
12 |
13 | int sdr_server_client_read_stream(float complex **output, size_t *output_len, void *plugin);
14 |
15 | int sdr_server_client_request(struct sdr_server_request request, struct sdr_server_response **response, sdr_server_client *client);
16 |
17 | void sdr_server_client_stop(void *plugin);
18 |
19 | void sdr_server_client_destroy(void *plugin);
20 |
21 | #endif /* SDR_SERVER_CLIENT_H_ */
22 |
--------------------------------------------------------------------------------
/src/dsp/doppler.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_DOPPLER_H
2 | #define SDR_MODEM_DOPPLER_H
3 |
4 | #define _POSIX_C_SOURCE 200809L
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 |
12 | typedef struct doppler_t doppler;
13 |
14 | int doppler_create(double latitude, double longitude, double altitude, uint64_t sampling_freq, uint64_t center_freq, int64_t constant_offset, time_t start_time_seconds, uint32_t max_output_buffer_length, char tle[3][80], doppler **result);
15 |
16 | void doppler_process_rx(float complex *input, size_t input_len, float complex **output, size_t *output_len, doppler *result);
17 |
18 | void doppler_process_tx(float complex *input, size_t input_len, float complex **output, size_t *output_len, doppler *result);
19 |
20 | void doppler_destroy(doppler *result);
21 |
22 | #endif //SDR_MODEM_DOPPLER_H
23 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | sdr-modem
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder
10 | clean,full,incremental,
11 |
12 |
13 |
14 |
15 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
16 | full,incremental,
17 |
18 |
19 |
20 |
21 |
22 | org.eclipse.cdt.core.cnature
23 | org.eclipse.cdt.managedbuilder.core.managedBuildNature
24 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/linked_list.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_LINKED_LIST_H
2 | #define SDR_MODEM_LINKED_LIST_H
3 |
4 | #include
5 |
6 | typedef struct linked_list_t linked_list;
7 |
8 | int linked_list_add(void *data, void (*destructor)(void *data), linked_list **list);
9 |
10 | void linked_list_destroy_by_selector(bool (*selector)(void *data), linked_list **list);
11 |
12 | void linked_list_foreach(void *arg, void (*foreach)(void *arg, void *data), linked_list *list);
13 |
14 | void linked_list_destroy_by_id(void *id, bool (*selector)(void *id, void *data), linked_list **list);
15 |
16 | void *linked_list_remove_by_id(void *id, bool (*selector)(void *id, void *data), linked_list **list);
17 |
18 | void *linked_list_find(void *id, bool (*selector)(void *id, void *data), linked_list *list);
19 |
20 | void linked_list_destroy(linked_list *list);
21 |
22 | #endif //SDR_MODEM_LINKED_LIST_H
23 |
--------------------------------------------------------------------------------
/src/dsp/gaussian_taps.c:
--------------------------------------------------------------------------------
1 | #include "gaussian_taps.h"
2 | #include
3 | #include
4 | #include
5 |
6 | #ifndef M_PI
7 | #define M_PI 3.14159265358979323846
8 | #endif
9 |
10 | int gaussian_taps_create(double gain, double samples_per_symbol, double bt, size_t taps_len, float **taps) {
11 | float *result = malloc(sizeof(float) * taps_len);
12 | if (result == NULL) {
13 | return -ENOMEM;
14 | }
15 |
16 | double scale = 0;
17 | double dt = 1.0 / samples_per_symbol;
18 | double s = 1.0 / (sqrt(log(2.0)) / (2 * M_PI * bt));
19 | double t0 = -0.5 * taps_len;
20 | double ts;
21 | for (int i = 0; i < taps_len; i++) {
22 | t0++;
23 | ts = s * dt * t0;
24 | result[i] = exp(-0.5 * ts * ts);
25 | scale += result[i];
26 | }
27 | for (int i = 0; i < taps_len; i++) {
28 | result[i] = result[i] / scale * gain;
29 | }
30 |
31 | *taps = result;
32 | return 0;
33 | }
--------------------------------------------------------------------------------
/src/dsp/fir_filter.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_FIR_FILTER_H
2 | #define SDR_MODEM_FIR_FILTER_H
3 |
4 | #include
5 | #include
6 |
7 | typedef struct fir_filter_t fir_filter;
8 |
9 | struct fir_filter_t {
10 | uint8_t decimation;
11 |
12 | float **taps;
13 | size_t aligned_taps_len;
14 | size_t alignment;
15 | size_t taps_len;
16 | float *original_taps;
17 |
18 | void *working_buffer;
19 | size_t history_offset;
20 | size_t working_len_total;
21 | void *volk_output;
22 | size_t max_input_buffer_length;
23 |
24 | void *output;
25 | size_t output_len;
26 | size_t num_bytes;
27 | };
28 |
29 | int fir_filter_create(uint8_t decimation, float *taps, size_t taps_len, size_t output_len, size_t num_bytes, fir_filter **filter);
30 |
31 | void fir_filter_process(const void *input, size_t input_len, void **output, size_t *output_len, fir_filter *filter);
32 |
33 | float fir_filter_process_float_single(const float *input, fir_filter *filter);
34 |
35 | void fir_filter_destroy(fir_filter *filter);
36 |
37 |
38 | #endif //SDR_MODEM_FIR_FILTER_H
39 |
--------------------------------------------------------------------------------
/src/sgpsdp/1_COPYING:
--------------------------------------------------------------------------------
1 | Copyright Information:
2 |
3 | Some parts of the C source code in this package were ported partly
4 | form NORAD's Spacetrack report #3, which included FORTRAN source for
5 | SGP, SGP4, SDP4, SGP8 and SDP8. According to a statement in that
6 | report, the document is free of copyrights and open to unlimited
7 | public distribution.
8 |
9 | Other parts of the C source code in this package were ported from
10 | Pascal code provided by Dr. TS. Kelso on his CelesTrack website at
11 | http://celestrack.com/ . I have reproduced the copyright information
12 | regarding the Pascal source I used below:
13 |
14 | { Author: Dr TS Kelso }
15 | { Original Version: 1991 Oct 30}
16 | { Current Revision: 1992 Sep 03}
17 | { Version: 1.50 }
18 | { Copyright: 1991-1992, All Rights Reserved }
19 |
20 | I make no copyright claims to the C ports in this package as I used
21 | the above material downloaded from the Internet to produce it.
22 | I suppose any sourcecode used from norad.c must contain the above
23 | copyright statement by Dr. TS. Kelso.
24 |
25 | Neoklis Kyriazis April 10 2001.
--------------------------------------------------------------------------------
/src/sdr/plutosdr.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_PLUTOSDR_H
2 | #define SDR_MODEM_PLUTOSDR_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "sdr_device.h"
8 | #include "iio_lib.h"
9 |
10 | typedef struct plutosdr_t plutosdr;
11 |
12 | #define IIO_GAIN_MODE_MANUAL 0
13 | #define IIO_GAIN_MODE_FAST_ATTACK 1
14 | #define IIO_GAIN_MODE_SLOW_ATTACK 2
15 | #define IIO_GAIN_MODE_HYBRID 3
16 |
17 | struct stream_cfg {
18 | uint64_t sampling_freq; // Baseband sample rate in Hz
19 | uint64_t center_freq; // Local oscillator frequency in Hz
20 | uint8_t gain_control_mode;
21 | int64_t offset;
22 | double manual_gain;
23 | };
24 |
25 | int plutosdr_create(uint32_t id, bool rx_only, struct stream_cfg *rx_config, struct stream_cfg *tx_config, unsigned int timeout_ms, uint32_t max_input_buffer_length, iio_lib *lib, sdr_device **result);
26 |
27 | int plutosdr_process_rx(float complex **output, size_t *output_len, void *plugin);
28 |
29 | int plutosdr_process_tx(float complex *input, size_t input_len, void *plugin);
30 |
31 | void plutosdr_destroy(void *plugin);
32 |
33 | #endif //SDR_MODEM_PLUTOSDR_H
34 |
--------------------------------------------------------------------------------
/src/server_config.h:
--------------------------------------------------------------------------------
1 | #ifndef SERVER_CONFIG_H_
2 | #define SERVER_CONFIG_H_
3 |
4 | #include
5 | #include
6 | #include "sdr/iio_lib.h"
7 |
8 | #define RX_SDR_TYPE_SDR_SERVER 0
9 | #define RX_SDR_TYPE_PLUTOSDR 1
10 | #define RX_SDR_TYPE_FILE 2
11 |
12 | #define TX_SDR_TYPE_NONE 0
13 | #define TX_SDR_TYPE_PLUTOSDR 1
14 | #define TX_SDR_TYPE_FILE 2
15 |
16 | struct server_config {
17 | // socket settings
18 | char* bind_address;
19 | uint16_t port;
20 | int read_timeout_seconds;
21 |
22 | uint32_t buffer_size;
23 | uint16_t queue_size;
24 |
25 | uint8_t rx_sdr_type;
26 | char *rx_sdr_server_address;
27 | int rx_sdr_server_port;
28 |
29 | // output settings
30 | char *base_path;
31 |
32 | char *rx_file_base_path;
33 | char *tx_file_base_path;
34 |
35 | uint8_t tx_sdr_type;
36 | double tx_plutosdr_gain;
37 | double rx_plutosdr_gain;
38 | unsigned int tx_plutosdr_timeout_millis;
39 | iio_lib *iio;
40 | };
41 |
42 | int server_config_create(struct server_config **config, const char *path);
43 |
44 | void server_config_destroy(struct server_config *config);
45 |
46 | #endif /* SERVER_CONFIG_H_ */
47 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "server_config.h"
6 | #include "tcp_server.h"
7 |
8 | static tcp_server *server = NULL;
9 |
10 | void sdrmodem_stop_async(int signum) {
11 | tcp_server_destroy(server);
12 | server = NULL;
13 | }
14 |
15 | int main(int argc, char **argv) {
16 | if (argc < 2) {
17 | fprintf(stderr, "<3>parameter missing: configuration file\n");
18 | exit(EXIT_FAILURE);
19 | }
20 | setvbuf(stdout, NULL, _IOLBF, 0);
21 |
22 | struct server_config *server_config = NULL;
23 | int code = server_config_create(&server_config, argv[1]);
24 | if (code != 0) {
25 | exit(EXIT_FAILURE);
26 | }
27 |
28 | signal(SIGINT, sdrmodem_stop_async);
29 | signal(SIGHUP, sdrmodem_stop_async);
30 | signal(SIGTERM, sdrmodem_stop_async);
31 | signal(SIGPIPE, SIG_IGN);
32 |
33 | code = tcp_server_create(server_config, &server);
34 | if (code != 0) {
35 | server_config_destroy(server_config);
36 | exit(EXIT_FAILURE);
37 | }
38 |
39 | // wait here until server terminates
40 | tcp_server_join_thread(server);
41 |
42 | // server will be freed on its own thread
43 | server_config_destroy(server_config);
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/src/sgpsdp/Makefile.am:
--------------------------------------------------------------------------------
1 | SUBDIRS = TR
2 |
3 | ##noinst_LIBRARIES = libsgp4sdp4.a
4 |
5 | AM_CPPFLAGS = $(all_includes) @PACKAGE_CFLAGS@
6 |
7 | ##libsgp4sdp4_a_METASOURCES = AUTO
8 |
9 | ##libsgp4sdp4_a_SOURCES = \
10 | ## solar.c \
11 | ## sgp_time.c \
12 | ## sgp_obs.c \
13 | ## sgp_math.c \
14 | ## sgp_in.c \
15 | ## sgp4sdp4.c
16 |
17 | ##libsgp4sdp4_a_LDFLAGS = `pkg-config --libs glib-2.0`
18 |
19 | noinst_PROGRAMS = test-001 test-002
20 |
21 | test_001_SOURCES = \
22 | solar.c \
23 | sgp_time.c \
24 | sgp_obs.c \
25 | sgp_math.c \
26 | sgp_in.c \
27 | sgp4sdp4.c \
28 | test-001.c
29 |
30 | test_001_LDADD = @PACKAGE_LIBS@
31 | ##test_001_LDFLAGS = `pkg-config --libs glib-2.0`
32 |
33 | test_002_SOURCES = \
34 | solar.c \
35 | sgp_time.c \
36 | sgp_obs.c \
37 | sgp_math.c \
38 | sgp_in.c \
39 | sgp4sdp4.c \
40 | test-002.c
41 |
42 | test_002_LDADD = @PACKAGE_LIBS@
43 | ##test_002_LDFLAGS = `pkg-config --libs glib-2.0`
44 |
45 | EXTRA_DIST = \
46 | 1_COPYING \
47 | 2_README \
48 | README \
49 | sgp4sdp4.c \
50 | sgp4sdp4.h \
51 | sgp_in.c \
52 | sgp_math.c \
53 | sgp_obs.c \
54 | sgp_time.c \
55 | solar.c \
56 | test-001.c \
57 | test-001.tle \
58 | test-002.c \
59 | test-002.tle
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/sdr/sdr_server_api.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_SERVER_API_H_
2 | #define SDR_SERVER_API_H_
3 |
4 | #include
5 |
6 | #define SDR_SERVER_PROTOCOL_VERSION 0
7 |
8 | // client to server
9 | #define SDR_SERVER_TYPE_REQUEST 0
10 | #define SDR_SERVER_TYPE_SHUTDOWN 1
11 | #define SDR_SERVER_TYPE_PING 3
12 | //server to client
13 | #define SDR_SERVER_TYPE_RESPONSE 2
14 |
15 | struct sdr_server_message_header {
16 | uint8_t protocol_version;
17 | uint8_t type;
18 | } __attribute__((packed));
19 |
20 | #define SDR_SERVER_REQUEST_DESTINATION_FILE 0
21 | #define SDR_SERVER_REQUEST_DESTINATION_SOCKET 1
22 |
23 | struct sdr_server_request {
24 | uint32_t center_freq;
25 | uint32_t sampling_rate;
26 | uint32_t band_freq;
27 | uint8_t destination;
28 | } __attribute__((packed));
29 |
30 | #define SDR_SERVER_RESPONSE_STATUS_SUCCESS 0
31 | #define SDR_SERVER_RESPONSE_STATUS_FAILURE 1
32 |
33 | #define SDR_SERVER_RESPONSE_DETAILS_INVALID_REQUEST 1
34 | #define SDR_SERVER_RESPONSE_DETAILS_OUT_OF_BAND_FREQ 2
35 | #define SDR_SERVER_RESPONSE_DETAILS_INTERNAL_ERROR 3
36 |
37 | struct sdr_server_response {
38 | uint8_t status;
39 | uint32_t details; // on success contains file index, on error contains error code
40 | } __attribute__((packed));
41 |
42 |
43 | #endif /* SDR_SERVER_API_H_ */
44 |
--------------------------------------------------------------------------------
/test/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_UTILS_H
2 | #define SDR_MODEM_UTILS_H
3 |
4 | #include
5 | #include
6 | #include "../src/api.pb-c.h"
7 | #include
8 |
9 | struct RxRequest *create_rx_request();
10 |
11 | struct TxRequest *create_tx_request();
12 |
13 | void setup_input_data(float **input, size_t input_offset, size_t len);
14 |
15 | void setup_volk_input_data(float **input, size_t input_offset, size_t len);
16 |
17 | void setup_input_complex_data(float complex **input, size_t input_offset, size_t len);
18 |
19 | void assert_float_array(const float expected[], size_t expected_size, float *actual, size_t actual_size);
20 |
21 | void assert_complex_array(const float expected[], size_t expected_size, float complex *actual, size_t actual_size);
22 |
23 | void assert_int16_array(const int16_t expected[], size_t expected_size, int16_t *actual, size_t actual_size);
24 |
25 | void assert_byte_array(const int8_t expected[], size_t expected_size, int8_t *actual, size_t actual_size, int tolerance);
26 |
27 | void assert_files(FILE *expected, size_t expected_total, uint8_t *expected_buffer, uint8_t *actual_buffer, size_t batch, FILE *actual, int tolerance);
28 |
29 | int read_data(uint8_t *output, size_t *output_len, size_t len, FILE *file);
30 |
31 | char *utils_read_and_copy_str(const char *value);
32 |
33 | char ** utils_allocate_tle(char tle[3][80]);
34 |
35 | #endif //SDR_MODEM_UTILS_H
36 |
--------------------------------------------------------------------------------
/test/sdr_modem_client.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_SDR_MODEM_CLIENT_H
2 | #define SDR_MODEM_SDR_MODEM_CLIENT_H
3 |
4 | #include
5 | #include "../src/api.h"
6 | #include "../src/api.pb-c.h"
7 |
8 | typedef struct sdr_modem_client_t sdr_modem_client;
9 |
10 | int sdr_modem_client_create(const char *addr, int port, uint32_t max_buffer_length, int read_timeout_seconds, sdr_modem_client **client);
11 |
12 | int sdr_modem_client_write_raw(uint8_t *buffer, size_t buffer_len, sdr_modem_client *client);
13 |
14 | int sdr_modem_client_write_request(struct message_header *header, struct RxRequest *req, sdr_modem_client *client);
15 |
16 | int sdr_modem_client_write_tx_request(struct message_header *header, struct TxRequest *req, sdr_modem_client *client);
17 |
18 | int sdr_modem_client_write_tx(struct message_header *header, struct TxData *req, sdr_modem_client *client);
19 |
20 | int sdr_modem_client_write_tx_raw(struct message_header *header, struct TxData *req, uint32_t req_len, sdr_modem_client *client);
21 |
22 | int sdr_modem_client_read_stream(int8_t **output, size_t expected_read, sdr_modem_client *client);
23 |
24 | int sdr_modem_client_read_response(struct message_header **response_header, struct Response **resp, sdr_modem_client *client);
25 |
26 | void sdr_modem_client_destroy(sdr_modem_client *client);
27 |
28 | // this will wait until server release all resources, stop all threads and closes connection
29 | void sdr_modem_client_destroy_gracefully(sdr_modem_client *client);
30 |
31 |
32 | #endif //SDR_MODEM_SDR_MODEM_CLIENT_H
33 |
--------------------------------------------------------------------------------
/test/test_sig_source.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "../src/dsp/sig_source.h"
4 | #include "utils.h"
5 |
6 | sig_source *source = NULL;
7 |
8 | START_TEST (test_success) {
9 | int code = sig_source_create(1.0F, 4, 4, &source);
10 | ck_assert_int_eq(code, 0);
11 |
12 | float complex *output = NULL;
13 | size_t output_len = 0;
14 | sig_source_process(1, 4, &output, &output_len, source);
15 |
16 | const float buffer[8] = {1, 0, 0, 1, -1, 0, 0, -1};
17 | assert_complex_array(buffer, sizeof(buffer) / sizeof(float) / 2, output, output_len);
18 | }
19 |
20 | END_TEST
21 |
22 | void teardown() {
23 | if (source != NULL) {
24 | sig_source_destroy(source);
25 | }
26 | }
27 |
28 | void setup() {
29 | //do nothing
30 | }
31 |
32 | Suite *common_suite(void) {
33 | Suite *s;
34 | TCase *tc_core;
35 |
36 | s = suite_create("sig_source");
37 |
38 | /* Core test case */
39 | tc_core = tcase_create("Core");
40 |
41 | tcase_add_test(tc_core, test_success);
42 |
43 | tcase_add_checked_fixture(tc_core, setup, teardown);
44 | suite_add_tcase(s, tc_core);
45 |
46 | return s;
47 | }
48 |
49 | int main(void) {
50 | int number_failed;
51 | Suite *s;
52 | SRunner *sr;
53 |
54 | s = common_suite();
55 | sr = srunner_create(s);
56 |
57 | srunner_set_fork_status(sr, CK_NOFORK);
58 | srunner_run_all(sr, CK_NORMAL);
59 | number_failed = srunner_ntests_failed(sr);
60 | srunner_free(sr);
61 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/src/dsp/lpf.c:
--------------------------------------------------------------------------------
1 | #include "lpf.h"
2 | #include
3 | #include
4 | #include
5 | #include "fir_filter.h"
6 | #include "lpf_taps.h"
7 |
8 | struct lpf_t {
9 | fir_filter *filter;
10 | };
11 |
12 | int lpf_create(uint8_t decimation, uint64_t sampling_freq, uint64_t cutoff_freq, uint32_t transition_width,
13 | size_t max_input_buffer_length, size_t num_bytes, lpf **filter) {
14 | struct lpf_t *result = malloc(sizeof(struct lpf_t));
15 | if (result == NULL) {
16 | return -ENOMEM;
17 | }
18 | // init all fields with 0 so that destroy_* method would work
19 | *result = (struct lpf_t) {0};
20 |
21 | float *taps = NULL;
22 | size_t taps_len = 0;
23 | int code = create_low_pass_filter(1.0F, sampling_freq, cutoff_freq, transition_width, &taps, &taps_len);
24 | if (code != 0) {
25 | lpf_destroy(result);
26 | return code;
27 | }
28 | code = fir_filter_create(decimation, taps, taps_len, max_input_buffer_length, num_bytes, &result->filter);
29 | if (code != 0) {
30 | lpf_destroy(result);
31 | return code;
32 | }
33 | *filter = result;
34 | return 0;
35 | }
36 |
37 |
38 | void lpf_process(const void *input, size_t input_len, void **output, size_t *output_len, lpf *filter) {
39 | fir_filter_process(input, input_len, output, output_len, filter->filter);
40 | }
41 |
42 | void lpf_destroy(lpf *filter) {
43 | if (filter == NULL) {
44 | return;
45 | }
46 | if (filter->filter != NULL) {
47 | fir_filter_destroy(filter->filter);
48 | }
49 | free(filter);
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/src/tcp_utils.c:
--------------------------------------------------------------------------------
1 | #include "tcp_utils.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | int tcp_utils_write_data(uint8_t *buffer, size_t total_len_bytes, int client_socket) {
8 | size_t left = total_len_bytes;
9 | while (left > 0) {
10 | ssize_t written = write(client_socket, buffer + (total_len_bytes - left), left);
11 | if (written < 0) {
12 | return -1;
13 | }
14 | left -= written;
15 | }
16 | return 0;
17 | }
18 |
19 | int tcp_utils_read_data_partially(void *result, size_t len_bytes, size_t *actually_read, int client_socket) {
20 | size_t left = len_bytes;
21 | int code = 0;
22 | while (left > 0) {
23 | ssize_t received = recv(client_socket, (char *) result + (len_bytes - left), left, 0);
24 | if (received < 0) {
25 | if (errno == EWOULDBLOCK || errno == EAGAIN) {
26 | code = -errno;
27 | break;
28 | }
29 | if (errno == EINTR) {
30 | continue;
31 | }
32 | code = -1;
33 | break;
34 | }
35 | // client has closed the socket
36 | if (received == 0) {
37 | code = -1;
38 | break;
39 | }
40 | left -= received;
41 | }
42 | *actually_read = len_bytes - left;
43 | return code;
44 | }
45 |
46 | int tcp_utils_read_data(void *result, size_t len_bytes, int client_socket) {
47 | size_t actually_read = 0;
48 | return tcp_utils_read_data_partially(result, len_bytes, &actually_read, client_socket);
49 | }
50 |
--------------------------------------------------------------------------------
/test/test_gaussian_taps.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "../src/dsp/gaussian_taps.h"
4 |
5 | float *taps = NULL;
6 |
7 | START_TEST (test_normal) {
8 | int code = gaussian_taps_create(1.5, 2 * (48000.0F / 9600), 0.5, 12, &taps);
9 | ck_assert_int_eq(code, 0);
10 |
11 | const float expected_taps[] = {0.039070457f, 0.07415177f, 0.12205514f, 0.17424175f, 0.21572968f, 0.23164831f, 0.21572968f, 0.17424175f, 0.12205514f, 0.07415177f, 0.039070457f, 0.017854061f};
12 |
13 | for (int i = 0; i < 12; i++) {
14 | ck_assert_int_eq((int32_t) (expected_taps[i] * 10000), (int32_t) (taps[i] * 10000));
15 | }
16 | }
17 |
18 | END_TEST
19 |
20 | void teardown() {
21 | if (taps != NULL) {
22 | free(taps);
23 | taps = NULL;
24 | }
25 | }
26 |
27 | void setup() {
28 | //do nothing
29 | }
30 |
31 | Suite *common_suite(void) {
32 | Suite *s;
33 | TCase *tc_core;
34 |
35 | s = suite_create("gaussian_taps");
36 |
37 | /* Core test case */
38 | tc_core = tcase_create("Core");
39 |
40 | tcase_add_test(tc_core, test_normal);
41 |
42 | tcase_add_checked_fixture(tc_core, setup, teardown);
43 | suite_add_tcase(s, tc_core);
44 |
45 | return s;
46 | }
47 |
48 | int main(void) {
49 | int number_failed;
50 | Suite *s;
51 | SRunner *sr;
52 |
53 | s = common_suite();
54 | sr = srunner_create(s);
55 |
56 | srunner_set_fork_status(sr, CK_NOFORK);
57 | srunner_run_all(sr, CK_NORMAL);
58 | number_failed = srunner_ntests_failed(sr);
59 | srunner_free(sr);
60 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/.github/workflows/cmake.yml:
--------------------------------------------------------------------------------
1 | name: CMake
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | env:
10 | BASE_VERSION: "1.0"
11 | DEBEMAIL: "gpg@r2cloud.ru"
12 | DEBFULLNAME: "r2cloud"
13 |
14 | jobs:
15 | build:
16 | name: Build and analyze
17 | runs-on: ubuntu-latest
18 | env:
19 | BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
20 | steps:
21 | - uses: actions/checkout@v4
22 | with:
23 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
24 | - name: Install sonar-scanner and build-wrapper
25 | uses: SonarSource/sonarcloud-github-c-cpp@v2
26 | - name: install dependencies
27 | run: |
28 | sudo apt-get update
29 | sudo apt-get install -y valgrind libconfig-dev check pkg-config libvolk-dev libiio-dev libprotobuf-c-dev
30 | - name: Run build-wrapper
31 | run: |
32 | mkdir build
33 | cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build
34 | build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Debug
35 | - name: run tests & coverage
36 | run: |
37 | cd build
38 | bash ./run_tests.sh
39 | make coverage
40 | cd ..
41 | - name: Run sonar-scanner
42 | env:
43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
45 | run: |
46 | sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}"
47 |
--------------------------------------------------------------------------------
/test/test_mmse_fir_interpolator.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "../src/dsp/mmse_fir_interpolator.h"
3 | #include "utils.h"
4 | #include
5 | #include
6 |
7 | mmse_fir_interpolator *interp = NULL;
8 | float *float_input = NULL;
9 |
10 | START_TEST(test_normal) {
11 | int code = mmse_fir_interpolator_create(&interp);
12 | ck_assert_int_eq(code, 0);
13 | setup_volk_input_data(&float_input, 0, 8);
14 | float result = mmse_fir_interpolator_process(float_input, 0.14, interp);
15 | ck_assert(fabsl(3.140217F - result) < 0.001);
16 | }
17 |
18 | END_TEST
19 |
20 | void teardown() {
21 | if (interp != NULL) {
22 | mmse_fir_interpolator_destroy(interp);
23 | interp = NULL;
24 | }
25 | if (float_input != NULL) {
26 | volk_free(float_input);
27 | float_input = NULL;
28 | }
29 | }
30 |
31 | void setup() {
32 | //do nothing
33 | }
34 |
35 | Suite *common_suite(void) {
36 | Suite *s;
37 | TCase *tc_core;
38 |
39 | s = suite_create("mmse_fir_interpolator");
40 |
41 | /* Core test case */
42 | tc_core = tcase_create("Core");
43 |
44 | tcase_add_test(tc_core, test_normal);
45 |
46 | tcase_add_checked_fixture(tc_core, setup, teardown);
47 | suite_add_tcase(s, tc_core);
48 |
49 | return s;
50 | }
51 |
52 | int main(void) {
53 | int number_failed;
54 | Suite *s;
55 | SRunner *sr;
56 |
57 | s = common_suite();
58 | sr = srunner_create(s);
59 |
60 | srunner_set_fork_status(sr, CK_NOFORK);
61 | srunner_run_all(sr, CK_NORMAL);
62 | number_failed = srunner_ntests_failed(sr);
63 | srunner_free(sr);
64 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
65 | }
--------------------------------------------------------------------------------
/src/resources/config.conf:
--------------------------------------------------------------------------------
1 | # bind address for a server
2 | bind_address="127.0.0.1"
3 |
4 | # port for a server
5 | port=8091
6 |
7 | # buffer size for passing the data from SDR
8 | # same buffer setting for passing data between threads
9 | # the bigger buffer the less context switching, but
10 | # bigger latency for RF messages
11 | buffer_size=131072
12 |
13 | # if client requests to save output locally, then
14 | # the base path controls the directory where it is saved
15 | # tmp directory is recommended
16 | base_path="/tmp/"
17 |
18 | # timeout for reading client's requests
19 | # in seconds
20 | # should be positive
21 | read_timeout_seconds=10
22 |
23 | # SDR for receiving the data. Supported values are:
24 | # - "sdr-server"
25 | # - "plutosdr"
26 | # - "file"
27 | rx_sdr_type="sdr-server"
28 |
29 | # SDR for transmitting the data. Supported values are:
30 | # - "none"
31 | # - "plutosdr". This would require libiio installed.
32 | # - "file"
33 | tx_sdr_type="none"
34 |
35 | # plutosdr-specific settings
36 | # tx gain. This passed as-is to "hardwaregain" parameter
37 | # default is 0.0
38 | tx_plutosdr_gain=0.0
39 | # timeout while communicating with the device
40 | # default is 10 seconds (10000)
41 | tx_plutosdr_timeout_millis=10000
42 | # rx gain. This passed as-is to "hardwaregain" parameter
43 | # default is 0.0
44 | rx_plutosdr_gain=0.0
45 |
46 | # sdr server connection details
47 | rx_sdr_server_address="127.0.0.1"
48 | rx_sdr_server_port=8090
49 |
50 | # file connection details
51 | rx_file_base_path="/tmp/"
52 | tx_file_base_path="/tmp/"
53 |
54 | # number of elements in the DSP queue
55 | # the more queue, the better performance spikes handled
56 | # the less queue, the better latency and memory consumption
57 | # total memory = queue_size * buffer_size * number_of_clients
58 | queue_size=64
59 |
--------------------------------------------------------------------------------
/test/resources/full.conf:
--------------------------------------------------------------------------------
1 | # bind address for a server
2 | bind_address="127.0.0.1"
3 |
4 | # port for a server
5 | port=8091
6 |
7 | # buffer size for passing the data from SDR
8 | # same buffer setting for passing data between threads
9 | # the bigger buffer the less context switching, but
10 | # bigger latency for RF messages
11 | buffer_size=2048
12 |
13 | # if client requests to save output locally, then
14 | # the base path controls the directory where it is saved
15 | # tmp directory is recommended
16 | base_path="/tmp/"
17 |
18 | # timeout for reading client's requests
19 | # in seconds
20 | # should be positive
21 | read_timeout_seconds=10
22 |
23 | # SDR for receiving the data. Supported values are:
24 | # - "sdr-server"
25 | # - "plutosdr"
26 | # - "file"
27 | rx_sdr_type="sdr-server"
28 |
29 | # SDR for transmitting the data. Supported values are:
30 | # - "none"
31 | # - "plutosdr". This would require libiio installed.
32 | # - "file"
33 | tx_sdr_type="none"
34 |
35 | # plutosdr-specific settings
36 | # tx gain. This passed as-is to "hardwaregain" parameter
37 | # default is 0.0
38 | tx_plutosdr_gain=0.0
39 | # timeout while communicating with the device
40 | # default is 10 seconds (10000)
41 | tx_plutosdr_timeout_millis=10000
42 | # rx gain. This passed as-is to "hardwaregain" parameter
43 | # default is 0.0
44 | rx_plutosdr_gain=0.0
45 |
46 | # sdr server connection details
47 | rx_sdr_server_address="127.0.0.1"
48 | rx_sdr_server_port=8090
49 |
50 | # file connection details
51 | rx_file_base_path="/tmp/"
52 | tx_file_base_path="/tmp/"
53 |
54 | # number of elements in the DSP queue
55 | # the more queue, the better performance spikes handled
56 | # the less queue, the better latency and memory consumption
57 | # total memory = queue_size * buffer_size * number_of_clients
58 | queue_size=64
59 |
60 |
61 |
--------------------------------------------------------------------------------
/api.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto2";
2 |
3 | enum modem_type {
4 | GMSK = 1;
5 | }
6 |
7 | enum demod_destination {
8 | FILE = 0;
9 | SOCKET = 1;
10 | BOTH = 2;
11 | }
12 |
13 | message doppler_settings {
14 | repeated string tle = 1;
15 | required uint32 latitude = 2; //degrees times 10^6
16 | required uint32 longitude = 3; //degrees times 10^6
17 | required uint32 altitude = 4; //kilometers times 10^6
18 | }
19 |
20 | message fsk_demodulation_settings {
21 | required int64 demod_fsk_deviation = 1;
22 | required uint32 demod_fsk_transition_width = 2;
23 | required bool demod_fsk_use_dc_block = 3;
24 | }
25 |
26 | message fsk_modulation_settings {
27 | required int64 mod_fsk_deviation = 1;
28 | }
29 |
30 | message file_settings {
31 | required string filename = 1;
32 | required uint64 start_time_seconds = 2;
33 | }
34 |
35 | message RxRequest {
36 | required uint64 rx_center_freq = 1;
37 | required uint64 rx_sampling_freq = 2;
38 | required bool rx_dump_file = 3;
39 | required int64 rx_offset = 4;
40 | required modem_type demod_type = 5;
41 | required uint32 demod_baud_rate = 6;
42 | // the actual is uint8
43 | required uint32 demod_decimation = 7;
44 | required demod_destination demod_destination = 8;
45 |
46 | optional doppler_settings doppler = 9;
47 | optional fsk_demodulation_settings fsk_settings = 10;
48 | optional file_settings file_settings = 11;
49 | }
50 |
51 | message TxRequest {
52 | required uint64 tx_center_freq = 1;
53 | required uint64 tx_sampling_freq = 2;
54 | required bool tx_dump_file = 3;
55 | required int64 tx_offset = 4;
56 | required modem_type mod_type = 5;
57 | required uint32 mod_baud_rate = 6;
58 |
59 | optional doppler_settings doppler = 7;
60 | optional fsk_modulation_settings fsk_settings = 8;
61 | optional file_settings file_settings = 9;
62 | }
63 |
64 | enum response_status {
65 | SUCCESS = 0;
66 | FAILURE = 1;
67 | }
68 |
69 | message Response {
70 | required response_status status = 1;
71 | required uint32 details = 2;
72 | }
73 |
74 | message TxData {
75 | required bytes data = 1;
76 | }
--------------------------------------------------------------------------------
/src/dsp/frequency_modulator.c:
--------------------------------------------------------------------------------
1 | #include "frequency_modulator.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #ifndef M_PI
8 | #define M_PI 3.14159265358979323846
9 | #endif
10 |
11 | #define M_2PI ((float) (2 * M_PI))
12 |
13 | struct frequency_modulator_t {
14 | float phase;
15 | float sensitivity;
16 |
17 | float complex *output;
18 | size_t output_len;
19 | };
20 |
21 | int frequency_modulator_create(float sensitivity, uint32_t max_input_buffer_length, frequency_modulator **mod) {
22 | struct frequency_modulator_t *result = malloc(sizeof(struct frequency_modulator_t));
23 | if (result == NULL) {
24 | return -ENOMEM;
25 | }
26 | // init all fields with 0 so that destroy_* method would work
27 | *result = (struct frequency_modulator_t) {0};
28 | result->phase = 0;
29 | result->sensitivity = sensitivity;
30 |
31 | result->output_len = max_input_buffer_length;
32 | result->output = malloc(sizeof(float complex) * result->output_len);
33 | if (result->output == NULL) {
34 | frequency_modulator_destroy(result);
35 | return -ENOMEM;
36 | }
37 | *mod = result;
38 | return 0;
39 | }
40 |
41 | void frequency_modulator_process(float *input, size_t input_len, float complex **output, size_t *output_len, frequency_modulator *mod) {
42 | if (input_len > mod->output_len) {
43 | fprintf(stderr, "<3>requested buffer %zu is more than max: %zu\n", input_len, mod->output_len);
44 | *output = NULL;
45 | *output_len = 0;
46 | return;
47 | }
48 | for (size_t i = 0; i < input_len; i++) {
49 | mod->phase = mod->phase + mod->sensitivity * input[i];
50 | if (mod->phase < -M_2PI) {
51 | mod->phase += M_2PI;
52 | }
53 | if (mod->phase > M_2PI) {
54 | mod->phase -= M_2PI;
55 | }
56 | mod->output[i] = cos(mod->phase) + I * sin(mod->phase);
57 | }
58 | *output = mod->output;
59 | *output_len = input_len;
60 | }
61 |
62 | void frequency_modulator_destroy(frequency_modulator *mod) {
63 | if (mod == NULL) {
64 | return;
65 | }
66 | if (mod->output != NULL) {
67 | free(mod->output);
68 | }
69 | free(mod);
70 | }
--------------------------------------------------------------------------------
/src/sgpsdp/solar.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Unit Solar
3 | * Author: Dr TS Kelso
4 | * Original Version: 1990 Jul 29
5 | * Current Revision: 1999 Nov 27
6 | * Version: 1.30
7 | * Copyright: 1990-1999, All Rights Reserved
8 | *
9 | * Ported to C by: Neoklis Kyriazis April 1 2001
10 | */
11 |
12 | #include "sgp4sdp4.h"
13 |
14 | /* Calculates solar position vector */
15 | void Calculate_Solar_Position(double _time, vector_t * solar_vector)
16 | {
17 | double mjd, year, T, M, L, e, C, O, Lsa, nu, R, eps;
18 |
19 | mjd = _time - 2415020.0;
20 | year = 1900 + mjd / 365.25;
21 | T = (mjd + Delta_ET(year) / secday) / 36525.0;
22 | M = Radians(Modulus(358.47583 + Modulus(35999.04975 * T, 360.0)
23 | - (0.000150 + 0.0000033 * T) * Sqr(T), 360.0));
24 | L = Radians(Modulus(279.69668 + Modulus(36000.76892 * T, 360.0)
25 | + 0.0003025 * Sqr(T), 360.0));
26 | e = 0.01675104 - (0.0000418 + 0.000000126 * T) * T;
27 | C = Radians((1.919460 - (0.004789 + 0.000014 * T) * T) * sin(M)
28 | + (0.020094 - 0.000100 * T) * sin(2 * M) +
29 | 0.000293 * sin(3 * M));
30 | O = Radians(Modulus(259.18 - 1934.142 * T, 360.0));
31 | Lsa = Modulus(L + C - Radians(0.00569 - 0.00479 * sin(O)), twopi);
32 | nu = Modulus(M + C, twopi);
33 | R = 1.0000002 * (1 - Sqr(e)) / (1 + e * cos(nu));
34 | eps = Radians(23.452294 - (0.0130125 + (0.00000164 -
35 | 0.000000503 * T) * T) * T +
36 | 0.00256 * cos(O));
37 | R = AU * R;
38 | solar_vector->x = R * cos(Lsa);
39 | solar_vector->y = R * sin(Lsa) * cos(eps);
40 | solar_vector->z = R * sin(Lsa) * sin(eps);
41 | solar_vector->w = R;
42 | }
43 |
44 |
45 | /* Calculates stellite's eclipse status and depth */
46 | int Sat_Eclipsed(vector_t * pos, vector_t * sol, double *depth)
47 | {
48 | double sd_sun, sd_earth, delta;
49 | vector_t Rho, earth;
50 |
51 | /* Determine partial eclipse */
52 | sd_earth = ArcSin(xkmper / pos->w);
53 | Vec_Sub(sol, pos, &Rho);
54 | sd_sun = ArcSin(__sr__ / Rho.w);
55 | Scalar_Multiply(-1, pos, &earth);
56 | delta = Angle(sol, &earth);
57 | *depth = sd_earth - sd_sun - delta;
58 | if (sd_earth < sd_sun)
59 | return (0);
60 | else if (*depth >= 0)
61 | return (1);
62 | else
63 | return (0);
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/sdr/iio_lib.h:
--------------------------------------------------------------------------------
1 | #ifndef SDR_MODEM_IIO_LIB_H
2 | #define SDR_MODEM_IIO_LIB_H
3 |
4 | #include
5 | #include
6 |
7 | typedef struct iio_lib_t iio_lib;
8 |
9 | struct iio_lib_t {
10 | void *handle;
11 |
12 | void *(*iio_buffer_end)(const struct iio_buffer *buf);
13 |
14 | void *(*iio_buffer_first)(const struct iio_buffer *buf, const struct iio_channel *chn);
15 |
16 | ssize_t (*iio_buffer_push_partial)(struct iio_buffer *buf, size_t samples_count);
17 |
18 | ssize_t (*iio_buffer_refill)(struct iio_buffer *buf);
19 |
20 | struct iio_device *(*iio_context_find_device)(const struct iio_context *ctx, const char *name);
21 |
22 | struct iio_channel *(*iio_device_find_channel)(const struct iio_device *dev, const char *name, bool output);
23 |
24 | void (*iio_strerror)(int err, char *dst, size_t len);
25 |
26 | ssize_t (*iio_channel_attr_write)(const struct iio_channel *chn, const char *attr, const char *src);
27 |
28 | int (*iio_channel_attr_write_longlong)(const struct iio_channel *chn, const char *attr, long long val);
29 |
30 | int (*iio_device_attr_write_bool)(const struct iio_device *dev, const char *attr, bool val);
31 |
32 | int (*iio_channel_attr_write_bool)(const struct iio_channel *chn, const char *attr, bool val);
33 |
34 | int (*iio_channel_attr_write_double)(const struct iio_channel *chn, const char *attr, double val);
35 |
36 | ssize_t (*iio_device_attr_write_raw)(const struct iio_device *dev, const char *attr, const void *src, size_t len);
37 |
38 | struct iio_scan_context *(*iio_create_scan_context)(const char *backend, unsigned int flags);
39 |
40 | ssize_t (*iio_scan_context_get_info_list)(struct iio_scan_context *ctx, struct iio_context_info ***info);
41 |
42 | void (*iio_scan_context_destroy)(struct iio_scan_context *ctx);
43 |
44 | const char *(*iio_context_info_get_uri)(const struct iio_context_info *info);
45 |
46 | struct iio_context *(*iio_create_context_from_uri)(const char *uri);
47 |
48 | void (*iio_context_info_list_free)(struct iio_context_info **info);
49 |
50 | int (*iio_context_set_timeout)(struct iio_context *ctx, unsigned int timeout_ms);
51 |
52 | void (*iio_channel_enable)(struct iio_channel *chn);
53 |
54 | struct iio_buffer *(*iio_device_create_buffer)(const struct iio_device *dev, size_t samples_count, bool cyclic);
55 |
56 | void (*iio_buffer_destroy)(struct iio_buffer *buf);
57 |
58 | void (*iio_channel_disable)(struct iio_channel *chn);
59 |
60 | void (*iio_context_destroy)(struct iio_context *ctx);
61 |
62 | };
63 |
64 | int iio_lib_create(iio_lib **lib);
65 | void iio_lib_destroy(iio_lib *lib);
66 |
67 | #endif //SDR_MODEM_IIO_LIB_H
68 |
--------------------------------------------------------------------------------
/test/test_lpf_taps.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "../src/dsp/lpf_taps.h"
4 |
5 | float *taps = NULL;
6 |
7 | START_TEST (test_bounds1) {
8 | size_t len;
9 | int code = create_low_pass_filter(1.0F, 0, 1750, 500, &taps, &len);
10 | ck_assert_int_eq(code, -1);
11 | }
12 | END_TEST
13 |
14 | START_TEST (test_bounds2) {
15 | size_t len;
16 | int code = create_low_pass_filter(1.0F, 8000, 5000, 500, &taps, &len);
17 | ck_assert_int_eq(code, -1);
18 | }
19 | END_TEST
20 |
21 | START_TEST (test_bounds3) {
22 | size_t len;
23 | int code = create_low_pass_filter(1.0F, 8000, 1750, 0, &taps, &len);
24 | ck_assert_int_eq(code, -1);
25 | }
26 | END_TEST
27 |
28 | START_TEST (test_lowpassTaps) {
29 | size_t len;
30 | int code = create_low_pass_filter(1.0F, 8000, 1750, 500, &taps, &len);
31 | ck_assert_int_eq(code, 0);
32 |
33 | const float expected_taps[] = { 0.00111410965f, -0.000583702058f, -0.00192639488f, 2.30933896e-18f, 0.00368289859f, 0.00198723329f, -0.0058701504f, -0.00666110823f, 0.0068643163f, 0.0147596458f, -0.00398709066f, -0.0259727165f, -0.0064281947f, 0.0387893915f, 0.0301109217f, -0.0507995859f, -0.0833103433f, 0.0593735874f, 0.310160041f, 0.437394291f, 0.310160041f, 0.0593735874f, -0.0833103433f, -0.0507995859f, 0.0301109217f, 0.0387893915f, -0.0064281947f, -0.0259727165f, -0.00398709066f, 0.0147596458f, 0.0068643163f, -0.00666110823f, -0.0058701504f, 0.00198723329f,
34 | 0.00368289859f, 2.30933896e-18f, -0.00192639488f, -0.000583702058f, 0.00111410965f };
35 |
36 | ck_assert_uint_eq(len, 39);
37 | for (int i = 0; i < len; i++) {
38 | ck_assert_int_eq((int32_t) (expected_taps[i] * 10000), (int32_t) (taps[i] * 10000));
39 | }
40 | }
41 | END_TEST
42 |
43 | void teardown() {
44 | if (taps != NULL) {
45 | free(taps);
46 | taps = NULL;
47 | }
48 | }
49 |
50 | void setup() {
51 | //do nothing
52 | }
53 |
54 | Suite* common_suite(void) {
55 | Suite *s;
56 | TCase *tc_core;
57 |
58 | s = suite_create("lpf_taps");
59 |
60 | /* Core test case */
61 | tc_core = tcase_create("Core");
62 |
63 | tcase_add_test(tc_core, test_lowpassTaps);
64 | tcase_add_test(tc_core, test_bounds1);
65 | tcase_add_test(tc_core, test_bounds2);
66 | tcase_add_test(tc_core, test_bounds3);
67 |
68 | tcase_add_checked_fixture(tc_core, setup, teardown);
69 | suite_add_tcase(s, tc_core);
70 |
71 | return s;
72 | }
73 |
74 | int main(void) {
75 | int number_failed;
76 | Suite *s;
77 | SRunner *sr;
78 |
79 | s = common_suite();
80 | sr = srunner_create(s);
81 |
82 | srunner_set_fork_status(sr, CK_NOFORK);
83 | srunner_run_all(sr, CK_NORMAL);
84 | number_failed = srunner_ntests_failed(sr);
85 | srunner_free(sr);
86 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
87 | }
88 |
89 |
--------------------------------------------------------------------------------
/src/sgpsdp/TR/test-001-01.res:
--------------------------------------------------------------------------------
1 | TEST DATA:
2 | TEST SAT SGP 001
3 | 1 88888U 80275.98708465 .00073094 13844-3 66816-4 0 9
4 | 2 88888 72.8435 115.9689 0086731 52.6988 110.5714 16.05824518 103
5 |
6 | DEEP_SPACE_EPHEM: 0 (expected 0)
7 |
8 | RESULT EXPECTED DELTA
9 | ------------------------------------------------------------------------------
10 | STEP 1 t: 0.0 X: 2328.97068761 2328.97048951 0.00019810 (0.00001%)
11 | Y: -5995.22085643 -5995.22076416 0.00009227 (0.00000%)
12 | Z: 1719.97068075 1719.97067261 0.00000814 (0.00000%)
13 | VX: 2.91207226 2.91207230 0.00000004 (0.00000%)
14 | VY: -0.98341533 -0.98341546 0.00000013 (0.00001%)
15 | VZ: -7.09081695 -7.09081703 0.00000008 (0.00000%)
16 | STEP 2 t: 360.0 X: 2456.10753857 2456.10705566 0.00048291 (0.00002%)
17 | Y: -6071.93865906 -6071.93853760 0.00012146 (0.00000%)
18 | Z: 1222.89643564 1222.89727783 0.00084219 (0.00007%)
19 | VX: 2.67938947 2.67938992 0.00000045 (0.00002%)
20 | VY: -0.44828939 -0.44829041 0.00000102 (0.00023%)
21 | VZ: -7.22879242 -7.22879231 0.00000011 (0.00000%)
22 | STEP 3 t: 720.0 X: 2567.56230055 2567.56195068 0.00034987 (0.00001%)
23 | Y: -6112.50386789 -6112.50384522 0.00002267 (0.00000%)
24 | Z: 713.96381249 713.96397400 0.00016151 (0.00002%)
25 | VX: 2.44024579 2.44024599 0.00000020 (0.00001%)
26 | VY: 0.09810893 0.09810869 0.00000024 (0.00025%)
27 | VZ: -7.31995922 -7.31995916 0.00000006 (0.00000%)
28 | STEP 4 t: 1080.0 X: 2663.08919967 2663.09078980 0.00159013 (0.00006%)
29 | Y: -6115.48308263 -6115.48229980 0.00078283 (0.00001%)
30 | Z: 196.40236060 196.39640427 0.00595633 (0.00303%)
31 | VX: 2.19612236 2.19611958 0.00000278 (0.00013%)
32 | VY: 0.65241327 0.65241995 0.00000668 (0.00102%)
33 | VZ: -7.36282406 -7.36282432 0.00000026 (0.00000%)
34 | STEP 5 t: 1440.0 X: 2742.55314743 2742.55133057 0.00181686 (0.00007%)
35 | Y: -6079.67068185 -6079.67144775 0.00076590 (0.00001%)
36 | Z: -326.38672720 -326.38095856 0.00576864 (0.00177%)
37 | VX: 1.94849935 1.94850229 0.00000294 (0.00015%)
38 | VY: 1.21106891 1.21106251 0.00000640 (0.00053%)
39 | VZ: -7.35619329 -7.35619372 0.00000043 (0.00001%)
40 |
--------------------------------------------------------------------------------
/src/sgpsdp/TR/test-002-01.res:
--------------------------------------------------------------------------------
1 | TEST DATA:
2 | TEST SAT SDP 001
3 | 1 11801U 80230.29629788 .01431103 00000-0 14311-1 0 2
4 | 2 11801 46.7916 230.4354 7318036 47.4722 10.4117 2.28537848 2
5 |
6 | DEEP_SPACE_EPHEM: 64 (expected 64)
7 |
8 | RESULT EXPECTED DELTA
9 | ---------------------------------------------------------------------------------
10 | STEP 1 t: 0.0 X: 7473.37235249 7473.37066650 0.00168599 (0.00002%)
11 | Y: 428.95458268 428.95261765 0.00196503 (0.00046%)
12 | Z: 5828.74803892 5828.74786377 0.00017515 (0.00000%)
13 | VX: 5.10715285 5.10715130 0.00000155 (0.00003%)
14 | VY: 6.44468277 6.44468284 0.00000007 (0.00000%)
15 | VZ: -0.18613180 -0.18613096 0.00000084 (0.00045%)
16 | STEP 2 t: 360.0 X: -3305.22249435 -3305.22537232 0.00287797 (0.00009%)
17 | Y: 32410.86724220 32410.86328125 0.00396095 (0.00001%)
18 | Z: -24697.17847749 -24697.17675781 0.00171968 (0.00001%)
19 | VX: -1.30113544 -1.30113538 0.00000006 (0.00000%)
20 | VY: -1.15131484 -1.15131518 0.00000034 (0.00003%)
21 | VZ: -0.28333545 -0.28333528 0.00000017 (0.00006%)
22 | STEP 3 t: 720.0 X: 14271.28902792 14271.28759766 0.00143026 (0.00001%)
23 | Y: 24110.45647174 24110.46411133 0.00763959 (0.00003%)
24 | Z: -4725.76149170 -4725.76837158 0.00687988 (0.00015%)
25 | VX: -0.32050356 -0.32050445 0.00000089 (0.00028%)
26 | VY: 2.67984224 2.67984074 0.00000150 (0.00006%)
27 | VZ: -2.08405317 -2.08405289 0.00000028 (0.00001%)
28 | STEP 4 t: 1080.0 X: -9990.05125819 -9990.05883789 0.00757970 (0.00008%)
29 | Y: 22717.38011629 22717.35522461 0.02489168 (0.00011%)
30 | Z: -23616.90130945 -23616.89066250 0.01064695 (0.00005%)
31 | VX: -1.01667324 -1.01667246 0.00000078 (0.00008%)
32 | VY: -2.29026532 -2.29026759 0.00000227 (0.00010%)
33 | VZ: 0.72892148 0.72892364 0.00000216 (0.00030%)
34 | STEP 5 t: 1440.0 X: 9787.88496660 9787.86975097 0.01521563 (0.00016%)
35 | Y: 33753.34020891 33753.34667969 0.00647078 (0.00002%)
36 | Z: -15030.79330940 -15030.81176758 0.01845818 (0.00012%)
37 | VX: -1.09424947 -1.09425966 0.00001019 (0.00093%)
38 | VY: 0.92359201 0.92358845 0.00000356 (0.00039%)
39 | VZ: -1.52231073 -1.52230928 0.00000145 (0.00010%)
40 |
--------------------------------------------------------------------------------
/src/dsp/sig_source.c:
--------------------------------------------------------------------------------
1 | #include "sig_source.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #ifndef M_PI
8 | #define M_PI 3.14159265358979323846
9 | #endif
10 |
11 | #define M_2PI ((float) (2 * M_PI))
12 |
13 | struct sig_source_t {
14 | float phase;
15 | float amplitude;
16 |
17 | float complex *output;
18 | uint32_t output_len;
19 | uint64_t rx_sampling_freq;
20 | };
21 |
22 | int sig_source_create(float amplitude, uint64_t rx_sampling_freq, uint32_t max_output_buffer_length, sig_source **source) {
23 | struct sig_source_t *result = malloc(sizeof(struct sig_source_t));
24 | if (result == NULL) {
25 | return -ENOMEM;
26 | }
27 | // init all fields with 0 so that destroy_* method would work
28 | *result = (struct sig_source_t) {0};
29 | result->phase = 0.0F;
30 | result->amplitude = amplitude;
31 | result->rx_sampling_freq = rx_sampling_freq;
32 | result->output_len = max_output_buffer_length;
33 | result->output = malloc(sizeof(float complex) * result->output_len);
34 | if (result->output == NULL) {
35 | sig_source_destroy(result);
36 | return -ENOMEM;
37 | }
38 |
39 | *source = result;
40 | return 0;
41 | }
42 |
43 | void sig_source_process(int64_t freq, size_t expected_output_len, float complex **output, size_t *output_len, sig_source *source) {
44 | float adjusted_freq = M_2PI * (float) freq / source->rx_sampling_freq;
45 | for (size_t i = 0; i < expected_output_len; i++) {
46 | source->output[i] = cos(source->phase) * source->amplitude + sin(source->phase) * source->amplitude * I;
47 | source->phase += adjusted_freq;
48 | if (source->phase < -M_2PI) {
49 | source->phase += M_2PI;
50 | }
51 | if (source->phase > M_2PI) {
52 | source->phase -= M_2PI;
53 | }
54 | }
55 |
56 | *output = source->output;
57 | *output_len = expected_output_len;
58 | }
59 |
60 | void sig_source_multiply(int64_t freq, const float complex *input, size_t input_len, float complex **output, size_t *output_len, sig_source *source) {
61 | if (input_len > source->output_len) {
62 | fprintf(stderr, "<3>requested buffer %zu is more than max: %u\n", input_len, source->output_len);
63 | *output = NULL;
64 | *output_len = 0;
65 | return;
66 | }
67 | float complex *sig_output = NULL;
68 | size_t sig_output_len = 0;
69 | sig_source_process(freq, input_len, &sig_output, &sig_output_len, source);
70 |
71 | volk_32fc_x2_multiply_32fc(source->output, (const lv_32fc_t *) input, (const lv_32fc_t *) sig_output, (unsigned int) input_len);
72 |
73 | *output = source->output;
74 | *output_len = input_len;
75 | }
76 |
77 | void sig_source_destroy(sig_source *source) {
78 | if (source == NULL) {
79 | return;
80 | }
81 | if (source->output != NULL) {
82 | free(source->output);
83 | }
84 | free(source);
85 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## sdr-modem
2 |
3 | [](https://github.com/dernasherbrezon/sdr-modem/actions/workflows/cmake.yml) [](https://sonarcloud.io/dashboard?id=dernasherbrezon_sdr-modem)
4 |
5 | Modem based on software defined radios.
6 |
7 | ## Design
8 |
9 | 
10 |
11 | ## Features
12 |
13 | * TCP-based
14 | * Custom [binary protocol](https://github.com/dernasherbrezon/sdr-modem/blob/main/api.proto) based on protobuf messages.
15 | * Supported modulation/demodulation:
16 | * GMSK
17 | * Supported SDRs:
18 | * [sdr-server](https://github.com/dernasherbrezon/sdr-server)
19 | * [plutosdr](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/adalm-pluto.html)
20 | * file
21 | * Misc:
22 | * Doppler's correction for satellites using SGP4 model
23 | * Save intermittent data onto disk for future analysis/replay
24 |
25 | ## API
26 |
27 | * Defined in the [api.h](https://github.com/dernasherbrezon/sdr-modem/blob/main/src/api.h)
28 | * And in the [api.proto](https://github.com/dernasherbrezon/sdr-modem/blob/main/api.proto)
29 |
30 |
31 | ## Configuration
32 |
33 | Sample configuration with reasonable defaults:
34 |
35 | [https://github.com/dernasherbrezon/sdr-modem/blob/main/src/resources/config.conf](https://github.com/dernasherbrezon/sdr-modem/blob/main/src/resources/config.conf)
36 |
37 | ## Dependencies
38 |
39 | sdr-modem depends on several libraries:
40 |
41 | * [libvolk](https://www.libvolk.org). It is recommended to use the latest version (Currently it is 2.x). After libvolk [installed or built](https://github.com/gnuradio/volk#building-on-most-x86-32-bit-and-64-bit-platforms), it needs to detect optimal kernels. Run the command ```volk_profile``` to generate and save profile.
42 | * [libconfig](https://hyperrealm.github.io/libconfig/libconfig_manual.html)
43 | * [libprotobuf-c](https://github.com/protobuf-c/protobuf-c)
44 | * libz. Should be installed in every operational system
45 | * libm. Same
46 | * [libiio](https://github.com/analogdevicesinc/libiio) for plutosdr SDR (Optional)
47 | * [libcheck](https://libcheck.github.io/check/) for tests (Optional)
48 |
49 | All dependencies can be easily installed from [r2cloud APT repository](https://r2server.ru/apt.html):
50 |
51 | ```
52 | sudo apt-get install curl lsb-release
53 | curl -fsSL https://leosatdata.com/r2cloud.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/r2cloud.gpg
54 | sudo bash -c "echo \"deb [signed-by=/usr/share/keyrings/r2cloud.gpg] http://apt.leosatdata.com $(lsb_release --codename --short) main\" > /etc/apt/sources.list.d/r2cloud.list"
55 | sudo bash -c "echo \"deb [signed-by=/usr/share/keyrings/r2cloud.gpg] http://apt.leosatdata.com/cpu-generic $(lsb_release --codename --short) main\" > /etc/apt/sources.list.d/r2cloud-generic.list"
56 | sudo apt-get update
57 | sudo apt-get install libvolk2-dev libprotobuf-c-dev libconfig-dev check libiio
58 | ```
59 |
60 | ## Build
61 |
62 | ```
63 | mkdir build
64 | cd build
65 | cmake ..
66 | make
67 | ```
68 |
--------------------------------------------------------------------------------
/src/dsp/lpf_taps.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #ifndef M_PI
8 | #define M_PI 3.14159265358979323846
9 | #endif
10 |
11 | #include "lpf_taps.h"
12 | #include
13 |
14 | int sanity_check_1f(uint64_t sampling_freq, uint64_t cutoff_freq, uint32_t transition_width) {
15 | if (sampling_freq <= 0) {
16 | fprintf(stderr, "<3>sampling frequency should be positive\n");
17 | return -1;
18 | }
19 |
20 | if (cutoff_freq <= 0 || (double) cutoff_freq > (double) sampling_freq / 2) {
21 | fprintf(stderr, "<3>cutoff frequency should be positive and less than sampling freq / 2. got: %" PRIu64 "\n", cutoff_freq);
22 | return -1;
23 | }
24 |
25 | if (transition_width <= 0) {
26 | fprintf(stderr, "<3>transition width should be positive\n");
27 | return -1;
28 | }
29 |
30 | return 0;
31 | }
32 |
33 | int computeNtaps(uint64_t sampling_freq, uint64_t transition_width) {
34 | double a = 53;
35 | int ntaps = (int) (a * (double) sampling_freq / (22.0 * (double) transition_width));
36 | if ((ntaps & 1) == 0) { // if even...
37 | ntaps++; // ...make odd
38 | }
39 | return ntaps;
40 | }
41 |
42 | int create_hamming_window(int ntaps, float **output) {
43 | float *result = malloc(sizeof(float) * ntaps);
44 | if (result == NULL) {
45 | return -ENOMEM;
46 | }
47 | int m = ntaps - 1;
48 | for (int n = 0; n < ntaps; n++) {
49 | result[n] = (float) (0.54 - 0.46 * cos((2 * M_PI * n) / m));
50 | }
51 | *output = result;
52 | return 0;
53 | }
54 |
55 | int create_low_pass_filter(float gain, uint64_t sampling_freq, uint64_t cutoff_freq, uint32_t transition_width, float **output_taps, size_t *len) {
56 | int code = sanity_check_1f(sampling_freq, cutoff_freq, transition_width);
57 | if (code != 0) {
58 | return code;
59 | }
60 |
61 | int ntaps = computeNtaps(sampling_freq, transition_width);
62 | float *taps = malloc(sizeof(float) * ntaps);
63 | if (taps == NULL) {
64 | return -ENOMEM;
65 | }
66 | memset(taps, 0, sizeof(float) * ntaps);
67 |
68 | float *w = NULL;
69 | code = create_hamming_window(ntaps, &w);
70 | if (code != 0) {
71 | free(taps);
72 | return code;
73 | }
74 |
75 | int M = (ntaps - 1) / 2;
76 | double fwT0 = 2 * M_PI * (double) cutoff_freq / (double) sampling_freq;
77 |
78 | for (int n = -M; n <= M; n++) {
79 | if (n == 0) {
80 | taps[n + M] = (float) (fwT0 / M_PI * w[n + M]);
81 | } else {
82 | // a little algebra gets this into the more familiar sin(x)/x form
83 | taps[n + M] = (float) (sin((double) n * fwT0) / (n * M_PI) * w[n + M]);
84 | }
85 | }
86 |
87 | free(w);
88 |
89 | float fmax = taps[0 + M];
90 | for (int n = 1; n <= M; n++) {
91 | fmax += 2 * taps[n + M];
92 | }
93 |
94 | gain /= fmax; // normalize
95 |
96 | for (int i = 0; i < ntaps; i++) {
97 | taps[i] *= gain;
98 | }
99 |
100 | *output_taps = taps;
101 | *len = ntaps;
102 | return 0;
103 | }
104 |
105 |
--------------------------------------------------------------------------------
/src/dsp/quadrature_demod.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "quadrature_demod.h"
7 | #include "../math/fast_atan2f.h"
8 |
9 | struct quadrature_demod_t {
10 | float gain;
11 |
12 | float complex *temp_buffer;
13 | size_t temp_buffer_len;
14 |
15 | float complex *working_buffer;
16 | size_t history_offset;
17 | size_t working_buffer_len;
18 |
19 | float *output;
20 | size_t output_len;
21 | };
22 |
23 | int quadrature_demod_create(float gain, uint32_t max_input_buffer_length, quadrature_demod **demod) {
24 | struct quadrature_demod_t *result = malloc(sizeof(struct quadrature_demod_t));
25 | if (result == NULL) {
26 | return -ENOMEM;
27 | }
28 | // init all fields with 0 so that destroy_* method would work
29 | *result = (struct quadrature_demod_t) {0};
30 | result->gain = gain;
31 | result->output_len = max_input_buffer_length;
32 | result->output = malloc(sizeof(float) * result->output_len);
33 | if (result->output == NULL) {
34 | quadrature_demod_destroy(result);
35 | return -ENOMEM;
36 | }
37 | result->history_offset = 1;
38 | result->working_buffer_len = result->output_len + result->history_offset;
39 | result->working_buffer = malloc(sizeof(float complex) * result->working_buffer_len);
40 | if (result->working_buffer == NULL) {
41 | quadrature_demod_destroy(result);
42 | return -ENOMEM;
43 | }
44 | memset(result->working_buffer, 0, result->working_buffer_len * sizeof(float complex));
45 |
46 | result->temp_buffer_len = max_input_buffer_length;
47 | result->temp_buffer = malloc(sizeof(float complex) * result->temp_buffer_len);
48 | if (result->temp_buffer == NULL) {
49 | quadrature_demod_destroy(result);
50 | return -ENOMEM;
51 | }
52 |
53 | *demod = result;
54 | return 0;
55 | }
56 |
57 | void quadrature_demod_process(float complex *input, size_t input_len, float **output, size_t *output_len, quadrature_demod *demod) {
58 | if (input_len > demod->output_len) {
59 | fprintf(stderr, "<3>requested buffer %zu is more than max: %zu\n", input_len, demod->output_len);
60 | *output = NULL;
61 | *output_len = 0;
62 | return;
63 | }
64 | memcpy(demod->working_buffer + demod->history_offset, input, sizeof(float complex) * input_len);
65 | volk_32fc_x2_multiply_conjugate_32fc(demod->temp_buffer, &demod->working_buffer[1], &demod->working_buffer[0], input_len);
66 | for (int i = 0; i < input_len; i++) {
67 | demod->output[i] = demod->gain * fast_atan2f(cimagf(demod->temp_buffer[i]), crealf(demod->temp_buffer[i]));
68 | }
69 | memmove(demod->working_buffer, demod->working_buffer + input_len, sizeof(float complex) * demod->history_offset);
70 |
71 | *output = demod->output;
72 | *output_len = input_len;
73 | }
74 |
75 | void quadrature_demod_destroy(quadrature_demod *demod) {
76 | if (demod == NULL) {
77 | return;
78 | }
79 | if (demod->output != NULL) {
80 | free(demod->output);
81 | }
82 | if (demod->working_buffer != NULL) {
83 | free(demod->working_buffer);
84 | }
85 | if (demod->temp_buffer != NULL) {
86 | free(demod->temp_buffer);
87 | }
88 | free(demod);
89 | }
90 |
--------------------------------------------------------------------------------
/test/perf_fsk_modem.c:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 | #include
3 | #include "../src/dsp/fsk_demod.h"
4 | #include "../src/dsp/gfsk_mod.h"
5 |
6 | #ifndef M_PI
7 | #define M_PI 3.14159265358979323846
8 | #endif
9 |
10 | void perf_fsk_demod();
11 |
12 | void perf_gfsk_mod();
13 |
14 | int main(void) {
15 |
16 | perf_fsk_demod();
17 | perf_gfsk_mod();
18 |
19 | return EXIT_SUCCESS;
20 | }
21 |
22 | void perf_gfsk_mod() {
23 | float sample_rate = 19200;
24 | float baud_rate = 9600;
25 | float deviation = 5000;
26 | float samples_per_symbol = sample_rate / baud_rate;
27 | gfsk_mod *mod = NULL;
28 | int code = gfsk_mod_create(samples_per_symbol, (float) (2 * M_PI * deviation / sample_rate), 0.5F, 2016000, &mod);
29 | if (code != 0) {
30 | return;
31 | }
32 | size_t input_len = 2048;
33 | uint8_t *input = malloc(sizeof(uint8_t) * input_len);
34 | if (input == NULL) {
35 | return;
36 | }
37 | for (size_t i = 0; i < input_len; i++) {
38 | input[i] = (uint8_t) i;
39 | }
40 |
41 | double totalTime = 0.0;
42 | int total = 10;
43 | for (int j = 0; j < total; j++) {
44 | int total_executions = 100;
45 | clock_t begin = clock();
46 | for (int i = 0; i < total_executions; i++) {
47 | complex float *output = NULL;
48 | size_t output_len = 0;
49 | gfsk_mod_process(input, input_len, &output, &output_len, mod);
50 | }
51 | clock_t end = clock();
52 | double time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
53 | totalTime += time_spent;
54 | }
55 | // MacBook Air M1
56 | // VOLK_GENERIC=1:
57 | // completed in: 0.054459 seconds
58 | // tuned kernel:
59 | // completed in: 0.044478 seconds
60 |
61 | // Raspberry pi 3 mod B
62 | // VOLK_GENERIC=1:
63 | // completed in: 9.711632 seconds
64 | // tuned kernel:
65 | // completed in: 0.851478 seconds
66 |
67 | printf("gfsk mod completed (average): %f seconds\n", (totalTime / total));
68 | }
69 |
70 | void perf_fsk_demod() {
71 | fsk_demod *demod = NULL;
72 | int code = fsk_demod_create(48000, 4800, 5000, 2, 2000, true, 2016000, &demod);
73 | if (code != 0) {
74 | return;
75 | }
76 | size_t input_len = 4096;
77 | float complex *input = malloc(sizeof(float complex) * input_len);
78 | if (input == NULL) {
79 | return;
80 | }
81 | for (size_t i = 0; i < input_len; i++) {
82 | input[i] = (uint8_t) i;
83 | }
84 |
85 | double totalTime = 0.0;
86 | int total = 10;
87 | for (int j = 0; j < total; j++) {
88 | int total_executions = 100;
89 | clock_t begin = clock();
90 | for (int i = 0; i < total_executions; i++) {
91 | int8_t *output = NULL;
92 | size_t output_len = 0;
93 | fsk_demod_process(input, input_len, &output, &output_len, demod);
94 | }
95 | clock_t end = clock();
96 | double time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
97 | totalTime += time_spent;
98 | }
99 |
100 | // MacBook Air M1
101 | // VOLK_GENERIC=1:
102 | // completed in: 0.037171 seconds
103 | // tuned kernel:
104 | // completed in: 0.036825 seconds
105 |
106 | // Raspberry pi 3 mod B
107 | // VOLK_GENERIC=1:
108 | // completed in: 0.640384 seconds
109 | // tuned kernel:
110 | // completed in: 0.655575 seconds
111 |
112 | printf("fsk demod completed (average): %f seconds\n", (totalTime / total));
113 | }
114 |
--------------------------------------------------------------------------------
/test/test_dc_blocker.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "../src/dsp/dc_blocker.h"
5 | #include "utils.h"
6 |
7 | dc_blocker *dcblocker = NULL;
8 | float *float_input = NULL;
9 |
10 | START_TEST (test_normal) {
11 | int code = dc_blocker_create(32, &dcblocker);
12 | ck_assert_int_eq(code, 0);
13 |
14 | setup_input_data(&float_input, 0, 200);
15 | float *output = NULL;
16 | size_t output_len = 0;
17 | dc_blocker_process(float_input, 200, &output, &output_len, dcblocker);
18 |
19 | const float expected[] = {0.000000F,-0.000001F,-0.000006F,-0.000020F,-0.000053F,-0.000120F,-0.000240F,-0.000441F,-0.000755F,-0.001227F,-0.001909F,-0.002864F,-0.004166F,-0.005901F,-0.008171F,-0.011089F,-0.014786F,-0.019406F,-0.025114F,-0.032090F,-0.040535F,-0.050669F,-0.062733F,-0.076990F,-0.093727F,-0.113254F,-0.135904F,-0.162040F,-0.192047F,-0.226341F,-0.265366F,-0.309593F,-0.359528F,-0.415700F,-0.478666F,-0.549005F,-0.627312F,-0.714201F,-0.810299F,-0.916243F,-1.032677F,-1.160251F,-1.299616F,-1.451423F,-1.616318F,-1.794941F,-1.987923F,-2.195881F,-2.419418F,-2.659120F,-2.915548F,-3.189244F,-3.480721F,-3.790461F,-4.118916F,-4.466501F,-4.833595F,-5.220534F,-5.627611F,-6.055072F,-6.503113F,-6.971878F,-7.461456F,-6.971878F,-6.503113F,-6.055072F,-5.627611F,-5.220534F,-4.833595F,-4.466501F,-4.118916F,-3.790461F,-3.480721F,-3.189244F,-2.915548F,-2.659120F,-2.419418F,-2.195881F,-1.987923F,-1.794941F,-1.616318F,-1.451424F,-1.299618F,-1.160252F,-1.032677F,-0.916243F,-0.810299F,-0.714201F,-0.627312F,-0.549004F,-0.478664F,-0.415699F,-0.359528F,-0.309593F,-0.265366F,-0.226341F,-0.192047F,-0.162041F,-0.135906F,-0.113255F,-0.093727F,-0.076988F,-0.062729F,-0.050667F,-0.040535F,-0.032089F,-0.025112F,-0.019405F,-0.014786F,-0.011089F,-0.008171F,-0.005901F,-0.004166F,-0.002865F,-0.001911F,-0.001228F,-0.000755F,-0.000443F,-0.000244F,-0.000122F,-0.000053F,-0.000019F,-0.000004F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F,0.000000F};
20 | assert_float_array(expected, sizeof(expected) / sizeof(float), output, output_len);
21 | }
22 | END_TEST
23 |
24 | void teardown() {
25 | if (dcblocker != NULL) {
26 | dc_blocker_destroy(dcblocker);
27 | dcblocker = NULL;
28 | }
29 | if (float_input != NULL) {
30 | free(float_input);
31 | float_input = NULL;
32 | }
33 | }
34 |
35 | void setup() {
36 | //do nothing
37 | }
38 |
39 | Suite *common_suite(void) {
40 | Suite *s;
41 | TCase *tc_core;
42 |
43 | s = suite_create("quadrature_demod");
44 |
45 | /* Core test case */
46 | tc_core = tcase_create("Core");
47 |
48 | tcase_add_test(tc_core, test_normal);
49 |
50 | tcase_add_checked_fixture(tc_core, setup, teardown);
51 | suite_add_tcase(s, tc_core);
52 |
53 | return s;
54 | }
55 |
56 | int main(void) {
57 | int number_failed;
58 | Suite *s;
59 | SRunner *sr;
60 |
61 | s = common_suite();
62 | sr = srunner_create(s);
63 |
64 | srunner_set_fork_status(sr, CK_NOFORK);
65 | srunner_run_all(sr, CK_NORMAL);
66 | number_failed = srunner_ntests_failed(sr);
67 | srunner_free(sr);
68 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
69 | }
70 |
--------------------------------------------------------------------------------
/test/test_server_config.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "../src/server_config.h"
5 |
6 | struct server_config *config = NULL;
7 |
8 | START_TEST (test_missing_file) {
9 | int code = server_config_create(&config, "non-existing-configuration-file.conf");
10 | ck_assert_int_eq(code, -1);
11 | }
12 |
13 | END_TEST
14 |
15 | START_TEST (test_invalid_format) {
16 | int code = server_config_create(&config, "invalid.format.conf");
17 | ck_assert_int_eq(code, -1);
18 | }
19 |
20 | END_TEST
21 |
22 | START_TEST (test_invalid_timeout) {
23 | int code = server_config_create(&config, "invalid.timeout.conf");
24 | ck_assert_int_eq(code, -1);
25 | }
26 |
27 | END_TEST
28 |
29 | START_TEST (test_unknown_tx_sdr_type) {
30 | int code = server_config_create(&config, "invalid.tx_sdr_type.conf");
31 | ck_assert_int_eq(code, -1);
32 | }
33 | END_TEST
34 |
35 | START_TEST (test_unknown_rx_sdr_type) {
36 | int code = server_config_create(&config, "invalid.rx_sdr_type.conf");
37 | ck_assert_int_eq(code, -1);
38 | }
39 | END_TEST
40 |
41 | START_TEST (test_minimal_config) {
42 | int code = server_config_create(&config, "minimal.conf");
43 | ck_assert_int_eq(code, 0);
44 | }
45 |
46 | END_TEST
47 |
48 | START_TEST (test_pluto_enabled) {
49 | int code = server_config_create(&config, "pluto_enabled.conf");
50 | ck_assert_int_eq(code, 0);
51 | ck_assert_int_eq(config->tx_sdr_type, TX_SDR_TYPE_PLUTOSDR);
52 | ck_assert(config->iio != NULL);
53 | ck_assert(fabsl(10.0 - config->tx_plutosdr_gain) < 0.001);
54 | ck_assert_int_eq(config->tx_plutosdr_timeout_millis, 20000);
55 | }
56 | END_TEST
57 |
58 | START_TEST (test_success) {
59 | int code = server_config_create(&config, "full.conf");
60 | ck_assert_int_eq(code, 0);
61 | ck_assert_str_eq(config->bind_address, "127.0.0.1");
62 | ck_assert_int_eq(config->port, 8091);
63 | ck_assert_int_eq(config->read_timeout_seconds, 10);
64 | ck_assert_int_eq(config->buffer_size, 2048);
65 | ck_assert_int_eq(config->rx_sdr_type, RX_SDR_TYPE_SDR_SERVER);
66 | ck_assert_str_eq(config->base_path, "/tmp/");
67 | ck_assert_int_eq(config->queue_size, 64);
68 | ck_assert_int_eq(config->tx_sdr_type, TX_SDR_TYPE_NONE);
69 | ck_assert(config->iio == NULL);
70 | ck_assert(fabsl(0.0 - config->tx_plutosdr_gain) < 0.001);
71 | ck_assert_int_eq(config->tx_plutosdr_timeout_millis, 10000);
72 | }
73 |
74 | END_TEST
75 |
76 | void teardown() {
77 | server_config_destroy(config);
78 | config = NULL;
79 | }
80 |
81 | void setup() {
82 | //do nothing
83 | }
84 |
85 | Suite *common_suite(void) {
86 | Suite *s;
87 | TCase *tc_core;
88 |
89 | s = suite_create("server_config");
90 |
91 | /* Core test case */
92 | tc_core = tcase_create("Core");
93 |
94 | tcase_add_test(tc_core, test_success);
95 | tcase_add_test(tc_core, test_minimal_config);
96 | tcase_add_test(tc_core, test_invalid_timeout);
97 | tcase_add_test(tc_core, test_invalid_format);
98 | tcase_add_test(tc_core, test_missing_file);
99 | tcase_add_test(tc_core, test_unknown_tx_sdr_type);
100 | tcase_add_test(tc_core, test_unknown_rx_sdr_type);
101 | tcase_add_test(tc_core, test_pluto_enabled);
102 |
103 | tcase_add_checked_fixture(tc_core, setup, teardown);
104 | suite_add_tcase(s, tc_core);
105 |
106 | return s;
107 | }
108 |
109 | int main(void) {
110 | int number_failed;
111 | Suite *s;
112 | SRunner *sr;
113 |
114 | s = common_suite();
115 | sr = srunner_create(s);
116 |
117 | srunner_set_fork_status(sr, CK_NOFORK);
118 | srunner_run_all(sr, CK_NORMAL);
119 | number_failed = srunner_ntests_failed(sr);
120 | srunner_free(sr);
121 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
122 | }
123 |
--------------------------------------------------------------------------------
/src/api_utils.c:
--------------------------------------------------------------------------------
1 | #include "api_utils.h"
2 | #include "tcp_utils.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | uint32_t MAX_MESSAGE_LENGTH = 32 * 1024; // kilobyte
9 |
10 | int api_utils_read_header(int socket, struct message_header *header) {
11 | int code = tcp_utils_read_data(header, sizeof(struct message_header), socket);
12 | if (code == 0) {
13 | header->message_length = ntohl(header->message_length);
14 | }
15 | return code;
16 | }
17 |
18 | int api_utils_read_tx_data(int socket, const struct message_header *header, struct TxData **request) {
19 | if (header->message_length > MAX_MESSAGE_LENGTH) {
20 | return -1;
21 | }
22 | uint8_t *buffer = malloc(sizeof(uint8_t) * header->message_length);
23 | if (buffer == NULL) {
24 | return -ENOMEM;
25 | }
26 | int code = tcp_utils_read_data(buffer, header->message_length, socket);
27 | if (code != 0) {
28 | free(buffer);
29 | return -1;
30 | }
31 | TxData *result = tx_data__unpack(NULL, header->message_length, buffer);
32 | free(buffer);
33 | if (result == NULL) {
34 | return -1;
35 | }
36 | *request = result;
37 | return 0;
38 | }
39 |
40 | int api_utils_read_tx_request(int socket, const struct message_header *header, struct TxRequest **request) {
41 | if (header->message_length > MAX_MESSAGE_LENGTH) {
42 | return -1;
43 | }
44 | uint8_t *buffer = malloc(sizeof(uint8_t) * header->message_length);
45 | if (buffer == NULL) {
46 | return -ENOMEM;
47 | }
48 | int code = tcp_utils_read_data(buffer, header->message_length, socket);
49 | if (code != 0) {
50 | return -1;
51 | }
52 | TxRequest *result = tx_request__unpack(NULL, header->message_length, buffer);
53 | free(buffer);
54 | if (result == NULL) {
55 | return -1;
56 | }
57 | *request = result;
58 | return 0;
59 | }
60 |
61 | int api_utils_read_rx_request(int socket, const struct message_header *header, struct RxRequest **request) {
62 | if (header->message_length > MAX_MESSAGE_LENGTH) {
63 | return -1;
64 | }
65 | uint8_t *buffer = malloc(sizeof(uint8_t) * header->message_length);
66 | if (buffer == NULL) {
67 | return -ENOMEM;
68 | }
69 | int code = tcp_utils_read_data(buffer, header->message_length, socket);
70 | if (code != 0) {
71 | return -1;
72 | }
73 | RxRequest *result = rx_request__unpack(NULL, header->message_length, buffer);
74 | free(buffer);
75 | if (result == NULL) {
76 | return -1;
77 | }
78 | *request = result;
79 | return 0;
80 | }
81 |
82 | int api_utils_write_response(int socket, ResponseStatus status, uint32_t details) {
83 | Response response = RESPONSE__INIT;
84 | response.status = status;
85 | response.details = details;
86 |
87 | size_t len = response__get_packed_size(&response);
88 | if (len > UINT32_MAX) {
89 | return -1;
90 | }
91 |
92 | struct message_header header;
93 | header.protocol_version = PROTOCOL_VERSION;
94 | header.type = TYPE_RESPONSE;
95 | header.message_length = htonl((uint32_t) len);
96 |
97 | size_t buffer_len = sizeof(struct message_header) + sizeof(uint8_t) * len;
98 | uint8_t *buffer = malloc(buffer_len);
99 | if (buffer == NULL) {
100 | return -ENOMEM;
101 | }
102 | memcpy(buffer, &header, sizeof(struct message_header));
103 | response__pack(&response, buffer + sizeof(struct message_header));
104 |
105 | int code = tcp_utils_write_data(buffer, buffer_len, socket);
106 | free(buffer);
107 | return code;
108 | }
109 |
110 | void api_utils_convert_tle(char **tle, char (*output)[80]) {
111 | for (int i = 0; i < 3; i++) {
112 | strncpy(output[i], tle[i], 80);
113 | }
114 | }
--------------------------------------------------------------------------------
/src/linked_list.c:
--------------------------------------------------------------------------------
1 | #include "linked_list.h"
2 | #include
3 | #include
4 |
5 | struct linked_list_t {
6 | struct linked_list_t *next;
7 |
8 | void *data;
9 |
10 | void (*destructor)(void *data);
11 | };
12 |
13 | int linked_list_add(void *data, void (*destructor)(void *data), linked_list **list) {
14 | struct linked_list_t *result = malloc(sizeof(struct linked_list_t));
15 | if (result == NULL) {
16 | return -ENOMEM;
17 | }
18 | *result = (struct linked_list_t) {0};
19 |
20 | result->data = data;
21 | result->destructor = destructor;
22 |
23 | if (*list == NULL) {
24 | *list = result;
25 | } else {
26 | linked_list *node = *list;
27 | while (node->next != NULL) {
28 | node = node->next;
29 | }
30 | node->next = result;
31 | }
32 | return 0;
33 | }
34 |
35 | void linked_list_foreach(void *arg, void (*foreach)(void *arg, void *data), linked_list *list) {
36 | linked_list *cur = list;
37 | while (cur != NULL) {
38 | foreach(arg, cur->data);
39 | cur = cur->next;
40 | }
41 | }
42 |
43 | void *linked_list_find(void *id, bool (*selector)(void *id, void *data), linked_list *list) {
44 | linked_list *cur_node = list;
45 | while (cur_node != NULL) {
46 | if (selector(id, cur_node->data) == true) {
47 | return cur_node->data;
48 | }
49 | cur_node = cur_node->next;
50 | }
51 | return NULL;
52 | }
53 |
54 | void *linked_list_remove_by_id(void *id, bool (*selector)(void *id, void *data), linked_list **list) {
55 | linked_list *cur_node = *list;
56 | linked_list *previous = NULL;
57 | void *result = NULL;
58 | while (cur_node != NULL) {
59 | linked_list *next = cur_node->next;
60 | if (selector(id, cur_node->data) == false) {
61 | previous = cur_node;
62 | cur_node = next;
63 | continue;
64 | }
65 | if (previous == NULL) {
66 | *list = next;
67 | } else {
68 | previous->next = next;
69 | }
70 | result = cur_node->data;
71 | free(cur_node);
72 | break;
73 | }
74 | return result;
75 | }
76 |
77 | void linked_list_destroy_by_id(void *id, bool (*selector)(void *id, void *data), linked_list **list) {
78 | linked_list *cur_node = *list;
79 | linked_list *previous = NULL;
80 | while (cur_node != NULL) {
81 | linked_list *next = cur_node->next;
82 | if (selector(id, cur_node->data) == false) {
83 | previous = cur_node;
84 | cur_node = next;
85 | continue;
86 | }
87 | if (previous == NULL) {
88 | *list = next;
89 | } else {
90 | previous->next = next;
91 | }
92 | cur_node->destructor(cur_node->data);
93 | free(cur_node);
94 | break;
95 | }
96 | }
97 |
98 | void linked_list_destroy_by_selector(bool (*selector)(void *data), linked_list **list) {
99 | linked_list *cur_node = *list;
100 | linked_list *previous = NULL;
101 | while (cur_node != NULL) {
102 | linked_list *next = cur_node->next;
103 | if (selector(cur_node->data) == false) {
104 | previous = cur_node;
105 | cur_node = next;
106 | continue;
107 | }
108 | if (previous == NULL) {
109 | *list = next;
110 | } else {
111 | previous->next = next;
112 | }
113 | cur_node->destructor(cur_node->data);
114 | free(cur_node);
115 | cur_node = next;
116 | }
117 | }
118 |
119 | void linked_list_destroy(linked_list *list) {
120 | if (list == NULL) {
121 | return;
122 | }
123 | linked_list *cur = list;
124 | while (cur != NULL) {
125 | linked_list *next = cur->next;
126 | cur->destructor(cur->data);
127 | free(cur);
128 | cur = next;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/test/test_clock_recovery_mm.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "../src/dsp/clock_recovery_mm.h"
6 | #include "utils.h"
7 |
8 | clock_mm *clock = NULL;
9 | float *float_input = NULL;
10 |
11 | START_TEST(test_big_buffers) {
12 | size_t max_input_buffer = 10;
13 | int code = clock_mm_create(2.0F, 0.25F * 0.175F * 0.175F, 0.005f, 0.175f, 0.005f, max_input_buffer, &clock);
14 | ck_assert_int_eq(code, 0);
15 |
16 | setup_input_data(&float_input, 0, max_input_buffer + 1);
17 | float *output = NULL;
18 | size_t output_len = 0;
19 | clock_mm_process(float_input, max_input_buffer + 1, &output, &output_len, clock);
20 | ck_assert_int_eq(output_len, 0);
21 | }
22 |
23 | END_TEST
24 |
25 | START_TEST(test_small_buffers) {
26 | int code = clock_mm_create(2.0F, 0.25F * 0.175F * 0.175F, 0.005f, 0.175f, 0.005f, 100, &clock);
27 | ck_assert_int_eq(code, 0);
28 |
29 | setup_input_data(&float_input, 0, 100);
30 | float *output = NULL;
31 | size_t output_len = 0;
32 | // no input, no output
33 | clock_mm_process(float_input, 0, &output, &output_len, clock);
34 | ck_assert_int_eq(output_len, 0);
35 | clock_mm_process(float_input, 4, &output, &output_len, clock);
36 | ck_assert_int_eq(output_len, 0);
37 | clock_mm_process(float_input + 4, 3, &output, &output_len, clock);
38 | ck_assert_int_eq(output_len, 0);
39 | clock_mm_process(float_input + 7, 1, &output, &output_len, clock);
40 | ck_assert_int_eq(output_len, 1);
41 | ck_assert(fabsl(3.007791F - output[0]) < 0.001);
42 | }
43 |
44 | END_TEST
45 |
46 | START_TEST (test_normal) {
47 | int code = clock_mm_create(2.0F, 0.25F * 0.175F * 0.175F, 0.005f, 0.175f, 0.005f, 100, &clock);
48 | ck_assert_int_eq(code, 0);
49 |
50 | setup_input_data(&float_input, 0, 100);
51 | float *output = NULL;
52 | size_t output_len = 0;
53 | clock_mm_process(float_input, 42, &output, &output_len, clock);
54 |
55 | const float expected[] = {3.007791F, 5.537506F, 7.992135F, 10.434598F, 12.865694F, 15.301093F, 17.738537F,
56 | 20.176548F, 22.611200F, 25.053421F, 27.484411F, 29.919767F, 32.351070F, 34.790939F,
57 | 37.227158F};
58 | assert_float_array(expected, sizeof(expected) / sizeof(float), output, output_len);
59 |
60 | clock_mm_process(float_input + 42, 36, &output, &output_len, clock);
61 | const float expected2[] = {39.662235F, 42.105213F, 44.534428F, 46.975555F, 49.400551F, 51.844826F, 54.276859F,
62 | 56.714336F, 59.148190F, 61.577019F, 64.029373F, 66.450058F, 68.900490F, 71.325760F,
63 | 73.759659F};
64 | assert_float_array(expected2, sizeof(expected2) / sizeof(float), output, output_len);
65 |
66 | }
67 |
68 | END_TEST
69 |
70 | void teardown() {
71 | if (clock != NULL) {
72 | clock_mm_destroy(clock);
73 | clock = NULL;
74 | }
75 | if (float_input != NULL) {
76 | free(float_input);
77 | float_input = NULL;
78 | }
79 | }
80 |
81 | void setup() {
82 | //do nothing
83 | }
84 |
85 | Suite *common_suite(void) {
86 | Suite *s;
87 | TCase *tc_core;
88 |
89 | s = suite_create("clock_recovery_mm");
90 |
91 | /* Core test case */
92 | tc_core = tcase_create("Core");
93 |
94 | tcase_add_test(tc_core, test_normal);
95 | tcase_add_test(tc_core, test_small_buffers);
96 | tcase_add_test(tc_core, test_big_buffers);
97 |
98 | tcase_add_checked_fixture(tc_core, setup, teardown);
99 | suite_add_tcase(s, tc_core);
100 |
101 | return s;
102 | }
103 |
104 | int main(void) {
105 | int number_failed;
106 | Suite *s;
107 | SRunner *sr;
108 |
109 | s = common_suite();
110 | sr = srunner_create(s);
111 |
112 | srunner_set_fork_status(sr, CK_NOFORK);
113 | srunner_run_all(sr, CK_NORMAL);
114 | number_failed = srunner_ntests_failed(sr);
115 | srunner_free(sr);
116 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
117 | }
118 |
119 |
120 |
--------------------------------------------------------------------------------
/test/test_dsp_worker.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "../src/dsp_worker.h"
5 | #include "utils.h"
6 |
7 | dsp_worker *worker = NULL;
8 | struct server_config *config = NULL;
9 | struct RxRequest *req = NULL;
10 |
11 | START_TEST (test_invalid_basepath) {
12 | int code = server_config_create(&config, "full.conf");
13 | ck_assert_int_eq(code, 0);
14 | free(config->base_path);
15 | config->base_path = utils_read_and_copy_str("/invalidpath/");
16 | req = create_rx_request();
17 | req->rx_dump_file = true;
18 | code = dsp_worker_create(1, 0, config, req, &worker);
19 | ck_assert_int_eq(code, -1);
20 |
21 | req->rx_dump_file = false;
22 | req->demod_destination = DEMOD_DESTINATION__FILE;
23 | code = dsp_worker_create(1, 0, config, req, &worker);
24 | ck_assert_int_eq(code, -1);
25 |
26 | }
27 |
28 | END_TEST
29 |
30 | START_TEST (test_invalid_queue_size) {
31 | int code = server_config_create(&config, "full.conf");
32 | ck_assert_int_eq(code, 0);
33 | config->queue_size = 0;
34 | req = create_rx_request();
35 | code = dsp_worker_create(1, 0, config, req, &worker);
36 | ck_assert_int_eq(code, -1);
37 | }
38 |
39 | END_TEST
40 |
41 | START_TEST (test_invalid_doppler_configuration) {
42 | int code = server_config_create(&config, "full.conf");
43 | ck_assert_int_eq(code, 0);
44 | req = create_rx_request();
45 | for (int i = 0; i < req->doppler->n_tle; i++) {
46 | free(req->doppler->tle[i]);
47 | }
48 | free(req->doppler->tle);
49 | char tle[3][80] = {"0\0", "1 0 0 0 0 00000-0 0 0 0\0", "2 0 0 0 0 0 0 0 0\0"};
50 | req->doppler->tle = utils_allocate_tle(tle);
51 | code = dsp_worker_create(1, 0, config, req, &worker);
52 | ck_assert_int_eq(code, -1);
53 | }
54 |
55 | END_TEST
56 |
57 | START_TEST (test_invalid_fsk_configuration) {
58 | int code = server_config_create(&config, "full.conf");
59 | ck_assert_int_eq(code, 0);
60 | req = create_rx_request();
61 | req->demod_baud_rate = req->rx_sampling_freq;
62 | code = dsp_worker_create(1, 0, config, req, &worker);
63 | ck_assert_int_eq(code, -1);
64 | }
65 |
66 | END_TEST
67 |
68 | START_TEST (test_create_delete) {
69 | int code = server_config_create(&config, "full.conf");
70 | ck_assert_int_eq(code, 0);
71 | req = create_rx_request();
72 | uint32_t id = 1;
73 | code = dsp_worker_create(id, 0, config, req, &worker);
74 | ck_assert_int_eq(code, 0);
75 |
76 | bool result = dsp_worker_find_by_id(&id, worker);
77 | ck_assert_int_eq(result, 1);
78 | }
79 |
80 | END_TEST
81 |
82 | void teardown() {
83 | if (worker != NULL) {
84 | dsp_worker_destroy(worker);
85 | worker = NULL;
86 | }
87 | if (config != NULL) {
88 | server_config_destroy(config);
89 | config = NULL;
90 | }
91 | if (req != NULL) {
92 | rx_request__free_unpacked(req, NULL);
93 | req = NULL;
94 | }
95 | }
96 |
97 | void setup() {
98 | //do nothing
99 | }
100 |
101 | Suite *common_suite(void) {
102 | Suite *s;
103 | TCase *tc_core;
104 |
105 | s = suite_create("dsp_worker");
106 |
107 | /* Core test case */
108 | tc_core = tcase_create("Core");
109 |
110 | tcase_add_test(tc_core, test_create_delete);
111 | tcase_add_test(tc_core, test_invalid_fsk_configuration);
112 | tcase_add_test(tc_core, test_invalid_doppler_configuration);
113 | tcase_add_test(tc_core, test_invalid_queue_size);
114 | tcase_add_test(tc_core, test_invalid_basepath);
115 |
116 | tcase_add_checked_fixture(tc_core, setup, teardown);
117 | suite_add_tcase(s, tc_core);
118 |
119 | return s;
120 | }
121 |
122 | int main(void) {
123 | int number_failed;
124 | Suite *s;
125 | SRunner *sr;
126 |
127 | s = common_suite();
128 | sr = srunner_create(s);
129 |
130 | srunner_set_fork_status(sr, CK_NOFORK);
131 | srunner_run_all(sr, CK_NORMAL);
132 | number_failed = srunner_ntests_failed(sr);
133 | srunner_free(sr);
134 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
135 | }
136 |
--------------------------------------------------------------------------------
/test/test_quadrature_demod.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "../src/dsp/quadrature_demod.h"
5 | #include "utils.h"
6 |
7 | quadrature_demod *quad_demod = NULL;
8 | float complex *complex_input = NULL;
9 |
10 | START_TEST (test_normal) {
11 | int code = quadrature_demod_create(25.4F, 2000, &quad_demod);
12 | ck_assert_int_eq(code, 0);
13 |
14 | setup_input_complex_data(&complex_input, 0, 200);
15 | float *output = NULL;
16 | size_t output_len = 0;
17 | quadrature_demod_process(complex_input, 2, &output, &output_len, quad_demod);
18 |
19 | const float expected[] = {0.000000F,-14.935266F};
20 | assert_float_array(expected, sizeof(expected) / sizeof(float), output, output_len);
21 |
22 | quadrature_demod_process(complex_input + 2, 198, &output, &output_len, quad_demod);
23 |
24 | const float expected2[] = {-2.203149F,-0.860684F,-0.457606F,-0.283786F,-0.193152F,-0.139943F,-0.106054F,-0.083142F,-0.066930F,-0.055038F,-0.046056F,-0.039107F,-0.033620F,-0.029212F,-0.025618F,-0.022648F,-0.020167F,-0.018072F,-0.016287F,-0.014755F,-0.013428F,-0.012273F,-0.011261F,-0.010369F,-0.009579F,-0.008876F,-0.008248F,-0.007684F,-0.007176F,-0.006717F,-0.006300F,-0.005921F,-0.005576F,-0.005259F,-0.004969F,-0.004702F,-0.004457F,-0.004229F,-0.004019F,-0.003824F,-0.003643F,-0.003475F,-0.003318F,-0.003171F,-0.003034F,-0.002906F,-0.002785F,-0.002672F,-0.002566F,-0.002466F,-0.002371F,-0.002282F,-0.002198F,-0.002119F,-0.002043F,-0.001972F,-0.001904F,-0.001840F,-0.001779F,-0.001721F,-0.001665F,-0.001613F,-0.001563F,-0.001515F,-0.001469F,-0.001425F,-0.001383F,-0.001344F,-0.001305F,-0.001269F,-0.001234F,-0.001200F,-0.001168F,-0.001136F,-0.001107F,-0.001078F,-0.001050F,-0.001024F,-0.000998F,-0.000974F,-0.000950F,-0.000927F,-0.000905F,-0.000884F,-0.000864F,-0.000844F,-0.000825F,-0.000806F,-0.000788F,-0.000771F,-0.000754F,-0.000738F,-0.000723F,-0.000707F,-0.000693F,-0.000678F,-0.000665F,-0.000651F,-0.000638F,-0.000626F,-0.000613F,-0.000601F,-0.000590F,-0.000579F,-0.000568F,-0.000557F,-0.000547F,-0.000537F,-0.000527F,-0.000518F,-0.000508F,-0.000500F,-0.000491F,-0.000482F,-0.000474F,-0.000466F,-0.000458F,-0.000450F,-0.000443F,-0.000436F,-0.000428F,-0.000421F,-0.000415F,-0.000408F,-0.000402F,-0.000395F,-0.000389F,-0.000383F,-0.000377F,-0.000371F,-0.000366F,-0.000360F,-0.000355F,-0.000350F,-0.000345F,-0.000340F,-0.000335F,-0.000330F,-0.000325F,-0.000321F,-0.000316F,-0.000312F,-0.000307F,-0.000303F,-0.000299F,-0.000295F,-0.000291F,-0.000287F,-0.000283F,-0.000279F,-0.000276F,-0.000272F,-0.000269F,-0.000265F,-0.000262F,-0.000258F,-0.000255F,-0.000252F,-0.000249F,-0.000246F,-0.000243F,-0.000240F,-0.000237F,-0.000234F,-0.000231F,-0.000228F,-0.000226F,-0.000223F,-0.000220F,-0.000218F,-0.000215F,-0.000213F,-0.000210F,-0.000208F,-0.000206F,-0.000203F,-0.000201F,-0.000199F,-0.000197F,-0.000194F,-0.000192F,-0.000190F,-0.000188F,-0.000186F,-0.000184F,-0.000182F,-0.000180F,-0.000178F,-0.000176F,-0.000175F,-0.000173F,-0.000171F,-0.000169F,-0.000167F,-0.000166F,-0.000164F,-0.000162F,-0.000161F};
25 | assert_float_array(expected2, sizeof(expected2) / sizeof(float), output, output_len);
26 |
27 | }
28 |
29 | END_TEST
30 |
31 | void teardown() {
32 | if (quad_demod != NULL) {
33 | quadrature_demod_destroy(quad_demod);
34 | quad_demod = NULL;
35 | }
36 | if (complex_input != NULL) {
37 | free(complex_input);
38 | complex_input = NULL;
39 | }
40 | }
41 |
42 | void setup() {
43 | //do nothing
44 | }
45 |
46 | Suite *common_suite(void) {
47 | Suite *s;
48 | TCase *tc_core;
49 |
50 | s = suite_create("quadrature_demod");
51 |
52 | /* Core test case */
53 | tc_core = tcase_create("Core");
54 |
55 | tcase_add_test(tc_core, test_normal);
56 |
57 | tcase_add_checked_fixture(tc_core, setup, teardown);
58 | suite_add_tcase(s, tc_core);
59 |
60 | return s;
61 | }
62 |
63 | int main(void) {
64 | int number_failed;
65 | Suite *s;
66 | SRunner *sr;
67 |
68 | s = common_suite();
69 | sr = srunner_create(s);
70 |
71 | srunner_set_fork_status(sr, CK_NOFORK);
72 | srunner_run_all(sr, CK_NORMAL);
73 | number_failed = srunner_ntests_failed(sr);
74 | srunner_free(sr);
75 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
76 | }
77 |
--------------------------------------------------------------------------------
/src/dsp/dc_blocker.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "dc_blocker.h"
6 |
7 | struct moving_average {
8 | float *delay_line;
9 | int delay_line_len;
10 | int length;
11 | float inDelayed;
12 | float outD1;
13 | };
14 |
15 | struct dc_blocker_t {
16 | struct moving_average *ma0;
17 | struct moving_average *ma1;
18 | struct moving_average *ma2;
19 | struct moving_average *ma3;
20 |
21 | float *delay_line;
22 | int delay_line_len;
23 | };
24 |
25 | void moving_average_destroy(struct moving_average *mavg) {
26 | if (mavg == NULL) {
27 | return;
28 | }
29 | if (mavg->delay_line != NULL) {
30 | free(mavg->delay_line);
31 | }
32 | free(mavg);
33 | }
34 |
35 | int moving_average_create(int length, struct moving_average **mavg) {
36 | struct moving_average *result = malloc(sizeof(struct moving_average));
37 | if (result == NULL) {
38 | return -ENOMEM;
39 | }
40 | // init all fields with 0 so that destroy_* method would work
41 | *result = (struct moving_average) {0};
42 | result->delay_line_len = length - 1;
43 | result->delay_line = malloc(sizeof(float) * result->delay_line_len);
44 | if (result->delay_line == NULL) {
45 | moving_average_destroy(result);
46 | return -ENOMEM;
47 | }
48 | memset(result->delay_line, 0, sizeof(float) * result->delay_line_len);
49 | result->length = length;
50 | result->inDelayed = 0.0F;
51 | result->outD1 = 0.0F;
52 | *mavg = result;
53 | return 0;
54 | }
55 |
56 | float moving_average_process(float input, struct moving_average *mavg) {
57 | float inDelayed = mavg->inDelayed;
58 | mavg->inDelayed = mavg->delay_line[0];
59 | memmove(mavg->delay_line, mavg->delay_line + 1, sizeof(float) * (mavg->delay_line_len - 1));
60 | mavg->delay_line[mavg->delay_line_len - 1] = input;
61 | float y = input - inDelayed + mavg->outD1;
62 | mavg->outD1 = y;
63 | return y / (float) mavg->length;
64 | }
65 |
66 | int dc_blocker_create(int length, dc_blocker **blocker) {
67 | struct dc_blocker_t *result = malloc(sizeof(struct dc_blocker_t));
68 | if (result == NULL) {
69 | return -ENOMEM;
70 | }
71 | // init all fields with 0 so that destroy_* method would work
72 | *result = (struct dc_blocker_t) {0};
73 | result->delay_line_len = length - 1;
74 | result->delay_line = malloc(sizeof(float) * result->delay_line_len);
75 | if (result->delay_line == NULL) {
76 | dc_blocker_destroy(result);
77 | return -ENOMEM;
78 | }
79 | memset(result->delay_line, 0, sizeof(float) * result->delay_line_len);
80 |
81 | int code = moving_average_create(length, &result->ma0);
82 | if (code != 0) {
83 | dc_blocker_destroy(result);
84 | return code;
85 | }
86 | code = moving_average_create(length, &result->ma1);
87 | if (code != 0) {
88 | dc_blocker_destroy(result);
89 | return code;
90 | }
91 | code = moving_average_create(length, &result->ma2);
92 | if (code != 0) {
93 | dc_blocker_destroy(result);
94 | return code;
95 | }
96 | code = moving_average_create(length, &result->ma3);
97 | if (code != 0) {
98 | dc_blocker_destroy(result);
99 | return code;
100 | }
101 | *blocker = result;
102 | return 0;
103 | }
104 |
105 | void dc_blocker_process(float *input, size_t input_len, float **output, size_t *output_len, dc_blocker *blocker) {
106 | for (size_t i = 0; i < input_len; i++) {
107 | float y1 = moving_average_process(input[i], blocker->ma0);
108 | float y2 = moving_average_process(y1, blocker->ma1);
109 | float y3 = moving_average_process(y2, blocker->ma2);
110 | float y4 = moving_average_process(y3, blocker->ma3);
111 |
112 | float d = blocker->delay_line[0];
113 | memmove(blocker->delay_line, blocker->delay_line + 1, sizeof(float) * (blocker->delay_line_len - 1));
114 | blocker->delay_line[blocker->delay_line_len - 1] = blocker->ma0->inDelayed;
115 | input[i] = d - y4;
116 | }
117 | *output = input;
118 | *output_len = input_len;
119 | }
120 |
121 | void dc_blocker_destroy(dc_blocker *blocker) {
122 | if (blocker == NULL) {
123 | return;
124 | }
125 | if (blocker->delay_line != NULL) {
126 | free(blocker->delay_line);
127 | }
128 | moving_average_destroy(blocker->ma0);
129 | moving_average_destroy(blocker->ma1);
130 | moving_average_destroy(blocker->ma2);
131 | moving_average_destroy(blocker->ma3);
132 | free(blocker);
133 | }
134 |
--------------------------------------------------------------------------------
/test/test_file_source.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "../src/sdr/file_source.h"
5 | #include "utils.h"
6 |
7 | const char *tmp_folder;
8 | sdr_device *device = NULL;
9 | char filename[4096];
10 |
11 | START_TEST(test_rx_invalid_arguments) {
12 | int max_output_buffer_length = 2000;
13 | int code = file_source_create(1, "/non-existing-file", NULL, 48000, 1000, max_output_buffer_length, &device);
14 | ck_assert_int_eq(code, -1);
15 |
16 | code = file_source_create(1, filename, NULL, 48000, 1000, max_output_buffer_length, &device);
17 | ck_assert_int_eq(code, 0);
18 |
19 | const float buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
20 | size_t buffer_len = sizeof(buffer) / sizeof(float) / 2;
21 | code = device->sdr_process_tx((complex float *) buffer, buffer_len, device->plugin);
22 | ck_assert_int_eq(code, -1);
23 | }
24 | END_TEST
25 |
26 | START_TEST(test_tx_invalid_arguments) {
27 | int max_output_buffer_length = 2000;
28 | int code = file_source_create(1, NULL, "/", 48000, 1000, max_output_buffer_length, &device);
29 | ck_assert_int_eq(code, -1);
30 |
31 | code = file_source_create(1, NULL, filename, 48000, 1000, max_output_buffer_length, &device);
32 | ck_assert_int_eq(code, 0);
33 |
34 | const float buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
35 | size_t buffer_len = sizeof(buffer) / sizeof(float) / 2;
36 | code = device->sdr_process_tx((complex float *) buffer, max_output_buffer_length + 1, device->plugin);
37 | ck_assert_int_eq(code, -1);
38 |
39 | complex float *output = NULL;
40 | size_t output_len = 0;
41 | code = device->sdr_process_rx(&output, &output_len, device->plugin);
42 | ck_assert_int_eq(code, -1);
43 | }
44 |
45 | END_TEST
46 |
47 | START_TEST (test_rx_offset) {
48 | int code = file_source_create(1, "tx.cf32", NULL, 48000, 1000, 2000, &device);
49 | ck_assert_int_eq(code, 0);
50 |
51 | complex float *output = NULL;
52 | size_t output_len = 0;
53 | code = device->sdr_process_rx(&output, &output_len, device->plugin);
54 | ck_assert_int_eq(code, 0);
55 |
56 | const float expected[10] = {1.000000F, 2.000000F, 2.452230F, 4.357358F, 3.276715F, 7.089650F, 3.405689F, 10.069820F, 2.794229F, 13.160254F};
57 | size_t expected_len = sizeof(expected) / sizeof(float) / 2;
58 | assert_complex_array(expected, expected_len, output, output_len);
59 | }
60 |
61 | END_TEST
62 |
63 | START_TEST (test_success) {
64 | int code = file_source_create(1, NULL, filename, 48000, 0, 2000, &device);
65 | ck_assert_int_eq(code, 0);
66 |
67 | const float buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
68 | size_t buffer_len = sizeof(buffer) / sizeof(float) / 2;
69 | code = device->sdr_process_tx((complex float *) buffer, buffer_len, device->plugin);
70 | ck_assert_int_eq(code, 0);
71 | device->destroy(device->plugin);
72 | free(device);
73 | device = NULL;
74 |
75 | code = file_source_create(1, filename, NULL, 48000, 0, 2000, &device);
76 | ck_assert_int_eq(code, 0);
77 | complex float *output = NULL;
78 | size_t output_len = 0;
79 | device->sdr_process_rx(&output, &output_len, device->plugin);
80 | assert_complex_array(buffer, buffer_len, output, output_len);
81 | }
82 |
83 | END_TEST
84 |
85 | void teardown() {
86 | if (device != NULL) {
87 | device->destroy(device->plugin);
88 | free(device);
89 | device = NULL;
90 | }
91 | }
92 |
93 | void setup() {
94 | tmp_folder = getenv("TMPDIR");
95 | if (tmp_folder == NULL) {
96 | tmp_folder = "/tmp";
97 | }
98 | snprintf(filename, sizeof(filename), "%s/tx.cf32", tmp_folder);
99 | }
100 |
101 | Suite *common_suite(void) {
102 | Suite *s;
103 | TCase *tc_core;
104 |
105 | s = suite_create("file_source");
106 |
107 | /* Core test case */
108 | tc_core = tcase_create("Core");
109 |
110 | tcase_add_test(tc_core, test_success);
111 | tcase_add_test(tc_core, test_rx_offset);
112 | tcase_add_test(tc_core, test_tx_invalid_arguments);
113 | tcase_add_test(tc_core, test_rx_invalid_arguments);
114 |
115 | tcase_add_checked_fixture(tc_core, setup, teardown);
116 | suite_add_tcase(s, tc_core);
117 |
118 | return s;
119 | }
120 |
121 | int main(void) {
122 | int number_failed;
123 | Suite *s;
124 | SRunner *sr;
125 |
126 | s = common_suite();
127 | sr = srunner_create(s);
128 |
129 | srunner_set_fork_status(sr, CK_NOFORK);
130 | srunner_run_all(sr, CK_NORMAL);
131 | number_failed = srunner_ntests_failed(sr);
132 | srunner_free(sr);
133 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
134 | }
135 |
--------------------------------------------------------------------------------
/src/dsp/fsk_demod.c:
--------------------------------------------------------------------------------
1 | #include "fsk_demod.h"
2 | #include "quadrature_demod.h"
3 | #include "clock_recovery_mm.h"
4 | #include "dc_blocker.h"
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "lpf.h"
11 |
12 | #ifndef M_PI
13 | #define M_PI 3.14159265358979323846
14 | #endif
15 |
16 | struct fsk_demod_t {
17 |
18 | lpf *lpf1;
19 | lpf *lpf2;
20 | quadrature_demod *quad_demod;
21 | dc_blocker *dc;
22 | clock_mm *clock;
23 |
24 | int8_t *output;
25 | size_t output_len;
26 | };
27 |
28 | int fsk_demod_create(uint64_t sampling_freq, uint32_t baud_rate, int64_t deviation, uint8_t decimation, uint32_t transition_width, bool use_dc_block, uint32_t max_input_buffer_length, fsk_demod **demod) {
29 | struct fsk_demod_t *result = malloc(sizeof(struct fsk_demod_t));
30 | if (result == NULL) {
31 | return -ENOMEM;
32 | }
33 | // init all fields with 0 so that destroy_* method would work
34 | *result = (struct fsk_demod_t) {0};
35 |
36 | double carson_cutoff = (double) llabs(deviation) + (double) baud_rate / 2;
37 | int code = lpf_create(1, sampling_freq, (uint64_t) carson_cutoff, (uint32_t) (0.1f * carson_cutoff), max_input_buffer_length, sizeof(float complex), &result->lpf1);
38 | if (code != 0) {
39 | fsk_demod_destroy(result);
40 | return code;
41 | }
42 | code = quadrature_demod_create((float) ((double) sampling_freq / (2 * M_PI * (double) deviation)), max_input_buffer_length, &result->quad_demod);
43 | if (code != 0) {
44 | fsk_demod_destroy(result);
45 | return code;
46 | }
47 | code = lpf_create(decimation, sampling_freq, baud_rate / 2, transition_width, max_input_buffer_length, sizeof(float), &result->lpf2);
48 | if (code != 0) {
49 | fsk_demod_destroy(result);
50 | return code;
51 | }
52 |
53 | float sps = (float) ((double) sampling_freq / baud_rate / decimation);
54 |
55 | if (use_dc_block) {
56 | code = dc_blocker_create((int) ceilf(sps * 32), &result->dc);
57 | if (code != 0) {
58 | fsk_demod_destroy(result);
59 | return code;
60 | }
61 | }
62 |
63 | code = clock_mm_create(sps, (sps * (float) M_PI) / 100, 0.5f, 0.5f / 8.0f, 0.01f, max_input_buffer_length, &result->clock);
64 | if (code != 0) {
65 | fsk_demod_destroy(result);
66 | return code;
67 | }
68 |
69 | result->output_len = max_input_buffer_length;
70 | result->output = malloc(sizeof(int8_t) * result->output_len);
71 | if (result->output == NULL) {
72 | fsk_demod_destroy(result);
73 | return -ENOMEM;
74 | }
75 |
76 | *demod = result;
77 | return 0;
78 | }
79 |
80 | void fsk_demod_process(const float complex *input, size_t input_len, int8_t **output, size_t *output_len, fsk_demod *demod) {
81 | float complex *lpf_output = NULL;
82 | size_t lpf_output_len = 0;
83 | lpf_process(input, input_len, (void **) &lpf_output, &lpf_output_len, demod->lpf1);
84 |
85 | float *qd_output = NULL;
86 | size_t qd_output_len = 0;
87 | quadrature_demod_process(lpf_output, lpf_output_len, &qd_output, &qd_output_len, demod->quad_demod);
88 |
89 | float *lpf2_output = NULL;
90 | size_t lpf2_output_len = 0;
91 | lpf_process(qd_output, qd_output_len, (void **) &lpf2_output, &lpf2_output_len, demod->lpf2);
92 |
93 | float *dc_output = NULL;
94 | size_t dc_output_len = 0;
95 | if (demod->dc != NULL) {
96 | dc_blocker_process(lpf2_output, lpf2_output_len, &dc_output, &dc_output_len, demod->dc);
97 | } else {
98 | dc_output = lpf2_output;
99 | dc_output_len = lpf2_output_len;
100 | }
101 |
102 | float *clock_output = NULL;
103 | size_t clock_output_len = 0;
104 | clock_mm_process(dc_output, dc_output_len, &clock_output, &clock_output_len, demod->clock);
105 |
106 | volk_32f_s32f_convert_8i(demod->output, clock_output, 127.0f, clock_output_len);
107 |
108 | *output = demod->output;
109 | *output_len = clock_output_len;
110 | }
111 |
112 | void fsk_demod_destroy(fsk_demod *demod) {
113 | if (demod == NULL) {
114 | return;
115 | }
116 | if (demod->lpf1 != NULL) {
117 | lpf_destroy(demod->lpf1);
118 | }
119 | if (demod->quad_demod != NULL) {
120 | quadrature_demod_destroy(demod->quad_demod);
121 | }
122 | if (demod->lpf2 != NULL) {
123 | lpf_destroy(demod->lpf2);
124 | }
125 | if (demod->dc != NULL) {
126 | dc_blocker_destroy(demod->dc);
127 | }
128 | if (demod->clock != NULL) {
129 | clock_mm_destroy(demod->clock);
130 | }
131 | if (demod->output != NULL) {
132 | free(demod->output);
133 | }
134 | free(demod);
135 | }
136 |
--------------------------------------------------------------------------------
/test/test_fsk_demod.c:
--------------------------------------------------------------------------------
1 | #define _POSIX_C_SOURCE 200809L
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "../src/dsp/fsk_demod.h"
8 | #include "utils.h"
9 |
10 | fsk_demod *demod = NULL;
11 | uint8_t *buffer = NULL;
12 | FILE *input = NULL;
13 | FILE *expected = NULL;
14 | // buffer length is important here. do not change it
15 | // all test data was generated and verified with this buffer length
16 | // and VOLK_GENERIC=1
17 | // different buffer length or using SIMD can cause different precision when dealing with floats
18 | // this small precision issue can propagate further and cause small differences in the output.
19 | // i.e. instead of -31, it can produce -30
20 | uint32_t max_buffer_length = 4096;
21 |
22 | void assert_files_and_demod(const char *input_filename, const char *expected_filename) {
23 | input = fopen(input_filename, "rb");
24 | ck_assert(input != NULL);
25 | expected = fopen(expected_filename, "rb");
26 | ck_assert(expected != NULL);
27 | size_t buffer_len = sizeof(float complex) * max_buffer_length;
28 | buffer = malloc(sizeof(uint8_t) * buffer_len);
29 | ck_assert(buffer != NULL);
30 | size_t j = 0;
31 | while (true) {
32 | size_t actual_read = 0;
33 | int code = read_data(buffer, &actual_read, buffer_len, input);
34 | if (code != 0 && actual_read == 0) {
35 | break;
36 | }
37 | int8_t *output = NULL;
38 | size_t output_len = 0;
39 | fsk_demod_process((const complex float *) buffer, actual_read / 8, &output, &output_len, demod);
40 | code = read_data(buffer, &actual_read, output_len, expected);
41 | ck_assert_int_eq(code, 0);
42 | ck_assert_int_eq(actual_read, output_len);
43 | for (size_t i = 0; i < actual_read; i++) {
44 | // can't make test working across macbook, raspberrypi and travis
45 | // all of them have different float-precision issues
46 | // where results slightly different
47 | ck_assert(abs((int8_t) buffer[i] - output[i]) <= 2);
48 | }
49 | }
50 | }
51 |
52 | START_TEST(test_normal) {
53 | int code = fsk_demod_create(192000, 40000, 5000, 1, 2000, true, max_buffer_length, &demod);
54 | ck_assert_int_eq(code, 0);
55 | assert_files_and_demod("nusat.cf32", "processed.s8");
56 | }
57 |
58 | END_TEST
59 |
60 | START_TEST(test_nan) {
61 | int code = fsk_demod_create(240000, 9600, 5000, 1, 2000, true, max_buffer_length, &demod);
62 | ck_assert_int_eq(code, 0);
63 | assert_files_and_demod("inputnan.cf32", "nan.s8");
64 | }
65 | END_TEST
66 |
67 | START_TEST(test_handle_lucky7) {
68 | int code = fsk_demod_create(48000, 4800, 5000, 2, 2000, true, max_buffer_length, &demod);
69 | ck_assert_int_eq(code, 0);
70 | assert_files_and_demod("lucky7.expected.cf32", "lucky7.expected.s8");
71 | }
72 |
73 | END_TEST
74 |
75 | START_TEST(test_no_dc) {
76 | int code = fsk_demod_create(48000, 4800, 5000, 2, 2000, false, max_buffer_length, &demod);
77 | ck_assert_int_eq(code, 0);
78 | assert_files_and_demod("lucky7.expected.cf32", "lucky7.expected.nodc.s8");
79 | }
80 |
81 | END_TEST
82 |
83 | void teardown() {
84 | if (demod != NULL) {
85 | fsk_demod_destroy(demod);
86 | demod = NULL;
87 | }
88 | if (buffer != NULL) {
89 | free(buffer);
90 | buffer = NULL;
91 | }
92 | if (input != NULL) {
93 | fclose(input);
94 | input = NULL;
95 | }
96 | if (expected != NULL) {
97 | fclose(expected);
98 | expected = NULL;
99 | }
100 | }
101 |
102 | void setup() {
103 | //do nothing
104 | }
105 |
106 | Suite *common_suite(void) {
107 | Suite *s;
108 | TCase *tc_core;
109 |
110 | s = suite_create("fsk_demod");
111 |
112 | /* Core test case */
113 | tc_core = tcase_create("Core");
114 |
115 | tcase_add_test(tc_core, test_normal);
116 | tcase_add_test(tc_core, test_nan);
117 | tcase_add_test(tc_core, test_handle_lucky7);
118 | tcase_add_test(tc_core, test_no_dc);
119 |
120 | tcase_add_checked_fixture(tc_core, setup, teardown);
121 | suite_add_tcase(s, tc_core);
122 |
123 | return s;
124 | }
125 |
126 | int main(void) {
127 | // this is especially important here
128 | // env variable is defined in run_tests.sh, but also here
129 | // to run this test from IDE
130 | setenv("VOLK_GENERIC", "1", 1);
131 | setenv("VOLK_ALIGNMENT", "16", 1);
132 |
133 | int number_failed;
134 | Suite *s;
135 | SRunner *sr;
136 |
137 | s = common_suite();
138 | sr = srunner_create(s);
139 |
140 | srunner_set_fork_status(sr, CK_NOFORK);
141 | srunner_run_all(sr, CK_NORMAL);
142 | number_failed = srunner_ntests_failed(sr);
143 | srunner_free(sr);
144 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
145 | }
--------------------------------------------------------------------------------
/src/sdr_worker.c:
--------------------------------------------------------------------------------
1 | #include "sdr_worker.h"
2 | #include "linked_list.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "sdr/sdr_server_client.h"
10 |
11 | struct sdr_worker_t {
12 | struct sdr_rx *rx;
13 | sdr_device *rx_device;
14 | uint32_t id;
15 | linked_list *dsp_configs;
16 | pthread_t sdr_thread;
17 | pthread_mutex_t mutex;
18 | };
19 |
20 | struct array_t {
21 | float complex *output;
22 | size_t output_len;
23 | };
24 |
25 | void sdr_worker_foreach_put(void *arg, void *data) {
26 | struct array_t *output = (struct array_t *) arg;
27 | dsp_worker *worker = (dsp_worker *) data;
28 | dsp_worker_put(output->output, output->output_len, worker);
29 | }
30 |
31 | static void *sdr_worker_callback(void *arg) {
32 | sdr_worker *worker = (sdr_worker *) arg;
33 | fprintf(stdout, "[%d] sdr_worker is starting\n", worker->id);
34 | struct array_t output;
35 | while (true) {
36 | int code = worker->rx_device->sdr_process_rx(&output.output, &output.output_len, worker->rx_device->plugin);
37 | if (code < -1) {
38 | // read timeout happened. it's ok.
39 | continue;
40 | }
41 | // terminate only when fully read from socket
42 | if (code != 0 && output.output_len == 0) {
43 | break;
44 | }
45 | pthread_mutex_lock(&worker->mutex);
46 | linked_list_foreach(&output, &sdr_worker_foreach_put, worker->dsp_configs);
47 | pthread_mutex_unlock(&worker->mutex);
48 | }
49 | //this would close all client sockets
50 | //and initiate cascade shutdown of sdr and dsp workers
51 | pthread_mutex_lock(&worker->mutex);
52 | linked_list_foreach(NULL, &dsp_worker_shutdown, worker->dsp_configs);
53 | pthread_mutex_unlock(&worker->mutex);
54 | return (void *) 0;
55 | }
56 |
57 | int sdr_worker_create(uint32_t id, struct sdr_rx *rx, sdr_device *rx_device, sdr_worker **worker) {
58 | struct sdr_worker_t *result = malloc(sizeof(struct sdr_worker_t));
59 | if (result == NULL) {
60 | return -ENOMEM;
61 | }
62 | // init all fields with 0 so that destroy_* method would work
63 | *result = (struct sdr_worker_t) {0};
64 |
65 | result->id = id;
66 | result->dsp_configs = NULL;
67 | result->rx = rx;
68 | result->mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
69 | result->rx_device = rx_device;
70 |
71 | pthread_t sdr_thread;
72 | int code = pthread_create(&sdr_thread, NULL, &sdr_worker_callback, result);
73 | if (code != 0) {
74 | sdr_worker_destroy(result);
75 | return code;
76 | }
77 | result->sdr_thread = sdr_thread;
78 |
79 | *worker = result;
80 | return 0;
81 | }
82 |
83 | bool sdr_worker_find_closest(void *id, void *data) {
84 | if (data == NULL) {
85 | return false;
86 | }
87 | struct sdr_rx *rx = (struct sdr_rx *) id;
88 | sdr_worker *worker = (sdr_worker *) data;
89 | if (worker->rx->rx_center_freq == rx->rx_center_freq &&
90 | worker->rx->rx_sampling_freq >= rx->rx_sampling_freq &&
91 | worker->rx->rx_offset == rx->rx_offset) {
92 | return true;
93 | }
94 | return false;
95 | }
96 |
97 | void sdr_worker_destroy_by_dsp_worker_id(uint32_t id, sdr_worker *worker) {
98 | if (worker == NULL) {
99 | return;
100 | }
101 | pthread_mutex_lock(&worker->mutex);
102 | if (worker->dsp_configs != NULL) {
103 | linked_list_destroy_by_id(&id, &dsp_worker_find_by_id, &worker->dsp_configs);
104 | }
105 | bool result = (worker->dsp_configs == NULL);
106 | pthread_mutex_unlock(&worker->mutex);
107 | if (result) {
108 | sdr_worker_destroy(worker);
109 | }
110 | }
111 |
112 | void sdr_worker_destroy(void *data) {
113 | if (data == NULL) {
114 | return;
115 | }
116 | sdr_worker *worker = (sdr_worker *) data;
117 | //gracefully shutdown connection
118 | if (worker->rx_device != NULL) {
119 | worker->rx_device->stop_rx(worker->rx_device->plugin);
120 | }
121 | pthread_join(worker->sdr_thread, NULL);
122 | //destroy the rx device
123 | if (worker->rx_device != NULL) {
124 | worker->rx_device->destroy(worker->rx_device->plugin);
125 | free(worker->rx_device);
126 | }
127 |
128 | pthread_mutex_lock(&worker->mutex);
129 | if (worker->dsp_configs != NULL) {
130 | linked_list_destroy(worker->dsp_configs);
131 | }
132 | pthread_mutex_unlock(&worker->mutex);
133 |
134 | if (worker->rx != NULL) {
135 | free(worker->rx);
136 | }
137 | uint32_t id = worker->id;
138 | free(worker);
139 | fprintf(stdout, "[%d] sdr_worker stopped\n", id);
140 | }
141 |
142 | int sdr_worker_add_dsp_worker(dsp_worker *worker, sdr_worker *sdr) {
143 | pthread_mutex_lock(&sdr->mutex);
144 | int code = linked_list_add(worker, &dsp_worker_destroy, &sdr->dsp_configs);
145 | pthread_mutex_unlock(&sdr->mutex);
146 | return code;
147 | }
--------------------------------------------------------------------------------
/test/test_queue.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "../src/queue.h"
4 | #include "utils.h"
5 |
6 | queue *queue_obj = NULL;
7 |
8 | void take_from_buffer_and_assert(const float *expected, size_t expected_len) {
9 | float complex *result = NULL;
10 | size_t len = 0;
11 | take_buffer_for_processing(&result, &len, queue_obj);
12 | if (expected == NULL) {
13 | ck_assert(result == NULL);
14 | return;
15 | }
16 | ck_assert(result != NULL);
17 | assert_complex_array(expected, expected_len, result, len);
18 | complete_buffer_processing(queue_obj);
19 | }
20 |
21 | START_TEST(test_invalid_arguments) {
22 | int code = create_queue(4, 0, false, &queue_obj);
23 | ck_assert_int_eq(code, -1);
24 |
25 | code = create_queue(0, 10, false, &queue_obj);
26 | ck_assert_int_eq(code, -1);
27 |
28 | code = create_queue(4, 10, false, &queue_obj);
29 | ck_assert_int_eq(code, 0);
30 |
31 | // this should be ignored
32 | ck_assert_int_eq(-1, queue_put(NULL, 25, queue_obj));
33 |
34 | const float buffer[2] = {1, 2};
35 | ck_assert_int_eq(-1, queue_put((const float complex *) buffer, 0, queue_obj));
36 |
37 | const float buffer2[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
38 | size_t buffer2_len = sizeof(buffer2) / sizeof(float) / 2;
39 | ck_assert_int_eq(-1, queue_put((const float complex *) buffer2, buffer2_len, queue_obj));
40 |
41 | }
42 |
43 | END_TEST
44 |
45 | START_TEST (test_terminated_only_after_fully_processed) {
46 | int code = create_queue(262144, 10, false, &queue_obj);
47 | ck_assert_int_eq(code, 0);
48 |
49 | const float buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
50 | size_t buffer_len = sizeof(buffer) / sizeof(float) / 2;
51 | queue_put((const float complex *) buffer, buffer_len, queue_obj);
52 |
53 | interrupt_waiting_the_data(queue_obj);
54 |
55 | take_from_buffer_and_assert(buffer, buffer_len);
56 | take_from_buffer_and_assert(NULL, 0);
57 |
58 | //no-op
59 | interrupt_waiting_the_data(NULL);
60 | }
61 |
62 | END_TEST
63 |
64 | START_TEST (test_put_take) {
65 | int code = create_queue(262144, 10, false, &queue_obj);
66 | ck_assert_int_eq(code, 0);
67 |
68 | const float buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
69 | ck_assert_int_eq(0, queue_put((const float complex *) buffer, sizeof(buffer) / sizeof(float) / 2, queue_obj));
70 |
71 | const float buffer2[2] = {1, 2};
72 | ck_assert_int_eq(0, queue_put((const float complex *) buffer2, sizeof(buffer2) / sizeof(float) / 2, queue_obj));
73 |
74 | take_from_buffer_and_assert(buffer, 10 / 2);
75 | take_from_buffer_and_assert(buffer2, 2 / 2);
76 | }
77 |
78 | END_TEST
79 |
80 | START_TEST (test_overflow) {
81 | int code = create_queue(262144, 1, false, &queue_obj);
82 | ck_assert_int_eq(code, 0);
83 |
84 | const float buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
85 | ck_assert_int_eq(0, queue_put((const float complex *) buffer, sizeof(buffer) / sizeof(float) / 2, queue_obj));
86 | const float buffer2[10] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
87 | ck_assert_int_eq(0, queue_put((const float complex *) buffer2, sizeof(buffer2) / sizeof(float) / 2, queue_obj));
88 |
89 | take_from_buffer_and_assert(buffer2, 10 / 2);
90 | }
91 |
92 | END_TEST
93 |
94 | START_TEST (test_putskipped) {
95 | int code = create_queue(262144, 1, true, &queue_obj);
96 | ck_assert_int_eq(code, 0);
97 |
98 | const float buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
99 | ck_assert_int_eq(0, queue_put((const float complex *) buffer, sizeof(buffer) / sizeof(float) / 2, queue_obj));
100 |
101 | interrupt_waiting_the_data(queue_obj);
102 |
103 | // any put ignored after queue terminated
104 | ck_assert_int_eq(-1, queue_put((const float complex *) buffer, sizeof(buffer) / sizeof(float) / 2, queue_obj));
105 | }
106 |
107 | END_TEST
108 |
109 | void teardown() {
110 | destroy_queue(queue_obj);
111 | }
112 |
113 | void setup() {
114 | //do nothing
115 | }
116 |
117 | Suite *common_suite(void) {
118 | Suite *s;
119 | TCase *tc_core;
120 |
121 | s = suite_create("queue");
122 |
123 | /* Core test case */
124 | tc_core = tcase_create("Core");
125 |
126 | tcase_add_test(tc_core, test_put_take);
127 | tcase_add_test(tc_core, test_overflow);
128 | tcase_add_test(tc_core, test_terminated_only_after_fully_processed);
129 | tcase_add_test(tc_core, test_invalid_arguments);
130 | tcase_add_test(tc_core, test_putskipped);
131 |
132 | tcase_add_checked_fixture(tc_core, setup, teardown);
133 | suite_add_tcase(s, tc_core);
134 |
135 | return s;
136 | }
137 |
138 | int main(void) {
139 | int number_failed;
140 | Suite *s;
141 | SRunner *sr;
142 |
143 | s = common_suite();
144 | sr = srunner_create(s);
145 |
146 | srunner_set_fork_status(sr, CK_NOFORK);
147 | srunner_run_all(sr, CK_NORMAL);
148 | number_failed = srunner_ntests_failed(sr);
149 | srunner_free(sr);
150 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
151 | }
152 |
153 |
--------------------------------------------------------------------------------
/src/dsp/gfsk_mod.c:
--------------------------------------------------------------------------------
1 | #include "gfsk_mod.h"
2 | #include "gaussian_taps.h"
3 | #include
4 | #include
5 | #include
6 | #include "interp_fir_filter.h"
7 | #include "frequency_modulator.h"
8 |
9 | struct gfsk_mod_t {
10 | interp_fir_filter *filter;
11 | frequency_modulator *freq_mod;
12 |
13 | float *temp_input;
14 | size_t temp_input_len;
15 | };
16 |
17 | int gfsk_mod_convolve(float *x, size_t x_len, float *y, size_t y_len, float **out, size_t *out_len) {
18 | size_t result_len = x_len + y_len - 1;
19 | float *result = malloc(sizeof(float) * result_len);
20 | if (result == NULL) {
21 | return -ENOMEM;
22 | }
23 | float *temp = malloc(sizeof(float) * result_len);
24 | if (temp == NULL) {
25 | return -ENOMEM;
26 | }
27 | memset(temp, 0, sizeof(float) * result_len);
28 | memcpy(temp, x, sizeof(float) * x_len);
29 | for (size_t i = 0; i < result_len; i++) {
30 | float sum = 0.0F;
31 | for (int j = 0, k = i; j < y_len && k >= 0; j++, k--) {
32 | sum += y[j] * temp[k];
33 | }
34 | result[i] = sum;
35 | }
36 | free(temp);
37 |
38 | *out = result;
39 | *out_len = result_len;
40 | return 0;
41 | }
42 |
43 | int gfsk_mod_create(float samples_per_symbol, float sensitivity, float bt, uint32_t max_input_buffer_length, gfsk_mod **mod) {
44 | struct gfsk_mod_t *result = malloc(sizeof(struct gfsk_mod_t));
45 | if (result == NULL) {
46 | return -ENOMEM;
47 | }
48 | // init all fields with 0 so that destroy_* method would work
49 | *result = (struct gfsk_mod_t) {0};
50 | result->temp_input_len = max_input_buffer_length * 8;
51 | result->temp_input = malloc(sizeof(float) * result->temp_input_len);
52 | if (result->temp_input == NULL) {
53 | gfsk_mod_destroy(result);
54 | return -ENOMEM;
55 | }
56 |
57 | size_t gaussian_taps_len = 4 * samples_per_symbol;
58 | float *gaussian_taps = NULL;
59 | int code = gaussian_taps_create(1.0F, samples_per_symbol, bt, gaussian_taps_len, &gaussian_taps);
60 | if (code != 0) {
61 | gfsk_mod_destroy(result);
62 | return code;
63 | }
64 | size_t square_wave_len = (int) samples_per_symbol;
65 | float *square_wave = malloc(sizeof(float) * square_wave_len);
66 | if (square_wave == NULL) {
67 | free(gaussian_taps);
68 | gfsk_mod_destroy(result);
69 | return -ENOMEM;
70 | }
71 | for (size_t i = 0; i < square_wave_len; i++) {
72 | square_wave[i] = 1.0F;
73 | }
74 |
75 | float *taps = NULL;
76 | size_t taps_len = 0;
77 | code = gfsk_mod_convolve(gaussian_taps, gaussian_taps_len, square_wave, square_wave_len, &taps, &taps_len);
78 | free(gaussian_taps);
79 | free(square_wave);
80 | if (code != 0) {
81 | gfsk_mod_destroy(result);
82 | return code;
83 | }
84 |
85 | code = interp_fir_filter_create(taps, taps_len, (int) samples_per_symbol, result->temp_input_len, &result->filter);
86 | if (code != 0) {
87 | free(taps);
88 | gfsk_mod_destroy(result);
89 | return code;
90 | }
91 |
92 | code = frequency_modulator_create(sensitivity, (int) samples_per_symbol * result->temp_input_len, &result->freq_mod);
93 | if (code != 0) {
94 | gfsk_mod_destroy(result);
95 | return code;
96 | }
97 |
98 | *mod = result;
99 | return 0;
100 | }
101 |
102 | void gfsk_mod_process(const uint8_t *input, size_t input_len, float complex **output, size_t *output_len, gfsk_mod *mod) {
103 | if (input_len > mod->temp_input_len / 8) {
104 | fprintf(stderr, "<3>requested buffer %zu is more than max: %zu\n", input_len, mod->temp_input_len / 8);
105 | *output = NULL;
106 | *output_len = 0;
107 | return;
108 | }
109 | size_t temp_index = 0;
110 | for (size_t i = 0; i < input_len; i++) {
111 | for (int j = 0; j < 8; j++) {
112 | int bit = (input[i] >> (7 - j)) & 1;
113 | if (bit == 0) {
114 | mod->temp_input[temp_index] = -1.0F;
115 | } else {
116 | mod->temp_input[temp_index] = 1.0F;
117 | }
118 | temp_index++;
119 | }
120 | }
121 |
122 | float *filtered = NULL;
123 | size_t filtered_len = 0;
124 | interp_fir_filter_process(mod->temp_input, temp_index, &filtered, &filtered_len, mod->filter);
125 |
126 | float complex *modulated = NULL;
127 | size_t modulated_len = 0;
128 | frequency_modulator_process(filtered, filtered_len, &modulated, &modulated_len, mod->freq_mod);
129 |
130 | *output = modulated;
131 | *output_len = modulated_len;
132 | }
133 |
134 | void gfsk_mod_destroy(gfsk_mod *mod) {
135 | if (mod == NULL) {
136 | return;
137 | }
138 | if (mod->temp_input != NULL) {
139 | free(mod->temp_input);
140 | }
141 | if (mod->filter != NULL) {
142 | interp_fir_filter_destroy(mod->filter);
143 | }
144 | if (mod->freq_mod != NULL) {
145 | frequency_modulator_destroy(mod->freq_mod);
146 | }
147 | free(mod);
148 | }
--------------------------------------------------------------------------------
/test/test_linked_list.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "../src/linked_list.h"
5 |
6 | linked_list *llist = NULL;
7 |
8 | struct sample_struct {
9 | int id;
10 | uint8_t *array;
11 | size_t array_len;
12 | };
13 |
14 | void sample_struct_destroy(void *data) {
15 | struct sample_struct *test = (struct sample_struct *) data;
16 | if (test->array != NULL) {
17 | free(test->array);
18 | }
19 | free(test);
20 | }
21 |
22 | bool sample_struct_selector(void *arg, void *data) {
23 | int *id = (int *) arg;
24 | struct sample_struct *test = (struct sample_struct *) data;
25 | if (test->id == *id) {
26 | return true;
27 | }
28 | return false;
29 | }
30 |
31 | void sample_struct_add(int id) {
32 | struct sample_struct *result = malloc(sizeof(struct sample_struct));
33 | ck_assert(result != NULL);
34 | *result = (struct sample_struct) {0};
35 |
36 | result->id = id;
37 | result->array_len = 8;
38 | result->array = malloc(sizeof(uint8_t) * result->array_len);
39 | ck_assert(result->array != NULL);
40 |
41 | int code = linked_list_add(result, &sample_struct_destroy, &llist);
42 | ck_assert_int_eq(code, 0);
43 | }
44 |
45 | bool sample_struct_selector_all(void *data) {
46 | return true;
47 | }
48 |
49 | bool sample_struct_selector_one(void *data) {
50 | struct sample_struct *test = (struct sample_struct *) data;
51 | if (test->id == 1) {
52 | return true;
53 | }
54 | return false;
55 | }
56 |
57 | void sample_struct_set_id(void *arg, void *data) {
58 | int *param = (int *) arg;
59 | struct sample_struct *test = (struct sample_struct *) data;
60 | test->id = *param;
61 | }
62 |
63 | START_TEST(test_remove_by_id) {
64 | sample_struct_add(1);
65 | int id = 8;
66 | void *result = linked_list_remove_by_id(&id, &sample_struct_selector, &llist);
67 | ck_assert(result == NULL);
68 | id = 1;
69 | result = linked_list_remove_by_id(&id, &sample_struct_selector, &llist);
70 | ck_assert(result != NULL);
71 | ck_assert(llist == NULL);
72 | sample_struct_destroy(result);
73 | }
74 | END_TEST
75 |
76 | START_TEST (test_foreach) {
77 | sample_struct_add(1);
78 | int id = 8;
79 | linked_list_foreach(&id, &sample_struct_set_id, llist);
80 |
81 | void *result = linked_list_find(&id, &sample_struct_selector, llist);
82 | ck_assert(result != NULL);
83 | struct sample_struct *actual = (struct sample_struct *) result;
84 | ck_assert_int_eq(id, actual->id);
85 | }
86 |
87 | END_TEST
88 |
89 | START_TEST (test_delete_by_selector) {
90 | sample_struct_add(1);
91 | sample_struct_add(2);
92 | linked_list_destroy_by_selector(&sample_struct_selector_all, &llist);
93 | ck_assert(llist == NULL);
94 | }
95 |
96 | END_TEST
97 |
98 | START_TEST (test_delete_by_selector1) {
99 | sample_struct_add(1);
100 | sample_struct_add(2);
101 | linked_list_destroy_by_selector(&sample_struct_selector_one, &llist);
102 | }
103 |
104 | END_TEST
105 |
106 | START_TEST (test_delete_last2) {
107 | sample_struct_add(1);
108 | sample_struct_add(2);
109 | int id = 1;
110 | linked_list_destroy_by_id(&id, &sample_struct_selector, &llist);
111 | id = 2;
112 | linked_list_destroy_by_id(&id, &sample_struct_selector, &llist);
113 | ck_assert(llist == NULL);
114 | }
115 |
116 | END_TEST
117 |
118 | START_TEST (test_delete_last) {
119 | sample_struct_add(1);
120 | int id = 1;
121 | linked_list_destroy_by_id(&id, &sample_struct_selector, &llist);
122 | ck_assert(llist == NULL);
123 | }
124 |
125 | END_TEST
126 |
127 | START_TEST (test_normal) {
128 | sample_struct_add(1);
129 | sample_struct_add(2);
130 |
131 | int id = 2;
132 | void *result = linked_list_find(&id, &sample_struct_selector, llist);
133 | ck_assert(result != NULL);
134 | struct sample_struct *actual = (struct sample_struct *) result;
135 | ck_assert_int_eq(id, actual->id);
136 | }
137 |
138 | END_TEST
139 |
140 | void teardown() {
141 | if (llist != NULL) {
142 | linked_list_destroy(llist);
143 | llist = NULL;
144 | }
145 | }
146 |
147 | void setup() {
148 | //do nothing
149 | }
150 |
151 | Suite *common_suite(void) {
152 | Suite *s;
153 | TCase *tc_core;
154 |
155 | s = suite_create("linked_list");
156 |
157 | /* Core test case */
158 | tc_core = tcase_create("Core");
159 |
160 | tcase_add_test(tc_core, test_normal);
161 | tcase_add_test(tc_core, test_delete_last);
162 | tcase_add_test(tc_core, test_delete_last2);
163 | tcase_add_test(tc_core, test_delete_by_selector);
164 | tcase_add_test(tc_core, test_delete_by_selector1);
165 | tcase_add_test(tc_core, test_foreach);
166 | tcase_add_test(tc_core, test_remove_by_id);
167 |
168 | tcase_add_checked_fixture(tc_core, setup, teardown);
169 | suite_add_tcase(s, tc_core);
170 |
171 | return s;
172 | }
173 |
174 | int main(void) {
175 | int number_failed;
176 | Suite *s;
177 | SRunner *sr;
178 |
179 | s = common_suite();
180 | sr = srunner_create(s);
181 |
182 | srunner_set_fork_status(sr, CK_NOFORK);
183 | srunner_run_all(sr, CK_NORMAL);
184 | number_failed = srunner_ntests_failed(sr);
185 | srunner_free(sr);
186 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
187 | }
188 |
--------------------------------------------------------------------------------
/src/sgpsdp/sgp_math.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Unit SGP_Math
3 | * Author: Dr TS Kelso
4 | * Original Version: 1991 Oct 30
5 | * Current Revision: 1998 Mar 17
6 | * Version: 3.00
7 | * Copyright: 1991-1998, All Rights Reserved
8 | *
9 | * ported to C by: Neoklis Kyriazis April 9 2001
10 | */
11 |
12 | #include "sgp4sdp4.h"
13 |
14 | /* Returns sign of a double */
15 | int Sign(double arg)
16 | {
17 | if (arg > 0)
18 | return (1);
19 | else if (arg < 0)
20 | return (-1);
21 | else
22 | return (0);
23 | }
24 |
25 | /* Returns square of a double */
26 | double Sqr(double arg)
27 | {
28 | return (arg * arg);
29 | }
30 |
31 | /* Returns cube of a double */
32 | double Cube(double arg)
33 | {
34 | return (arg * arg * arg);
35 | }
36 |
37 | /* Returns angle in radians from arg id degrees */
38 | double Radians(double arg)
39 | {
40 | return (arg * de2ra);
41 | }
42 |
43 | /* Returns angle in degrees from arg in rads */
44 | double Degrees(double arg)
45 | {
46 | return (arg / de2ra);
47 | }
48 |
49 | /* Returns the arcsine of the argument */
50 | double ArcSin(double arg)
51 | {
52 | if (fabs(arg) >= 1)
53 | return (Sign(arg) * pio2);
54 | else
55 | return (atan(arg / sqrt(1 - arg * arg)));
56 | }
57 |
58 | /* Returns orccosine of rgument */
59 | double ArcCos(double arg)
60 | {
61 | return (pio2 - ArcSin(arg));
62 | }
63 |
64 | /* Calculates scalar magnitude of a vector_t argument */
65 | void Magnitude(vector_t * v)
66 | {
67 | v->w = sqrt(Sqr(v->x) + Sqr(v->y) + Sqr(v->z));
68 | }
69 |
70 | /* Adds vectors v1 and v2 together to produce v3 */
71 | void Vec_Add(vector_t * v1, vector_t * v2, vector_t * v3)
72 | {
73 | v3->x = v1->x + v2->x;
74 | v3->y = v1->y + v2->y;
75 | v3->z = v1->z + v2->z;
76 |
77 | Magnitude(v3);
78 | }
79 |
80 | /* Subtracts vector v2 from v1 to produce v3 */
81 | void Vec_Sub(vector_t * v1, vector_t * v2, vector_t * v3)
82 | {
83 | v3->x = v1->x - v2->x;
84 | v3->y = v1->y - v2->y;
85 | v3->z = v1->z - v2->z;
86 |
87 | Magnitude(v3);
88 | }
89 |
90 | /* Multiplies the vector v1 by the scalar k to produce the vector v2 */
91 | void Scalar_Multiply(double k, vector_t * v1, vector_t * v2)
92 | {
93 | v2->x = k * v1->x;
94 | v2->y = k * v1->y;
95 | v2->z = k * v1->z;
96 | v2->w = fabs(k) * v1->w;
97 | }
98 |
99 | /* Multiplies the vector v1 by the scalar k */
100 | void Scale_Vector(double k, vector_t * v)
101 | {
102 | v->x *= k;
103 | v->y *= k;
104 | v->z *= k;
105 | Magnitude(v);
106 | }
107 |
108 | /* Returns the dot product of two vectors */
109 | double Dot(vector_t * v1, vector_t * v2)
110 | {
111 | return (v1->x * v2->x + v1->y * v2->y + v1->z * v2->z);
112 | }
113 |
114 | /* Calculates the angle between vectors v1 and v2 */
115 | double Angle(vector_t * v1, vector_t * v2)
116 | {
117 | Magnitude(v1);
118 | Magnitude(v2);
119 | return (ArcCos(Dot(v1, v2) / (v1->w * v2->w)));
120 | }
121 |
122 | /* Produces cross product of v1 and v2, and returns in v3 */
123 | void Cross(vector_t * v1, vector_t * v2, vector_t * v3)
124 | {
125 | v3->x = v1->y * v2->z - v1->z * v2->y;
126 | v3->y = v1->z * v2->x - v1->x * v2->z;
127 | v3->z = v1->x * v2->y - v1->y * v2->x;
128 | Magnitude(v3);
129 | }
130 |
131 | /* Normalizes a vector */
132 | void Normalize(vector_t * v)
133 | {
134 | v->x /= v->w;
135 | v->y /= v->w;
136 | v->z /= v->w;
137 | }
138 |
139 | /* Four-quadrant arctan function */
140 | double AcTan(double sinx, double cosx)
141 | {
142 | if (cosx == 0)
143 | {
144 | if (sinx > 0)
145 | return (pio2);
146 | else
147 | return (x3pio2);
148 | }
149 | else
150 | {
151 | if (cosx > 0)
152 | {
153 | if (sinx > 0)
154 | return (atan(sinx / cosx));
155 | else
156 | return (twopi + atan(sinx / cosx));
157 | }
158 | else
159 | return (pi + atan(sinx / cosx));
160 | }
161 |
162 | }
163 |
164 | /* Returns mod 2pi of argument */
165 | double FMod2p(double x)
166 | {
167 | int i;
168 | double ret_val;
169 |
170 | ret_val = x;
171 | i = ret_val / twopi;
172 | ret_val -= i * twopi;
173 | if (ret_val < 0)
174 | ret_val += twopi;
175 |
176 | return (ret_val);
177 | }
178 |
179 | /* Returns arg1 mod arg2 */
180 | double Modulus(double arg1, double arg2)
181 | {
182 | int i;
183 | double ret_val;
184 |
185 | ret_val = arg1;
186 | i = ret_val / arg2;
187 | ret_val -= i * arg2;
188 | if (ret_val < 0)
189 | ret_val += arg2;
190 |
191 | return (ret_val);
192 | }
193 |
194 | /* Returns fractional part of double argument */
195 | double Frac(double arg)
196 | {
197 | return (arg - floor(arg));
198 | }
199 |
200 | /* Returns argument rounded up to nearest integer */
201 | int Round(double arg)
202 | {
203 | return ((int)floor(arg + 0.5));
204 | }
205 |
206 | /* Returns the floor integer of a double arguement, as double */
207 | double Int(double arg)
208 | {
209 | return (floor(arg));
210 | }
211 |
212 | /* Converts the satellite's position and velocity */
213 | /* vectors from normalised values to km and km/sec */
214 | void Convert_Sat_State(vector_t * pos, vector_t * vel)
215 | {
216 | Scale_Vector(xkmper, pos);
217 | Scale_Vector(xkmper * xmnpda / secday, vel);
218 | }
219 |
--------------------------------------------------------------------------------
/src/dsp/clock_recovery_mm.c:
--------------------------------------------------------------------------------
1 | #include "clock_recovery_mm.h"
2 | #include "mmse_fir_interpolator.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | struct clock_mm_t {
10 | mmse_fir_interpolator *interp;
11 | float omega;
12 | float omega_mid;
13 | float omega_lim;
14 | float gain_omega;
15 | float mu;
16 | float gain_mu;
17 | float omega_relative_limit;
18 | float last_sample;
19 |
20 | float *working_buffer;
21 | size_t history_offset;
22 | size_t working_len_total;
23 |
24 | float *output;
25 | size_t output_len;
26 | };
27 |
28 | int
29 | clock_mm_create(float omega, float gain_omega, float mu, float gain_mu, float omega_relative_limit, size_t output_len,
30 | clock_mm **clock) {
31 | struct clock_mm_t *result = malloc(sizeof(struct clock_mm_t));
32 | if (result == NULL) {
33 | return -ENOMEM;
34 | }
35 | // init all fields with 0 so that destroy_* method would work
36 | *result = (struct clock_mm_t) {0};
37 | result->mu = mu;
38 | result->omega = omega;
39 | result->gain_omega = gain_omega;
40 | result->gain_mu = gain_mu;
41 | result->omega_relative_limit = omega_relative_limit;
42 | result->omega_mid = omega;
43 | result->omega_lim = result->omega_mid * result->omega_relative_limit;
44 |
45 | result->last_sample = 0.0F;
46 | int code = mmse_fir_interpolator_create(&result->interp);
47 | if (code != 0) {
48 | clock_mm_destroy(result);
49 | return code;
50 | }
51 | result->output_len = output_len;
52 | result->output = malloc(sizeof(float) * output_len);
53 | if (result->output == NULL) {
54 | clock_mm_destroy(result);
55 | return -ENOMEM;
56 | }
57 | result->history_offset = 0;
58 | result->working_len_total = output_len + mmse_fir_interpolator_taps(result->interp);
59 | result->working_buffer = volk_malloc(sizeof(float) * result->working_len_total, volk_get_alignment());
60 | if (result->working_buffer == NULL) {
61 | clock_mm_destroy(result);
62 | return -ENOMEM;
63 | }
64 | memset(result->working_buffer, 0, sizeof(float) * result->working_len_total);
65 |
66 | *clock = result;
67 | return 0;
68 | }
69 |
70 | float slice(float x) {
71 | return x < 0 ? -1.0F : 1.0F;
72 | }
73 |
74 | static inline float branchless_clip(float x, float clip) {
75 | return 0.5F * (fabsf(x + clip) - fabsf(x - clip));
76 | }
77 |
78 | void clock_mm_process(const float *input, size_t input_len, float **output, size_t *output_len, clock_mm *clock) {
79 | // even if mu == 1 input buffer should not be more than output buffer
80 | if (input_len > clock->output_len) {
81 | fprintf(stderr, "<3>requested buffer %zu is more than max: %zu\n", input_len, clock->output_len);
82 | *output = NULL;
83 | *output_len = 0;
84 | return;
85 | }
86 | // prepend history to the input
87 | memcpy(clock->working_buffer + clock->history_offset, input, input_len * sizeof(float));
88 | int taps_len = mmse_fir_interpolator_taps(clock->interp);
89 | int ii = 0; // input index
90 | int oo = 0; // output index
91 | int previous = 0;
92 | size_t working_len = clock->history_offset + input_len;
93 | // not enough input
94 | if (working_len < taps_len) {
95 | clock->history_offset = working_len;
96 | *output = NULL;
97 | *output_len = 0;
98 | return;
99 | }
100 | size_t max_index = working_len - (taps_len - 1);
101 | float mm_val;
102 |
103 | while (ii < max_index && oo < clock->output_len) {
104 | // produce output sample
105 | clock->output[oo] = mmse_fir_interpolator_process(clock->working_buffer + ii, clock->mu,
106 | clock->interp);
107 | if (isnan(clock->output[oo])) {
108 | clock->output[oo] = 0.0f;
109 | previous = ii;
110 | ii += (int) floorf(clock->omega);
111 | oo++;
112 | continue;
113 | }
114 |
115 | mm_val = slice(clock->last_sample) * clock->output[oo] - slice(clock->output[oo]) * clock->last_sample;
116 | clock->last_sample = clock->output[oo];
117 | previous = ii;
118 |
119 | clock->omega = clock->omega + clock->gain_omega * mm_val;
120 | clock->omega = clock->omega_mid + branchless_clip(clock->omega - clock->omega_mid, clock->omega_lim);
121 | clock->mu = clock->mu + clock->omega + clock->gain_mu * mm_val;
122 | ii += (int) floorf(clock->mu);
123 | clock->mu = clock->mu - floorf(clock->mu);
124 | oo++;
125 | }
126 |
127 | size_t last_index;
128 | if (ii > working_len) {
129 | last_index = previous;
130 | } else {
131 | last_index = ii;
132 | }
133 | clock->history_offset = working_len - last_index;
134 |
135 | memmove(clock->working_buffer, clock->working_buffer + last_index, sizeof(float) * clock->history_offset);
136 |
137 | *output = clock->output;
138 | *output_len = oo;
139 | }
140 |
141 | void clock_mm_destroy(clock_mm *clock) {
142 | if (clock == NULL) {
143 | return;
144 | }
145 | if (clock->interp != NULL) {
146 | mmse_fir_interpolator_destroy(clock->interp);
147 | }
148 | if (clock->output != NULL) {
149 | free(clock->output);
150 | }
151 | if (clock->working_buffer != NULL) {
152 | volk_free(clock->working_buffer);
153 | }
154 | free(clock);
155 | }
156 |
--------------------------------------------------------------------------------
/test/test_sgp4_002.c:
--------------------------------------------------------------------------------
1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 | /*
3 | Gpredict: Real-time satellite tracking and orbit prediction program
4 |
5 | Copyright (C) 2001-2008 Alexandru Csete.
6 |
7 | Comments, questions and bugreports should be submitted via
8 | http://sourceforge.net/projects/gpredict/
9 | More details can be found at the project home page:
10 |
11 | http://gpredict.oz9aec.net/
12 |
13 | This program is free software; you can redistribute it and/or modify
14 | it under the terms of the GNU General Public License as published by
15 | the Free Software Foundation; either version 2 of the License, or
16 | (at your option) any later version.
17 |
18 | This program is distributed in the hope that it will be useful,
19 | but WITHOUT ANY WARRANTY; without even the implied warranty of
20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 | GNU General Public License for more details.
22 |
23 | You should have received a copy of the GNU General Public License
24 | along with this program; if not, write to the
25 | Free Software Foundation, Inc.,
26 | 59 Temple Place, Suite 330,
27 | Boston, MA 02111-1307
28 | USA
29 | */
30 | /* Unit test for SGP4 */
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include "../src/sgpsdp/sgp4sdp4.h"
37 |
38 | #define TEST_STEPS 5
39 |
40 | /* structure to hold a set of data */
41 | typedef struct {
42 | double t;
43 | double x;
44 | double y;
45 | double z;
46 | double vx;
47 | double vy;
48 | double vz;
49 | } dataset_t;
50 |
51 |
52 | const dataset_t expected[TEST_STEPS] = {
53 | {0.0,
54 | 7473.37235249, 428.95458268, 5828.74803892,
55 | 5.1071513, 6.44468284, -0.18613096},
56 | {360.0,
57 | -3305.22249435, 32410.86724220, -24697.17847749,
58 | -1.30113538, -1.15131518, -0.28333528},
59 | {720.0,
60 | 14271.28902792, 24110.45647174, -4725.76149170,
61 | -0.32050445, 2.67984074, -2.08405289},
62 | {1080.0,
63 | -9990.05125819, 22717.38011629, -23616.90130945,
64 | -1.01667246, -2.29026759, 0.72892364},
65 | {1440.0,
66 | 9787.88496660, 33753.34020891, -15030.79330940,
67 | -1.09424947, 0.92358845, -1.52230928}
68 | };
69 |
70 |
71 | char tle_str[3][80];
72 | sat_t sat;
73 | FILE *fp = NULL;
74 |
75 | START_TEST(test_stipping_spaces2) {
76 | char tle_with_spaces[3][80] = {" ", "1 44406U 19038W 20069.88080907 .00000505 00000-0 32890-4 0 9992", "2 44406 97.5270 32.5584 0026284 107.4758 252.9348 15.12089395 37524"};
77 | sat_t result;
78 | int code = Get_Next_Tle_Set(tle_with_spaces, &result.tle);
79 | ck_assert_int_eq(code, 1);
80 | ck_assert_str_eq("", result.tle.sat_name);
81 | }
82 | END_TEST
83 |
84 | START_TEST(test_stipping_spaces) {
85 | char tle_with_spaces[3][80] = {"01234567890123456789123 ", "1 44406U 19038W 20069.88080907 .00000505 00000-0 32890-4 0 9992", "2 44406 97.5270 32.5584 0026284 107.4758 252.9348 15.12089395 37524"};
86 | sat_t result;
87 | int code = Get_Next_Tle_Set(tle_with_spaces, &result.tle);
88 | ck_assert_int_eq(code, 1);
89 | ck_assert_str_eq("01234567890123456789123", result.tle.sat_name);
90 | }
91 | END_TEST
92 |
93 | START_TEST(test_normal) {
94 |
95 | int i;
96 |
97 | /* read tle file */
98 | fp = fopen("test-002.tle", "r");
99 | ck_assert(fp != NULL);
100 | char *code = fgets(tle_str[0], 80, fp);
101 | ck_assert(code != NULL);
102 | code = fgets(tle_str[1], 80, fp);
103 | ck_assert(code != NULL);
104 | code = fgets(tle_str[2], 80, fp);
105 | ck_assert(code != NULL);
106 | int ret_code = Get_Next_Tle_Set(tle_str, &sat.tle);
107 | ck_assert_int_eq(ret_code, 1);
108 |
109 | select_ephemeris(&sat);
110 |
111 | for (i = 0; i < TEST_STEPS; i++) {
112 | SDP4(&sat, expected[i].t);
113 | Convert_Sat_State(&sat.pos, &sat.vel);
114 |
115 | ck_assert(fabsl(sat.pos.x - expected[i].x) < 0.00001);
116 | ck_assert(fabsl(sat.pos.y - expected[i].y) < 0.00001);
117 | ck_assert(fabsl(sat.pos.z - expected[i].z) < 0.00001);
118 | ck_assert(fabsl(sat.vel.x - expected[i].vx) < 0.00001);
119 | ck_assert(fabsl(sat.vel.y - expected[i].vy) < 0.00001);
120 | ck_assert(fabsl(sat.vel.z - expected[i].vz) < 0.00001);
121 | }
122 |
123 | }
124 | END_TEST
125 |
126 | void teardown() {
127 | if (fp != NULL) {
128 | fclose(fp);
129 | fp = NULL;
130 | }
131 | }
132 |
133 | void setup() {
134 | //do nothing
135 | }
136 |
137 | Suite *common_suite(void) {
138 | Suite *s;
139 | TCase *tc_core;
140 |
141 | s = suite_create("sgp4_002");
142 |
143 | /* Core test case */
144 | tc_core = tcase_create("Core");
145 |
146 | tcase_add_test(tc_core, test_normal);
147 | tcase_add_test(tc_core, test_stipping_spaces);
148 | tcase_add_test(tc_core, test_stipping_spaces2);
149 |
150 | tcase_add_checked_fixture(tc_core, setup, teardown);
151 | suite_add_tcase(s, tc_core);
152 |
153 | return s;
154 | }
155 |
156 | int main(void) {
157 | int number_failed;
158 | Suite *s;
159 | SRunner *sr;
160 |
161 | s = common_suite();
162 | sr = srunner_create(s);
163 |
164 | srunner_set_fork_status(sr, CK_NOFORK);
165 | srunner_run_all(sr, CK_NORMAL);
166 | number_failed = srunner_ntests_failed(sr);
167 | srunner_free(sr);
168 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
169 | }
--------------------------------------------------------------------------------
/src/dsp/interp_fir_filter.c:
--------------------------------------------------------------------------------
1 | #include "interp_fir_filter.h"
2 | #include
3 | #include
4 | #include "fir_filter.h"
5 |
6 | #include
7 |
8 | struct interp_fir_filter_t {
9 | float *taps;
10 | size_t taps_len;
11 |
12 | fir_filter **filters;
13 | size_t filters_len;
14 |
15 | float *output;
16 | size_t output_len;
17 | };
18 |
19 | int interp_fir_filter_roundup_to_interpolation(float *taps, size_t taps_len, uint8_t interpolation, float **result_taps, size_t *result_taps_len) {
20 | size_t len;
21 | // round up length to a multiple of the interpolation factor
22 | size_t n = taps_len % interpolation;
23 | if (n > 0) {
24 | n = interpolation - n;
25 | len = taps_len + n;
26 | } else {
27 | len = taps_len;
28 | }
29 |
30 | float *result = malloc(sizeof(float) * len);
31 | if (result == NULL) {
32 | return -ENOMEM;
33 | }
34 | memset(result, 0, sizeof(float) * len);
35 | memcpy(result, taps, sizeof(float) * taps_len);
36 |
37 | *result_taps = result;
38 | *result_taps_len = len;
39 | return 0;
40 | }
41 |
42 | float **interp_fir_filter_create_interleaved_taps(const float *taps, size_t taps_len, uint8_t interpolation) {
43 | // guaranteed to be integer. see interp_fir_filter_roundup_to_interpolation
44 | size_t filter_taps_len = taps_len / interpolation;
45 | float **interleaved_taps = malloc(sizeof(float *) * interpolation);
46 | if (interleaved_taps == NULL) {
47 | return NULL;
48 | }
49 | *interleaved_taps = (float *) {0};
50 |
51 | //init individual taps so that they can be freed by the corresponding filter
52 | int result_code = 0;
53 | for (uint8_t i = 0; i < interpolation; i++) {
54 | interleaved_taps[i] = malloc(sizeof(float) * filter_taps_len);
55 | if (interleaved_taps[i] == NULL) {
56 | result_code = -ENOMEM;
57 | }
58 | }
59 | if (result_code != 0) {
60 | for (uint8_t i = 0; i < interpolation; i++) {
61 | if (interleaved_taps[i] != NULL) {
62 | free(interleaved_taps[i]);
63 | }
64 | }
65 | free(interleaved_taps);
66 | return NULL;
67 | }
68 |
69 | for (size_t i = 0; i < taps_len; i++) {
70 | interleaved_taps[i % interpolation][i / interpolation] = taps[i];
71 | }
72 | return interleaved_taps;
73 | }
74 |
75 | int interp_fir_filter_create(float *taps, size_t taps_len, uint8_t interpolation, uint32_t max_input_buffer_length, interp_fir_filter **filter) {
76 | struct interp_fir_filter_t *result = malloc(sizeof(struct interp_fir_filter_t));
77 | if (result == NULL) {
78 | return -ENOMEM;
79 | }
80 | // init all fields with 0 so that destroy_* method would work
81 | *result = (struct interp_fir_filter_t) {0};
82 |
83 | result->output_len = max_input_buffer_length * interpolation;
84 | result->output = malloc(sizeof(float) * result->output_len);
85 | if (result->output == NULL) {
86 | interp_fir_filter_destroy(result);
87 | return -ENOMEM;
88 | }
89 |
90 | int code = interp_fir_filter_roundup_to_interpolation(taps, taps_len, interpolation, &result->taps, &result->taps_len);
91 | if (code != 0) {
92 | interp_fir_filter_destroy(result);
93 | return code;
94 | }
95 |
96 | float **interleaved_taps = interp_fir_filter_create_interleaved_taps(result->taps, result->taps_len, interpolation);
97 | if (interleaved_taps == NULL) {
98 | interp_fir_filter_destroy(result);
99 | return -ENOMEM;
100 | }
101 |
102 | result->filters_len = interpolation;
103 | result->filters = malloc(sizeof(fir_filter *) * result->filters_len);
104 | if (result->filters == NULL) {
105 | for (uint8_t i = 0; i < interpolation; i++) {
106 | free(interleaved_taps[i]);
107 | }
108 | free(interleaved_taps);
109 | interp_fir_filter_destroy(result);
110 | return -ENOMEM;
111 | }
112 |
113 | int result_code = 0;
114 | size_t filter_taps_len = result->taps_len / interpolation;
115 | for (size_t i = 0; i < result->filters_len; i++) {
116 | fir_filter *curFilter = NULL;
117 | code = fir_filter_create(1, interleaved_taps[i], filter_taps_len, max_input_buffer_length, sizeof(float), &curFilter);
118 | //init all filters anyway
119 | if (code != 0) {
120 | free(interleaved_taps[i]);
121 | result_code = code;
122 | result->filters[i] = NULL;
123 | } else {
124 | result->filters[i] = curFilter;
125 | }
126 | }
127 | //release array of pointers and not taps themselves
128 | free(interleaved_taps);
129 | if (result_code != 0) {
130 | interp_fir_filter_destroy(result);
131 | return result_code;
132 | }
133 |
134 | free(taps);
135 | *filter = result;
136 | return 0;
137 | }
138 |
139 | void interp_fir_filter_process(float *input, size_t input_len, float **output, size_t *output_len, interp_fir_filter *filter) {
140 | size_t result_len = 0;
141 | for (size_t i = 0; i < filter->filters_len; i++) {
142 | float *cur_output = NULL;
143 | size_t cur_output_len = 0;
144 | fir_filter_process(input, input_len, (void **) &cur_output, &cur_output_len, filter->filters[i]);
145 | result_len = filter->filters_len * cur_output_len;
146 | //de-interleave results
147 | for (size_t j = i, k = 0; j < result_len && k < cur_output_len; j += filter->filters_len, k++) {
148 | filter->output[j] = cur_output[k];
149 | }
150 | }
151 |
152 | *output_len = result_len;
153 | *output = filter->output;
154 | }
155 |
156 | void interp_fir_filter_destroy(interp_fir_filter *filter) {
157 | if (filter == NULL) {
158 | return;
159 | }
160 | if (filter->taps != NULL) {
161 | free(filter->taps);
162 | }
163 | if (filter->filters != NULL) {
164 | for (size_t i = 0; i < filter->filters_len; i++) {
165 | fir_filter_destroy(filter->filters[i]);
166 | }
167 | free(filter->filters);
168 | }
169 | if (filter->output != NULL) {
170 | free(filter->output);
171 | }
172 | free(filter);
173 | }
--------------------------------------------------------------------------------
/src/sdr/file_source.c:
--------------------------------------------------------------------------------
1 | #include "file_source.h"
2 | #include "../dsp/sig_source.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | struct file_device_t {
9 | uint32_t id;
10 |
11 | FILE *rx_file;
12 | FILE *tx_file;
13 |
14 | int64_t freq_offset;
15 | sig_source *signal;
16 |
17 | float complex *output;
18 | size_t output_len;
19 |
20 | bool running;
21 | pthread_mutex_t mutex;
22 | pthread_cond_t condition;
23 | };
24 |
25 | void file_source_stop(void *plugin) {
26 | file_device *device = (file_device *) plugin;
27 | pthread_mutex_lock(&device->mutex);
28 | device->running = false;
29 | pthread_cond_broadcast(&device->condition);
30 | pthread_mutex_unlock(&device->mutex);
31 | }
32 |
33 | int file_source_create(uint32_t id, const char *rx_filename, const char *tx_filename, uint64_t sampling_freq, int64_t freq_offset, uint32_t max_output_buffer_length, sdr_device **output) {
34 | struct file_device_t *device = malloc(sizeof(struct file_device_t));
35 | if (device == NULL) {
36 | return -ENOMEM;
37 | }
38 | *device = (struct file_device_t) {0};
39 | device->id = id;
40 | device->freq_offset = freq_offset;
41 | device->output_len = max_output_buffer_length;
42 | device->output = malloc(sizeof(float complex) * device->output_len);
43 | if (device->output == NULL) {
44 | file_source_destroy(device);
45 | return -ENOMEM;
46 | }
47 | device->condition = (pthread_cond_t) PTHREAD_COND_INITIALIZER;
48 | device->mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
49 | device->running = true;
50 |
51 | if (freq_offset != 0) {
52 | int code = sig_source_create(1.0F, sampling_freq, max_output_buffer_length, &device->signal);
53 | if (code != 0) {
54 | fprintf(stderr, "<3>[%d] unable to create signal generator\n", device->id);
55 | file_source_destroy(device);
56 | return -1;
57 | }
58 | }
59 |
60 | if (rx_filename != NULL) {
61 | device->rx_file = fopen(rx_filename, "rb");
62 | if (device->rx_file == NULL) {
63 | fprintf(stderr, "<3>[%d] unable to open file for input: %s\n", device->id, rx_filename);
64 | file_source_destroy(device);
65 | return -1;
66 | }
67 | }
68 |
69 | if (tx_filename != NULL) {
70 | device->tx_file = fopen(tx_filename, "wb");
71 | if (device->tx_file == NULL) {
72 | fprintf(stderr, "<3>[%d] unable to open file for output: %s\n", device->id, tx_filename);
73 | file_source_destroy(device);
74 | return -1;
75 | }
76 | }
77 |
78 | struct sdr_device_t *result = malloc(sizeof(struct sdr_device_t));
79 | if (result == NULL) {
80 | file_source_destroy(device);
81 | return -ENOMEM;
82 | }
83 | result->plugin = device;
84 | result->destroy = file_source_destroy;
85 | result->sdr_process_rx = file_source_process_rx;
86 | result->sdr_process_tx = file_source_process_tx;
87 | result->stop_rx = file_source_stop;
88 |
89 | *output = result;
90 | return 0;
91 | }
92 |
93 | int file_source_process_rx(float complex **output, size_t *output_len, void *plugin) {
94 | file_device *device = (file_device *) plugin;
95 | if (device->rx_file == NULL) {
96 | fprintf(stderr, "<3>[%d] rx file was not initialized\n", device->id);
97 | *output = NULL;
98 | *output_len = 0;
99 | return -1;
100 | }
101 | size_t actually_read = fread(device->output, sizeof(float complex), device->output_len, device->rx_file);
102 | if (actually_read == 0) {
103 | // on any error terminate early
104 | if (ferror(device->rx_file) != 0) {
105 | *output = NULL;
106 | *output_len = 0;
107 | return -1;
108 | }
109 | // wait until client disconnects
110 | pthread_mutex_lock(&device->mutex);
111 | while (device->running) {
112 | pthread_cond_wait(&device->condition, &device->mutex);
113 | }
114 | pthread_mutex_unlock(&device->mutex);
115 | *output = NULL;
116 | *output_len = 0;
117 | return -1;
118 | }
119 | if (device->signal != NULL) {
120 | float complex *sig_output = NULL;
121 | size_t sig_output_len = 0;
122 | sig_source_multiply(device->freq_offset, device->output, actually_read, &sig_output, &sig_output_len, device->signal);
123 | *output = sig_output;
124 | *output_len = sig_output_len;
125 | } else {
126 | *output = device->output;
127 | *output_len = actually_read;
128 | }
129 | return 0;
130 | }
131 |
132 | int file_source_process_tx(float complex *input, size_t input_len, void *plugin) {
133 | file_device *device = (file_device *) plugin;
134 | if (input_len > device->output_len) {
135 | fprintf(stderr, "<3>requested buffer %zu is more than max: %zu\n", input_len, device->output_len);
136 | return -1;
137 | }
138 | if (device->tx_file == NULL) {
139 | fprintf(stderr, "<3>[%d] tx file was not initialized\n", device->id);
140 | return -1;
141 | }
142 | if (device->signal != NULL) {
143 | float complex *sig_output = NULL;
144 | size_t sig_output_len = 0;
145 | sig_source_multiply(device->freq_offset, input, input_len, &sig_output, &sig_output_len, device->signal);
146 | //ignore actually written
147 | fwrite(sig_output, sizeof(float complex), sig_output_len, device->tx_file);
148 | } else {
149 | //ignore actually written
150 | fwrite(input, sizeof(float complex), input_len, device->tx_file);
151 | }
152 | return 0;
153 | }
154 |
155 | void file_source_destroy(void *plugin) {
156 | if (plugin == NULL) {
157 | return;
158 | }
159 | file_device *device = (file_device *) plugin;
160 | if (device->rx_file != NULL) {
161 | fclose(device->rx_file);
162 | }
163 | if (device->tx_file != NULL) {
164 | fclose(device->tx_file);
165 | }
166 | if (device->output != NULL) {
167 | free(device->output);
168 | }
169 | if (device->signal != NULL) {
170 | sig_source_destroy(device->signal);
171 | }
172 | free(device);
173 | }
--------------------------------------------------------------------------------
/test/test_doppler.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "../src/dsp/doppler.h"
6 | #include "utils.h"
7 | #include
8 |
9 | FILE *input_file = NULL;
10 | uint8_t *input_buffer = NULL;
11 | FILE *expected_file = NULL;
12 | uint8_t *expected_buffer = NULL;
13 | doppler *dopp = NULL;
14 | char tle[3][80] = {"LUCKY-7", "1 44406U 19038W 20069.88080907 .00000505 00000-0 32890-4 0 9992", "2 44406 97.5270 32.5584 0026284 107.4758 252.9348 15.12089395 37524"};
15 | int max_buffer_length = 2000;
16 |
17 | START_TEST (test_invalid_arguments) {
18 | int code = doppler_create(53.72F, 47.57F, 0.0F, 48000, 437525000, 0, 1583840449, max_buffer_length, tle, &dopp);
19 | ck_assert_int_eq(code, 0);
20 |
21 | float complex *output = NULL;
22 | size_t output_len = 0;
23 | doppler_process_rx(NULL, 12, &output, &output_len, dopp);
24 | ck_assert(output == NULL);
25 |
26 | const float buffer[2] = {1, 2};
27 | doppler_process_rx((float complex *) buffer, 0, &output, &output_len, dopp);
28 | ck_assert(output == NULL);
29 |
30 | size_t input_buffer_len = max_buffer_length + 1;
31 | input_buffer = malloc(sizeof(float complex) * input_buffer_len);
32 | ck_assert(input_buffer != NULL);
33 | doppler_process_rx((float complex *) input_buffer, input_buffer_len, &output, &output_len, dopp);
34 | ck_assert(output == NULL);
35 | }
36 |
37 | END_TEST
38 |
39 | void assert_success_rx(int buffer_length, const char *expected_filename) {
40 | int code = doppler_create(53.72F, 47.57F, 0.0F, 48000, 437525000, 0, 1583840449, buffer_length, tle, &dopp);
41 | ck_assert_int_eq(code, 0);
42 |
43 | input_file = fopen("lucky7.cf32", "rb");
44 | ck_assert(input_file != NULL);
45 | expected_file = fopen(expected_filename, "rb");
46 | ck_assert(expected_file != NULL);
47 |
48 | input_buffer = malloc(max_buffer_length * sizeof(float complex));
49 | ck_assert(input_buffer != NULL);
50 | expected_buffer = malloc(max_buffer_length * sizeof(float complex));
51 | ck_assert(expected_buffer != NULL);
52 | while (true) {
53 | size_t actually_read = fread(input_buffer, sizeof(float complex), max_buffer_length, input_file);
54 | if (actually_read == 0) {
55 | break;
56 | }
57 | float complex *output = NULL;
58 | size_t output_len = 0;
59 | doppler_process_rx((float complex *) input_buffer, actually_read, &output, &output_len, dopp);
60 |
61 | size_t actually_expected_read = fread(expected_buffer, sizeof(float complex), actually_read, expected_file);
62 | ck_assert_int_eq(actually_read, actually_expected_read);
63 |
64 | assert_complex_array((const float *) expected_buffer, actually_expected_read, output, output_len);
65 | }
66 | }
67 |
68 | START_TEST (test_success_rx) {
69 | assert_success_rx(max_buffer_length, "lucky7.expected.cf32");
70 | }
71 |
72 | END_TEST
73 |
74 | START_TEST (test_success_rx_47000) {
75 | assert_success_rx(47000, "lucky7.expected.47000.cf32");
76 | }
77 |
78 | END_TEST
79 |
80 | START_TEST (test_success_rx_95000) {
81 | assert_success_rx(95000, "lucky7.expected.95000.cf32");
82 | }
83 |
84 | END_TEST
85 |
86 |
87 | START_TEST (test_success_tx) {
88 | int code = doppler_create(53.72F, 47.57F, 0.0F, 48000, 437525000, 0, 1583840449, max_buffer_length, tle, &dopp);
89 | ck_assert_int_eq(code, 0);
90 |
91 | // use RX inverted input data for test
92 | input_file = fopen("lucky7.expected.cf32", "rb");
93 | ck_assert(input_file != NULL);
94 | expected_file = fopen("lucky7.cf32", "rb");
95 | ck_assert(expected_file != NULL);
96 |
97 | input_buffer = malloc(max_buffer_length * sizeof(float complex));
98 | ck_assert(input_buffer != NULL);
99 | expected_buffer = malloc(max_buffer_length * sizeof(float complex));
100 | ck_assert(expected_buffer != NULL);
101 | while (true) {
102 | size_t actually_read = fread(input_buffer, sizeof(float complex), max_buffer_length, input_file);
103 | if (actually_read == 0) {
104 | break;
105 | }
106 | float complex *output = NULL;
107 | size_t output_len = 0;
108 | doppler_process_tx((float complex *) input_buffer, actually_read, &output, &output_len, dopp);
109 |
110 | size_t actually_expected_read = fread(expected_buffer, sizeof(float complex), actually_read, expected_file);
111 | ck_assert_int_eq(actually_read, actually_expected_read);
112 |
113 | assert_complex_array((const float *) expected_buffer, actually_expected_read, output, output_len);
114 | }
115 | }
116 |
117 | END_TEST
118 |
119 | void teardown() {
120 | if (dopp != NULL) {
121 | doppler_destroy(dopp);
122 | dopp = NULL;
123 | }
124 | if (input_file != NULL) {
125 | fclose(input_file);
126 | input_file = NULL;
127 | }
128 | if (input_buffer != NULL) {
129 | free(input_buffer);
130 | input_buffer = NULL;
131 | }
132 | if (expected_file != NULL) {
133 | fclose(expected_file);
134 | expected_file = NULL;
135 | }
136 | if (expected_buffer != NULL) {
137 | free(expected_buffer);
138 | expected_buffer = NULL;
139 | }
140 | }
141 |
142 | void setup() {
143 | //do nothing
144 | }
145 |
146 | Suite *common_suite(void) {
147 | Suite *s;
148 | TCase *tc_core;
149 |
150 | s = suite_create("doppler");
151 |
152 | /* Core test case */
153 | tc_core = tcase_create("Core");
154 |
155 | tcase_add_test(tc_core, test_success_rx);
156 | tcase_add_test(tc_core, test_success_rx_47000);
157 | tcase_add_test(tc_core, test_success_rx_95000);
158 | tcase_add_test(tc_core, test_success_tx);
159 | tcase_add_test(tc_core, test_invalid_arguments);
160 |
161 | tcase_add_checked_fixture(tc_core, setup, teardown);
162 | suite_add_tcase(s, tc_core);
163 |
164 | return s;
165 | }
166 |
167 | int main(void) {
168 | int number_failed;
169 | Suite *s;
170 | SRunner *sr;
171 |
172 | s = common_suite();
173 | sr = srunner_create(s);
174 |
175 | srunner_set_fork_status(sr, CK_NOFORK);
176 | srunner_run_all(sr, CK_NORMAL);
177 | number_failed = srunner_ntests_failed(sr);
178 | srunner_free(sr);
179 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
180 | }
181 |
--------------------------------------------------------------------------------
/src/sdr/iio_lib.c:
--------------------------------------------------------------------------------
1 | #include "iio_lib.h"
2 | #include
3 | #include
4 | #include
5 |
6 | void *iio_lib_dlsym(void *handle, const char *symbol) {
7 | void *result = dlsym(handle, symbol);
8 | if (result == NULL) {
9 | fprintf(stderr, "unable to load function %s: %s\n", symbol, dlerror());
10 | }
11 | return result;
12 | }
13 |
14 | int iio_lib_create(iio_lib **lib) {
15 | struct iio_lib_t *result = malloc(sizeof(struct iio_lib_t));
16 | if (result == NULL) {
17 | return -ENOMEM;
18 | }
19 | *result = (struct iio_lib_t) {0};
20 |
21 | #if defined(__APPLE__)
22 | void *handle = dlopen("iio", RTLD_LAZY);
23 | #else
24 | void *handle = dlopen("libiio.so", RTLD_LAZY);
25 | #endif
26 | if (!handle) {
27 | fprintf(stderr, "unable to load libiio: %s\n", dlerror());
28 | iio_lib_destroy(result);
29 | return -1;
30 | }
31 | result->handle = handle;
32 | result->iio_buffer_first = iio_lib_dlsym(result->handle, "iio_buffer_first");
33 | if (result->iio_buffer_first == NULL) {
34 | iio_lib_destroy(result);
35 | return -1;
36 | }
37 | result->iio_buffer_end = iio_lib_dlsym(result->handle, "iio_buffer_end");
38 | if (result->iio_buffer_end == NULL) {
39 | iio_lib_destroy(result);
40 | return -1;
41 | }
42 | result->iio_buffer_push_partial = iio_lib_dlsym(result->handle, "iio_buffer_push_partial");
43 | if (result->iio_buffer_push_partial == NULL) {
44 | iio_lib_destroy(result);
45 | return -1;
46 | }
47 | result->iio_buffer_refill = iio_lib_dlsym(result->handle, "iio_buffer_refill");
48 | if (result->iio_buffer_refill == NULL) {
49 | iio_lib_destroy(result);
50 | return -1;
51 | }
52 | result->iio_context_find_device = iio_lib_dlsym(result->handle, "iio_context_find_device");
53 | if (result->iio_context_find_device == NULL) {
54 | iio_lib_destroy(result);
55 | return -1;
56 | }
57 | result->iio_device_find_channel = iio_lib_dlsym(result->handle, "iio_device_find_channel");
58 | if (result->iio_device_find_channel == NULL) {
59 | iio_lib_destroy(result);
60 | return -1;
61 | }
62 | result->iio_strerror = iio_lib_dlsym(result->handle, "iio_strerror");
63 | if (result->iio_strerror == NULL) {
64 | iio_lib_destroy(result);
65 | return -1;
66 | }
67 | result->iio_channel_attr_write = iio_lib_dlsym(result->handle, "iio_channel_attr_write");
68 | if (result->iio_channel_attr_write == NULL) {
69 | iio_lib_destroy(result);
70 | return -1;
71 | }
72 | result->iio_channel_attr_write_longlong = iio_lib_dlsym(result->handle, "iio_channel_attr_write_longlong");
73 | if (result->iio_channel_attr_write_longlong == NULL) {
74 | iio_lib_destroy(result);
75 | return -1;
76 | }
77 | result->iio_device_attr_write_bool = iio_lib_dlsym(result->handle, "iio_device_attr_write_bool");
78 | if (result->iio_device_attr_write_bool == NULL) {
79 | iio_lib_destroy(result);
80 | return -1;
81 | }
82 | result->iio_channel_attr_write_bool = iio_lib_dlsym(result->handle, "iio_channel_attr_write_bool");
83 | if (result->iio_channel_attr_write_bool == NULL) {
84 | iio_lib_destroy(result);
85 | return -1;
86 | }
87 | result->iio_channel_attr_write_double = iio_lib_dlsym(result->handle, "iio_channel_attr_write_double");
88 | if (result->iio_channel_attr_write_double == NULL) {
89 | iio_lib_destroy(result);
90 | return -1;
91 | }
92 | result->iio_device_attr_write_raw = iio_lib_dlsym(result->handle, "iio_device_attr_write_raw");
93 | if (result->iio_device_attr_write_raw == NULL) {
94 | iio_lib_destroy(result);
95 | return -1;
96 | }
97 | result->iio_create_scan_context = iio_lib_dlsym(result->handle, "iio_create_scan_context");
98 | if (result->iio_create_scan_context == NULL) {
99 | iio_lib_destroy(result);
100 | return -1;
101 | }
102 | result->iio_scan_context_get_info_list = iio_lib_dlsym(result->handle, "iio_scan_context_get_info_list");
103 | if (result->iio_scan_context_get_info_list == NULL) {
104 | iio_lib_destroy(result);
105 | return -1;
106 | }
107 | result->iio_scan_context_destroy = iio_lib_dlsym(result->handle, "iio_scan_context_destroy");
108 | if (result->iio_scan_context_destroy == NULL) {
109 | iio_lib_destroy(result);
110 | return -1;
111 | }
112 | result->iio_context_info_get_uri = iio_lib_dlsym(result->handle, "iio_context_info_get_uri");
113 | if (result->iio_context_info_get_uri == NULL) {
114 | iio_lib_destroy(result);
115 | return -1;
116 | }
117 | result->iio_create_context_from_uri = iio_lib_dlsym(result->handle, "iio_create_context_from_uri");
118 | if (result->iio_create_context_from_uri == NULL) {
119 | iio_lib_destroy(result);
120 | return -1;
121 | }
122 | result->iio_context_info_list_free = iio_lib_dlsym(result->handle, "iio_context_info_list_free");
123 | if (result->iio_context_info_list_free == NULL) {
124 | iio_lib_destroy(result);
125 | return -1;
126 | }
127 | result->iio_context_set_timeout = iio_lib_dlsym(result->handle, "iio_context_set_timeout");
128 | if (result->iio_context_set_timeout == NULL) {
129 | iio_lib_destroy(result);
130 | return -1;
131 | }
132 | result->iio_channel_enable = iio_lib_dlsym(result->handle, "iio_channel_enable");
133 | if (result->iio_channel_enable == NULL) {
134 | iio_lib_destroy(result);
135 | return -1;
136 | }
137 | result->iio_device_create_buffer = iio_lib_dlsym(result->handle, "iio_device_create_buffer");
138 | if (result->iio_device_create_buffer == NULL) {
139 | iio_lib_destroy(result);
140 | return -1;
141 | }
142 | result->iio_buffer_destroy = iio_lib_dlsym(result->handle, "iio_buffer_destroy");
143 | if (result->iio_buffer_destroy == NULL) {
144 | iio_lib_destroy(result);
145 | return -1;
146 | }
147 | result->iio_channel_disable = iio_lib_dlsym(result->handle, "iio_channel_disable");
148 | if (result->iio_channel_disable == NULL) {
149 | iio_lib_destroy(result);
150 | return -1;
151 | }
152 | result->iio_context_destroy = iio_lib_dlsym(result->handle, "iio_context_destroy");
153 | if (result->iio_context_destroy == NULL) {
154 | iio_lib_destroy(result);
155 | return -1;
156 | }
157 |
158 | *lib = result;
159 | return 0;
160 | }
161 |
162 | void iio_lib_destroy(iio_lib *lib) {
163 | if (lib == NULL) {
164 | return;
165 | }
166 | if (lib->handle != NULL) {
167 | dlclose(lib->handle);
168 | }
169 | free(lib);
170 | }
171 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8)
2 | project(sdr-modem)
3 |
4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
6 | if (CMAKE_BUILD_TYPE MATCHES Debug)
7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
8 | endif ()
9 |
10 | add_library(sdr_modemLib
11 | ${CMAKE_CURRENT_SOURCE_DIR}/src/api_utils.c
12 | ${CMAKE_CURRENT_SOURCE_DIR}/src/api.pb-c.c
13 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp_worker.c
14 | ${CMAKE_CURRENT_SOURCE_DIR}/src/linked_list.c
15 | ${CMAKE_CURRENT_SOURCE_DIR}/src/queue.c
16 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sdr_worker.c
17 | ${CMAKE_CURRENT_SOURCE_DIR}/src/server_config.c
18 | ${CMAKE_CURRENT_SOURCE_DIR}/src/tcp_server.c
19 | ${CMAKE_CURRENT_SOURCE_DIR}/src/tcp_utils.c
20 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/clock_recovery_mm.c
21 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/dc_blocker.c
22 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/doppler.c
23 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/fir_filter.c
24 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/frequency_modulator.c
25 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/fsk_demod.c
26 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/gaussian_taps.c
27 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/gfsk_mod.c
28 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/interp_fir_filter.c
29 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/lpf.c
30 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/lpf_taps.c
31 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/mmse_fir_interpolator.c
32 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/quadrature_demod.c
33 | ${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/sig_source.c
34 | ${CMAKE_CURRENT_SOURCE_DIR}/src/math/fast_atan2f.c
35 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sdr/plutosdr.c
36 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sdr/file_source.c
37 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sdr/iio_lib.c
38 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sdr/sdr_server_client.c
39 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sgpsdp/sgp4sdp4.c
40 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sgpsdp/sgp_in.c
41 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sgpsdp/sgp_math.c
42 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sgpsdp/sgp_obs.c
43 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sgpsdp/sgp_time.c
44 | ${CMAKE_CURRENT_SOURCE_DIR}/src/sgpsdp/solar.c
45 | )
46 |
47 | find_package(PkgConfig REQUIRED)
48 |
49 | pkg_check_modules(PC_VOLK REQUIRED volk)
50 | include_directories(${PC_VOLK_INCLUDE_DIRS})
51 | link_directories(${PC_VOLK_LIBRARY_DIRS})
52 | target_link_libraries(sdr_modemLib ${PC_VOLK_LIBRARIES})
53 |
54 | pkg_check_modules(PC_LIBCONFIG REQUIRED libconfig)
55 | include_directories(${PC_LIBCONFIG_INCLUDE_DIRS})
56 | link_directories(${PC_LIBCONFIG_LIBRARY_DIRS})
57 | target_link_libraries(sdr_modemLib ${PC_LIBCONFIG_LIBRARIES})
58 |
59 | pkg_check_modules(PC_LIBPROTOBUFC REQUIRED libprotobuf-c)
60 | include_directories(${PC_LIBPROTOBUFC_INCLUDE_DIRS})
61 | link_directories(${PC_LIBPROTOBUFC_LIBRARY_DIRS})
62 | target_link_libraries(sdr_modemLib ${PC_LIBPROTOBUFC_LIBRARIES})
63 |
64 | if (APPLE)
65 | find_path(LIBIIO_INCLUDE_DIRS iio.h)
66 | include_directories(${LIBIIO_INCLUDE_DIRS})
67 | link_directories("/Library/Frameworks/iio.framework")
68 | else ()
69 | pkg_check_modules(PC_LIBIIO REQUIRED libiio)
70 | include_directories(${PC_LIBIIO_INCLUDE_DIRS})
71 | endif ()
72 |
73 | find_package(Threads REQUIRED)
74 | target_link_libraries(sdr_modemLib ${CMAKE_THREAD_LIBS_INIT})
75 | target_link_libraries(sdr_modemLib m ${CMAKE_DL_LIBS})
76 |
77 | add_executable(sdr_modem ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c)
78 | target_link_libraries(sdr_modem sdr_modemLib)
79 |
80 | install(TARGETS sdr_modem DESTINATION /usr/bin/)
81 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/config.conf DESTINATION /etc/sdr-modem/)
82 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/sdr-modem.service DESTINATION /lib/systemd/system/)
83 |
84 | enable_testing()
85 |
86 | file(GLOB TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test/test_*.c)
87 | file(GLOB TEST_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test/resources/*)
88 | file(COPY ${TEST_RESOURCES} DESTINATION "${CMAKE_BINARY_DIR}")
89 | file(GLOB PERF_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test/perf_*.c)
90 |
91 | file(GLOB AUX_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/api.pb-c.c)
92 | list(FILTER AUX_TEST_SOURCES EXCLUDE REGEX "test_.*\\.c")
93 | list(FILTER AUX_TEST_SOURCES EXCLUDE REGEX "perf_.*\\.c")
94 | list(FILTER AUX_TEST_SOURCES EXCLUDE REGEX "mock_.*\\.c")
95 |
96 | add_library(sdr_modemTestLib ${AUX_TEST_SOURCES})
97 |
98 | pkg_check_modules(PC_CHECK REQUIRED check)
99 | include_directories(${PC_CHECK_INCLUDE_DIRS})
100 | link_directories(${PC_CHECK_LIBRARY_DIRS})
101 |
102 | foreach (curTest ${TEST_SOURCES})
103 | get_filename_component(curTestName ${curTest} NAME_WE)
104 | add_test(NAME ${curTestName} COMMAND ${curTestName} ${curTest})
105 | add_executable(${curTestName} ${curTest})
106 | target_link_libraries(${curTestName} sdr_modemLib sdr_modemTestLib ${PC_CHECK_LIBRARIES})
107 | endforeach ()
108 |
109 | foreach (curPerfTest ${PERF_SOURCES})
110 | get_filename_component(curPerfTestName ${curPerfTest} NAME_WE)
111 | add_executable(${curPerfTestName} ${curPerfTest})
112 | target_link_libraries(${curPerfTestName} sdr_modemLib sdr_modemTestLib ${PC_CHECK_LIBRARIES})
113 | endforeach ()
114 |
115 | if (CMAKE_BUILD_TYPE MATCHES Debug)
116 | add_custom_target("coverage")
117 | add_custom_command(TARGET "coverage" COMMAND gcov ${CMAKE_BINARY_DIR}/CMakeFiles/sdr_modemLib.dir/src/*.c.o ${CMAKE_BINARY_DIR}/CMakeFiles/sdr_modemLib.dir/src/dsp/*.c.o ${CMAKE_BINARY_DIR}/CMakeFiles/sdr_modemLib.dir/src/math/*.c.o ${CMAKE_BINARY_DIR}/CMakeFiles/sdr_modemLib.dir/src/sdr/*.c.o ${CMAKE_BINARY_DIR}/CMakeFiles/sdr_modemLib.dir/src/sgpsdp/*.c.o)
118 | endif ()
119 |
120 | set(CPACK_GENERATOR "DEB")
121 | set(CPACK_DEBIAN_PACKAGE_NAME "sdr-modem")
122 | set(OS_CODENAME "$ENV{VERSION_CODENAME}")
123 | if (OS_CODENAME STREQUAL "trixie" OR
124 | OS_CODENAME STREQUAL "noble")
125 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libvolk-bin, libprotobuf-c1, libconfig11, zlib1g")
126 | else ()
127 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libvolk2-bin, libprotobuf-c1, libconfig9, zlib1g")
128 | endif()
129 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Andrey Rodionov ")
130 | set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Modem based on software defined radios")
131 | set(CPACK_DEBIAN_PACKAGE_SECTION "comm")
132 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/dernasherbrezon/sdr-modem")
133 | set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/debian/postinst;${CMAKE_CURRENT_SOURCE_DIR}/debian/prerm;${CMAKE_CURRENT_SOURCE_DIR}/debian/postrm")
134 | set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON)
135 | set(CPACK_DEBIAN_FILE_NAME "sdr-modem_${CPACK_DEBIAN_PACKAGE_VERSION}_${CUSTOM_ARCHITECTURE}.deb")
136 | include(CPack)
--------------------------------------------------------------------------------
/test/test_frequency_modulator.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "../src/dsp/frequency_modulator.h"
6 | #include "utils.h"
7 |
8 | frequency_modulator *mod = NULL;
9 | float *float_input = NULL;
10 |
11 | START_TEST (test_input_exceeded) {
12 | int code = frequency_modulator_create(1.2F, 100, &mod);
13 | ck_assert_int_eq(code, 0);
14 |
15 | setup_input_data(&float_input, 0, 200);
16 |
17 | float complex *output = NULL;
18 | size_t output_len = 0;
19 | frequency_modulator_process(float_input, 200, &output, &output_len, mod);
20 | ck_assert_int_eq(code, 0);
21 | }
22 | END_TEST
23 |
24 | START_TEST (test_normal) {
25 | int code = frequency_modulator_create(1.2F, 1000, &mod);
26 | ck_assert_int_eq(code, 0);
27 |
28 | setup_input_data(&float_input, 0, 200);
29 |
30 | float complex *output = NULL;
31 | size_t output_len = 0;
32 | frequency_modulator_process(float_input, 100, &output, &output_len, mod);
33 |
34 | const float expected[] = {1.000000F, 0.000000F, 0.362358F, 0.932039F, -0.896758F, -0.442521F, 0.608351F, 0.793668F, 0.843854F, -0.536573F, 0.660317F, -0.750987F, 0.997739F, 0.067209F, -0.575552F, 0.817765F, 0.709299F, -0.704907F, -0.829308F, -0.558792F, -0.999647F, -0.026551F, -0.789881F,
35 | -0.613259F, 0.797425F, -0.603419F, -0.727760F, 0.685832F, 0.943984F, 0.329991F, 0.871147F, -0.491022F, 0.986774F, -0.162103F, 0.182142F, 0.983272F, -0.543253F, -0.839569F, -0.232403F, 0.972620F, 0.782203F, 0.623024F, 0.738564F, 0.674184F, -0.422603F, 0.906315F,
36 | -0.235772F, -0.971808F, -0.283691F, 0.958916F, 0.903679F, 0.428210F, 0.974437F, 0.224662F, 0.352398F, 0.935850F, -0.968128F, -0.250456F, 0.879674F, 0.475577F, 0.359101F, -0.933299F, -0.131414F, -0.991328F, 0.538888F, -0.842377F, 0.622249F, 0.782820F, -0.653598F,
37 | -0.756842F, -0.432232F, 0.901762F, 0.329070F, 0.944306F, -0.082316F, 0.996606F, -0.991654F, -0.128931F, 0.981123F, -0.193385F, -0.776629F, -0.629958F, -0.927162F, 0.374662F, -0.969038F, 0.246910F, -0.466910F, -0.884305F, 0.887927F, 0.459984F, -0.479333F, -0.877633F,
38 | -0.961657F, 0.274255F, -0.910437F, 0.413649F, -0.811051F, -0.584975F, 0.964998F, -0.262258F, -0.999015F, -0.044363F, 0.016727F, 0.999860F, 0.433665F, 0.901074F, -0.314155F, 0.949372F, -0.753803F, -0.657100F, 0.736091F, 0.676883F, 0.388708F, -0.921361F, -0.310563F,
39 | -0.950553F, 0.168347F, -0.985728F, 0.960120F, 0.279589F, -0.999643F, -0.026732F, 0.565254F, 0.824917F, 0.999601F, -0.028262F, 0.984968F, 0.172737F, -0.004637F, 0.999989F, -0.509996F, -0.860177F, -0.124630F, 0.992203F, 0.915421F, 0.402499F, 0.945088F, 0.326817F,
40 | 0.118941F, 0.992901F, -0.808868F, -0.587990F, 0.535648F, 0.844441F, 0.847695F, -0.530483F, 0.602898F, -0.797818F, 0.996112F, -0.088098F, -0.367572F, 0.929995F, 0.453030F, -0.891495F, -0.981418F, -0.191882F, -0.898436F, 0.439105F, -0.995234F, -0.097512F, 0.275482F,
41 | -0.961306F, -0.089550F, 0.995982F, 0.891298F, -0.453418F, 0.168284F, -0.985739F, 0.424064F, -0.905632F, 0.944026F, 0.329871F, -0.990740F, 0.135774F, 0.830300F, 0.557316F, 0.812188F, -0.583395F, 0.803736F, -0.594986F, 0.853255F, 0.521494F, -0.978663F, 0.205472F,
42 | 0.971997F, 0.234995F, 0.305794F, -0.952098F, 0.013241F, -0.999912F, 0.793114F, -0.609073F, 0.122519F, 0.992466F, 0.038138F, -0.999273F, -0.985333F, 0.170641F, -0.730190F, 0.683244F, -0.991206F, 0.132328F, 0.115900F, -0.993261F, 0.006050F, 0.999982F, 0.878127F,
43 | -0.478427F, 0.206535F, -0.978439F, 0.517842F, -0.855476F, 0.872735F, 0.488194F, -0.994532F, -0.104432F, 0.622063F, 0.782967F, 0.969487F, -0.245141F, 0.981134F, -0.193331F, 0.489964F, 0.871742F, -0.932267F, -0.361770F, 0.635779F, 0.771871F, 0.853311F, -0.521402F,
44 | 0.711919F, -0.702262F, 0.981711F, 0.190380F, -0.710760F, 0.703434F, 0.851332F, -0.524628F, -0.640339F, -0.768093F, -0.934979F, -0.354702F, -0.497980F, -0.867188F, 0.979074F, -0.203504F, -0.966343F, 0.257256F, 0.633468F, 0.773769F, 0.996102F, 0.088207F, 0.881357F,
45 | 0.472451F, -0.501400F, 0.865215F, 0.185701F, -0.982606F, -0.866701F, 0.498828F, 0.031077F, 0.999517F, -0.089361F, 0.995999F, -0.987138F, 0.159869F, 0.709383F, -0.704823F, -0.979346F, 0.202191F, -0.004384F, 0.999990F, 0.157579F, 0.987506F, -0.770314F, 0.637665F,
46 | -0.025465F, -0.999676F, -0.266660F, 0.963791F, 0.981102F, 0.193491F, 0.968643F, -0.248459F, 0.875996F, 0.482318F, -0.774617F, 0.632431F, 0.782280F, -0.622927F, -0.857747F, -0.514072F, -0.982180F, 0.187944F, -0.960464F, -0.278406F, 0.372553F, -0.928011F, -0.110555F,
47 | 0.993870F, 0.862404F, -0.506220F, 0.027818F, -0.999613F, 0.214167F, -0.976797F, 0.999479F, 0.032272F, -0.866881F, 0.498514F, 0.992303F, 0.123830F, 0.389033F, -0.921224F, 0.299733F, -0.954023F, 0.986853F, -0.161621F, -0.540571F, 0.841298F, 0.803984F, -0.594652F,
48 | -0.600867F, -0.799349F, -0.853643F, -0.520859F, -0.200702F, -0.979652F, 0.969359F, 0.245650F, -0.946421F, -0.322935F, -0.036906F, 0.999319F, 0.584793F, 0.811183F, 0.086836F, 0.996223F, -0.996712F, -0.081021F, 0.992517F, -0.122107F, -0.642313F, -0.766442F, -0.997449F,
49 | 0.071380F, -0.983292F, -0.182033F, 0.067871F, -0.997694F, 0.406651F, 0.913584F, 0.289521F, -0.957172F, -0.804114F, -0.594475F, -0.820837F, -0.571163F, 0.208075F, -0.978113F, 0.531429F, 0.847103F, -0.130587F, -0.991437F, -0.997462F, 0.071200F, -0.927497F, 0.373831F,
50 | -0.874613F, -0.484821F, 0.854131F, -0.520058F, -0.920757F, 0.390136F, 0.584500F, 0.811394F, 0.938867F, 0.344280F, 0.575499F, 0.817802F, -0.929153F, 0.369696F, 0.869934F, -0.493167F, -0.853360F, -0.521322F, -0.946092F, 0.323898F, -0.999976F, 0.006922F, -0.055546F,
51 | -0.998456F, 0.458054F, 0.888924F, 0.300423F, -0.953806F, -0.755421F, -0.655240F, -0.728792F, -0.684735F};
52 | assert_complex_array(expected, 100, output, output_len);
53 | }
54 |
55 | END_TEST
56 |
57 | void teardown() {
58 | if (mod != NULL) {
59 | frequency_modulator_destroy(mod);
60 | mod = NULL;
61 | }
62 | if (float_input != NULL) {
63 | free(float_input);
64 | float_input = NULL;
65 | }
66 | }
67 |
68 | void setup() {
69 | //do nothing
70 | }
71 |
72 | Suite *common_suite(void) {
73 | Suite *s;
74 | TCase *tc_core;
75 |
76 | s = suite_create("frequency_modulator");
77 |
78 | /* Core test case */
79 | tc_core = tcase_create("Core");
80 |
81 | tcase_add_test(tc_core, test_normal);
82 | tcase_add_test(tc_core, test_input_exceeded);
83 |
84 | tcase_add_checked_fixture(tc_core, setup, teardown);
85 | suite_add_tcase(s, tc_core);
86 |
87 | return s;
88 | }
89 |
90 | int main(void) {
91 | int number_failed;
92 | Suite *s;
93 | SRunner *sr;
94 |
95 | s = common_suite();
96 | sr = srunner_create(s);
97 |
98 | srunner_set_fork_status(sr, CK_NOFORK);
99 | srunner_run_all(sr, CK_NORMAL);
100 | number_failed = srunner_ntests_failed(sr);
101 | srunner_free(sr);
102 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
103 | }
104 |
--------------------------------------------------------------------------------
/test/sdr_server_mock.c:
--------------------------------------------------------------------------------
1 | #include "sdr_server_mock.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include "../src/sdr/sdr_server_api.h"
16 | #include "../src/tcp_utils.h"
17 |
18 | struct sdr_server_mock_t {
19 | int server_socket;
20 | volatile sig_atomic_t is_running;
21 | pthread_t acceptor_thread;
22 |
23 | void (*handler)(int client_socket, sdr_server_mock *server);
24 |
25 | float complex *input;
26 | size_t input_len;
27 |
28 | pthread_mutex_t mutex;
29 | pthread_cond_t condition;
30 | int client_socket;
31 | };
32 |
33 | void mock_response_success(int client_socket, sdr_server_mock *server) {
34 | struct sdr_server_message_header header;
35 | int code = -2;
36 | while (code < -1) {
37 | code = tcp_utils_read_data(&header, sizeof(struct sdr_server_message_header), client_socket);
38 | }
39 | if (code != 0) {
40 | fprintf(stderr, "unable to read header from sdr-modem\n");
41 | return;
42 | }
43 | if (header.protocol_version != SDR_SERVER_PROTOCOL_VERSION) {
44 | fprintf(stderr, "invalid protocol version: %d\n", header.protocol_version);
45 | return;
46 | }
47 | if (header.type != SDR_SERVER_TYPE_REQUEST) {
48 | fprintf(stderr, "expected type = request. got: %d\n", header.type);
49 | return;
50 | }
51 | struct sdr_server_request request;
52 | code = tcp_utils_read_data(&request, sizeof(struct sdr_server_request), client_socket);
53 | if (code != 0) {
54 | fprintf(stderr, "unable to read request from sdr-modem\n");
55 | return;
56 | }
57 |
58 | header.type = SDR_SERVER_TYPE_RESPONSE;
59 | header.protocol_version = SDR_SERVER_PROTOCOL_VERSION;
60 |
61 | struct sdr_server_response response;
62 | response.status = SDR_SERVER_RESPONSE_STATUS_SUCCESS;
63 | response.details = 0;
64 |
65 | // it is possible to directly populate *buffer with the fields,
66 | // however populating structs and then serializing them into byte array
67 | // is more readable
68 | size_t total_len = sizeof(struct sdr_server_message_header) + sizeof(struct sdr_server_response);
69 | uint8_t *buffer = malloc(total_len);
70 | if (buffer == NULL) {
71 | return;
72 | }
73 | memcpy(buffer, &header, sizeof(struct sdr_server_message_header));
74 | memcpy(buffer + sizeof(struct sdr_server_message_header), &response, sizeof(struct sdr_server_response));
75 | tcp_utils_write_data(buffer, total_len, client_socket);
76 | free(buffer);
77 | }
78 |
79 | static void *acceptor_worker(void *arg) {
80 | sdr_server_mock *server = (sdr_server_mock *) arg;
81 | struct sockaddr_in address;
82 | while (server->is_running) {
83 | int client_socket;
84 | int addrlen = sizeof(address);
85 | if ((client_socket = accept(server->server_socket, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0) {
86 | break;
87 | }
88 |
89 | struct timeval tv;
90 | tv.tv_sec = 5000;
91 | tv.tv_usec = 0;
92 | if (setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv)) {
93 | close(client_socket);
94 | perror("setsockopt - SO_RCVTIMEO");
95 | continue;
96 | }
97 |
98 | //doesn't support multiple clients actually
99 | //no needed for tests
100 | pthread_mutex_lock(&server->mutex);
101 | if (server->handler != NULL) {
102 | server->handler(client_socket, server);
103 | }
104 | server->client_socket = client_socket;
105 | pthread_cond_broadcast(&server->condition);
106 | pthread_mutex_unlock(&server->mutex);
107 | }
108 |
109 | printf("sdr server mock stopped\n");
110 | if (server->client_socket >= 0) {
111 | close(server->client_socket);
112 | }
113 | return (void *) 0;
114 | }
115 |
116 | int sdr_server_mock_send(float complex *input, size_t input_len, sdr_server_mock *server) {
117 | pthread_mutex_lock(&server->mutex);
118 | while (server->client_socket < 0) {
119 | pthread_cond_wait(&server->condition, &server->mutex);
120 | }
121 | pthread_mutex_unlock(&server->mutex);
122 | return tcp_utils_write_data((uint8_t *) input, sizeof(float complex) * input_len, server->client_socket);
123 | }
124 |
125 | int sdr_server_mock_create(const char *addr, int port, void (*handler)(int client_socket, sdr_server_mock *server), uint32_t max_output_buffer_length, sdr_server_mock **server) {
126 | struct sdr_server_mock_t *result = malloc(sizeof(struct sdr_server_mock_t));
127 | if (result == NULL) {
128 | return -ENOMEM;
129 | }
130 | *result = (struct sdr_server_mock_t) {0};
131 |
132 | int server_socket = socket(AF_INET, SOCK_STREAM, 0);
133 | if (server_socket == 0) {
134 | free(result);
135 | perror("socket creation failed");
136 | return -1;
137 | }
138 | result->server_socket = server_socket;
139 | result->is_running = true;
140 | result->handler = handler;
141 | result->input_len = max_output_buffer_length;
142 | result->input = malloc(sizeof(float complex) * result->input_len);
143 | if (result->input == NULL) {
144 | return -ENOMEM;
145 | }
146 | result->condition = (pthread_cond_t) PTHREAD_COND_INITIALIZER;
147 | result->mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
148 | result->client_socket = -1;
149 |
150 | int opt = 1;
151 | if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
152 | free(result);
153 | perror("setsockopt - SO_REUSEADDR");
154 | return -1;
155 | }
156 |
157 | #ifdef SO_REUSEPORT
158 | if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt))) {
159 | free(result);
160 | perror("setsockopt - SO_REUSEPORT");
161 | return -1;
162 | }
163 | #endif
164 |
165 | struct sockaddr_in address;
166 | address.sin_family = AF_INET;
167 | if (inet_pton(AF_INET, addr, &address.sin_addr) <= 0) {
168 | free(result);
169 | perror("invalid address");
170 | return -1;
171 | }
172 | address.sin_port = htons(port);
173 |
174 | if (bind(server_socket, (struct sockaddr *) &address, sizeof(address)) < 0) {
175 | free(result);
176 | perror("bind failed");
177 | return -1;
178 | }
179 | if (listen(server_socket, 3) < 0) {
180 | free(result);
181 | perror("listen failed");
182 | return -1;
183 | }
184 |
185 | pthread_t acceptor_thread;
186 | int code = pthread_create(&acceptor_thread, NULL, &acceptor_worker, result);
187 | if (code != 0) {
188 | free(result);
189 | return -1;
190 | }
191 | result->acceptor_thread = acceptor_thread;
192 |
193 | *server = result;
194 | return 0;
195 | }
196 |
197 | void sdr_server_mock_destroy(sdr_server_mock *server) {
198 | if (server == NULL) {
199 | return;
200 | }
201 | server->is_running = false;
202 | // close is not enough to exit from the blocking "accept" method
203 | // execute shutdown first
204 | int code = shutdown(server->server_socket, SHUT_RDWR);
205 | if (code != 0) {
206 | close(server->server_socket);
207 | }
208 | pthread_join(server->acceptor_thread, NULL);
209 |
210 | if (server->input != NULL) {
211 | free(server->input);
212 | }
213 | free(server);
214 | }
--------------------------------------------------------------------------------