Add SDR loop api

This commit is contained in:
Christian W. Zuckschwerdt 2020-10-31 07:55:14 +01:00
parent e7eacfd4b7
commit 619ca1fa18
5 changed files with 276 additions and 96 deletions

View file

@ -53,7 +53,7 @@ typedef struct r_cfg {
char const *test_data;
list_t in_files;
char const *in_filename;
volatile sig_atomic_t break_async;
volatile sig_atomic_t hop_now;
volatile sig_atomic_t exit_async;
volatile sig_atomic_t exit_code; ///< 0=no err, 1=params or cmd line err, 2=sdr device read error, 3=usb init error, 5=USB error (reset), other=other error
int frequencies;

View file

@ -15,7 +15,27 @@
#include <stdint.h>
typedef struct sdr_dev sdr_dev_t;
typedef void (*sdr_read_cb_t)(unsigned char *buf, uint32_t len, void *ctx);
typedef enum sdr_event_flags {
SDR_EV_EMPTY = 0,
SDR_EV_DATA = 1 << 0,
SDR_EV_RATE = 1 << 1,
SDR_EV_CORR = 1 << 2,
SDR_EV_FREQ = 1 << 3,
SDR_EV_GAIN = 1 << 4,
} sdr_event_flags_t;
typedef struct sdr_event {
sdr_event_flags_t ev;
uint32_t sample_rate;
int freq_correction;
uint32_t center_frequency;
char const *gain_str;
void *buf;
int len;
} sdr_event_t;
typedef void (*sdr_event_cb_t)(sdr_event_t *ev, void *ctx);
/** Find the closest matching device, optionally report status.
@ -139,7 +159,7 @@ int sdr_deactivate(sdr_dev_t *dev);
*/
int sdr_reset(sdr_dev_t *dev, int verbose);
int sdr_start(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
int sdr_start(sdr_dev_t *dev, sdr_event_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
int sdr_stop(sdr_dev_t *dev);
#endif /* INCLUDE_SDR_H_ */

View file

@ -94,22 +94,19 @@ void set_center_freq(r_cfg_t *cfg, uint32_t center_freq)
cfg->frequencies = 1;
cfg->frequency_index = 0;
cfg->frequency[0] = center_freq;
cfg->break_async = 1;
sdr_stop(cfg->dev);
sdr_set_center_freq(cfg->dev, center_freq, 0);
}
void set_freq_correction(r_cfg_t *cfg, int freq_correction)
{
cfg->ppm_error = freq_correction;
cfg->break_async = 1;
sdr_stop(cfg->dev);
sdr_set_freq_correction(cfg->dev, freq_correction, 0);
}
void set_sample_rate(r_cfg_t *cfg, uint32_t sample_rate)
{
cfg->samp_rate = sample_rate;
cfg->break_async = 1;
sdr_stop(cfg->dev);
sdr_set_sample_rate(cfg->dev, sample_rate, 0);
}
void set_gain_str(struct r_cfg *cfg, char const *gain_str)
@ -123,8 +120,7 @@ void set_gain_str(struct r_cfg *cfg, char const *gain_str)
if (!cfg->gain_str)
WARN_STRDUP("set_gain_str()");
}
cfg->break_async = 1;
sdr_stop(cfg->dev);
sdr_set_tuner_gain(cfg->dev, gain_str, 0);
}
/* general */

View file

@ -297,10 +297,6 @@ static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
char time_str[LOCAL_TIME_BUFLEN];
unsigned long n_samples;
if (cfg->mgr) {
mg_mgr_poll(cfg->mgr, 0);
}
if ((cfg->bytes_to_read > 0) && (cfg->bytes_to_read <= len)) {
len = cfg->bytes_to_read;
cfg->exit_async = 1;
@ -583,7 +579,7 @@ static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
cfg->exit_async = 1;
}
else {
cfg->break_async = 1;
cfg->hop_now = 1;
}
}
@ -596,7 +592,7 @@ static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
#ifndef _WIN32
alarm(0); // cancel the watchdog timer
#endif
cfg->break_async = 1;
cfg->hop_now = 1;
}
if (cfg->duration > 0 && rawtime >= cfg->stop_time) {
#ifndef _WIN32
@ -614,8 +610,13 @@ static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
cfg->stats_now--;
}
if (cfg->exit_async || cfg->break_async)
sdr_stop(cfg->dev);
if (cfg->hop_now && !cfg->exit_async) {
cfg->hop_now = 0;
time(&cfg->hop_start_time);
cfg->frequency_index = (cfg->frequency_index + 1) % cfg->frequencies;
cfg->center_frequency = cfg->frequency[cfg->frequency_index];
sdr_set_center_freq(cfg->dev, cfg->center_frequency, 0);
}
}
static int hasopt(int test, int argc, char *argv[], char const *optstring)
@ -1137,7 +1138,7 @@ sighandler(int signum)
}
else if (CTRL_BREAK_EVENT == signum) {
fprintf(stderr, "CTRL-BREAK detected, hopping to next frequency (-f). Use CTRL-C to quit.\n");
g_cfg.break_async = 1;
g_cfg.hop_now = 1;
return TRUE;
}
return FALSE;
@ -1153,7 +1154,7 @@ static void sighandler(int signum)
return;
}
else if (signum == SIGUSR1) {
g_cfg.break_async = 1;
g_cfg.hop_now = 1;
return;
}
else if (signum == SIGALRM) {
@ -1167,6 +1168,53 @@ static void sighandler(int signum)
}
#endif
static void sdr_handler(sdr_event_t *ev, void *ctx)
{
r_cfg_t *cfg = ctx;
data_t *data = NULL;
if (ev->ev & SDR_EV_RATE) {
data = data_append(data,
"sample_rate", "", DATA_INT, ev->sample_rate,
NULL);
}
if (ev->ev & SDR_EV_CORR) {
data = data_append(data,
"freq_correction", "", DATA_INT, ev->freq_correction,
NULL);
}
if (ev->ev & SDR_EV_FREQ) {
data = data_append(data,
"center_frequency", "", DATA_INT, ev->center_frequency,
"frequencies", "", DATA_COND, cfg->frequencies > 1, DATA_ARRAY, data_array(cfg->frequencies, DATA_INT, cfg->frequency),
"hop_times", "", DATA_COND, cfg->frequencies > 1, DATA_ARRAY, data_array(cfg->hop_times, DATA_INT, cfg->hop_time),
NULL);
}
if (ev->ev & SDR_EV_GAIN) {
data = data_append(data,
"gain", "", DATA_STRING, ev->gain_str,
NULL);
}
if (data) {
for (size_t i = 0; i < cfg->output_handler.len; ++i) { // list might contain NULLs
data_output_print(cfg->output_handler.elems[i], data);
}
data_free(data);
}
if (ev->ev == SDR_EV_DATA) {
if (cfg->mgr) {
mg_mgr_poll(cfg->mgr, 0);
}
if (!cfg->exit_async)
sdr_callback((unsigned char *)ev->buf, ev->len, ctx);
}
if (cfg->exit_async)
sdr_stop(cfg->dev);
}
int main(int argc, char **argv) {
#ifndef _WIN32
struct sigaction sigact;
@ -1614,73 +1662,22 @@ int main(int argc, char **argv) {
cfg->stop_time += cfg->duration;
}
uint32_t samp_rate = cfg->samp_rate;
char *gain_str = cfg->gain_str;
int ppm_error = cfg->ppm_error;
cfg->center_frequency = cfg->frequency[cfg->frequency_index];
r = sdr_set_center_freq(cfg->dev, cfg->center_frequency, 1); // always verbose
while (!cfg->exit_async) {
time(&cfg->hop_start_time);
data_t *data = NULL;
if (samp_rate != cfg->samp_rate) {
samp_rate = cfg->samp_rate;
r = sdr_set_sample_rate(cfg->dev, cfg->samp_rate, 1); // always verbose
data = data_append(data,
"sample_rate", "", DATA_INT, cfg->samp_rate,
NULL);
}
if (ppm_error != cfg->ppm_error) {
ppm_error = cfg->ppm_error;
r = sdr_set_freq_correction(cfg->dev, cfg->ppm_error, 1); // always verbose
data = data_append(data,
"freq_correction", "", DATA_INT, cfg->ppm_error,
NULL);
}
if (cfg->center_frequency != cfg->frequency[cfg->frequency_index]) {
cfg->center_frequency = cfg->frequency[cfg->frequency_index];
r = sdr_set_center_freq(cfg->dev, cfg->center_frequency, 1); // always verbose
data = data_append(data,
"center_frequency", "", DATA_INT, cfg->center_frequency,
"frequencies", "", DATA_COND, cfg->frequencies > 1, DATA_ARRAY, data_array(cfg->frequencies, DATA_INT, cfg->frequency),
"hop_times", "", DATA_COND, cfg->frequencies > 1, DATA_ARRAY, data_array(cfg->hop_times, DATA_INT, cfg->hop_time),
NULL);
}
if (gain_str != cfg->gain_str) {
gain_str = cfg->gain_str;
r = sdr_set_tuner_gain(cfg->dev, cfg->gain_str, 1); // always verbose
data = data_append(data,
"gain", "", DATA_STRING, cfg->gain_str,
NULL);
}
if (data) {
for (size_t i = 0; i < cfg->output_handler.len; ++i) { // list might contain NULLs
data_output_print(cfg->output_handler.elems[i], data);
}
data_free(data);
}
#ifndef _WIN32
signal(SIGALRM, sighandler);
alarm(3); // require callback to run every 3 second, abort otherwise
#endif
cfg->break_async = 0;
r = sdr_start(cfg->dev, sdr_callback, (void *)cfg,
r = sdr_start(cfg->dev, sdr_handler, (void *)cfg,
DEFAULT_ASYNC_BUF_NUMBER, cfg->out_block_size);
if (r < 0) {
fprintf(stderr, "WARNING: async read failed (%i).\n", r);
break;
}
#ifndef _WIN32
alarm(0); // cancel the watchdog timer
#endif
cfg->frequency_index = (cfg->frequency_index + 1) % cfg->frequencies;
}
if (cfg->report_stats > 0) {
event_occurred_handler(cfg, create_report_data(cfg, cfg->report_stats));

213
src/sdr.c
View file

@ -21,7 +21,7 @@
#include "optparse.h"
#include "fatal.h"
#ifdef RTLSDR
#include "rtl-sdr.h"
#include <rtl-sdr.h>
#include <libusb.h> /* libusb_error_name(), libusb_strerror() */
#endif
#ifdef SOAPYSDR
@ -78,17 +78,75 @@ struct sdr_dev {
#ifdef RTLSDR
rtlsdr_dev_t *rtlsdr_dev;
sdr_event_cb_t rtlsdr_cb;
void *rtlsdr_cb_ctx;
#endif
char *dev_info;
int running;
int polling;
void *buffer;
size_t buffer_size;
int sample_size;
int apply_rate;
int apply_freq;
int apply_corr;
int apply_gain;
uint32_t sample_rate;
int freq_correction;
uint32_t center_frequency;
char *gain_str;
};
/* internal helpers */
static int apply_changes(sdr_dev_t *dev, sdr_event_cb_t cb, void *ctx)
{
int r = 0;
sdr_event_flags_t flags = 0;
if (dev->apply_rate) {
r = sdr_set_sample_rate(dev, dev->sample_rate, 1); // always verbose
dev->apply_rate = 0;
flags |= SDR_EV_RATE;
}
if (dev->apply_corr) {
r = sdr_set_freq_correction(dev, dev->freq_correction, 1); // always verbose
dev->apply_corr = 0;
flags |= SDR_EV_CORR;
}
if (dev->apply_freq) {
r = sdr_set_center_freq(dev, dev->center_frequency, 1); // always verbose
dev->apply_freq = 0;
flags |= SDR_EV_FREQ;
}
char *gain_str = dev->gain_str;
dev->gain_str = NULL;
if (dev->apply_gain) {
r = sdr_set_tuner_gain(dev, gain_str, 1); // always verbose
dev->apply_gain = 0;
flags |= SDR_EV_GAIN;
}
if (flags) {
sdr_event_t ev = {
.ev = flags,
.sample_rate = dev->sample_rate,
.freq_correction = dev->freq_correction,
.center_frequency = dev->center_frequency,
.gain_str = gain_str,
};
if (cb)
cb(&ev, ctx);
free(gain_str);
}
return r;
}
/* rtl_tcp helpers */
#pragma pack(push, 1)
@ -208,7 +266,7 @@ static int rtltcp_close(SOCKET sock)
return 0;
}
static int rtltcp_read_loop(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
static int rtltcp_read_loop(sdr_dev_t *dev, sdr_event_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
if (dev->buffer_size != buf_len) {
free(dev->buffer);
@ -243,8 +301,16 @@ static int rtltcp_read_loop(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_
dev->running = 0;
}
sdr_event_t ev = {
.ev = SDR_EV_DATA,
.buf = buffer,
.len = n_read,
};
dev->polling = 1;
if (n_read > 0) // prevent a crash in callback
cb((unsigned char *)buffer, n_read, ctx);
cb(&ev, ctx);
dev->polling = 0;
apply_changes(dev, cb, ctx);
} while (dev->running);
@ -328,6 +394,7 @@ static int sdr_open_rtl(sdr_dev_t **out_dev, int *sample_size, char *dev_query,
WARN_CALLOC("sdr_open_rtl()");
return -1; // NOTE: returns error on alloc failure.
}
for (uint32_t i = dev_query ? dev_index : 0;
//cast quiets -Wsign-compare; if dev_index were < 0, would have returned -1 above
i < (dev_query ? (unsigned)dev_index + 1 : device_count);
@ -409,6 +476,51 @@ static int rtlsdr_find_tuner_gain(sdr_dev_t *dev, int centigain, int verbose)
return centigain;
}
static void rtlsdr_read_cb(unsigned char *iq_buf, uint32_t len, void *ctx)
{
sdr_dev_t *dev = ctx;
sdr_event_t ev = {
.ev = SDR_EV_DATA,
.buf = iq_buf,
.len = len,
};
if (len > 0) // prevent a crash in callback
dev->rtlsdr_cb(&ev, dev->rtlsdr_cb_ctx);
// FIXME: we actually need to copy the buffer to prevent it going away on cancel_async
}
static int rtlsdr_read_loop(sdr_dev_t *dev, sdr_event_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
int r = 0;
dev->rtlsdr_cb = cb;
dev->rtlsdr_cb_ctx = ctx;
dev->running = 1;
do {
dev->polling = 1;
r = rtlsdr_read_async(dev->rtlsdr_dev, rtlsdr_read_cb, dev, buf_num, buf_len);
// rtlsdr_read_async() returns possible error codes from:
// if (!dev) return -1;
// if (RTLSDR_INACTIVE != dev->async_status) return -2;
// r = libusb_submit_transfer(dev->xfer[i]);
// r = libusb_handle_events_timeout_completed(dev->ctx, &tv,
// r = libusb_cancel_transfer(dev->xfer[i]);
// We can safely assume it's an libusb error.
if (r < 0) {
fprintf(stderr, "\n%s: %s!\n"
"Check your RTL-SDR dongle, USB cables, and power supply.\n\n",
libusb_error_name(r), libusb_strerror(r));
}
dev->polling = 0;
apply_changes(dev, cb, ctx);
} while (dev->running);
return r;
}
#endif
/* SoapySDR helpers */
@ -784,7 +896,7 @@ static int sdr_open_soapy(sdr_dev_t **out_dev, int *sample_size, char *dev_query
return 0;
}
static int soapysdr_read_loop(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
static int soapysdr_read_loop(sdr_dev_t *dev, sdr_event_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
if (dev->buffer_size != buf_len) {
free(dev->buffer);
@ -842,8 +954,16 @@ static int soapysdr_read_loop(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint3
buffer[i] *= upscale;
}
sdr_event_t ev = {
.ev = SDR_EV_DATA,
.buf = buffer,
.len = n_read * 2 * dev->sample_size,
};
dev->polling = 1;
if (n_read > 0) // prevent a crash in callback
cb((unsigned char *)buffer, n_read * 2 * dev->sample_size, ctx);
cb(&ev, ctx);
dev->polling = 0;
apply_changes(dev, cb, ctx);
} while (dev->running);
@ -913,6 +1033,16 @@ char const *sdr_get_dev_info(sdr_dev_t *dev)
int sdr_set_center_freq(sdr_dev_t *dev, uint32_t freq, int verbose)
{
if (dev->polling) {
dev->center_frequency = freq;
dev->apply_freq = 1;
#ifdef RTLSDR
if (dev->rtlsdr_dev)
rtlsdr_cancel_async(dev->rtlsdr_dev);
#endif
return 0;
}
int r = -1;
if (dev->rtl_tcp) {
@ -961,6 +1091,16 @@ uint32_t sdr_get_center_freq(sdr_dev_t *dev)
int sdr_set_freq_correction(sdr_dev_t *dev, int ppm, int verbose)
{
if (dev->polling) {
dev->freq_correction = ppm;
dev->apply_corr = 1;
#ifdef RTLSDR
if (dev->rtlsdr_dev)
rtlsdr_cancel_async(dev->rtlsdr_dev);
#endif
return 0;
}
int r = -1;
if (dev->rtl_tcp)
@ -987,6 +1127,17 @@ int sdr_set_freq_correction(sdr_dev_t *dev, int ppm, int verbose)
int sdr_set_auto_gain(sdr_dev_t *dev, int verbose)
{
if (dev->polling) {
free(dev->gain_str);
dev->gain_str = NULL; // auto gain
dev->apply_gain = 1;
#ifdef RTLSDR
if (dev->rtlsdr_dev)
rtlsdr_cancel_async(dev->rtlsdr_dev);
#endif
return 0;
}
int r = -1;
if (dev->rtl_tcp)
@ -1013,6 +1164,24 @@ int sdr_set_auto_gain(sdr_dev_t *dev, int verbose)
int sdr_set_tuner_gain(sdr_dev_t *dev, char const *gain_str, int verbose)
{
if (dev->polling) {
free(dev->gain_str);
if (!gain_str) {
dev->gain_str = NULL; // auto gain
}
else {
dev->gain_str = strdup(gain_str);
if (!dev->gain_str)
WARN_STRDUP("set_gain_str()");
}
dev->apply_gain = 1;
#ifdef RTLSDR
if (dev->rtlsdr_dev)
rtlsdr_cancel_async(dev->rtlsdr_dev);
#endif
return 0;
}
int r = -1;
if (!gain_str || !*gain_str) {
@ -1090,6 +1259,16 @@ int sdr_set_antenna(sdr_dev_t *dev, char const *antenna_str, int verbose)
int sdr_set_sample_rate(sdr_dev_t *dev, uint32_t rate, int verbose)
{
if (dev->polling) {
dev->sample_rate = rate;
dev->apply_rate = 1;
#ifdef RTLSDR
if (dev->rtlsdr_dev)
rtlsdr_cancel_async(dev->rtlsdr_dev);
#endif
return 0;
}
int r = -1;
if (dev->rtl_tcp) {
@ -1224,7 +1403,7 @@ int sdr_reset(sdr_dev_t *dev, int verbose)
return r;
}
int sdr_start(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
int sdr_start(sdr_dev_t *dev, sdr_event_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
if (dev->rtl_tcp)
return rtltcp_read_loop(dev, cb, ctx, buf_num, buf_len);
@ -1235,22 +1414,8 @@ int sdr_start(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_t buf_num, uin
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev) {
int r = rtlsdr_read_async(dev->rtlsdr_dev, cb, ctx, buf_num, buf_len);
// rtlsdr_read_async() returns possible error codes from:
// if (!dev) return -1;
// if (RTLSDR_INACTIVE != dev->async_status) return -2;
// r = libusb_submit_transfer(dev->xfer[i]);
// r = libusb_handle_events_timeout_completed(dev->ctx, &tv,
// r = libusb_cancel_transfer(dev->xfer[i]);
// We can safely assume it's an libusb error.
if (r < 0) {
fprintf(stderr, "\n%s: %s!\n"
"Check your RTL-SDR dongle, USB cables, and power supply.\n\n",
libusb_error_name(r), libusb_strerror(r));
}
return r;
}
if (dev->rtlsdr_dev)
return rtlsdr_read_loop(dev, cb, ctx, buf_num, buf_len);
#endif
return -1;
@ -1274,8 +1439,10 @@ int sdr_stop(sdr_dev_t *dev)
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
if (dev->rtlsdr_dev) {
dev->running = 0;
return rtlsdr_cancel_async(dev->rtlsdr_dev);
}
#endif
return -1;