diff options
author | Andreas Eversberg <jolly@eversberg.eu> | 2017-07-24 10:09:05 +0200 |
---|---|---|
committer | Andreas Eversberg <jolly@eversberg.eu> | 2017-08-08 12:53:02 +0200 |
commit | 92ce6d4a428bb72692800ef32b5b80e69fef032b (patch) | |
tree | b403ce4d9afd7631585333f53b7ec2b66334b017 | |
parent | 3274812eab64469a118dc66f902eb182d55975e9 (diff) |
SDR: Using threads for read and write when doing resampling
-rw-r--r-- | src/common/main_common.c | 31 | ||||
-rw-r--r-- | src/common/sdr.c | 342 | ||||
-rw-r--r-- | src/common/sdr.h | 2 | ||||
-rw-r--r-- | src/common/soapy.c | 30 | ||||
-rw-r--r-- | src/common/uhd.c | 2 |
5 files changed, 351 insertions, 56 deletions
diff --git a/src/common/main_common.c b/src/common/main_common.c index 8955a25..c471574 100644 --- a/src/common/main_common.c +++ b/src/common/main_common.c @@ -66,6 +66,7 @@ const char *read_rx_wave = NULL; int use_sdr = 0; int sdr_channel = 0; static const char *sdr_device_args = "", *sdr_stream_args = "", *sdr_tune_args = ""; +static int sdr_samplerate = 0; static double sdr_bandwidth = 0.0; #ifdef HAVE_SDR static int sdr_uhd = 0; @@ -141,6 +142,7 @@ void print_help_common(const char *arg0, const char *ext_usage) printf(" Replace transmitted audio by given wave file.\n"); #ifdef HAVE_SDR printf("\nSDR options:\n"); + /* - - */ #ifdef HAVE_UHD printf(" --sdr-uhd\n"); printf(" Force UHD driver\n"); @@ -156,6 +158,9 @@ void print_help_common(const char *arg0, const char *ext_usage) printf(" --sdr-tune-args <args>\n"); printf(" Optional SDR device arguments, seperated by comma\n"); printf(" e.g. --sdr-device-args <key>=<value>[,<key>=<value>[,...]]\n"); + printf(" --sdr-samplerate <samplerate>\n"); + printf(" Sample rate to use with SDR. By default it equals the regular sample\n"); + printf(" rate.\n"); printf(" --sdr-bandwidth <bandwidth>\n"); printf(" Give IF filter bandwidth to use. If not, sample rate is used.\n"); printf(" --sdr-rx-antenna <name>\n"); @@ -209,11 +214,12 @@ void print_hotkeys_common(void) #define OPT_SDR_TX_ANTENNA 1107 #define OPT_SDR_RX_GAIN 1108 #define OPT_SDR_TX_GAIN 1109 -#define OPT_SDR_BANDWIDTH 1110 -#define OPT_WRITE_IQ_RX_WAVE 1111 -#define OPT_WRITE_IQ_TX_WAVE 1112 -#define OPT_READ_IQ_RX_WAVE 1113 -#define OPT_READ_IQ_TX_WAVE 1114 +#define OPT_SDR_SAMPLERATE 1110 +#define OPT_SDR_BANDWIDTH 1111 +#define OPT_WRITE_IQ_RX_WAVE 1112 +#define OPT_WRITE_IQ_TX_WAVE 1113 +#define OPT_READ_IQ_RX_WAVE 1114 +#define OPT_READ_IQ_TX_WAVE 1115 static struct option long_options_common[] = { {"help", 0, 0, 'h'}, @@ -244,6 +250,7 @@ static struct option long_options_common[] = { {"sdr-device-args", 1, 0, OPT_SDR_DEVICE_ARGS}, {"sdr-stream-args", 1, 0, OPT_SDR_STREAM_ARGS}, {"sdr-tune-args", 1, 0, OPT_SDR_TUNE_ARGS}, + {"sdr-samplerate", 1, 0, OPT_SDR_SAMPLERATE}, {"sdr-bandwidth", 1, 0, OPT_SDR_BANDWIDTH}, {"sdr-rx-antenna", 1, 0, OPT_SDR_RX_ANTENNA}, {"sdr-tx-antenna", 1, 0, OPT_SDR_TX_ANTENNA}, @@ -440,6 +447,10 @@ void opt_switch_common(int c, char *arg0, int *skip_args) sdr_tune_args = strdup(optarg); *skip_args += 2; break; + case OPT_SDR_SAMPLERATE: + sdr_samplerate = atoi(optarg); + *skip_args += 2; + break; case OPT_SDR_BANDWIDTH: sdr_bandwidth = atof(optarg); *skip_args += 2; @@ -524,6 +535,9 @@ void main_common(int *quit, int latency, int interval, void (*myhandler)(void), int c; int rc; + /* latency of send buffer in samples */ + latspl = samplerate * latency / 1000; + /* init mncc */ if (use_mncc_sock) { char mncc_sock_name[64]; @@ -552,9 +566,11 @@ void main_common(int *quit, int latency, int interval, void (*myhandler)(void), return; } + if (sdr_samplerate == 0.0) + sdr_samplerate = samplerate; if (sdr_bandwidth == 0.0) - sdr_bandwidth = samplerate; - rc = sdr_init(sdr_uhd, sdr_soapy, sdr_channel, sdr_device_args, sdr_stream_args, sdr_tune_args, sdr_tx_antenna, sdr_rx_antenna, sdr_tx_gain, sdr_rx_gain, sdr_bandwidth, write_iq_tx_wave, write_iq_rx_wave, read_iq_tx_wave, read_iq_rx_wave); + sdr_bandwidth = sdr_samplerate; + rc = sdr_init(sdr_uhd, sdr_soapy, sdr_channel, sdr_device_args, sdr_stream_args, sdr_tune_args, sdr_tx_antenna, sdr_rx_antenna, sdr_tx_gain, sdr_rx_gain, sdr_samplerate, sdr_bandwidth, write_iq_tx_wave, write_iq_rx_wave, read_iq_tx_wave, read_iq_rx_wave, latspl); if (rc < 0) return; #endif @@ -605,7 +621,6 @@ void main_common(int *quit, int latency, int interval, void (*myhandler)(void), /* do not process audio for an audio slave, since it is done by audio master */ if (sender->master) /* if master is set, we are an audio slave */ continue; - latspl = sender->samplerate * latency / 1000; process_sender_audio(sender, quit, latspl); } diff --git a/src/common/sdr.c b/src/common/sdr.c index 049e6af..7f465c4 100644 --- a/src/common/sdr.c +++ b/src/common/sdr.c @@ -23,10 +23,13 @@ #include <string.h> #include <errno.h> #include <math.h> +#include <pthread.h> +#include <unistd.h> #include "sample.h" #include "iir_filter.h" #include "fm_modulation.h" #include "sender.h" +#include "timer.h" #ifdef HAVE_UHD #include "uhd.h" #endif @@ -35,6 +38,9 @@ #endif #include "debug.h" +/* enable to debug buffer handling */ +//#define DEBUG_BUFFER + typedef struct sdr_chan { double tx_frequency; /* frequency used */ double rx_frequency; /* frequency used */ @@ -47,24 +53,42 @@ typedef struct sdr { int paging_channel; /* if set, points to paging channel */ sdr_chan_t paging_chan; /* settings for extra paging channel */ int channels; /* number of frequencies */ - double samplerate; /* IQ rate */ double amplitude; /* amplitude of each carrier */ + int samplerate; /* sample rate of audio data */ wave_rec_t wave_rx_rec; wave_rec_t wave_tx_rec; wave_play_t wave_rx_play; wave_play_t wave_tx_play; } sdr_t; +typedef struct sdr_thread { + int use; + volatile int running, exit; /* flags to control exit of threads */ + int buffer_size; + volatile float *buffer; + volatile int in, out; /* in and out pointers (atomic, so no locking required) */ + int max_fill; /* measure maximum buffer fill */ + double max_fill_timer; /* timer to display/reset maximum fill */ +} sdr_thread_t; + static int sdr_use_uhd, sdr_use_soapy; static int sdr_channel; static const char *sdr_device_args, *sdr_stream_args, *sdr_tune_args; static const char *sdr_rx_antenna, *sdr_tx_antenna; static double sdr_rx_gain, sdr_tx_gain; -const char *sdr_write_iq_rx_wave, *sdr_write_iq_tx_wave, *sdr_read_iq_rx_wave, *sdr_read_iq_tx_wave; +static const char *sdr_write_iq_rx_wave, *sdr_write_iq_tx_wave, *sdr_read_iq_rx_wave, *sdr_read_iq_tx_wave; +static int sdr_samplerate; /* sample rate of IQ data */ static double sdr_bandwidth; +static int sdr_oversample; +static int sdr_latspl; +static int sdr_threads; +static sdr_thread_t sdr_thread_read, sdr_thread_write; -int sdr_init(int sdr_uhd, int sdr_soapy, int channel, const char *device_args, const char *stream_args, const char *tune_args, const char *tx_antenna, const char *rx_antenna, double tx_gain, double rx_gain, double bandwidth, const char *write_iq_tx_wave, const char *write_iq_rx_wave, const char *read_iq_tx_wave, const char *read_iq_rx_wave) +int sdr_init(int sdr_uhd, int sdr_soapy, int channel, const char *device_args, const char *stream_args, const char *tune_args, const char *tx_antenna, const char *rx_antenna, double tx_gain, double rx_gain, int samplerate, double bandwidth, const char *write_iq_tx_wave, const char *write_iq_rx_wave, const char *read_iq_tx_wave, const char *read_iq_rx_wave, int latspl) { + PDEBUG(DSDR, DEBUG_DEBUG, "Init SDR\n"); + + sdr_threads = 0; /* only requried for oversampling */ sdr_use_uhd = sdr_uhd; sdr_use_soapy = sdr_soapy; sdr_channel = channel; @@ -80,6 +104,9 @@ int sdr_init(int sdr_uhd, int sdr_soapy, int channel, const char *device_args, c sdr_write_iq_rx_wave = write_iq_rx_wave; sdr_read_iq_tx_wave = read_iq_tx_wave; sdr_read_iq_rx_wave = read_iq_rx_wave; + sdr_samplerate = samplerate; + sdr_oversample = 1; + sdr_latspl = latspl; return 0; } @@ -92,6 +119,41 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq int rc; int c; + PDEBUG(DSDR, DEBUG_DEBUG, "Open SDR device\n"); + + if (sdr_samplerate != samplerate) { + if (samplerate > sdr_samplerate) { + PDEBUG(DSDR, DEBUG_ERROR, "SDR sample rate must be greater than audio sample rate!\n"); + PDEBUG(DSDR, DEBUG_ERROR, "You selected an SDR rate of %d and an audio rate of %d.\n", sdr_samplerate, samplerate); + return NULL; + } + if ((sdr_samplerate % samplerate)) { + PDEBUG(DSDR, DEBUG_ERROR, "SDR sample rate must be a multiple of audio sample rate!\n"); + PDEBUG(DSDR, DEBUG_ERROR, "You selected an SDR rate of %d and an audio rate of %d.\n", sdr_samplerate, samplerate); + return NULL; + } + sdr_oversample = sdr_samplerate / samplerate; + sdr_threads = 1; + } + if (sdr_threads) { + memset(&sdr_thread_read, 0, sizeof(sdr_thread_read)); + sdr_thread_read.buffer_size = sdr_latspl * 2 * sdr_oversample + 2; + sdr_thread_read.buffer = calloc(sdr_thread_read.buffer_size, sizeof(*sdr_thread_read.buffer)); + if (!sdr_thread_read.buffer) { + PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); + return NULL; + } + sdr_thread_read.in = sdr_thread_read.out = 0; + memset(&sdr_thread_write, 0, sizeof(sdr_thread_write)); + sdr_thread_write.buffer_size = sdr_latspl * 2 + 2; + sdr_thread_write.buffer = calloc(sdr_thread_write.buffer_size, sizeof(*sdr_thread_write.buffer)); + if (!sdr_thread_write.buffer) { + PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); + return NULL; + } + sdr_thread_write.in = sdr_thread_write.out = 0; + } + display_iq_init(samplerate); display_spectrum_init(samplerate); @@ -109,8 +171,8 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq goto error; } sdr->channels = channels; - sdr->samplerate = samplerate; sdr->amplitude = 1.0 / (double)channels; + sdr->samplerate = samplerate; /* special case where we use a paging frequency */ if (paging_frequency) { @@ -153,10 +215,10 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq double range = tx_high_frequency - tx_low_frequency; if (range) PDEBUG(DSDR, DEBUG_DEBUG, "Range between all TX Frequencies: %.6f MHz\n", range / 1e6); - if (range * 2 > sdr->samplerate) { + if (range * 2 > samplerate) { // why that? actually i don't know. i just want to be safe.... PDEBUG(DSDR, DEBUG_NOTICE, "The sample rate must be at least twice the range between frequencies.\n"); - PDEBUG(DSDR, DEBUG_NOTICE, "The given rate is %.6f MHz, but required rate must be >= %.6f MHz\n", sdr->samplerate / 1e6, range * 2.0 / 1e6); + PDEBUG(DSDR, DEBUG_NOTICE, "The given rate is %.6f MHz, but required rate must be >= %.6f MHz\n", samplerate / 1e6, range * 2.0 / 1e6); PDEBUG(DSDR, DEBUG_NOTICE, "Please increase samplerate!\n"); goto error; } @@ -167,26 +229,26 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq double tx_offset; tx_offset = sdr->chan[c].tx_frequency - tx_center_frequency; PDEBUG(DSDR, DEBUG_DEBUG, "Frequency #%d: TX offset: %.6f MHz\n", c, tx_offset / 1e6); - fm_mod_init(&sdr->chan[c].mod, sdr->samplerate, tx_offset, sdr->amplitude); + fm_mod_init(&sdr->chan[c].mod, samplerate, tx_offset, sdr->amplitude); } if (sdr->paging_channel) { double tx_offset; tx_offset = sdr->chan[sdr->paging_channel].tx_frequency - tx_center_frequency; PDEBUG(DSDR, DEBUG_DEBUG, "Paging Frequency: TX offset: %.6f MHz\n", tx_offset / 1e6); - fm_mod_init(&sdr->chan[sdr->paging_channel].mod, sdr->samplerate, tx_offset, sdr->amplitude); + fm_mod_init(&sdr->chan[sdr->paging_channel].mod, samplerate, tx_offset, sdr->amplitude); } /* show gain */ PDEBUG(DSDR, DEBUG_INFO, "Using gain: TX %.1f dB\n", sdr_tx_gain); /* open wave */ if (sdr_write_iq_tx_wave) { - rc = wave_create_record(&sdr->wave_tx_rec, sdr_write_iq_tx_wave, sdr->samplerate, 2, 1.0); + rc = wave_create_record(&sdr->wave_tx_rec, sdr_write_iq_tx_wave, samplerate, 2, 1.0); if (rc < 0) { PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n"); goto error; } } if (sdr_read_iq_tx_wave) { - rc = wave_create_playback(&sdr->wave_tx_play, sdr_read_iq_tx_wave, sdr->samplerate, 2, 1.0); + rc = wave_create_playback(&sdr->wave_tx_play, sdr_read_iq_tx_wave, samplerate, 2, 1.0); if (rc < 0) { PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE playback instance!\n"); goto error; @@ -212,7 +274,7 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq double range = rx_high_frequency - rx_low_frequency; if (range) PDEBUG(DSDR, DEBUG_DEBUG, "Range between all RX Frequencies: %.6f MHz\n", range / 1e6); - if (range * 2.0 > sdr->samplerate) { + if (range * 2.0 > samplerate) { // why that? actually i don't know. i just want to be safe.... PDEBUG(DSDR, DEBUG_NOTICE, "The sample rate must be at least twice the range between frequencies. Please increment samplerate!\n"); goto error; @@ -224,20 +286,20 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq double rx_offset; rx_offset = sdr->chan[c].rx_frequency - rx_center_frequency; PDEBUG(DSDR, DEBUG_DEBUG, "Frequency #%d: RX offset: %.6f MHz\n", c, rx_offset / 1e6); - fm_demod_init(&sdr->chan[c].demod, sdr->samplerate, rx_offset, bandwidth); + fm_demod_init(&sdr->chan[c].demod, samplerate, rx_offset, bandwidth); } /* show gain */ PDEBUG(DSDR, DEBUG_INFO, "Using gain: RX %.1f dB\n", sdr_rx_gain); /* open wave */ if (sdr_write_iq_rx_wave) { - rc = wave_create_record(&sdr->wave_rx_rec, sdr_write_iq_rx_wave, sdr->samplerate, 2, 1.0); + rc = wave_create_record(&sdr->wave_rx_rec, sdr_write_iq_rx_wave, samplerate, 2, 1.0); if (rc < 0) { PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n"); goto error; } } if (sdr_read_iq_rx_wave) { - rc = wave_create_playback(&sdr->wave_rx_play, sdr_read_iq_rx_wave, sdr->samplerate, 2, 1.0); + rc = wave_create_playback(&sdr->wave_rx_play, sdr_read_iq_rx_wave, samplerate, 2, 1.0); if (rc < 0) { PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE playback instance!\n"); goto error; @@ -247,7 +309,7 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq #ifdef HAVE_UHD if (sdr_use_uhd) { - rc = uhd_open(sdr_channel, sdr_device_args, sdr_stream_args, sdr_tune_args, sdr_tx_antenna, sdr_rx_antenna, tx_center_frequency, rx_center_frequency, sdr->samplerate, sdr_tx_gain, sdr_rx_gain, sdr_bandwidth); + rc = uhd_open(sdr_channel, sdr_device_args, sdr_stream_args, sdr_tune_args, sdr_tx_antenna, sdr_rx_antenna, tx_center_frequency, rx_center_frequency, sdr_samplerate, sdr_tx_gain, sdr_rx_gain, sdr_bandwidth); if (rc) goto error; } @@ -255,7 +317,7 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq #ifdef HAVE_SOAPY if (sdr_use_soapy) { - rc = soapy_open(sdr_channel, sdr_device_args, sdr_stream_args, sdr_tune_args, sdr_tx_antenna, sdr_rx_antenna, tx_center_frequency, rx_center_frequency, sdr->samplerate, sdr_tx_gain, sdr_rx_gain, sdr_bandwidth); + rc = soapy_open(sdr_channel, sdr_device_args, sdr_stream_args, sdr_tune_args, sdr_tx_antenna, sdr_rx_antenna, tx_center_frequency, rx_center_frequency, sdr_samplerate, sdr_tx_gain, sdr_rx_gain, sdr_bandwidth); if (rc) goto error; } @@ -268,26 +330,174 @@ error: return NULL; } +static void *sdr_write_child(void __attribute__((__unused__)) *arg) +{ + sdr_t *sdr = (sdr_t *)arg; + int num; + int fill, out; + int s, ss, o; + + while (sdr_thread_write.running) { + /* write to SDR */ + fill = (sdr_thread_write.in - sdr_thread_write.out + sdr_thread_write.buffer_size) % sdr_thread_write.buffer_size; + if (fill > sdr_thread_write.max_fill) + sdr_thread_write.max_fill = fill; + if (sdr_thread_write.max_fill_timer == 0.0) + sdr_thread_write.max_fill_timer = get_time(); + if (get_time() - sdr_thread_write.max_fill_timer > 1.0) { + double delay; + delay = (double)sdr_thread_write.max_fill / 2.0 / (double)sdr->samplerate; + sdr_thread_write.max_fill = 0; + sdr_thread_write.max_fill_timer += 1.0; + PDEBUG(DSDR, DEBUG_DEBUG, "write delay = %.3f ms\n", delay * 1000.0); + } + num = fill / 2; + if (num) { + float buff[num * 2 * sdr_oversample]; +#ifdef DEBUG_BUFFER + printf("Thread found %d samples in write buffer and forwards them to SDR.\n", num); +#endif + out = sdr_thread_write.out; + for (s = 0, ss = 0; s < num; s++) { + for (o = 0; o < sdr_oversample; o++) { + buff[ss++] = sdr_thread_write.buffer[out]; + buff[ss++] = sdr_thread_write.buffer[out + 1]; + } + out = (out + 2) % sdr_thread_write.buffer_size; + } +#ifdef HAVE_UHD + if (sdr_use_uhd) + uhd_send(buff, num * sdr_oversample); +#endif +#ifdef HAVE_SOAPY + if (sdr_use_soapy) + soapy_send(buff, num * sdr_oversample); +#endif + sdr_thread_write.out = out; + } + + /* delay some time */ + usleep(1000); + } + + PDEBUG(DSDR, DEBUG_DEBUG, "Thread received exit!\n"); + sdr_thread_write.exit = 1; + return NULL; +} + +static void *sdr_read_child(void __attribute__((__unused__)) *arg) +{ +// sdr_t *sdr = (sdr_t *)arg; + int num, count = 0; + int space, in; + int s, ss; + + while (sdr_thread_read.running) { + /* read from SDR */ + space = (sdr_thread_read.out - sdr_thread_read.in - 2 + sdr_thread_read.buffer_size) % sdr_thread_read.buffer_size; + num = space / 2; + if (num) { + float buff[num * 2]; +#ifdef HAVE_UHD + if (sdr_use_uhd) + count = uhd_receive(buff, num); +#endif +#ifdef HAVE_SOAPY + if (sdr_use_soapy) + count = soapy_receive(buff, num); +#endif + if (count > 0) { +#ifdef DEBUG_BUFFER + printf("Thread read %d samples from SDR and writes them to read buffer.\n", count); +#endif + in = sdr_thread_read.in; + for (s = 0, ss = 0; s < count; s++) { + sdr_thread_read.buffer[in++] = buff[ss++]; + sdr_thread_read.buffer[in++] = buff[ss++]; + in %= sdr_thread_read.buffer_size; + } + sdr_thread_read.in = in; + } + } + + /* delay some time */ + usleep(1000); + } + + PDEBUG(DSDR, DEBUG_DEBUG, "Thread received exit!\n"); + sdr_thread_read.exit = 1; + return NULL; +} + /* start streaming */ int sdr_start(void __attribute__((__unused__)) *inst) { // sdr_t *sdr = (sdr_t *)inst; + int rc = -EINVAL; #ifdef HAVE_UHD if (sdr_use_uhd) - return uhd_start(); + rc = uhd_start(); #endif #ifdef HAVE_SOAPY if (sdr_use_soapy) - return soapy_start(); + rc = soapy_start(); #endif - return -EINVAL; + if (rc < 0) + return rc; + + if (sdr_threads) { + int rc; + pthread_t tid; + + PDEBUG(DSDR, DEBUG_DEBUG, "Create threads!\n"); + sdr_thread_write.running = 1; + sdr_thread_write.exit = 0; + rc = pthread_create(&tid, NULL, sdr_write_child, inst); + if (rc < 0) { + sdr_thread_write.running = 0; + PDEBUG(DSDR, DEBUG_ERROR, "Failed to create thread!\n"); + return rc; + } + sdr_thread_read.running = 1; + sdr_thread_read.exit = 0; + rc = pthread_create(&tid, NULL, sdr_read_child, inst); + if (rc < 0) { + sdr_thread_read.running = 0; + PDEBUG(DSDR, DEBUG_ERROR, "Failed to create thread!\n"); + return rc; + } + } + + return 0; } void sdr_close(void *inst) { sdr_t *sdr = (sdr_t *)inst; + PDEBUG(DSDR, DEBUG_DEBUG, "Close SDR device\n"); + + if (sdr_threads) { + if (sdr_thread_write.running) { + PDEBUG(DSDR, DEBUG_DEBUG, "Thread sending exit!\n"); + sdr_thread_write.running = 0; + while (sdr_thread_write.exit == 0) + usleep(1000); + } + if (sdr_thread_read.running) { + PDEBUG(DSDR, DEBUG_DEBUG, "Thread sending exit!\n"); + sdr_thread_read.running = 0; + while (sdr_thread_read.exit == 0) + usleep(1000); + } + } + + if (sdr_thread_read.buffer) + free((void *)sdr_thread_read.buffer); + if (sdr_thread_write.buffer) + free((void *)sdr_thread_write.buffer); + #ifdef HAVE_UHD if (sdr_use_uhd) uhd_close(); @@ -353,16 +563,38 @@ int sdr_write(void *inst, sample_t **samples, int num, enum paging_signal __attr } } + if (sdr_threads) { + /* store data towards SDR in ring buffer */ + int space, in; + + space = (sdr_thread_write.out - sdr_thread_write.in - 2 + sdr_thread_write.buffer_size) % sdr_thread_write.buffer_size; + if (space < num * 2) { + PDEBUG(DSDR, DEBUG_ERROR, "Write SDR buffer overflow!\n"); + num = space / 2; + } +#ifdef DEBUG_BUFFER + printf("Writing %d samples to write buffer.\n", num); +#endif + in = sdr_thread_write.in; + for (s = 0, ss = 0; s < num; s++) { + sdr_thread_write.buffer[in++] = buff[ss++]; + sdr_thread_write.buffer[in++] = buff[ss++]; + in %= sdr_thread_write.buffer_size; + } + sdr_thread_write.in = in; + sent = num; + } else { #ifdef HAVE_UHD - if (sdr_use_uhd) - sent = uhd_send(buff, num); + if (sdr_use_uhd) + sent = uhd_send(buff, num); #endif #ifdef HAVE_SOAPY - if (sdr_use_soapy) - sent = soapy_send(buff, num); + if (sdr_use_soapy) + sent = soapy_send(buff, num); #endif - if (sent < 0) - return sent; + if (sent < 0) + return sent; + } return sent; } @@ -380,16 +612,47 @@ int sdr_read(void *inst, sample_t **samples, int num, int channels) buff = (float *)samples; } + if (sdr_threads) { + /* load data from SDR out of ring buffer */ + int fill, out; + + fill = (sdr_thread_read.in - sdr_thread_read.out + sdr_thread_read.buffer_size) % sdr_thread_read.buffer_size; + if (fill > sdr_thread_read.max_fill) + sdr_thread_read.max_fill = fill; + if (sdr_thread_read.max_fill_timer == 0.0) + sdr_thread_read.max_fill_timer = get_time(); + if (get_time() - sdr_thread_read.max_fill_timer > 1.0) { + double delay; + delay = (double)sdr_thread_read.max_fill / 2.0 / (double)sdr_samplerate; + sdr_thread_read.max_fill = 0; + sdr_thread_read.max_fill_timer += 1.0; + PDEBUG(DSDR, DEBUG_DEBUG, "read delay = %.3f ms\n", delay * 1000.0); + } + if (fill / 2 / sdr_oversample < num) + num = fill / 2 / sdr_oversample; +#ifdef DEBUG_BUFFER + printf("Reading %d samples from read buffer.\n", num); +#endif + out = sdr_thread_read.out; + for (s = 0, ss = 0; s < num; s++) { + buff[ss++] = sdr_thread_read.buffer[out]; + buff[ss++] = sdr_thread_read.buffer[out + 1]; + out = (out + 2 * sdr_oversample) % sdr_thread_read.buffer_size; + } + sdr_thread_read.out = out; + count = num; + } else { #ifdef HAVE_UHD - if (sdr_use_uhd) - count = uhd_receive(buff, num); + if (sdr_use_uhd) + count = uhd_receive(buff, num); #endif #ifdef HAVE_SOAPY - if (sdr_use_soapy) - count = soapy_receive(buff, num); + if (sdr_use_soapy) + count = soapy_receive(buff, num); #endif - if (count <= 0) - return count; + if (count <= 0) + return count; +} if (sdr->wave_rx_rec.fp) { sample_t spl[2][count], *spl_list[2] = { spl[0], spl[1] }; @@ -418,7 +681,7 @@ int sdr_read(void *inst, sample_t **samples, int num, int channels) return count; } -/* how many delay (in audio sample duration) do we have in the buffer */ +/* how much do we need to send (in audio sample duration) to get the target delay (latspl) */ int sdr_get_tosend(void __attribute__((__unused__)) *inst, int latspl) { // sdr_t *sdr = (sdr_t *)inst; @@ -426,14 +689,25 @@ int sdr_get_tosend(void __attribute__((__unused__)) *inst, int latspl) #ifdef HAVE_UHD if (sdr_use_uhd) - count = uhd_get_tosend(latspl); + count = uhd_get_tosend(latspl * sdr_oversample); #endif #ifdef HAVE_SOAPY if (sdr_use_soapy) - count = soapy_get_tosend(latspl); + count = soapy_get_tosend(latspl * sdr_oversample); #endif if (count < 0) return count; + count /= sdr_oversample; + + if (sdr_threads) { + /* substract what we have in write buffer, because this is not jent sent to the SDR */ + int fill; + + fill = (sdr_thread_write.in - sdr_thread_write.out + sdr_thread_write.buffer_size) % sdr_thread_write.buffer_size; + count -= fill / 2; + if (count < 0) + count = 0; + } return count; } diff --git a/src/common/sdr.h b/src/common/sdr.h index 69c8d19..68d5cc3 100644 --- a/src/common/sdr.h +++ b/src/common/sdr.h @@ -1,5 +1,5 @@ -int sdr_init(int sdr_uhd, int sdr_soapy, int channel, const char *device_args, const char *stream_args, const char *tune_args, const char *tx_antenna, const char *rx_antenna, double tx_gain, double rx_gain, double bandwidth, const char *write_iq_tx_wave, const char *write_iq_rx_wave, const char *read_iq_tx_wave, const char *read_iq_rx_wave); +int sdr_init(int sdr_uhd, int sdr_soapy, int channel, const char *device_args, const char *stream_args, const char *tune_args, const char *tx_antenna, const char *rx_antenna, double tx_gain, double rx_gain, int samplerate, double bandwidth, const char *write_iq_tx_wave, const char *write_iq_rx_wave, const char *read_iq_tx_wave, const char *read_iq_rx_wave, int latspl); int sdr_start(void *inst); void *sdr_open(const char *audiodev, double *tx_frequency, double *rx_frequency, int channels, double paging_frequency, int samplerate, double bandwidth, double sample_deviation); void sdr_close(void *inst); diff --git a/src/common/soapy.c b/src/common/soapy.c index 72a3c4d..8a25e4c 100644 --- a/src/common/soapy.c +++ b/src/common/soapy.c @@ -17,6 +17,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> @@ -145,7 +146,7 @@ int soapy_open(size_t channel, const char *_device_args, const char *_stream_arg /* see what rate actually is */ got_rate = SoapySDRDevice_getSampleRate(sdr, SOAPY_SDR_TX, channel); - if (fabs(got_rate - rate) > 0.01) { + if (fabs(got_rate - rate) > 1.0) { PDEBUG(DSOAPY, DEBUG_ERROR, "Given TX rate %.3f Hz is not supported, try %.3f Hz\n", rate, got_rate); soapy_close(); return -EINVAL; @@ -265,7 +266,7 @@ int soapy_open(size_t channel, const char *_device_args, const char *_stream_arg /* see what rate actually is */ got_rate = SoapySDRDevice_getSampleRate(sdr, SOAPY_SDR_RX, channel); - if (fabs(got_rate - rate) > 0.01) { + if (fabs(got_rate - rate) > 1.0) { PDEBUG(DSOAPY, DEBUG_ERROR, "Given RX rate %.3f Hz is not supported, try %.3f Hz\n", rate, got_rate); soapy_close(); return -EINVAL; @@ -385,17 +386,18 @@ int soapy_send(float *buff, int num) chunk = tx_samps_per_buff; /* create tx metadata */ buffs_ptr[0] = buff; - count = SoapySDRDevice_writeStream(sdr, txStream, buffs_ptr, chunk, &flags, 0, 0); - if (count <= 0) + count = SoapySDRDevice_writeStream(sdr, txStream, buffs_ptr, chunk, &flags, 0, 1000000); + if (count <= 0) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to write to TX streamr (error=%d)\n", count); break; - - /* increment tx counter */ - tx_count += count; + } sent += count; buff += count * 2; num -= count; } + /* increment tx counter */ + tx_count += sent; return sent; } @@ -418,8 +420,6 @@ int soapy_receive(float *buff, int max) buffs_ptr[0] = buff; count = SoapySDRDevice_readStream(sdr, rxStream, buffs_ptr, rx_samps_per_buff, &flags, &timeNs, 0); if (count > 0) { - /* update current rx time */ - rx_count += count; /* commit received data to buffer */ got += count; buff += count * 2; @@ -429,6 +429,8 @@ int soapy_receive(float *buff, int max) break; } } + /* update current rx time */ + rx_count += got; return got; } @@ -449,12 +451,16 @@ int soapy_get_tosend(int latspl) /* we check how advance our transmitted time stamp is */ tosend = latspl - (tx_count - rx_count); /* in case of underrun: */ - if (tosend < 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "SDR TX underrun!\n"); + if (tosend > latspl) { +// It is normal that we have underruns, prior inital filling of buffer. +// FIXME: better solution to detect underrun +// PDEBUG(DSOAPY, DEBUG_ERROR, "SDR TX underrun!\n"); tosend = 0; + tx_count = rx_count; } + if (tosend < 0) + tosend = 0; return tosend; } - diff --git a/src/common/uhd.c b/src/common/uhd.c index b22f722..45eab54 100644 --- a/src/common/uhd.c +++ b/src/common/uhd.c @@ -480,7 +480,7 @@ int uhd_send(float *buff, int num) PDEBUG(DUHD, DEBUG_ERROR, "Failed to create TX metadata\n"); buffs_ptr[0] = buff; count = 0; - error = uhd_tx_streamer_send(tx_streamer, buffs_ptr, chunk, &tx_metadata, 0.0, &count); + error = uhd_tx_streamer_send(tx_streamer, buffs_ptr, chunk, &tx_metadata, 1.0, &count); if (error) { PDEBUG(DUHD, DEBUG_ERROR, "Failed to write to TX streamer\n"); break; |