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(); } // Wake the main thread (if it's still waiting) pthread_mutex_lock(&Modes.data_mutex); Modes.exit = 1; // just in case pthread_cond_signal(&Modes.data_cond); pthread_mutex_unlock(&Modes.data_mutex); #ifndef _WIN32 pthread_exit(NULL); #else return NULL; #endif }
// //========================================================================= // int main(int argc, char **argv) { int j; // Set sane defaults modesInitConfig(); // signal handlers: signal(SIGINT, sigintHandler); signal(SIGTERM, sigtermHandler); // Parse the command line options for (j = 1; j < argc; j++) { int more = j+1 < argc; // There are more arguments if (!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")) { Modes.measure_noise = 1; } else if (!strcmp(argv[j],"--fix")) { Modes.nfix_crc = 1; } else if (!strcmp(argv[j],"--no-fix")) { Modes.nfix_crc = 0; } else if (!strcmp(argv[j],"--no-crc-check")) { Modes.check_crc = 0; } else if (!strcmp(argv[j],"--phase-enhance")) { Modes.phase_enhance = 1; } else if (!strcmp(argv[j],"--raw")) { Modes.raw = 1; } else if (!strcmp(argv[j],"--net")) { Modes.net = 1; } else if (!strcmp(argv[j],"--modeac")) { Modes.mode_ac = 1; } else if (!strcmp(argv[j],"--net-beast")) { Modes.beast = 1; } else if (!strcmp(argv[j],"--net-only")) { Modes.net = 1; Modes.net_only = 1; } 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) { Modes.net_output_flush_size = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-ro-rate") && more) { Modes.net_output_flush_interval = 1000 * atoi(argv[++j]) / 15; // backwards compatibility } else if (!strcmp(argv[j],"--net-ro-interval") && more) { Modes.net_output_flush_interval = (uint64_t)(1000 * atof(argv[++j])); } else if (!strcmp(argv[j],"--net-ro-port") && more) { if (Modes.beast) // Required for legacy backward compatibility {Modes.net_output_beast_port = atoi(argv[++j]);;} else {Modes.net_output_raw_port = atoi(argv[++j]);} } else if (!strcmp(argv[j],"--net-ri-port") && more) { Modes.net_input_raw_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-bo-port") && more) { Modes.net_output_beast_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-bi-port") && more) { Modes.net_input_beast_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-bind-address") && more) { Modes.net_bind_address = strdup(argv[++j]); } else if (!strcmp(argv[j],"--net-http-port") && more) { Modes.net_http_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-fatsv-port") && more) { Modes.net_fatsv_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-sbs-port") && more) { Modes.net_output_sbs_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-buffer") && more) { Modes.net_sndbuf_size = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-verbatim")) { Modes.net_verbatim = 1; } else if (!strcmp(argv[j],"--forward-mlat")) { Modes.forward_mlat = 1; } else if (!strcmp(argv[j],"--onlyaddr")) { Modes.onlyaddr = 1; } else if (!strcmp(argv[j],"--metric")) { Modes.metric = 1; } else if (!strcmp(argv[j],"--aggressive")) { Modes.nfix_crc = MODES_MAX_BITERRORS; } else if (!strcmp(argv[j],"--interactive")) { Modes.interactive = Modes.throttle = 1; } else if (!strcmp(argv[j],"--throttle")) { Modes.throttle = 1; } else if (!strcmp(argv[j],"--interactive-rows") && more) { Modes.interactive_rows = atoi(argv[++j]); } 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) { Modes.fUserLat = atof(argv[++j]); } else if (!strcmp(argv[j],"--lon") && more) { Modes.fUserLon = atof(argv[++j]); } else if (!strcmp(argv[j],"--max-range") && more) { Modes.maxRange = atof(argv[++j]) * 1852.0; // convert to metres } else if (!strcmp(argv[j],"--debug") && more) { char *f = argv[++j]; while(*f) { switch(*f) { case 'D': Modes.debug |= MODES_DEBUG_DEMOD; break; case 'd': Modes.debug |= MODES_DEBUG_DEMODERR; break; case 'C': Modes.debug |= MODES_DEBUG_GOODCRC; break; case 'c': Modes.debug |= MODES_DEBUG_BADCRC; break; case 'p': Modes.debug |= MODES_DEBUG_NOPREAMBLE; break; case 'n': Modes.debug |= MODES_DEBUG_NET; break; case 'j': Modes.debug |= MODES_DEBUG_JS; break; default: fprintf(stderr, "Unknown debugging flag: %c\n", *f); exit(1); break; } f++; } } else if (!strcmp(argv[j],"--stats")) { if (!Modes.stats) Modes.stats = (uint64_t)1 << 60; // "never" } else if (!strcmp(argv[j],"--stats-range")) { Modes.stats_range_histo = 1; } else if (!strcmp(argv[j],"--stats-every") && more) { Modes.stats = (uint64_t) (1000 * atof(argv[++j])); } else if (!strcmp(argv[j],"--snip") && more) { snipMode(atoi(argv[++j])); exit(0); } 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) { Modes.show_only = (uint32_t) strtoul(argv[++j], NULL, 16); } else if (!strcmp(argv[j],"--mlat")) { Modes.mlat = 1; } else if (!strcmp(argv[j],"--interactive-rtl1090")) { Modes.interactive = 1; Modes.interactive_rtl1090 = 1; } else if (!strcmp(argv[j],"--oversample")) { Modes.oversample = 1; #ifndef _WIN32 } else if (!strcmp(argv[j], "--write-json") && more) { Modes.json_dir = strdup(argv[++j]); } else if (!strcmp(argv[j], "--write-json-every") && more) { Modes.json_interval = (uint64_t)(1000 * atof(argv[++j])); if (Modes.json_interval < 100) // 0.1s Modes.json_interval = 100; } else if (!strcmp(argv[j], "--json-location-accuracy") && more) { Modes.json_location_accuracy = atoi(argv[++j]); #endif } else { fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", argv[j]); showHelp(); exit(1); } } #ifndef _WIN32 // Setup for SIGWINCH for handling lines if (Modes.interactive) {signal(SIGWINCH, sigWinchCallback);} #endif if (Modes.mode_ac && Modes.oversample) { fprintf(stderr, "Warning: --modeac is currently ignored when --oversample is used;\n" " no ModeA/C messages will be decoded.\n"); } // Initialization 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 (Modes.net) modesInitNet(); // init stats: Modes.stats_current.start = Modes.stats_current.end = Modes.stats_alltime.start = Modes.stats_alltime.end = Modes.stats_periodic.start = Modes.stats_periodic.end = Modes.stats_5min.start = Modes.stats_5min.end = Modes.stats_15min.start = Modes.stats_15min.end = mstime(); for (j = 0; j < 15; ++j) Modes.stats_1min[j].start = Modes.stats_1min[j].end = Modes.stats_current.start; // write initial json files so they're not missing writeJsonToFile("receiver.json", generateReceiverJson); writeJsonToFile("stats.json", generateStatsJson); writeJsonToFile("aircraft.json", generateAircraftJson); // 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) { while (!Modes.exit) { struct timespec start_time; start_cpu_timing(&start_time); backgroundTasks(); end_cpu_timing(&start_time, &Modes.stats_current.background_cpu); usleep(100000); } } else { // Create the thread that will read the data from the device. pthread_mutex_lock(&Modes.data_mutex); pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL); while (Modes.exit == 0) { struct timespec start_time; if (Modes.first_free_buffer == Modes.first_filled_buffer) { /* wait for more data. * we should be getting data every 50-60ms. wait for max 100ms before we give up and do some background work. * this is fairly aggressive as all our network I/O runs out of the background work! */ struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_nsec += 100000000; normalize_timespec(&ts); pthread_cond_timedwait(&Modes.data_cond, &Modes.data_mutex, &ts); // This unlocks Modes.data_mutex, and waits for Modes.data_cond } // Modes.data_mutex is locked, and possibly we have data. // copy out reader CPU time and reset it add_timespecs(&Modes.reader_cpu_accumulator, &Modes.stats_current.reader_cpu, &Modes.stats_current.reader_cpu); Modes.reader_cpu_accumulator.tv_sec = 0; Modes.reader_cpu_accumulator.tv_nsec = 0; if (Modes.first_free_buffer != Modes.first_filled_buffer) { // FIFO is not empty, process one buffer. struct mag_buf *buf; start_cpu_timing(&start_time); buf = &Modes.mag_buffers[Modes.first_filled_buffer]; // Process data after releasing the lock, so that the capturing // thread can read data while we perform computationally expensive // stuff at the same time. pthread_mutex_unlock(&Modes.data_mutex); if (Modes.oversample) { demodulate2400(buf); } else { demodulate2000(buf); } Modes.stats_current.samples_processed += buf->length; Modes.stats_current.samples_dropped += buf->dropped; end_cpu_timing(&start_time, &Modes.stats_current.demod_cpu); // Mark the buffer we just processed as completed. pthread_mutex_lock(&Modes.data_mutex); Modes.first_filled_buffer = (Modes.first_filled_buffer + 1) % MODES_MAG_BUFFERS; pthread_cond_signal(&Modes.data_cond); pthread_mutex_unlock(&Modes.data_mutex); } else { // Nothing to process this time around. pthread_mutex_unlock(&Modes.data_mutex); } start_cpu_timing(&start_time); backgroundTasks(); end_cpu_timing(&start_time, &Modes.stats_current.background_cpu); pthread_mutex_lock(&Modes.data_mutex); } pthread_mutex_unlock(&Modes.data_mutex); pthread_join(Modes.reader_thread,NULL); // Wait on reader thread exit pthread_cond_destroy(&Modes.data_cond); // Thread cleanup - only after the reader thread is dead! pthread_mutex_destroy(&Modes.data_mutex); } // If --stats were given, print statistics if (Modes.stats) { display_total_stats(); } cleanup_converter(Modes.converter_state); log_with_timestamp("Normal exit."); pthread_exit(0); #ifdef _WIN32 return (0); #endif }
// //========================================================================= // // 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->total_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); }
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->total_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); }
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 outbuf->sysTimestamp = mstime(); 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); }