// Part of dump1090, a Mode S message decoder for RTLSDR devices. // // sdr_ifile.c: "file" SDR support // // Copyright (c) 2014-2017 Oliver Jowett // Copyright (c) 2017 FlightAware LLC // // This file is free software: you may copy, redistribute and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation, either version 2 of the License, or (at your // option) any later version. // // This file is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // This file incorporates work covered by the following copyright and // permission notice: // // Copyright (C) 2012 by Salvatore Sanfilippo // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #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; } }