Add CS16 input and output (#773)

This commit is contained in:
Christian W. Zuckschwerdt
2018-07-25 10:26:17 +02:00
committed by GitHub
parent 5b098cea03
commit eb02ef4bab
3 changed files with 74 additions and 35 deletions

View File

@@ -82,9 +82,9 @@ Usage: = Tuner options =
[-m <mode>] Data file mode for input / output file (default: 0) [-m <mode>] Data file mode for input / output file (default: 0)
0 = Raw I/Q samples (uint8, 2 channel) 0 = Raw I/Q samples (uint8, 2 channel)
1 = AM demodulated samples (int16 pcm, 1 channel) 1 = AM demodulated samples (int16 pcm, 1 channel)
2 = FM demodulated samples (int16) (experimental) 2 = FM demodulated samples (int16) (output only)
3 = Raw I/Q samples (cf32, 2 channel) 3 = Raw I/Q samples (cf32, 2 channel)
Note: If output file is specified, input will always be I/Q 4 = Raw I/Q samples (cs16, 2 channel)
[-F] kv|json|csv|syslog Produce decoded output in given format. Not yet supported by all drivers. [-F] kv|json|csv|syslog Produce decoded output in given format. Not yet supported by all drivers.
append output to file with :<filename> (e.g. -F csv:log.csv), defaults to stdout. append output to file with :<filename> (e.g. -F csv:log.csv), defaults to stdout.
specify host/port for syslog with e.g. -F syslog:127.0.0.1:1514 specify host/port for syslog with e.g. -F syslog:127.0.0.1:1514

View File

@@ -72,6 +72,7 @@ struct dm_state {
int16_t fm[MAXIMAL_BUF_LENGTH]; // FM demodulated signal (for FSK decoding) int16_t fm[MAXIMAL_BUF_LENGTH]; // FM demodulated signal (for FSK decoding)
uint16_t temp[MAXIMAL_BUF_LENGTH]; // Temporary buffer (to be optimized out..) uint16_t temp[MAXIMAL_BUF_LENGTH]; // Temporary buffer (to be optimized out..)
} buf; } buf;
int sample_size; // CU8: 1, CS16: 2
FilterState lowpass_filter_state; FilterState lowpass_filter_state;
DemodFM_State demod_FM_state; DemodFM_State demod_FM_state;
int enable_FM_demod; int enable_FM_demod;
@@ -141,9 +142,9 @@ void usage(r_device *devices) {
"\t[-m <mode>] Data file mode for input / output file (default: 0)\n" "\t[-m <mode>] Data file mode for input / output file (default: 0)\n"
"\t\t 0 = Raw I/Q samples (uint8, 2 channel)\n" "\t\t 0 = Raw I/Q samples (uint8, 2 channel)\n"
"\t\t 1 = AM demodulated samples (int16 pcm, 1 channel)\n" "\t\t 1 = AM demodulated samples (int16 pcm, 1 channel)\n"
"\t\t 2 = FM demodulated samples (int16) (experimental)\n" "\t\t 2 = FM demodulated samples (int16) (output only)\n"
"\t\t 3 = Raw I/Q samples (cf32, 2 channel)\n" "\t\t 3 = Raw I/Q samples (cf32, 2 channel)\n"
"\t\t Note: If output file is specified, input will always be I/Q\n" "\t\t 4 = Raw I/Q samples (cs16, 2 channel)\n"
"\t[-F] kv|json|csv|syslog Produce decoded output in given format. Not yet supported by all drivers.\n" "\t[-F] kv|json|csv|syslog Produce decoded output in given format. Not yet supported by all drivers.\n"
"\t\t append output to file with :<filename> (e.g. -F csv:log.csv), defaults to stdout.\n" "\t\t append output to file with :<filename> (e.g. -F csv:log.csv), defaults to stdout.\n"
"\t\t specify host/port for syslog with e.g. -F syslog:127.0.0.1:1514\n" "\t\t specify host/port for syslog with e.g. -F syslog:127.0.0.1:1514\n"
@@ -690,6 +691,7 @@ static void rtlsdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx) {
struct dm_state *demod = ctx; struct dm_state *demod = ctx;
int i; int i;
char time_str[LOCAL_TIME_BUFLEN]; char time_str[LOCAL_TIME_BUFLEN];
unsigned long n_samples;
if (do_exit || do_exit_async) if (do_exit || do_exit_async)
return; return;
@@ -700,6 +702,8 @@ static void rtlsdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx) {
rtlsdr_cancel_async(dev); rtlsdr_cancel_async(dev);
} }
n_samples = len / 2 / demod->sample_size;
#ifndef _WIN32 #ifndef _WIN32
alarm(3); // require callback to run every 3 second, abort otherwise alarm(3); // require callback to run every 3 second, abort otherwise
#endif #endif
@@ -714,29 +718,41 @@ static void rtlsdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx) {
} }
// AM demodulation // AM demodulation
envelope_detect(iq_buf, demod->buf.temp, len/2); if (demod->sample_size == 1) { // CU8
baseband_low_pass_filter(demod->buf.temp, demod->am_buf, len/2, &demod->lowpass_filter_state); envelope_detect(iq_buf, demod->buf.temp, n_samples);
//magnitude_true_cu8(iq_buf, demod->buf.temp, n_samples);
//magnitude_est_cu8(iq_buf, demod->buf.temp, n_samples);
} else { // CS16
//magnitude_true_cs16((int16_t *)iq_buf, demod->buf.temp, n_samples);
magnitude_est_cs16((int16_t *)iq_buf, demod->buf.temp, n_samples);
}
baseband_low_pass_filter(demod->buf.temp, demod->am_buf, n_samples, &demod->lowpass_filter_state);
// FM demodulation // FM demodulation
if (demod->enable_FM_demod) { if (demod->enable_FM_demod) {
baseband_demod_FM(iq_buf, demod->buf.fm, len/2, &demod->demod_FM_state); if (demod->sample_size == 1) { // CU8
baseband_demod_FM(iq_buf, demod->buf.fm, n_samples, &demod->demod_FM_state);
} else { // CS16
baseband_demod_FM_cs16((int16_t *)iq_buf, demod->buf.fm, n_samples, &demod->demod_FM_state);
}
} }
// Handle special input formats // Handle special input formats
if (demod->load_mode == 1) { // The IQ buffer is really AM demodulated data if (demod->load_mode == 1) { // The IQ buffer is really AM demodulated data
memcpy(demod->am_buf, iq_buf, len); memcpy(demod->am_buf, iq_buf, len);
} else if (demod->load_mode == 2) { // The IQ buffer is really FM demodulated data } else if (demod->load_mode == 2) { // The IQ buffer is really FM demodulated data
// we would need AM for the envelope too
memcpy(demod->buf.fm, iq_buf, len); memcpy(demod->buf.fm, iq_buf, len);
} }
if (demod->analyze || (demod->out_file == stdout)) { // We don't want to decode devices when outputting to stdout if (demod->analyze || (demod->out_file == stdout)) { // We don't want to decode devices when outputting to stdout
pwm_analyze(demod, demod->am_buf, len / 2); pwm_analyze(demod, demod->am_buf, n_samples);
} else { } else {
// Detect a package and loop through demodulators with pulse data // Detect a package and loop through demodulators with pulse data
int package_type = 1; // Just to get us started int package_type = 1; // Just to get us started
int p_events = 0; // Sensor events successfully detected per package int p_events = 0; // Sensor events successfully detected per package
while (package_type) { while (package_type) {
package_type = pulse_detect_package(demod->am_buf, demod->buf.fm, len/2, demod->level_limit, samp_rate, &demod->pulse_data, &demod->fsk_pulse_data); package_type = pulse_detect_package(demod->am_buf, demod->buf.fm, n_samples, demod->level_limit, samp_rate, &demod->pulse_data, &demod->fsk_pulse_data);
if (package_type == 1) { if (package_type == 1) {
if (demod->analyze_pulses) fprintf(stderr, "Detected OOK package\t@ %s\n", local_time_str(0, time_str)); if (demod->analyze_pulses) fprintf(stderr, "Detected OOK package\t@ %s\n", local_time_str(0, time_str));
for (i = 0; i < demod->r_dev_num; i++) { for (i = 0; i < demod->r_dev_num; i++) {
@@ -824,12 +840,15 @@ static void rtlsdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx) {
if (demod->out_file) { if (demod->out_file) {
uint8_t *out_buf = iq_buf; // Default is to dump IQ samples uint8_t *out_buf = iq_buf; // Default is to dump IQ samples
unsigned long out_len = n_samples * 2 * demod->sample_size;
if (demod->dump_mode == 1) { // AM data if (demod->dump_mode == 1) { // AM data
out_buf = (uint8_t*)demod->am_buf; out_buf = (uint8_t*)demod->am_buf;
out_len = n_samples * sizeof(int16_t);
} else if (demod->dump_mode == 2) { // FM data } else if (demod->dump_mode == 2) { // FM data
out_buf = (uint8_t*)demod->buf.fm; out_buf = (uint8_t*)demod->buf.fm;
out_len = n_samples * sizeof(int16_t);
} }
if (fwrite(out_buf, 1, len, demod->out_file) != len) { if (fwrite(out_buf, 1, out_len, demod->out_file) != out_len) {
fprintf(stderr, "Short write, samples lost, exiting!\n"); fprintf(stderr, "Short write, samples lost, exiting!\n");
rtlsdr_cancel_async(dev); rtlsdr_cancel_async(dev);
} }
@@ -1072,6 +1091,10 @@ int main(int argc, char **argv) {
break; break;
case 'r': case 'r':
in_filename = optarg; in_filename = optarg;
if (loaddump_mode == 2) {
fprintf(stderr, "FM input not supported\n");
usage(devices);
}
demod->load_mode = loaddump_mode; demod->load_mode = loaddump_mode;
break; break;
case 't': case 't':
@@ -1079,7 +1102,7 @@ int main(int argc, char **argv) {
break; break;
case 'm': case 'm':
loaddump_mode = atoi(optarg); loaddump_mode = atoi(optarg);
if (loaddump_mode < 0 || loaddump_mode > 3) { if (loaddump_mode < 0 || loaddump_mode > 4) {
fprintf(stderr, "Invalid sample mode %s\n", optarg); fprintf(stderr, "Invalid sample mode %s\n", optarg);
usage(devices); usage(devices);
} }
@@ -1275,6 +1298,7 @@ int main(int argc, char **argv) {
} else { } else {
if (!quiet_mode) fprintf(stderr, "Using device %d: %s\n", if (!quiet_mode) fprintf(stderr, "Using device %d: %s\n",
i, rtlsdr_get_device_name(i)); i, rtlsdr_get_device_name(i));
demod->sample_size = sizeof(uint8_t); // CU8
break; break;
} }
} }
@@ -1352,9 +1376,8 @@ int main(int argc, char **argv) {
demod->sg_buf = malloc(SIGNAL_GRABBER_BUFFER); demod->sg_buf = malloc(SIGNAL_GRABBER_BUFFER);
if (in_filename) { if (in_filename) {
int i = 0;
unsigned char *test_mode_buf = malloc(DEFAULT_BUF_LENGTH * sizeof(unsigned char)); unsigned char *test_mode_buf = malloc(DEFAULT_BUF_LENGTH * sizeof(unsigned char));
float *test_mode_float_buf = malloc(DEFAULT_BUF_LENGTH * sizeof(float)); float *test_mode_float_buf = malloc(DEFAULT_BUF_LENGTH / sizeof(int16_t) * sizeof(float));
if (!test_mode_buf || !test_mode_float_buf) if (!test_mode_buf || !test_mode_float_buf)
{ {
fprintf(stderr, "Couldn't allocate read buffers!\n"); fprintf(stderr, "Couldn't allocate read buffers!\n");
@@ -1371,30 +1394,45 @@ int main(int argc, char **argv) {
} }
} }
fprintf(stderr, "Test mode active. Reading samples from file: %s\n", in_filename); // Essential information (not quiet) fprintf(stderr, "Test mode active. Reading samples from file: %s\n", in_filename); // Essential information (not quiet)
if (demod->load_mode < 3) {
demod->sample_size = sizeof(uint8_t); // CU8, AM, FM
} else {
demod->sample_size = sizeof(int16_t); // CF32, CS16
}
if (!quiet_mode) { if (!quiet_mode) {
fprintf(stderr, "Input format: %s\n", (demod->load_mode == 3) ? "2ch cf32" : "2ch uint8 / 1ch uint16"); char *load_mode_str;
switch (demod->load_mode) {
case 0: load_mode_str = "CU8 (2ch uint8)"; break;
case 1: load_mode_str = "S16 AM (1ch int16)"; break;
case 2: load_mode_str = "S16 FM (1ch int16)"; break;
case 3: load_mode_str = "CF32 (2ch Float32)"; break;
case 4: load_mode_str = "CS16 (2ch int16)"; break;
}
fprintf(stderr, "Input format: %s\n", load_mode_str);
} }
sample_file_pos = 0.0; sample_file_pos = 0.0;
int n_read, cf32_tmp; int n_blocks = 0;
unsigned long n_read;
do { do {
if (demod->load_mode == 3) { if (demod->load_mode == 3) {
n_read = fread(test_mode_float_buf, sizeof(float), DEFAULT_BUF_LENGTH, in_file); n_read = fread(test_mode_float_buf, sizeof(float), DEFAULT_BUF_LENGTH / 2, in_file);
for(int n = 0; n < n_read; n++) { // clamp float to [-1,1] and scale to Q0.15
cf32_tmp = test_mode_float_buf[n]*127 + 127; for(unsigned long n = 0; n < n_read; n++) {
if (cf32_tmp < 0) int s_tmp = test_mode_float_buf[n] * INT16_MAX;
cf32_tmp = 0; if (s_tmp < -INT16_MAX)
else if (cf32_tmp > 255) s_tmp = -INT16_MAX;
cf32_tmp = 255; else if (s_tmp > INT16_MAX)
test_mode_buf[n] = (uint8_t)cf32_tmp; s_tmp = INT16_MAX;
test_mode_buf[n] = (int16_t)s_tmp;
} }
} else { } else {
n_read = fread(test_mode_buf, 1, DEFAULT_BUF_LENGTH, in_file); n_read = fread(test_mode_buf, 1, DEFAULT_BUF_LENGTH, in_file);
} }
if (n_read == 0) break; // rtlsdr_callback() will Segmentation Fault with len=0 if (n_read == 0) break; // rtlsdr_callback() will Segmentation Fault with len=0
rtlsdr_callback(test_mode_buf, n_read, demod); rtlsdr_callback(test_mode_buf, n_read, demod);
i++; n_blocks++;
sample_file_pos = (float)i * n_read / samp_rate / 2; sample_file_pos = (float)n_blocks * n_read / samp_rate / 2 / demod->sample_size;
} while (n_read != 0); } while (n_read != 0);
// Call a last time with cleared samples to ensure EOP detection // Call a last time with cleared samples to ensure EOP detection

View File

@@ -66,7 +66,6 @@ int main(int argc, char *argv[])
uint32_t *u32_buf; uint32_t *u32_buf;
int16_t *s16_buf; int16_t *s16_buf;
int32_t *s32_buf; int32_t *s32_buf;
int fd;
char *filename; char *filename;
long n_read; long n_read;
unsigned long n_samples; unsigned long n_samples;
@@ -95,6 +94,8 @@ int main(int argc, char *argv[])
n_samples = n_read / (sizeof(uint8_t) * 2); n_samples = n_read / (sizeof(uint8_t) * 2);
for (unsigned long i = 0; i < n_samples * 2; i++) { for (unsigned long i = 0; i < n_samples * 2; i++) {
//cs16_buf[i] = 127 - cu8_buf[i];
//cs16_buf[i] = (int16_t)cu8_buf[i] * 16 - 2040;
cs16_buf[i] = (int16_t)cu8_buf[i] * 128 - 16320; cs16_buf[i] = (int16_t)cu8_buf[i] * 128 - 16320;
//cs16_buf[i] = (int16_t)cu8_buf[i] * 256 - 32640; //cs16_buf[i] = (int16_t)cu8_buf[i] * 256 - 32640;
} }
@@ -111,21 +112,21 @@ int main(int argc, char *argv[])
MEASURE("magnitude_true_cu8", MEASURE("magnitude_true_cu8",
magnitude_true_cu8(cu8_buf, y16_buf, n_samples); magnitude_true_cu8(cu8_buf, y16_buf, n_samples);
); );
write_buf("bb_am.u16", y16_buf, sizeof(uint16_t) * n_samples); write_buf("bb.am.s16", y16_buf, sizeof(uint16_t) * n_samples);
MEASURE("baseband_low_pass_filter", MEASURE("baseband_low_pass_filter",
baseband_low_pass_filter(y16_buf, (int16_t *)u16_buf, n_samples, &state); baseband_low_pass_filter(y16_buf, (int16_t *)u16_buf, n_samples, &state);
); );
write_buf("bb_am.lp.u16", u16_buf, sizeof(int16_t) * n_samples); write_buf("bb.lp.am.s16", u16_buf, sizeof(int16_t) * n_samples);
MEASURE("baseband_demod_FM", MEASURE("baseband_demod_FM",
baseband_demod_FM(cu8_buf, s16_buf, n_samples, &fm_state); baseband_demod_FM(cu8_buf, s16_buf, n_samples, &fm_state);
); );
write_buf("bb_fm.s16", s16_buf, sizeof(int16_t) * n_samples); write_buf("bb.fm.s16", s16_buf, sizeof(int16_t) * n_samples);
write_buf("bb.cs16", cs16_buf, sizeof(int16_t) * 2 * n_samples); write_buf("bb.cs16", cs16_buf, sizeof(int16_t) * 2 * n_samples);
//envelope_detect_cs16(cs16_buf, y32_buf, n_samples); //envelope_detect_cs16(cs16_buf, y32_buf, n_samples);
//write_buf("bb_am.u32", y32_buf, sizeof(uint32_t) * n_samples); //write_buf("bb.am.u32", y32_buf, sizeof(uint32_t) * n_samples);
//baseband_low_pass_filter_u32(y32_buf, u32_buf, n_samples, &state); //baseband_low_pass_filter_u32(y32_buf, u32_buf, n_samples, &state);
//write_buf("bb_am.lp.u32", u32_buf, sizeof(uint32_t) * n_samples); //write_buf("bb.lp.am.u32", u32_buf, sizeof(uint32_t) * n_samples);
MEASURE("magnitude_est_cs16", MEASURE("magnitude_est_cs16",
magnitude_est_cs16(cs16_buf, y16_buf, n_samples); magnitude_est_cs16(cs16_buf, y16_buf, n_samples);
@@ -133,17 +134,17 @@ int main(int argc, char *argv[])
MEASURE("magnitude_true_cs16", MEASURE("magnitude_true_cs16",
magnitude_true_cs16(cs16_buf, y16_buf, n_samples); magnitude_true_cs16(cs16_buf, y16_buf, n_samples);
); );
write_buf("bb_m.u16", y16_buf, sizeof(uint16_t) * n_samples); write_buf("bb.mag.s16", y16_buf, sizeof(uint16_t) * n_samples);
MEASURE("baseband_low_pass_filter", MEASURE("baseband_low_pass_filter",
baseband_low_pass_filter(y16_buf, (int16_t *)u16_buf, n_samples, &state); baseband_low_pass_filter(y16_buf, (int16_t *)u16_buf, n_samples, &state);
); );
write_buf("bb_m.lp.u16", u16_buf, sizeof(int16_t) * n_samples); write_buf("bb.mag.lp.s16", u16_buf, sizeof(int16_t) * n_samples);
//baseband_demod_FM_cs16(cs16_buf, s32_buf, n_samples, &fm_state); //baseband_demod_FM_cs16(cs16_buf, s32_buf, n_samples, &fm_state);
//write_buf("bb_fm.s32", s32_buf, sizeof(int32_t) * n_samples); //write_buf("bb.fm.s32", s32_buf, sizeof(int32_t) * n_samples);
MEASURE("baseband_demod_FM_cs16", MEASURE("baseband_demod_FM_cs16",
baseband_demod_FM_cs16(cs16_buf, s16_buf, n_samples, &fm_state); baseband_demod_FM_cs16(cs16_buf, s16_buf, n_samples, &fm_state);
); );
write_buf("bb_fm.cs16.s16", s16_buf, sizeof(int16_t) * n_samples); write_buf("bb.cs16.fm.s16", s16_buf, sizeof(int16_t) * n_samples);
} }