From 75b221c0e3f0592e0a60540c49f4545df4ecf746 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 27 Jan 2017 17:30:40 +0000 Subject: [PATCH] Lots of refactoring to move the SDR-type-specific bits out of the main code and make them optionally buildable. --- Makefile | 2 +- dump1090.c | 523 ++++----------------------------------------------- dump1090.h | 20 +- sdr.c | 145 ++++++++++++++ sdr.h | 13 ++ sdr_ifile.c | 236 +++++++++++++++++++++++ sdr_ifile.h | 13 ++ sdr_rtlsdr.c | 335 +++++++++++++++++++++++++++++++++ sdr_rtlsdr.h | 13 ++ 9 files changed, 800 insertions(+), 500 deletions(-) create mode 100644 sdr.c create mode 100644 sdr.h create mode 100644 sdr_ifile.c create mode 100644 sdr_ifile.h create mode 100644 sdr_rtlsdr.c create mode 100644 sdr_rtlsdr.h diff --git a/Makefile b/Makefile index a09c267..3ef571d 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ all: dump1090 view1090 %.o: %.c *.h $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o $(COMPAT) +dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o sdr_ifile.o sdr_rtlsdr.o sdr.o $(COMPAT) $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_RTLSDR) -lncurses view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o $(COMPAT) diff --git a/dump1090.c b/dump1090.c index 2f80541..4003c74 100644 --- a/dump1090.c +++ b/dump1090.c @@ -49,12 +49,8 @@ #include "dump1090.h" -#include - #include -static int verbose_device_search(char *s); - // // ============================= Utility functions ========================== // @@ -113,7 +109,6 @@ void modesInitConfig(void) { // Now initialise things that should not be 0/NULL to their defaults Modes.gain = MODES_MAX_GAIN; Modes.freq = MODES_DEFAULT_FREQ; - Modes.ppm_error = MODES_DEFAULT_PPM; Modes.check_crc = 1; Modes.net_heartbeat_interval = MODES_NET_HEARTBEAT_INTERVAL; Modes.net_input_raw_ports = strdup("30001"); @@ -127,6 +122,8 @@ void modesInitConfig(void) { Modes.json_location_accuracy = 1; Modes.maxRange = 1852 * 300; // 300NM default max range Modes.mode_ac_auto = 1; + + sdrInitConfig(); } // //========================================================================= @@ -212,138 +209,11 @@ void modesInit(void) { modesChecksumInit(Modes.nfix_crc); icaoFilterInit(); modeACInit(); - interactiveInit(); if (Modes.show_only) icaoFilterAdd(Modes.show_only); - - // Prepare sample conversion - if (!Modes.net_only) { - if (Modes.filename == NULL) // using a real RTLSDR, use UC8 input always - Modes.input_format = INPUT_UC8; - - Modes.converter_function = init_converter(Modes.input_format, - Modes.sample_rate, - Modes.dc_filter, - &Modes.converter_state); - if (!Modes.converter_function) { - fprintf(stderr, "Can't initialize sample converter, giving up.\n"); - exit(1); - } - } } -static void convert_samples(void *iq, - uint16_t *mag, - unsigned nsamples, - double *mean_level, - double *mean_power) -{ - Modes.converter_function(iq, mag, nsamples, Modes.converter_state, mean_level, mean_power); -} - -// -// =============================== RTLSDR handling ========================== -// -int modesInitRTLSDR(void) { - int j; - int device_count, dev_index = 0; - char vendor[256], product[256], serial[256]; - - if (Modes.dev_name) { - if ( (dev_index = verbose_device_search(Modes.dev_name)) < 0 ) - return -1; - } - - device_count = rtlsdr_get_device_count(); - if (!device_count) { - fprintf(stderr, "No supported RTLSDR devices found.\n"); - return -1; - } - - fprintf(stderr, "Found %d device(s):\n", device_count); - for (j = 0; j < device_count; j++) { - if (rtlsdr_get_device_usb_strings(j, vendor, product, serial) != 0) { - fprintf(stderr, "%d: unable to read device details\n", j); - } else { - fprintf(stderr, "%d: %s, %s, SN: %s %s\n", j, vendor, product, serial, - (j == dev_index) ? "(currently selected)" : ""); - } - } - - if (rtlsdr_open(&Modes.dev, dev_index) < 0) { - fprintf(stderr, "Error opening the RTLSDR device: %s\n", - strerror(errno)); - return -1; - } - - // Set gain, frequency, sample rate, and reset the device - rtlsdr_set_tuner_gain_mode(Modes.dev, - (Modes.gain == MODES_AUTO_GAIN) ? 0 : 1); - if (Modes.gain != MODES_AUTO_GAIN) { - int *gains; - int numgains; - - numgains = rtlsdr_get_tuner_gains(Modes.dev, NULL); - if (numgains <= 0) { - fprintf(stderr, "Error getting tuner gains\n"); - return -1; - } - - gains = malloc(numgains * sizeof(int)); - if (rtlsdr_get_tuner_gains(Modes.dev, gains) != numgains) { - fprintf(stderr, "Error getting tuner gains\n"); - free(gains); - return -1; - } - - if (Modes.gain == MODES_MAX_GAIN) { - int highest = -1; - int i; - - for (i = 0; i < numgains; ++i) { - if (gains[i] > highest) - highest = gains[i]; - } - - Modes.gain = highest; - fprintf(stderr, "Max available gain is: %.2f dB\n", Modes.gain/10.0); - } else { - int closest = -1; - int i; - - for (i = 0; i < numgains; ++i) { - if (closest == -1 || abs(gains[i] - Modes.gain) < abs(closest - Modes.gain)) - closest = gains[i]; - } - - if (closest != Modes.gain) { - Modes.gain = closest; - fprintf(stderr, "Closest available gain: %.2f dB\n", Modes.gain/10.0); - } - } - - free(gains); - - fprintf(stderr, "Setting gain to: %.2f dB\n", Modes.gain/10.0); - if (rtlsdr_set_tuner_gain(Modes.dev, Modes.gain) < 0) { - fprintf(stderr, "Error setting tuner gains\n"); - return -1; - } - } else { - fprintf(stderr, "Using automatic gain control.\n"); - } - rtlsdr_set_freq_correction(Modes.dev, Modes.ppm_error); - if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1); - rtlsdr_set_center_freq(Modes.dev, Modes.freq); - rtlsdr_set_sample_rate(Modes.dev, (unsigned)Modes.sample_rate); - - rtlsdr_reset_buffer(Modes.dev); - fprintf(stderr, "Gain reported by device: %.2f dB\n", - rtlsdr_get_tuner_gain(Modes.dev)/10.0); - - return 0; -} // //========================================================================= // @@ -356,208 +226,6 @@ int modesInitRTLSDR(void) { // A Mutex is used to avoid races with the decoding thread. // -static struct timespec reader_thread_start; - -void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { - struct mag_buf *outbuf; - struct mag_buf *lastbuf; - uint32_t slen; - unsigned next_free_buffer; - unsigned free_bufs; - unsigned block_duration; - - static int was_odd = 0; // paranoia!! - static int dropping = 0; - - MODES_NOTUSED(ctx); - - // Lock the data buffer variables before accessing them - pthread_mutex_lock(&Modes.data_mutex); - if (Modes.exit) { - rtlsdr_cancel_async(Modes.dev); // ask our caller to exit - } - - next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS; - outbuf = &Modes.mag_buffers[Modes.first_free_buffer]; - lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS]; - free_bufs = (Modes.first_filled_buffer - next_free_buffer + MODES_MAG_BUFFERS) % MODES_MAG_BUFFERS; - - // Paranoia! Unlikely, but let's go for belt and suspenders here - - if (len != MODES_RTL_BUF_SIZE) { - fprintf(stderr, "weirdness: rtlsdr gave us a block with an unusual size (got %u bytes, expected %u bytes)\n", - (unsigned)len, (unsigned)MODES_RTL_BUF_SIZE); - - if (len > MODES_RTL_BUF_SIZE) { - // wat?! Discard the start. - unsigned discard = (len - MODES_RTL_BUF_SIZE + 1) / 2; - outbuf->dropped += discard; - buf += discard*2; - len -= discard*2; - } - } - - if (was_odd) { - // Drop a sample so we are in sync with I/Q samples again (hopefully) - ++buf; - --len; - ++outbuf->dropped; - } - - was_odd = (len & 1); - slen = len/2; - - if (free_bufs == 0 || (dropping && free_bufs < MODES_MAG_BUFFERS/2)) { - // FIFO is full. Drop this block. - dropping = 1; - outbuf->dropped += slen; - pthread_mutex_unlock(&Modes.data_mutex); - return; - } - - dropping = 0; - pthread_mutex_unlock(&Modes.data_mutex); - - // Compute the sample timestamp and system timestamp for the start of the block - outbuf->sampleTimestamp = lastbuf->sampleTimestamp + 12e6 * (lastbuf->length + outbuf->dropped) / Modes.sample_rate; - block_duration = 1e9 * slen / Modes.sample_rate; - - // Get the approx system time for the start of this block - clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp); - outbuf->sysTimestamp.tv_nsec -= block_duration; - normalize_timespec(&outbuf->sysTimestamp); - - // Copy trailing data from last block (or reset if not valid) - if (outbuf->dropped == 0 && lastbuf->length >= Modes.trailing_samples) { - memcpy(outbuf->data, lastbuf->data + lastbuf->length - Modes.trailing_samples, Modes.trailing_samples * sizeof(uint16_t)); - } else { - memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t)); - } - - // Convert the new data - outbuf->length = slen; - convert_samples(buf, &outbuf->data[Modes.trailing_samples], slen, &outbuf->mean_level, &outbuf->mean_power); - - // Push the new data to the demodulation thread - pthread_mutex_lock(&Modes.data_mutex); - - Modes.mag_buffers[next_free_buffer].dropped = 0; - Modes.mag_buffers[next_free_buffer].length = 0; // just in case - Modes.first_free_buffer = next_free_buffer; - - // accumulate CPU while holding the mutex, and restart measurement - end_cpu_timing(&reader_thread_start, &Modes.reader_cpu_accumulator); - start_cpu_timing(&reader_thread_start); - - pthread_cond_signal(&Modes.data_cond); - pthread_mutex_unlock(&Modes.data_mutex); -} -// -//========================================================================= -// -// This is used when --ifile is specified in order to read data from file -// instead of using an RTLSDR device -// -void readDataFromFile(void) { - int eof = 0; - struct timespec next_buffer_delivery; - void *readbuf; - int bytes_per_sample = 0; - - switch (Modes.input_format) { - case INPUT_UC8: - bytes_per_sample = 2; - break; - case INPUT_SC16: - case INPUT_SC16Q11: - bytes_per_sample = 4; - break; - } - - if (!(readbuf = malloc(MODES_MAG_BUF_SAMPLES * bytes_per_sample))) { - fprintf(stderr, "failed to allocate read buffer\n"); - exit(1); - } - - clock_gettime(CLOCK_MONOTONIC, &next_buffer_delivery); - - pthread_mutex_lock(&Modes.data_mutex); - while (!Modes.exit && !eof) { - ssize_t nread, toread; - void *r; - struct mag_buf *outbuf, *lastbuf; - unsigned next_free_buffer; - unsigned slen; - - next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS; - if (next_free_buffer == Modes.first_filled_buffer) { - // no space for output yet - pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex); - continue; - } - - outbuf = &Modes.mag_buffers[Modes.first_free_buffer]; - lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS]; - pthread_mutex_unlock(&Modes.data_mutex); - - // Compute the sample timestamp and system timestamp for the start of the block - outbuf->sampleTimestamp = lastbuf->sampleTimestamp + 12e6 * lastbuf->length / Modes.sample_rate; - - // Copy trailing data from last block (or reset if not valid) - if (lastbuf->length >= Modes.trailing_samples) { - memcpy(outbuf->data, lastbuf->data + lastbuf->length - Modes.trailing_samples, Modes.trailing_samples * sizeof(uint16_t)); - } else { - memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t)); - } - - // Get the system time for the start of this block - clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp); - - toread = MODES_MAG_BUF_SAMPLES * bytes_per_sample; - r = readbuf; - while (toread) { - nread = read(Modes.fd, r, toread); - if (nread <= 0) { - // Done. - eof = 1; - break; - } - r += nread; - toread -= nread; - } - - slen = outbuf->length = MODES_MAG_BUF_SAMPLES - toread/bytes_per_sample; - - // Convert the new data - convert_samples(readbuf, &outbuf->data[Modes.trailing_samples], slen, &outbuf->mean_level, &outbuf->mean_power); - - if (Modes.throttle) { - // Wait until we are allowed to release this buffer to the main thread - while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_buffer_delivery, NULL) == EINTR) - ; - - // compute the time we can deliver the next buffer. - next_buffer_delivery.tv_nsec += outbuf->length * 1e9 / Modes.sample_rate; - normalize_timespec(&next_buffer_delivery); - } - - // Push the new data to the main thread - pthread_mutex_lock(&Modes.data_mutex); - Modes.first_free_buffer = next_free_buffer; - // accumulate CPU while holding the mutex, and restart measurement - end_cpu_timing(&reader_thread_start, &Modes.reader_cpu_accumulator); - start_cpu_timing(&reader_thread_start); - pthread_cond_signal(&Modes.data_cond); - } - - free(readbuf); - - // Wait for the main thread to consume all data - while (!Modes.exit && Modes.first_filled_buffer != Modes.first_free_buffer) - pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex); - - pthread_mutex_unlock(&Modes.data_mutex); -} // //========================================================================= // @@ -565,36 +233,11 @@ void readDataFromFile(void) { // without caring about data acquisition // -void *readerThreadEntryPoint(void *arg) { +void *readerThreadEntryPoint(void *arg) +{ MODES_NOTUSED(arg); - start_cpu_timing(&reader_thread_start); // we accumulate in rtlsdrCallback() or readDataFromFile() - - if (Modes.filename == NULL) { - while (!Modes.exit) { - rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL, - MODES_RTL_BUFFERS, - MODES_RTL_BUF_SIZE); - - if (!Modes.exit) { - log_with_timestamp("Warning: lost the connection to the RTLSDR device."); - rtlsdr_close(Modes.dev); - Modes.dev = NULL; - - do { - sleep(5); - log_with_timestamp("Trying to reconnect to the RTLSDR device.."); - } while (!Modes.exit && modesInitRTLSDR() < 0); - } - } - - if (Modes.dev != NULL) { - rtlsdr_close(Modes.dev); - Modes.dev = NULL; - } - } else { - readDataFromFile(); - } + sdrRun(); // Wake the main thread (if it's still waiting) pthread_mutex_lock(&Modes.data_mutex); @@ -634,16 +277,18 @@ void snipMode(int level) { // void showHelp(void) { printf( -"-----------------------------------------------------------------------------\n" -"| dump1090 ModeS Receiver %45s |\n" -"-----------------------------------------------------------------------------\n" -"--device-index Select RTL device (default: 0)\n" + "-----------------------------------------------------------------------------\n" + "| dump1090 ModeS Receiver %45s |\n" + "-----------------------------------------------------------------------------\n", + MODES_DUMP1090_VARIANT " " MODES_DUMP1090_VERSION); + + sdrShowHelp(); + + printf( +" Common options\n" +"\n" "--gain Set gain (default: max gain. Use -10 for auto-gain)\n" -"--enable-agc Enable the Automatic Gain Control (default: off)\n" "--freq Set frequency (default: 1090 Mhz)\n" -"--ifile Read data from file (use '-' for stdin)\n" -"--iformat Sample format for --ifile: UC8 (default), SC16, or SC16Q11\n" -"--throttle When reading from a file, play back in realtime, not at max speed\n" "--interactive Interactive mode refreshing data on screen. Implies --throttle\n" "--interactive-ttl Remove from list if idle for (default: 60)\n" "--raw Show only messages hex values\n" @@ -683,12 +328,11 @@ void showHelp(void) { "--debug Debug mode (verbose), see README for details\n" "--quiet Disable output to stdout. Use for daemon applications\n" "--show-only Show only messages from the given ICAO on stdout\n" -"--ppm Set receiver error in parts per million (default 0)\n" "--html-dir Use as base directory for the internal HTTP server. Defaults to " HTMLPATH "\n" "--write-json Periodically write json output to (for serving by a separate webserver)\n" "--write-json-every Write json output every t seconds (default 1)\n" "--json-location-accuracy Accuracy of receiver location in json metadata: 0=no location, 1=approximate, 2=exact\n" -"--dcfilter Apply a 1Hz DC filter to input data (requires lots more CPU)\n" +"--dcfilter Apply a 1Hz DC filter to input data (requires more CPU)\n" "--help Show this help\n" "\n" "Debug mode flags: d = Log frames decoded with errors\n" @@ -697,8 +341,7 @@ void showHelp(void) { " C = Log frames with good CRC\n" " p = Log frames with bad preamble\n" " n = Log network debugging info\n" -" j = Log frames to frames.js, loadable by debug.html\n", -MODES_DUMP1090_VARIANT " " MODES_DUMP1090_VERSION +" j = Log frames to frames.js, loadable by debug.html\n" ); } @@ -815,68 +458,6 @@ void backgroundTasks(void) { // //========================================================================= // -int verbose_device_search(char *s) -{ - int i, device_count, device, offset; - char *s2; - char vendor[256], product[256], serial[256]; - device_count = rtlsdr_get_device_count(); - if (!device_count) { - fprintf(stderr, "No supported devices found.\n"); - return -1; - } - fprintf(stderr, "Found %d device(s):\n", device_count); - for (i = 0; i < device_count; i++) { - if (rtlsdr_get_device_usb_strings(i, vendor, product, serial) != 0) { - fprintf(stderr, " %d: unable to read device details\n", i); - } else { - fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); - } - } - fprintf(stderr, "\n"); - /* does string look like raw id number */ - device = (int)strtol(s, &s2, 0); - if (s2[0] == '\0' && device >= 0 && device < device_count) { - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string exact match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - if (strcmp(s, serial) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string prefix match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - if (strncmp(s, serial, strlen(s)) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string suffix match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - offset = strlen(serial) - strlen(s); - if (offset < 0) { - continue;} - if (strncmp(s, serial+offset, strlen(s)) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - fprintf(stderr, "No matching devices found.\n"); - return -1; -} // //========================================================================= // @@ -894,29 +475,12 @@ int main(int argc, char **argv) { for (j = 1; j < argc; j++) { int more = j+1 < argc; // There are more arguments - if (!strcmp(argv[j],"--device-index") && more) { + if (!strcmp(argv[j],"--freq") && more) { + Modes.freq = (int) strtoll(argv[++j],NULL,10); + } else if ( (!strcmp(argv[j], "--device") || !strcmp(argv[j], "--device-index")) && more) { Modes.dev_name = strdup(argv[++j]); } else if (!strcmp(argv[j],"--gain") && more) { Modes.gain = (int) (atof(argv[++j])*10); // Gain is in tens of DBs - } else if (!strcmp(argv[j],"--enable-agc")) { - Modes.enable_agc++; - } else if (!strcmp(argv[j],"--freq") && more) { - Modes.freq = (int) strtoll(argv[++j],NULL,10); - } else if (!strcmp(argv[j],"--ifile") && more) { - Modes.filename = strdup(argv[++j]); - } else if (!strcmp(argv[j],"--iformat") && more) { - ++j; - if (!strcasecmp(argv[j], "uc8")) { - Modes.input_format = INPUT_UC8; - } else if (!strcasecmp(argv[j], "sc16")) { - Modes.input_format = INPUT_SC16; - } else if (!strcasecmp(argv[j], "sc16q11")) { - Modes.input_format = INPUT_SC16Q11; - } else { - fprintf(stderr, "Input format '%s' not understood (supported values: UC8, SC16, SC16Q11)\n", - argv[j]); - exit(1); - } } else if (!strcmp(argv[j],"--dcfilter")) { Modes.dc_filter = 1; } else if (!strcmp(argv[j],"--measure-noise")) { @@ -942,7 +506,7 @@ int main(int argc, char **argv) { fprintf(stderr, "--net-beast ignored, use --net-bo-port to control where Beast output is generated\n"); } else if (!strcmp(argv[j],"--net-only")) { Modes.net = 1; - Modes.net_only = 1; + Modes.sdr_type = SDR_NONE; } else if (!strcmp(argv[j],"--net-heartbeat") && more) { Modes.net_heartbeat_interval = (uint64_t)(1000 * atof(argv[++j])); } else if (!strcmp(argv[j],"--net-ro-size") && more) { @@ -992,9 +556,7 @@ int main(int argc, char **argv) { fprintf(stderr, "warning: --aggressive not supported in this build, option ignored.\n"); #endif } else if (!strcmp(argv[j],"--interactive")) { - Modes.interactive = Modes.throttle = 1; - } else if (!strcmp(argv[j],"--throttle")) { - Modes.throttle = 1; + Modes.interactive = 1; } else if (!strcmp(argv[j],"--interactive-ttl") && more) { Modes.interactive_display_ttl = (uint64_t)(1000 * atof(argv[++j])); } else if (!strcmp(argv[j],"--lat") && more) { @@ -1034,8 +596,6 @@ int main(int argc, char **argv) { } else if (!strcmp(argv[j],"--help")) { showHelp(); exit(0); - } else if (!strcmp(argv[j],"--ppm") && more) { - Modes.ppm_error = atoi(argv[++j]); } else if (!strcmp(argv[j],"--quiet")) { Modes.quiet = 1; } else if (!strcmp(argv[j],"--show-only") && more) { @@ -1056,6 +616,8 @@ int main(int argc, char **argv) { } else if (!strcmp(argv[j], "--json-location-accuracy") && more) { Modes.json_location_accuracy = atoi(argv[++j]); #endif + } else if (sdrHandleOption(argc, argv, &j)) { + /* handled */ } else { fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", @@ -1074,27 +636,13 @@ int main(int argc, char **argv) { log_with_timestamp("%s %s starting up.", MODES_DUMP1090_VARIANT, MODES_DUMP1090_VERSION); modesInit(); - if (Modes.net_only) { - fprintf(stderr,"Net-only mode, no RTL device or file open.\n"); - } else if (Modes.filename == NULL) { - if (modesInitRTLSDR() < 0) { - exit(1); - } - } else { - if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') { - Modes.fd = STDIN_FILENO; - } else if ((Modes.fd = open(Modes.filename, -#ifdef _WIN32 - (O_RDONLY | O_BINARY) -#else - (O_RDONLY) -#endif - )) == -1) { - perror("Opening data file"); - exit(1); - } + if (!sdrOpen()) { + exit(1); + } + + if (Modes.net) { + modesInitNet(); } - if (Modes.net) modesInitNet(); // init stats: Modes.stats_current.start = Modes.stats_current.end = @@ -1111,9 +659,11 @@ int main(int argc, char **argv) { writeJsonToFile("stats.json", generateStatsJson); writeJsonToFile("aircraft.json", generateAircraftJson); + interactiveInit(); + // If the user specifies --net-only, just run in order to serve network // clients without reading data from the RTL device - if (Modes.net_only) { + if (Modes.sdr_type == SDR_NONE) { while (!Modes.exit) { struct timespec start_time; @@ -1130,7 +680,7 @@ int main(int argc, char **argv) { pthread_mutex_lock(&Modes.data_mutex); pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL); - while (Modes.exit == 0) { + while (!Modes.exit) { struct timespec start_time; if (Modes.first_free_buffer == Modes.first_filled_buffer) { @@ -1186,7 +736,7 @@ int main(int argc, char **argv) { // Nothing to process this time around. pthread_mutex_unlock(&Modes.data_mutex); if (--watchdogCounter <= 0) { - log_with_timestamp("No data received from the dongle for a long time, it may have wedged"); + log_with_timestamp("No data received from the SDR for a long time, it may have wedged"); watchdogCounter = 600; } } @@ -1213,9 +763,10 @@ int main(int argc, char **argv) { display_total_stats(); } - cleanup_converter(Modes.converter_state); log_with_timestamp("Normal exit."); + sdrClose(); + #ifndef _WIN32 pthread_exit(0); #else diff --git a/dump1090.h b/dump1090.h index a6490c5..5e89c11 100644 --- a/dump1090.h +++ b/dump1090.h @@ -65,6 +65,7 @@ #include #include #include + #include #include #include #include @@ -84,12 +85,8 @@ #include "compat/compat.h" -// Avoid a dependency on rtl-sdr except where it's really needed. -typedef struct rtlsdr_dev rtlsdr_dev_t; - // ============================= #defines =============================== -#define MODES_DEFAULT_PPM 52 #define MODES_DEFAULT_FREQ 1090000000 #define MODES_DEFAULT_WIDTH 1000 #define MODES_DEFAULT_HEIGHT 700 @@ -245,9 +242,14 @@ typedef enum { #include "cpr.h" #include "icao_filter.h" #include "convert.h" +#include "sdr.h" //======================== structure declarations ========================= +typedef enum { + SDR_NONE, SDR_IFILE, SDR_RTLSDR +} sdr_type_t; + // Structure representing one magnitude buffer struct mag_buf { uint16_t *data; // Magnitude data. Starts with Modes.trailing_samples worth of overlap from the previous block @@ -274,24 +276,17 @@ struct { // Internal state unsigned trailing_samples; // extra trailing samples in magnitude buffers double sample_rate; // actual sample rate in use (in hz) - int fd; // --ifile option file descriptor - input_format_t input_format; // --iformat option uint16_t *maglut; // I/Q -> Magnitude lookup table uint16_t *log10lut; // Magnitude -> log10 lookup table int exit; // Exit from the main loop when true // Sample conversion int dc_filter; // should we apply a DC filter? - iq_convert_fn converter_function; - struct converter_state *converter_state; // RTLSDR char * dev_name; int gain; - int enable_agc; - rtlsdr_dev_t *dev; int freq; - int ppm_error; // Networking char aneterr[ANET_ERR_LEN]; @@ -308,7 +303,7 @@ struct { // Internal state #endif // Configuration - char *filename; // Input form file, --ifile option + sdr_type_t sdr_type; // where are we getting data from? int nfix_crc; // Number of crc bit error(s) to correct int check_crc; // Only display messages with good CRC int raw; // Raw output format @@ -343,7 +338,6 @@ struct { // Internal state uint64_t json_interval; // Interval between rewriting the json aircraft file, in milliseconds; also the advertised map refresh interval char *html_dir; // Path to www base directory. int json_location_accuracy; // Accuracy of location metadata: 0=none, 1=approx, 2=exact - int throttle; // When reading from a file, throttle file playback to realtime? int json_aircraft_history_next; struct { diff --git a/sdr.c b/sdr.c new file mode 100644 index 0000000..41e8ea1 --- /dev/null +++ b/sdr.c @@ -0,0 +1,145 @@ +#include "dump1090.h" +#include "sdr.h" + +#define ENABLE_RTLSDR + +#include "sdr_ifile.h" +#ifdef ENABLE_RTLSDR +# include "sdr_rtlsdr.h" +#endif + +typedef struct { + const char *name; + sdr_type_t sdr_type; + void (*initConfig)(); + void (*showHelp)(); + bool (*handleOption)(int, char**, int*); + bool (*open)(); + void (*run)(); + void (*close)(); +} sdr_handler; + +static void noInitConfig() +{ +} + +static void noShowHelp() +{ +} + +static bool noHandleOption(int argc, char **argv, int *jptr) +{ + MODES_NOTUSED(argc); + MODES_NOTUSED(argv); + MODES_NOTUSED(jptr); + + return false; +} + +static bool noOpen() +{ + fprintf(stderr, "Net-only mode, no SDR device or file open.\n"); + return true; +} + +static void noRun() +{ +} + +static void noClose() +{ +} + +static bool unsupportedOpen() +{ + fprintf(stderr, "Support for this SDR type was not enabled in this build.\n"); + return false; +} + +static sdr_handler sdr_handlers[] = { +#ifdef ENABLE_RTLSDR + { "rtlsdr", SDR_RTLSDR, rtlsdrInitConfig, rtlsdrShowHelp, rtlsdrHandleOption, rtlsdrOpen, rtlsdrRun, rtlsdrClose }, +#endif + + { "ifile", SDR_IFILE, ifileInitConfig, ifileShowHelp, ifileHandleOption, ifileOpen, ifileRun, ifileClose }, + { "none", SDR_NONE, noInitConfig, noShowHelp, noHandleOption, noOpen, noRun, noClose }, + + { NULL, SDR_NONE, NULL, NULL, NULL, NULL, NULL, NULL } /* must come last */ +}; + +void sdrInitConfig() +{ + // Default SDR is the first type available in the handlers array. + Modes.sdr_type = sdr_handlers[0].sdr_type; + + for (int i = 0; sdr_handlers[i].name; ++i) { + sdr_handlers[i].initConfig(); + } +} + +void sdrShowHelp() +{ + printf("--device-type Select SDR type (default: %s)\n", sdr_handlers[0].name); + printf("\n"); + + for (int i = 0; sdr_handlers[i].name; ++i) { + sdr_handlers[i].showHelp(); + } +} + +bool sdrHandleOption(int argc, char **argv, int *jptr) +{ + int j = *jptr; + if (!strcmp(argv[j], "--device-type") && (j+1) < argc) { + ++j; + for (int i = 0; sdr_handlers[i].name; ++i) { + if (!strcasecmp(sdr_handlers[i].name, argv[j])) { + Modes.sdr_type = sdr_handlers[i].sdr_type; + *jptr = j; + return true; + } + } + + fprintf(stderr, "SDR type '%s' not recognized; supported SDR types are:\n", argv[j]); + for (int i = 0; sdr_handlers[i].name; ++i) { + fprintf(stderr, " %s\n", sdr_handlers[i].name); + } + + return false; + } + + for (int i = 0; sdr_handlers[i].name; ++i) { + if (sdr_handlers[i].handleOption(argc, argv, jptr)) + return true; + } + + return false; +} + +static sdr_handler *current_handler() +{ + static sdr_handler unsupported_handler = { "unsupported", SDR_NONE, noInitConfig, noShowHelp, noHandleOption, unsupportedOpen, noRun, noClose }; + + for (int i = 0; sdr_handlers[i].name; ++i) { + if (Modes.sdr_type == sdr_handlers[i].sdr_type) { + return &sdr_handlers[i]; + } + } + + return &unsupported_handler; +} + +bool sdrOpen() +{ + return current_handler()->open(); +} + +void sdrRun() +{ + return current_handler()->run(); +} + +void sdrClose() +{ + current_handler()->close(); +} diff --git a/sdr.h b/sdr.h new file mode 100644 index 0000000..b578446 --- /dev/null +++ b/sdr.h @@ -0,0 +1,13 @@ +#ifndef SDR_H +#define SDR_H + +// Common interface to different SDR inputs. + +void sdrInitConfig(); +void sdrShowHelp(); +bool sdrHandleOption(int argc, char **argv, int *jptr); +bool sdrOpen(); +void sdrRun(); +void sdrClose(); + +#endif diff --git a/sdr_ifile.c b/sdr_ifile.c new file mode 100644 index 0000000..42fee29 --- /dev/null +++ b/sdr_ifile.c @@ -0,0 +1,236 @@ +#include "dump1090.h" +#include "sdr_ifile.h" + +static struct { + const char *filename; + input_format_t input_format; + bool throttle; + + int fd; + unsigned bytes_per_sample; + void *readbuf; + iq_convert_fn converter; + struct converter_state *converter_state; +} ifile; + +void ifileInitConfig(void) +{ + ifile.filename = NULL; + ifile.input_format = INPUT_UC8; + ifile.throttle = false; + ifile.fd = -1; + ifile.bytes_per_sample = 0; + ifile.readbuf = NULL; + ifile.converter = NULL; + ifile.converter_state = NULL; +} + +void ifileShowHelp() +{ + printf(" ifile-specific options (use with --ifile)\n"); + printf("\n"); + printf("--ifile read samples from given file ('-' for stdin)\n"); + printf("--iformat set sample format (UC8, SC16, SC16Q11)\n"); + printf("--throttle process samples at the original capture speed\n"); + printf("\n"); +} + +bool ifileHandleOption(int argc, char **argv, int *jptr) +{ + int j = *jptr; + bool more = (j +1 < argc); + + if (!strcmp(argv[j], "--ifile") && more) { + // implies --device-type ifile + ifile.filename = strdup(argv[++j]); + Modes.sdr_type = SDR_IFILE; + } else if (!strcmp(argv[j],"--iformat") && more) { + ++j; + if (!strcasecmp(argv[j], "uc8")) { + ifile.input_format = INPUT_UC8; + } else if (!strcasecmp(argv[j], "sc16")) { + ifile.input_format = INPUT_SC16; + } else if (!strcasecmp(argv[j], "sc16q11")) { + ifile.input_format = INPUT_SC16Q11; + } else { + fprintf(stderr, "Input format '%s' not understood (supported values: UC8, SC16, SC16Q11)\n", + argv[j]); + return false; + } + } else if (!strcmp(argv[j],"--throttle")) { + ifile.throttle = true; + } else { + return false; + } + + *jptr = j; + return true; +} + +// +//========================================================================= +// +// This is used when --ifile is specified in order to read data from file +// instead of using an RTLSDR device +// +bool ifileOpen(void) +{ + if (!ifile.filename) { + fprintf(stderr, "SDR type 'ifile' requires an --ifile argument\n"); + return false; + } + + if (!strcmp(ifile.filename, "-")) { + ifile.fd = STDIN_FILENO; + } else if ((ifile.fd = open(ifile.filename, O_RDONLY)) < 0) { + fprintf(stderr, "ifile: could not open %s: %s\n", + ifile.filename, strerror(errno)); + return false; + } + + switch (ifile.input_format) { + case INPUT_UC8: + ifile.bytes_per_sample = 2; + break; + case INPUT_SC16: + case INPUT_SC16Q11: + ifile.bytes_per_sample = 4; + break; + default: + fprintf(stderr, "ifile: unhandled input format\n"); + ifileClose(); + return false; + } + + if (!(ifile.readbuf = malloc(MODES_MAG_BUF_SAMPLES * ifile.bytes_per_sample))) { + fprintf(stderr, "ifile: failed to allocate read buffer\n"); + ifileClose(); + return false; + } + + ifile.converter = init_converter(ifile.input_format, + Modes.sample_rate, + Modes.dc_filter, + &ifile.converter_state); + if (!ifile.converter) { + fprintf(stderr, "ifile: can't initialize sample converter\n"); + ifileClose(); + return false; + } + + return true; +} + +void ifileRun() +{ + if (ifile.fd < 0) + return; + + int eof = 0; + struct timespec next_buffer_delivery; + + struct timespec thread_cpu; + start_cpu_timing(&thread_cpu); + + uint64_t sampleCounter = 0; + + clock_gettime(CLOCK_MONOTONIC, &next_buffer_delivery); + + pthread_mutex_lock(&Modes.data_mutex); + while (!Modes.exit && !eof) { + ssize_t nread, toread; + void *r; + struct mag_buf *outbuf, *lastbuf; + unsigned next_free_buffer; + unsigned slen; + + next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS; + if (next_free_buffer == Modes.first_filled_buffer) { + // no space for output yet + pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex); + continue; + } + + outbuf = &Modes.mag_buffers[Modes.first_free_buffer]; + lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS]; + pthread_mutex_unlock(&Modes.data_mutex); + + // Compute the sample timestamp for the start of the block + outbuf->sampleTimestamp = sampleCounter * 12e6 / Modes.sample_rate; + sampleCounter += MODES_MAG_BUF_SAMPLES; + + // Copy trailing data from last block (or reset if not valid) + if (lastbuf->length >= Modes.trailing_samples) { + memcpy(outbuf->data, lastbuf->data + lastbuf->length, Modes.trailing_samples * sizeof(uint16_t)); + } else { + memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t)); + } + + // Get the system time for the start of this block + clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp); + + toread = MODES_MAG_BUF_SAMPLES * ifile.bytes_per_sample; + r = ifile.readbuf; + while (toread) { + nread = read(ifile.fd, r, toread); + if (nread <= 0) { + if (nread < 0) { + fprintf(stderr, "ifile: error reading input file: %s\n", strerror(errno)); + } + // Done. + eof = 1; + break; + } + r += nread; + toread -= nread; + } + + slen = outbuf->length = MODES_MAG_BUF_SAMPLES - toread / ifile.bytes_per_sample; + + // Convert the new data + ifile.converter(ifile.readbuf, &outbuf->data[Modes.trailing_samples], slen, ifile.converter_state, &outbuf->mean_level, &outbuf->mean_power); + + if (ifile.throttle || Modes.interactive) { + // Wait until we are allowed to release this buffer to the main thread + while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_buffer_delivery, NULL) == EINTR) + ; + + // compute the time we can deliver the next buffer. + next_buffer_delivery.tv_nsec += outbuf->length * 1e9 / Modes.sample_rate; + normalize_timespec(&next_buffer_delivery); + } + + // Push the new data to the main thread + pthread_mutex_lock(&Modes.data_mutex); + Modes.first_free_buffer = next_free_buffer; + // accumulate CPU while holding the mutex, and restart measurement + end_cpu_timing(&thread_cpu, &Modes.reader_cpu_accumulator); + start_cpu_timing(&thread_cpu); + pthread_cond_signal(&Modes.data_cond); + } + + // Wait for the main thread to consume all data + while (!Modes.exit && Modes.first_filled_buffer != Modes.first_free_buffer) + pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex); + + pthread_mutex_unlock(&Modes.data_mutex); +} + +void ifileClose() +{ + if (ifile.converter) { + cleanup_converter(ifile.converter_state); + ifile.converter = NULL; + ifile.converter_state = NULL; + } + + if (ifile.readbuf) { + free(ifile.readbuf); + ifile.readbuf = NULL; + } + + if (ifile.fd >= 0 && ifile.fd != STDIN_FILENO) { + close(ifile.fd); + ifile.fd = -1; + } +} diff --git a/sdr_ifile.h b/sdr_ifile.h new file mode 100644 index 0000000..63b97c9 --- /dev/null +++ b/sdr_ifile.h @@ -0,0 +1,13 @@ +#ifndef SDR_IFILE_H +#define SDR_IFILE_H + +// Pseudo-SDR that reads from a sample file + +void ifileInitConfig(); +void ifileShowHelp(); +bool ifileHandleOption(int argc, char **argv, int *jptr); +bool ifileOpen(); +void ifileRun(); +void ifileClose(); + +#endif diff --git a/sdr_rtlsdr.c b/sdr_rtlsdr.c new file mode 100644 index 0000000..9f73aca --- /dev/null +++ b/sdr_rtlsdr.c @@ -0,0 +1,335 @@ +#include "dump1090.h" +#include "sdr_rtlsdr.h" + +#include + +static struct { + rtlsdr_dev_t *dev; + bool digital_agc; + int ppm_error; + + iq_convert_fn converter; + struct converter_state *converter_state; +} RTLSDR; + +// +// =============================== RTLSDR handling ========================== +// + +void rtlsdrInitConfig() +{ + RTLSDR.dev = NULL; + RTLSDR.digital_agc = false; + RTLSDR.ppm_error = 0; + RTLSDR.converter = NULL; + RTLSDR.converter_state = NULL; +} + +static void show_rtlsdr_devices() +{ + int device_count = rtlsdr_get_device_count(); + fprintf(stderr, "rtlsdr: found %d device(s):\n", device_count); + for (int i = 0; i < device_count; i++) { + char vendor[256], product[256], serial[256]; + + if (rtlsdr_get_device_usb_strings(i, vendor, product, serial) != 0) { + fprintf(stderr, " %d: unable to read device details\n", i); + } else { + fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); + } + } +} + +static int find_device_index(char *s) +{ + int device_count = rtlsdr_get_device_count(); + if (!device_count) { + return -1; + } + + /* does string look like raw id number */ + if (!strcmp(s, "0")) { + return 0; + } else if (s[0] != '0') { + char *s2; + int device = (int)strtol(s, &s2, 10); + if (s2[0] == '\0' && device >= 0 && device < device_count) { + return device; + } + } + + /* does string exact match a serial */ + for (int i = 0; i < device_count; i++) { + char serial[256]; + if (rtlsdr_get_device_usb_strings(i, NULL, NULL, serial) == 0 && !strcmp(s, serial)) { + return i; + } + } + + /* does string prefix match a serial */ + for (int i = 0; i < device_count; i++) { + char serial[256]; + if (rtlsdr_get_device_usb_strings(i, NULL, NULL, serial) == 0 && !strncmp(s, serial, strlen(s))) { + return i; + } + } + + /* does string suffix match a serial */ + for (int i = 0; i < device_count; i++) { + char serial[256]; + if (rtlsdr_get_device_usb_strings(i, NULL, NULL, serial) == 0 && strlen(s) < strlen(serial) && !strcmp(serial + strlen(serial) - strlen(s), s)) { + return i; + } + } + + return -1; +} + +void rtlsdrShowHelp() +{ + printf(" rtlsdr-specific options (use with --device-type rtlsdr)\n"); + printf("\n"); + printf("--device select device by index or serial number\n"); + printf("--enable-agc enable digital AGC (not tuner AGC!)\n"); + printf("--ppm set oscillator frequency correction in PPM\n"); + printf("\n"); +} + +bool rtlsdrHandleOption(int argc, char **argv, int *jptr) +{ + int j = *jptr; + bool more = (j +1 < argc); + + if (!strcmp(argv[j], "--enable-agc")) { + RTLSDR.digital_agc = true; + } else if (!strcmp(argv[j], "--ppm") && more) { + RTLSDR.ppm_error = atoi(argv[++j]); + } else { + return false; + } + + *jptr = j; + return true; +} + +bool rtlsdrOpen(void) { + if (!rtlsdr_get_device_count()) { + fprintf(stderr, "rtlsdr: no supported devices found.\n"); + return false; + } + + int dev_index = 0; + if (Modes.dev_name) { + if ((dev_index = find_device_index(Modes.dev_name)) < 0) { + fprintf(stderr, "rtlsdr: no device matching '%s' found.\n", Modes.dev_name); + show_rtlsdr_devices(); + return false; + } + } + + char manufacturer[256]; + char product[256]; + char serial[256]; + if (rtlsdr_get_device_usb_strings(dev_index, manufacturer, product, serial) < 0) { + fprintf(stderr, "rtlsdr: error querying device #%d: %s\n", dev_index, strerror(errno)); + return false; + } + + fprintf(stderr, "rtlsdr: using device #%d: %s (%s, %s, SN %s)\n", + dev_index, rtlsdr_get_device_name(dev_index), + manufacturer, product, serial); + + if (rtlsdr_open(&RTLSDR.dev, dev_index) < 0) { + fprintf(stderr, "rtlsdr: error opening the RTLSDR device: %s\n", + strerror(errno)); + return false; + } + + // Set gain, frequency, sample rate, and reset the device + if (Modes.gain == MODES_AUTO_GAIN) { + fprintf(stderr, "rtlsdr: enabling tuner AGC\n"); + rtlsdr_set_tuner_gain_mode(RTLSDR.dev, 1); + } else { + int *gains; + int numgains; + + numgains = rtlsdr_get_tuner_gains(RTLSDR.dev, NULL); + if (numgains <= 0) { + fprintf(stderr, "rtlsdr: error getting tuner gains\n"); + return false; + } + + gains = malloc(numgains * sizeof(int)); + if (rtlsdr_get_tuner_gains(RTLSDR.dev, gains) != numgains) { + fprintf(stderr, "rtlsdr: error getting tuner gains\n"); + free(gains); + return false; + } + + int target = (Modes.gain == MODES_MAX_GAIN ? 9999 : Modes.gain); + int closest = -1; + + for (int i = 0; i < numgains; ++i) { + if (closest == -1 || abs(gains[i] - target) < abs(gains[closest] - target)) + closest = i; + } + + free(gains); + + rtlsdr_set_tuner_gain(RTLSDR.dev, gains[closest]); + fprintf(stderr, "rtlsdr: tuner gain set to %.1f dB\n", + rtlsdr_get_tuner_gain(RTLSDR.dev)/10.0); + } + + if (RTLSDR.digital_agc) { + fprintf(stderr, "rtlsdr: enabling digital AGC\n"); + rtlsdr_set_agc_mode(RTLSDR.dev, 1); + } + + rtlsdr_set_freq_correction(RTLSDR.dev, RTLSDR.ppm_error); + rtlsdr_set_center_freq(RTLSDR.dev, Modes.freq); + rtlsdr_set_sample_rate(RTLSDR.dev, (unsigned)Modes.sample_rate); + + rtlsdr_reset_buffer(RTLSDR.dev); + + RTLSDR.converter = init_converter(INPUT_UC8, + Modes.sample_rate, + Modes.dc_filter, + &RTLSDR.converter_state); + if (!RTLSDR.converter) { + fprintf(stderr, "rtlsdr: can't initialize sample converter\n"); + rtlsdrClose(); + return false; + } + + return true; +} + +static struct timespec rtlsdr_thread_cpu; + +void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { + struct mag_buf *outbuf; + struct mag_buf *lastbuf; + uint32_t slen; + unsigned next_free_buffer; + unsigned free_bufs; + unsigned block_duration; + + static int was_odd = 0; // paranoia!! + static int dropping = 0; + static uint64_t sampleCounter = 0; + + MODES_NOTUSED(ctx); + + // Lock the data buffer variables before accessing them + pthread_mutex_lock(&Modes.data_mutex); + if (Modes.exit) { + rtlsdr_cancel_async(RTLSDR.dev); // ask our caller to exit + } + + next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS; + outbuf = &Modes.mag_buffers[Modes.first_free_buffer]; + lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS]; + free_bufs = (Modes.first_filled_buffer - next_free_buffer + MODES_MAG_BUFFERS) % MODES_MAG_BUFFERS; + + // Paranoia! Unlikely, but let's go for belt and suspenders here + + if (len != MODES_RTL_BUF_SIZE) { + fprintf(stderr, "weirdness: rtlsdr gave us a block with an unusual size (got %u bytes, expected %u bytes)\n", + (unsigned)len, (unsigned)MODES_RTL_BUF_SIZE); + + if (len > MODES_RTL_BUF_SIZE) { + // wat?! Discard the start. + unsigned discard = (len - MODES_RTL_BUF_SIZE + 1) / 2; + outbuf->dropped += discard; + buf += discard*2; + len -= discard*2; + } + } + + if (was_odd) { + // Drop a sample so we are in sync with I/Q samples again (hopefully) + ++buf; + --len; + ++outbuf->dropped; + } + + was_odd = (len & 1); + slen = len/2; + + if (free_bufs == 0 || (dropping && free_bufs < MODES_MAG_BUFFERS/2)) { + // FIFO is full. Drop this block. + dropping = 1; + outbuf->dropped += slen; + sampleCounter += slen; + pthread_mutex_unlock(&Modes.data_mutex); + return; + } + + dropping = 0; + pthread_mutex_unlock(&Modes.data_mutex); + + // Compute the sample timestamp and system timestamp for the start of the block + outbuf->sampleTimestamp = sampleCounter * 12e6 / Modes.sample_rate; + sampleCounter += slen; + block_duration = 1e9 * slen / Modes.sample_rate; + + // Get the approx system time for the start of this block + clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp); + outbuf->sysTimestamp.tv_nsec -= block_duration; + normalize_timespec(&outbuf->sysTimestamp); + + // Copy trailing data from last block (or reset if not valid) + if (outbuf->dropped == 0) { + memcpy(outbuf->data, lastbuf->data + lastbuf->length, Modes.trailing_samples * sizeof(uint16_t)); + } else { + memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t)); + } + + // Convert the new data + outbuf->length = slen; + RTLSDR.converter(buf, &outbuf->data[Modes.trailing_samples], slen, RTLSDR.converter_state, &outbuf->mean_level, &outbuf->mean_power); + + // Push the new data to the demodulation thread + pthread_mutex_lock(&Modes.data_mutex); + + Modes.mag_buffers[next_free_buffer].dropped = 0; + Modes.mag_buffers[next_free_buffer].length = 0; // just in case + Modes.first_free_buffer = next_free_buffer; + + // accumulate CPU while holding the mutex, and restart measurement + end_cpu_timing(&rtlsdr_thread_cpu, &Modes.reader_cpu_accumulator); + start_cpu_timing(&rtlsdr_thread_cpu); + + pthread_cond_signal(&Modes.data_cond); + pthread_mutex_unlock(&Modes.data_mutex); +} + +void rtlsdrRun() +{ + if (!RTLSDR.dev) { + return; + } + + start_cpu_timing(&rtlsdr_thread_cpu); + + while (!Modes.exit) { + rtlsdr_read_async(RTLSDR.dev, rtlsdrCallback, NULL, + /* MODES_RTL_BUFFERS */ 4, + MODES_RTL_BUF_SIZE); + } +} + +void rtlsdrClose() +{ + if (RTLSDR.dev) { + rtlsdr_close(RTLSDR.dev); + RTLSDR.dev = NULL; + } + + if (RTLSDR.converter) { + cleanup_converter(RTLSDR.converter_state); + RTLSDR.converter = NULL; + RTLSDR.converter_state = NULL; + } +} diff --git a/sdr_rtlsdr.h b/sdr_rtlsdr.h new file mode 100644 index 0000000..d83cac5 --- /dev/null +++ b/sdr_rtlsdr.h @@ -0,0 +1,13 @@ +#ifndef SDR_RTLSDR_H +#define SDR_RTLSDR_H + +// Support for DVB-T dongles in SDR mode via librtlsdr + +void rtlsdrInitConfig(); +void rtlsdrShowHelp(); +bool rtlsdrOpen(); +void rtlsdrRun(); +void rtlsdrClose(); +bool rtlsdrHandleOption(int argc, char **argv, int *jptr); + +#endif