dump1090/sdr_ifile.c
Oliver Jowett 75b221c0e3 Lots of refactoring to move the SDR-type-specific bits out of the
main code and make them optionally buildable.
2017-01-27 17:30:40 +00:00

237 lines
7.2 KiB
C

#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 <path> read samples from given file ('-' for stdin)\n");
printf("--iformat <type> 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;
}
}