diff --git a/include/rtl_433.h b/include/rtl_433.h index 5b43ab57..2a589b5b 100644 --- a/include/rtl_433.h +++ b/include/rtl_433.h @@ -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; diff --git a/include/sdr.h b/include/sdr.h index 8c3553c3..7eb2cb51 100644 --- a/include/sdr.h +++ b/include/sdr.h @@ -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_ */ diff --git a/src/r_api.c b/src/r_api.c index 9afe5157..dd68bf8a 100644 --- a/src/r_api.c +++ b/src/r_api.c @@ -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 */ diff --git a/src/rtl_433.c b/src/rtl_433.c index 0c5a7242..37e51797 100644 --- a/src/rtl_433.c +++ b/src/rtl_433.c @@ -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)); diff --git a/src/sdr.c b/src/sdr.c index d11830b8..00c68550 100644 --- a/src/sdr.c +++ b/src/sdr.c @@ -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;