1 /** @file
2     rtl_433, turns your Realtek RTL2832 based DVB dongle into a 433.92MHz generic data receiver.
3 
4     Copyright (C) 2012 by Benjamin Larsson <benjamin@southpole.se>
5 
6     Based on rtl_sdr
7     Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <stdbool.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <signal.h>
30 
31 #include "rtl_433.h"
32 #include "r_private.h"
33 #include "r_device.h"
34 #include "r_api.h"
35 #include "sdr.h"
36 #include "baseband.h"
37 #include "pulse_analyzer.h"
38 #include "pulse_detect.h"
39 #include "pulse_detect_fsk.h"
40 #include "pulse_demod.h"
41 #include "rfraw.h"
42 #include "data.h"
43 #include "r_util.h"
44 #include "optparse.h"
45 #include "abuf.h"
46 #include "fileformat.h"
47 #include "samp_grab.h"
48 #include "am_analyze.h"
49 #include "confparse.h"
50 #include "term_ctl.h"
51 #include "compat_alarm.h"
52 #include "compat_paths.h"
53 #include "fatal.h"
54 #include "write_sigrok.h"
55 #include "mongoose.h"
56 
57 #ifdef _WIN32
58 #include <io.h>
59 #include <fcntl.h>
60 #ifdef _MSC_VER
61 #define F_OK 0
62 #endif
63 #endif
64 #ifndef _MSC_VER
65 #include <unistd.h>
66 #endif
67 
68 #ifndef _MSC_VER
69 #include <getopt.h>
70 #else
71 #include "getopt/getopt.h"
72 #endif
73 
74 // note that Clang has _Noreturn but it's C11
75 // #if defined(__clang__) ...
76 #if !defined _Noreturn
77 #if defined(__GNUC__)
78 #define _Noreturn __attribute__((noreturn))
79 #elif defined(_MSC_VER)
80 #define _Noreturn __declspec(noreturn)
81 #else
82 #define _Noreturn
83 #endif
84 #endif
85 
86 r_device *flex_create_device(char *spec); // maybe put this in some header file?
87 
print_version(void)88 static void print_version(void)
89 {
90     fprintf(stderr, "%s\n", version_string());
91     fprintf(stderr, "Use -h for usage help and see https://triq.org/ for documentation.\n");
92 }
93 
94 _Noreturn
usage(int exit_code)95 static void usage(int exit_code)
96 {
97     term_help_printf(
98             "Generic RF data receiver and decoder for ISM band devices using RTL-SDR and SoapySDR.\n"
99             "\nUsage:\n"
100             "\t\t= General options =\n"
101             "  [-V] Output the version string and exit\n"
102             "  [-v] Increase verbosity (can be used multiple times).\n"
103             "       -v : verbose, -vv : verbose decoders, -vvv : debug decoders, -vvvv : trace decoding).\n"
104             "  [-c <path>] Read config options from a file\n"
105             "\t\t= Tuner options =\n"
106             "  [-d <RTL-SDR USB device index> | :<RTL-SDR USB device serial> | <SoapySDR device query> | rtl_tcp | help]\n"
107             "  [-g <gain> | help] (default: auto)\n"
108             "  [-t <settings>] apply a list of keyword=value settings for SoapySDR devices\n"
109             "       e.g. -t \"antenna=A,bandwidth=4.5M,rfnotch_ctrl=false\"\n"
110             "  [-f <frequency>] Receive frequency(s) (default: %i Hz)\n"
111             "  [-H <seconds>] Hop interval for polling of multiple frequencies (default: %i seconds)\n"
112             "  [-p <ppm_error] Correct rtl-sdr tuner frequency offset error (default: 0)\n"
113             "  [-s <sample rate>] Set sample rate (default: %i Hz)\n"
114             "\t\t= Demodulator options =\n"
115             "  [-R <device> | help] Enable only the specified device decoding protocol (can be used multiple times)\n"
116             "       Specify a negative number to disable a device decoding protocol (can be used multiple times)\n"
117             "  [-G] Enable blacklisted device decoding protocols, for testing only.\n"
118             "  [-X <spec> | help] Add a general purpose decoder (prepend -R 0 to disable all decoders)\n"
119             "  [-Y auto | classic | minmax] FSK pulse detector mode.\n"
120             "  [-Y level=<dB level>] Manual detection level used to determine pulses (-1.0 to -30.0) (0=auto).\n"
121             "  [-Y minlevel=<dB level>] Manual minimum detection level used to determine pulses (-1.0 to -99.0).\n"
122             "  [-Y minsnr=<dB level>] Minimum SNR to determine pulses (1.0 to 99.0).\n"
123             "  [-Y autolevel] Set minlevel automatically based on average estimated noise.\n"
124             "  [-Y squelch] Skip frames below estimated noise level to reduce cpu load.\n"
125             "  [-Y ampest | magest] Choose amplitude or magnitude level estimator.\n"
126             "\t\t= Analyze/Debug options =\n"
127             "  [-a] Analyze mode. Print a textual description of the signal.\n"
128             "  [-A] Pulse Analyzer. Enable pulse analysis and decode attempt.\n"
129             "       Disable all decoders with -R 0 if you want analyzer output only.\n"
130             "  [-y <code>] Verify decoding of demodulated test data (e.g. \"{25}fb2dd58\") with enabled devices\n"
131             "\t\t= File I/O options =\n"
132             "  [-S none | all | unknown | known] Signal auto save. Creates one file per signal.\n"
133             "       Note: Saves raw I/Q samples (uint8 pcm, 2 channel). Preferred mode for generating test files.\n"
134             "  [-r <filename> | help] Read data from input file instead of a receiver\n"
135             "  [-w <filename> | help] Save data stream to output file (a '-' dumps samples to stdout)\n"
136             "  [-W <filename> | help] Save data stream to output file, overwrite existing file\n"
137             "\t\t= Data output options =\n"
138             "  [-F kv | json | csv | mqtt | influx | syslog | null | help] Produce decoded output in given format.\n"
139             "       Append output to file with :<filename> (e.g. -F csv:log.csv), defaults to stdout.\n"
140             "       Specify host/port for syslog with e.g. -F syslog:127.0.0.1:1514\n"
141             "  [-M time[:<options>] | protocol | level | noise[:secs] | stats | bits | help] Add various meta data to each output.\n"
142             "  [-K FILE | PATH | <tag> | <key>=<tag>] Add an expanded token or fixed tag to every output line.\n"
143             "  [-C native | si | customary] Convert units in decoded output.\n"
144             "  [-n <value>] Specify number of samples to take (each sample is an I/Q pair)\n"
145             "  [-T <seconds>] Specify number of seconds to run, also 12:34 or 1h23m45s\n"
146             "  [-E hop | quit] Hop/Quit after outputting successful event(s)\n"
147             "  [-h] Output this usage help and exit\n"
148             "       Use -d, -g, -R, -X, -F, -M, -r, -w, or -W without argument for more help\n\n",
149             DEFAULT_FREQUENCY, DEFAULT_HOP_TIME, DEFAULT_SAMPLE_RATE);
150     exit(exit_code);
151 }
152 
153 _Noreturn
help_protocols(r_device * devices,unsigned num_devices,int exit_code)154 static void help_protocols(r_device *devices, unsigned num_devices, int exit_code)
155 {
156     unsigned i;
157     char disabledc;
158 
159     if (devices) {
160         term_help_printf("\t\t= Supported device protocols =\n");
161         for (i = 0; i < num_devices; i++) {
162             disabledc = devices[i].disabled ? '*' : ' ';
163             if (devices[i].disabled <= 2) // if not hidden
164                 fprintf(stderr, "    [%02u]%c %s\n", i + 1, disabledc, devices[i].name);
165         }
166         fprintf(stderr, "\n* Disabled by default, use -R n or -G\n");
167     }
168     exit(exit_code);
169 }
170 
171 _Noreturn
help_device(void)172 static void help_device(void)
173 {
174     term_help_printf(
175             "\t\t= Input device selection =\n"
176 #ifdef RTLSDR
177             "\tRTL-SDR device driver is available.\n"
178 #else
179             "\tRTL-SDR device driver is not available.\n"
180 #endif
181             "  [-d <RTL-SDR USB device index>] (default: 0)\n"
182             "  [-d :<RTL-SDR USB device serial (can be set with rtl_eeprom -s)>]\n"
183             "\tTo set gain for RTL-SDR use -g <gain> to set an overall gain in dB.\n"
184 #ifdef SOAPYSDR
185             "\tSoapySDR device driver is available.\n"
186 #else
187             "\tSoapySDR device driver is not available.\n"
188 #endif
189             "  [-d \"\"] Open default SoapySDR device\n"
190             "  [-d driver=rtlsdr] Open e.g. specific SoapySDR device\n"
191             "\tTo set gain for SoapySDR use -g ELEM=val,ELEM=val,... e.g. -g LNA=20,TIA=8,PGA=2 (for LimeSDR).\n"
192             "  [-d rtl_tcp[:[//]host[:port]] (default: localhost:1234)\n"
193             "\tSpecify host/port to connect to with e.g. -d rtl_tcp:127.0.0.1:1234\n");
194     exit(0);
195 }
196 
197 _Noreturn
help_gain(void)198 static void help_gain(void)
199 {
200     term_help_printf(
201             "\t\t= Gain option =\n"
202             "  [-g <gain>] (default: auto)\n"
203             "\tFor RTL-SDR: gain in dB (\"0\" is auto).\n"
204             "\tFor SoapySDR: gain in dB for automatic distribution (\"\" is auto), or string of gain elements.\n"
205             "\tE.g. \"LNA=20,TIA=8,PGA=2\" for LimeSDR.\n");
206     exit(0);
207 }
208 
209 _Noreturn
help_output(void)210 static void help_output(void)
211 {
212     term_help_printf(
213             "\t\t= Output format option =\n"
214             "  [-F kv|json|csv|mqtt|influx|syslog|null] Produce decoded output in given format.\n"
215             "\tWithout this option the default is KV output. Use \"-F null\" to remove the default.\n"
216             "\tAppend output to file with :<filename> (e.g. -F csv:log.csv), defaults to stdout.\n"
217             "\tSpecify MQTT server with e.g. -F mqtt://localhost:1883\n"
218             "\tAdd MQTT options with e.g. -F \"mqtt://host:1883,opt=arg\"\n"
219             "\tMQTT options are: user=foo, pass=bar, retain[=0|1], qos=N, <format>[=topic]\n"
220             "\tSupported MQTT formats: (default is all)\n"
221             "\t  events: posts JSON event data\n"
222             "\t  states: posts JSON state data\n"
223             "\t  devices: posts device and sensor info in nested topics\n"
224             "\tThe topic string will expand keys like [/model]\n"
225             "\tE.g. -F \"mqtt://localhost:1883,user=USERNAME,pass=PASSWORD,retain=0,devices=rtl_433[/id]\"\n"
226             "\tWith MQTT each rtl_433 instance needs a distinct driver selection. The MQTT Client-ID is computed from the driver string.\n"
227             "\tIf you use multiple RTL-SDR, perhaps set a serial and select by that (helps not to get the wrong antenna).\n"
228             "\tSpecify InfluxDB 2.0 server with e.g. -F \"influx://localhost:9999/api/v2/write?org=<org>&bucket=<bucket>,token=<authtoken>\"\n"
229             "\tSpecify InfluxDB 1.x server with e.g. -F \"influx://localhost:8086/write?db=<db>&p=<password>&u=<user>\"\n"
230             "\t  Additional parameter -M time:unix:usec:utc for correct timestamps in InfluxDB recommended\n"
231             "\tSpecify host/port for syslog with e.g. -F syslog:127.0.0.1:1514\n");
232     exit(0);
233 }
234 
235 _Noreturn
help_tags(void)236 static void help_tags(void)
237 {
238     term_help_printf(
239             "\t\t= Data tags option =\n"
240             "  [-K FILE | PATH | <tag> | <key>=<tag>] Add an expanded token or fixed tag to every output line.\n"
241             "\tIf <tag> is \"FILE\" or \"PATH\" an expanded token will be added.\n"
242             "\tThe <tag> can also be a GPSd URL, e.g.\n"
243             "\t\t\"-K gpsd,lat,lon\" (report lat and lon keys from local gpsd)\n"
244             "\t\t\"-K loc=gpsd,lat,lon\" (report lat and lon in loc object)\n"
245             "\t\t\"-K gpsd\" (full json TPV report, in default \"gps\" object)\n"
246             "\t\t\"-K foo=gpsd://127.0.0.1:2947\" (with key and address)\n"
247             "\t\t\"-K bar=gpsd,nmea\" (NMEA deault GPGGA report)\n"
248             "\t\t\"-K rmc=gpsd,nmea,filter='$GPRMC'\" (NMEA GPRMC report)\n"
249             "\tAlso <tag> can be a generic tcp address, e.g.\n"
250             "\t\t\"-K foo=tcp:localhost:4000\" (read lines as TCP client)\n"
251             "\t\t\"-K bar=tcp://127.0.0.1:3000,init='subscribe tags\\r\\n'\"\n"
252             "\t\t\"-K baz=tcp://127.0.0.1:5000,filter='a prefix to match'\"\n");
253     exit(0);
254 }
255 
256 _Noreturn
help_meta(void)257 static void help_meta(void)
258 {
259     term_help_printf(
260             "\t\t= Meta information option =\n"
261             "  [-M time[:<options>]|protocol|level|noise[:<secs>]|stats|bits] Add various metadata to every output line.\n"
262             "\tUse \"time\" to add current date and time meta data (preset for live inputs).\n"
263             "\tUse \"time:rel\" to add sample position meta data (preset for read-file and stdin).\n"
264             "\tUse \"time:unix\" to show the seconds since unix epoch as time meta data.\n"
265             "\tUse \"time:iso\" to show the time with ISO-8601 format (YYYY-MM-DD\"T\"hh:mm:ss).\n"
266             "\tUse \"time:off\" to remove time meta data.\n"
267             "\tUse \"time:usec\" to add microseconds to date time meta data.\n"
268             "\tUse \"time:tz\" to output time with timezone offset.\n"
269             "\tUse \"time:utc\" to output time in UTC.\n"
270             "\t\t(this may also be accomplished by invocation with TZ environment variable set).\n"
271             "\t\t\"usec\" and \"utc\" can be combined with other options, eg. \"time:unix:utc:usec\".\n"
272             "\tUse \"protocol\" / \"noprotocol\" to output the decoder protocol number meta data.\n"
273             "\tUse \"level\" to add Modulation, Frequency, RSSI, SNR, and Noise meta data.\n"
274             "\tUse \"noise[:secs]\" to report estimated noise level at intervals (default: 10 seconds).\n"
275             "\tUse \"stats[:[<level>][:<interval>]]\" to report statistics (default: 600 seconds).\n"
276             "\t  level 0: no report, 1: report successful devices, 2: report active devices, 3: report all\n"
277             "\tUse \"bits\" to add bit representation to code outputs (for debug).\n");
278     exit(0);
279 }
280 
281 _Noreturn
help_read(void)282 static void help_read(void)
283 {
284     term_help_printf(
285             "\t\t= Read file option =\n"
286             "  [-r <filename>] Read data from input file instead of a receiver\n"
287             "\tParameters are detected from the full path, file name, and extension.\n\n"
288             "\tA center frequency is detected as (fractional) number suffixed with 'M',\n"
289             "\t'Hz', 'kHz', 'MHz', or 'GHz'.\n\n"
290             "\tA sample rate is detected as (fractional) number suffixed with 'k',\n"
291             "\t'sps', 'ksps', 'Msps', or 'Gsps'.\n\n"
292             "\tFile content and format are detected as parameters, possible options are:\n"
293             "\t'cu8', 'cs16', 'cf32' ('IQ' implied), and 'am.s16'.\n\n"
294             "\tParameters must be separated by non-alphanumeric chars and are case-insensitive.\n"
295             "\tOverrides can be prefixed, separated by colon (':')\n\n"
296             "\tE.g. default detection by extension: path/filename.am.s16\n"
297             "\tforced overrides: am:s16:path/filename.ext\n\n"
298             "\tReading from pipes also support format options.\n"
299             "\tE.g reading complex 32-bit float: CU32:-\n");
300     exit(0);
301 }
302 
303 _Noreturn
help_write(void)304 static void help_write(void)
305 {
306     term_help_printf(
307             "\t\t= Write file option =\n"
308             "  [-w <filename>] Save data stream to output file (a '-' dumps samples to stdout)\n"
309             "  [-W <filename>] Save data stream to output file, overwrite existing file\n"
310             "\tParameters are detected from the full path, file name, and extension.\n\n"
311             "\tFile content and format are detected as parameters, possible options are:\n"
312             "\t'cu8', 'cs8', 'cs16', 'cf32' ('IQ' implied),\n"
313             "\t'am.s16', 'am.f32', 'fm.s16', 'fm.f32',\n"
314             "\t'i.f32', 'q.f32', 'logic.u8', 'ook', and 'vcd'.\n\n"
315             "\tParameters must be separated by non-alphanumeric chars and are case-insensitive.\n"
316             "\tOverrides can be prefixed, separated by colon (':')\n\n"
317             "\tE.g. default detection by extension: path/filename.am.s16\n"
318             "\tforced overrides: am:s16:path/filename.ext\n");
319     exit(0);
320 }
321 
sdr_callback(unsigned char * iq_buf,uint32_t len,void * ctx)322 static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
323 {
324     r_cfg_t *cfg = ctx;
325     struct dm_state *demod = cfg->demod;
326     char time_str[LOCAL_TIME_BUFLEN];
327     unsigned long n_samples;
328 
329     if ((cfg->bytes_to_read > 0) && (cfg->bytes_to_read <= len)) {
330         len = cfg->bytes_to_read;
331         cfg->exit_async = 1;
332     }
333 
334     // save last frame time to see if a new second started
335     time_t last_frame_sec = demod->now.tv_sec;
336     get_time_now(&demod->now);
337 
338     n_samples = len / demod->sample_size;
339     if (n_samples * demod->sample_size != len) {
340         fprintf(stderr, "Sample buffer length not aligned to sample size!\n");
341     }
342     if (!n_samples) {
343         fprintf(stderr, "Sample buffer too short!\n");
344         return; // keep the watchdog timer running
345     }
346 
347     // age the frame position if there is one
348     if (demod->frame_start_ago)
349         demod->frame_start_ago += n_samples;
350     if (demod->frame_end_ago)
351         demod->frame_end_ago += n_samples;
352 
353     alarm(3); // require callback to run every 3 second, abort otherwise
354 
355     if (demod->samp_grab) {
356         samp_grab_push(demod->samp_grab, iq_buf, len);
357     }
358 
359     // AM demodulation
360     float avg_db;
361     if (demod->sample_size == 2) { // CU8
362         if (demod->use_mag_est) {
363             //magnitude_true_cu8(iq_buf, demod->buf.temp, n_samples);
364             avg_db = magnitude_est_cu8(iq_buf, demod->buf.temp, n_samples);
365         }
366         else { // amp est
367             avg_db = envelope_detect(iq_buf, demod->buf.temp, n_samples);
368         }
369     } else { // CS16
370         //magnitude_true_cs16((int16_t *)iq_buf, demod->buf.temp, n_samples);
371         avg_db = magnitude_est_cs16((int16_t *)iq_buf, demod->buf.temp, n_samples);
372     }
373 
374     //fprintf(stderr, "noise level: %.1f dB current: %.1f dB min level: %.1f dB\n", demod->noise_level, avg_db, demod->min_level_auto);
375     if (demod->min_level_auto == 0.0f) {
376         demod->min_level_auto = demod->min_level;
377     }
378     if (demod->noise_level == 0.0f) {
379         demod->noise_level = demod->min_level_auto - 3.0f;
380     }
381     int noise_only = avg_db < demod->noise_level + 3.0f; // or demod->min_level_auto?
382     // always process frames if loader, dumper, or analyzers are in use, otherwise skip silent frames
383     int process_frame = demod->squelch_offset <= 0 || !noise_only || demod->load_info.format || demod->analyze_pulses || demod->dumper.len || demod->samp_grab;
384     if (noise_only) {
385         demod->noise_level = (demod->noise_level * 7 + avg_db) / 8; // fast fall over 8 frames
386         // If auto_level and noise level well below min_level and significant change in noise level
387         if (demod->auto_level > 0 && demod->noise_level < demod->min_level - 3.0f
388                 && fabsf(demod->min_level_auto - demod->noise_level - 3.0f) > 1.0f) {
389             demod->min_level_auto = demod->noise_level + 3.0f;
390             fprintf(stderr, "Estimated noise level is %.1f dB, adjusting minimum detection level to %.1f dB\n", demod->noise_level, demod->min_level_auto);
391             pulse_detect_set_levels(demod->pulse_detect, demod->use_mag_est, demod->level_limit, demod->min_level_auto, demod->min_snr, demod->detect_verbosity);
392         }
393     } else {
394         demod->noise_level = (demod->noise_level * 31 + avg_db) / 32; // slow rise over 32 frames
395     }
396     // Report noise every report_noise seconds, but only for the first frame that second
397     if (cfg->report_noise && last_frame_sec != demod->now.tv_sec && demod->now.tv_sec % cfg->report_noise == 0) {
398         fprintf(stderr, "Current %s level %.1f dB, estimated noise %.1f dB\n",
399                 noise_only ? "noise" : "signal", avg_db, demod->noise_level);
400     }
401 
402     if (process_frame)
403     baseband_low_pass_filter(demod->buf.temp, demod->am_buf, n_samples, &demod->lowpass_filter_state);
404 
405     // FM demodulation
406     // Select the correct fsk pulse detector
407     unsigned fpdm = cfg->fsk_pulse_detect_mode;
408     if (cfg->fsk_pulse_detect_mode == FSK_PULSE_DETECT_AUTO) {
409         if (cfg->frequency[cfg->frequency_index] > FSK_PULSE_DETECTOR_LIMIT)
410             fpdm = FSK_PULSE_DETECT_NEW;
411         else
412             fpdm = FSK_PULSE_DETECT_OLD;
413     }
414 
415     if (demod->enable_FM_demod && process_frame) {
416         float low_pass = demod->low_pass != 0.0f ? demod->low_pass : fpdm ? 0.2f : 0.1f;
417         if (demod->sample_size == 2) { // CU8
418             baseband_demod_FM(iq_buf, demod->buf.fm, n_samples, cfg->samp_rate, low_pass, &demod->demod_FM_state);
419         } else { // CS16
420             baseband_demod_FM_cs16((int16_t *)iq_buf, demod->buf.fm, n_samples, cfg->samp_rate, low_pass, &demod->demod_FM_state);
421         }
422     }
423 
424     // Handle special input formats
425     if (demod->load_info.format == S16_AM) { // The IQ buffer is really AM demodulated data
426         if (len > sizeof(demod->am_buf))
427             FATAL("Buffer too small");
428         memcpy(demod->am_buf, iq_buf, len);
429     } else if (demod->load_info.format == S16_FM) { // The IQ buffer is really FM demodulated data
430         // we would need AM for the envelope too
431         if (len > sizeof(demod->buf.fm))
432             FATAL("Buffer too small");
433         memcpy(demod->buf.fm, iq_buf, len);
434     }
435 
436     int d_events = 0; // Sensor events successfully detected
437     if (demod->r_devs.len || demod->analyze_pulses || demod->dumper.len || demod->samp_grab) {
438         // Detect a package and loop through demodulators with pulse data
439         int package_type = PULSE_DATA_OOK;  // Just to get us started
440         for (void **iter = demod->dumper.elems; iter && *iter; ++iter) {
441             file_info_t const *dumper = *iter;
442             if (dumper->format == U8_LOGIC) {
443                 memset(demod->u8_buf, 0, n_samples);
444                 break;
445             }
446         }
447         while (package_type && process_frame) {
448             int p_events = 0; // Sensor events successfully detected per package
449             package_type = pulse_detect_package(demod->pulse_detect, demod->am_buf, demod->buf.fm, n_samples, cfg->samp_rate, cfg->input_pos, &demod->pulse_data, &demod->fsk_pulse_data, fpdm);
450             if (package_type) {
451                 // new package: set a first frame start if we are not tracking one already
452                 if (!demod->frame_start_ago)
453                     demod->frame_start_ago = demod->pulse_data.start_ago;
454                 // always update the last frame end
455                 demod->frame_end_ago = demod->pulse_data.end_ago;
456             }
457             if (package_type == PULSE_DATA_OOK) {
458                 calc_rssi_snr(cfg, &demod->pulse_data);
459                 if (demod->analyze_pulses) fprintf(stderr, "Detected OOK package\t%s\n", time_pos_str(cfg, demod->pulse_data.start_ago, time_str));
460 
461                 p_events += run_ook_demods(&demod->r_devs, &demod->pulse_data);
462                 cfg->frames_count++;
463                 cfg->frames_events += p_events > 0;
464 
465                 for (void **iter = demod->dumper.elems; iter && *iter; ++iter) {
466                     file_info_t const *dumper = *iter;
467                     if (dumper->format == VCD_LOGIC) pulse_data_print_vcd(dumper->file, &demod->pulse_data, '\'');
468                     if (dumper->format == U8_LOGIC) pulse_data_dump_raw(demod->u8_buf, n_samples, cfg->input_pos, &demod->pulse_data, 0x02);
469                     if (dumper->format == PULSE_OOK) pulse_data_dump(dumper->file, &demod->pulse_data);
470                 }
471 
472                 if (cfg->verbosity > 2) pulse_data_print(&demod->pulse_data);
473                 if (cfg->raw_mode == 1 || (cfg->raw_mode == 2 && p_events == 0) || (cfg->raw_mode == 3 && p_events > 0)) {
474                     data_t *data = pulse_data_print_data(&demod->pulse_data);
475                     event_occurred_handler(cfg, data);
476                 }
477                 if (demod->analyze_pulses && (cfg->grab_mode <= 1 || (cfg->grab_mode == 2 && p_events == 0) || (cfg->grab_mode == 3 && p_events > 0)) ) {
478                     pulse_analyzer(&demod->pulse_data, package_type);
479                 }
480 
481             } else if (package_type == PULSE_DATA_FSK) {
482                 calc_rssi_snr(cfg, &demod->fsk_pulse_data);
483                 if (demod->analyze_pulses) fprintf(stderr, "Detected FSK package\t%s\n", time_pos_str(cfg, demod->fsk_pulse_data.start_ago, time_str));
484 
485                 p_events += run_fsk_demods(&demod->r_devs, &demod->fsk_pulse_data);
486                 cfg->frames_fsk++;
487                 cfg->frames_events += p_events > 0;
488 
489                 for (void **iter = demod->dumper.elems; iter && *iter; ++iter) {
490                     file_info_t const *dumper = *iter;
491                     if (dumper->format == VCD_LOGIC) pulse_data_print_vcd(dumper->file, &demod->fsk_pulse_data, '"');
492                     if (dumper->format == U8_LOGIC) pulse_data_dump_raw(demod->u8_buf, n_samples, cfg->input_pos, &demod->fsk_pulse_data, 0x04);
493                     if (dumper->format == PULSE_OOK) pulse_data_dump(dumper->file, &demod->fsk_pulse_data);
494                 }
495 
496                 if (cfg->verbosity > 2) pulse_data_print(&demod->fsk_pulse_data);
497                 if (cfg->raw_mode == 1 || (cfg->raw_mode == 2 && p_events == 0) || (cfg->raw_mode == 3 && p_events > 0)) {
498                     data_t *data = pulse_data_print_data(&demod->fsk_pulse_data);
499                     event_occurred_handler(cfg, data);
500                 }
501                 if (demod->analyze_pulses && (cfg->grab_mode <= 1 || (cfg->grab_mode == 2 && p_events == 0) || (cfg->grab_mode == 3 && p_events > 0))) {
502                     pulse_analyzer(&demod->fsk_pulse_data, package_type);
503                 }
504             } // if (package_type == ...
505             d_events += p_events;
506         } // while (package_type)...
507 
508         // add event counter to the frames currently tracked
509         demod->frame_event_count += d_events;
510 
511         // end frame tracking if older than a whole buffer
512         if (demod->frame_start_ago && demod->frame_end_ago > n_samples) {
513             if (demod->samp_grab) {
514                 if (cfg->grab_mode == 1
515                         || (cfg->grab_mode == 2 && demod->frame_event_count == 0)
516                         || (cfg->grab_mode == 3 && demod->frame_event_count > 0)) {
517                     unsigned frame_pad = n_samples / 8; // this could also be a fixed value, e.g. 10000 samples
518                     unsigned start_padded = demod->frame_start_ago + frame_pad;
519                     unsigned end_padded = demod->frame_end_ago - frame_pad;
520                     unsigned len_padded = start_padded - end_padded;
521                     samp_grab_write(demod->samp_grab, len_padded, end_padded);
522                 }
523             }
524             demod->frame_start_ago = 0;
525             demod->frame_event_count = 0;
526         }
527 
528         // dump partial pulse_data for this buffer
529         for (void **iter = demod->dumper.elems; iter && *iter; ++iter) {
530             file_info_t const *dumper = *iter;
531             if (dumper->format == U8_LOGIC) {
532                 pulse_data_dump_raw(demod->u8_buf, n_samples, cfg->input_pos, &demod->pulse_data, 0x02);
533                 pulse_data_dump_raw(demod->u8_buf, n_samples, cfg->input_pos, &demod->fsk_pulse_data, 0x04);
534                 break;
535             }
536         }
537     }
538 
539     if (demod->am_analyze) {
540         am_analyze(demod->am_analyze, demod->am_buf, n_samples, cfg->verbosity > 1, NULL);
541     }
542 
543     for (void **iter = demod->dumper.elems; iter && *iter; ++iter) {
544         file_info_t const *dumper = *iter;
545         if (!dumper->file
546                 || dumper->format == VCD_LOGIC
547                 || dumper->format == PULSE_OOK)
548             continue;
549         uint8_t *out_buf = iq_buf;  // Default is to dump IQ samples
550         unsigned long out_len = n_samples * demod->sample_size;
551 
552         if (dumper->format == CU8_IQ) {
553             if (demod->sample_size == 4) {
554                 for (unsigned long n = 0; n < n_samples * 2; ++n)
555                     ((uint8_t *)demod->buf.temp)[n] = (((int16_t *)iq_buf)[n] / 256) + 128; // scale Q0.15 to Q0.7
556                 out_buf = (uint8_t *)demod->buf.temp;
557                 out_len = n_samples * 2 * sizeof(uint8_t);
558             }
559         }
560         else if (dumper->format == CS16_IQ) {
561             if (demod->sample_size == 2) {
562                 for (unsigned long n = 0; n < n_samples * 2; ++n)
563                     ((int16_t *)demod->buf.temp)[n] = (iq_buf[n] * 256) - 32768; // scale Q0.7 to Q0.15
564                 out_buf = (uint8_t *)demod->buf.temp; // this buffer is too small if out_block_size is large
565                 out_len = n_samples * 2 * sizeof(int16_t);
566             }
567         }
568         else if (dumper->format == CS8_IQ) {
569             if (demod->sample_size == 2) {
570                 for (unsigned long n = 0; n < n_samples * 2; ++n)
571                     ((int8_t *)demod->buf.temp)[n] = (iq_buf[n] - 128);
572             }
573             else if (demod->sample_size == 4) {
574                 for (unsigned long n = 0; n < n_samples * 2; ++n)
575                     ((int8_t *)demod->buf.temp)[n] = ((int16_t *)iq_buf)[n] >> 8;
576             }
577             out_buf = (uint8_t *)demod->buf.temp;
578             out_len = n_samples * 2 * sizeof(int8_t);
579         }
580         else if (dumper->format == CF32_IQ) {
581             if (demod->sample_size == 2) {
582                 for (unsigned long n = 0; n < n_samples * 2; ++n)
583                     ((float *)demod->buf.temp)[n] = (iq_buf[n] - 128) / 128.0f;
584             }
585             else if (demod->sample_size == 4) {
586                 for (unsigned long n = 0; n < n_samples * 2; ++n)
587                     ((float *)demod->buf.temp)[n] = ((int16_t *)iq_buf)[n] / 32768.0f;
588             }
589             out_buf = (uint8_t *)demod->buf.temp; // this buffer is too small if out_block_size is large
590             out_len = n_samples * 2 * sizeof(float);
591         }
592         else if (dumper->format == S16_AM) {
593             out_buf = (uint8_t *)demod->am_buf;
594             out_len = n_samples * sizeof(int16_t);
595         }
596         else if (dumper->format == S16_FM) {
597             out_buf = (uint8_t *)demod->buf.fm;
598             out_len = n_samples * sizeof(int16_t);
599         }
600         else if (dumper->format == F32_AM) {
601             for (unsigned long n = 0; n < n_samples; ++n)
602                 demod->f32_buf[n] = demod->am_buf[n] * (1.0f / 0x8000); // scale from Q0.15
603             out_buf = (uint8_t *)demod->f32_buf;
604             out_len = n_samples * sizeof(float);
605         }
606         else if (dumper->format == F32_FM) {
607             for (unsigned long n = 0; n < n_samples; ++n)
608                 demod->f32_buf[n] = demod->buf.fm[n] * (1.0f / 0x8000); // scale from Q0.15
609             out_buf = (uint8_t *)demod->f32_buf;
610             out_len = n_samples * sizeof(float);
611         }
612         else if (dumper->format == F32_I) {
613             if (demod->sample_size == 2)
614                 for (unsigned long n = 0; n < n_samples; ++n)
615                     demod->f32_buf[n] = (iq_buf[n * 2] - 128) * (1.0f / 0x80); // scale from Q0.7
616             else
617                 for (unsigned long n = 0; n < n_samples; ++n)
618                     demod->f32_buf[n] = ((int16_t *)iq_buf)[n * 2] * (1.0f / 0x8000); // scale from Q0.15
619             out_buf = (uint8_t *)demod->f32_buf;
620             out_len = n_samples * sizeof(float);
621         }
622         else if (dumper->format == F32_Q) {
623             if (demod->sample_size == 2)
624                 for (unsigned long n = 0; n < n_samples; ++n)
625                     demod->f32_buf[n] = (iq_buf[n * 2 + 1] - 128) * (1.0f / 0x80); // scale from Q0.7
626             else
627                 for (unsigned long n = 0; n < n_samples; ++n)
628                     demod->f32_buf[n] = ((int16_t *)iq_buf)[n * 2 + 1] * (1.0f / 0x8000); // scale from Q0.15
629             out_buf = (uint8_t *)demod->f32_buf;
630             out_len = n_samples * sizeof(float);
631         }
632         else if (dumper->format == U8_LOGIC) { // state data
633             out_buf = demod->u8_buf;
634             out_len = n_samples;
635         }
636 
637         if (fwrite(out_buf, 1, out_len, dumper->file) != out_len) {
638             fprintf(stderr, "Short write, samples lost, exiting!\n");
639             cfg->exit_async = 1;
640         }
641     }
642 
643     cfg->input_pos += n_samples;
644     if (cfg->bytes_to_read > 0)
645         cfg->bytes_to_read -= len;
646 
647     if (cfg->after_successful_events_flag && (d_events > 0)) {
648         alarm(0); // cancel the watchdog timer
649         if (cfg->after_successful_events_flag == 1) {
650             cfg->exit_async = 1;
651         }
652         else {
653             cfg->hop_now = 1;
654         }
655     }
656 
657     time_t rawtime;
658     time(&rawtime);
659     // choose hop_index as frequency_index, if there are too few hop_times use the last one
660     int hop_index = cfg->hop_times > cfg->frequency_index ? cfg->frequency_index : cfg->hop_times - 1;
661     if (cfg->hop_times > 0 && cfg->frequencies > 1
662             && difftime(rawtime, cfg->hop_start_time) > cfg->hop_time[hop_index]) {
663         alarm(0); // cancel the watchdog timer
664         cfg->hop_now = 1;
665     }
666     if (cfg->duration > 0 && rawtime >= cfg->stop_time) {
667         alarm(0); // cancel the watchdog timer
668         cfg->exit_async = 1;
669         fprintf(stderr, "Time expired, exiting!\n");
670     }
671     if (cfg->stats_now || (cfg->report_stats && cfg->stats_interval && rawtime >= cfg->stats_time)) {
672         event_occurred_handler(cfg, create_report_data(cfg, cfg->stats_now ? 3 : cfg->report_stats));
673         flush_report_data(cfg);
674         if (rawtime >= cfg->stats_time)
675             cfg->stats_time += cfg->stats_interval;
676         if (cfg->stats_now)
677             cfg->stats_now--;
678     }
679 
680     if (cfg->hop_now && !cfg->exit_async) {
681         cfg->hop_now = 0;
682         time(&cfg->hop_start_time);
683         cfg->frequency_index = (cfg->frequency_index + 1) % cfg->frequencies;
684         sdr_set_center_freq(cfg->dev, cfg->frequency[cfg->frequency_index], 0);
685     }
686 }
687 
hasopt(int test,int argc,char * argv[],char const * optstring)688 static int hasopt(int test, int argc, char *argv[], char const *optstring)
689 {
690     int opt;
691 
692     optind = 1; // reset getopt
693     while ((opt = getopt(argc, argv, optstring)) != -1) {
694         if (opt == test || optopt == test)
695             return opt;
696     }
697     return 0;
698 }
699 
700 static void parse_conf_option(r_cfg_t *cfg, int opt, char *arg);
701 
702 #define OPTSTRING "hVvqDc:x:z:p:a:AI:S:m:M:r:w:W:l:d:t:f:H:g:s:b:n:R:X:F:K:C:T:UG:y:E:Y:"
703 
704 // these should match the short options exactly
705 static struct conf_keywords const conf_keywords[] = {
706         {"help", 'h'},
707         {"verbose", 'v'},
708         {"version", 'V'},
709         {"config_file", 'c'},
710         {"report_meta", 'M'},
711         {"device", 'd'},
712         {"settings", 't'},
713         {"gain", 'g'},
714         {"frequency", 'f'},
715         {"hop_interval", 'H'},
716         {"ppm_error", 'p'},
717         {"sample_rate", 's'},
718         {"protocol", 'R'},
719         {"decoder", 'X'},
720         {"register_all", 'G'},
721         {"out_block_size", 'b'},
722         {"level_limit", 'l'},
723         {"samples_to_read", 'n'},
724         {"analyze", 'a'},
725         {"analyze_pulses", 'A'},
726         {"include_only", 'I'},
727         {"read_file", 'r'},
728         {"write_file", 'w'},
729         {"overwrite_file", 'W'},
730         {"signal_grabber", 'S'},
731         {"override_short", 'z'},
732         {"override_long", 'x'},
733         {"pulse_detect", 'Y'},
734         {"output", 'F'},
735         {"output_tag", 'K'},
736         {"convert", 'C'},
737         {"duration", 'T'},
738         {"test_data", 'y'},
739         {"stop_after_successful_events", 'E'},
740         {NULL, 0}};
741 
parse_conf_text(r_cfg_t * cfg,char * conf)742 static void parse_conf_text(r_cfg_t *cfg, char *conf)
743 {
744     int opt;
745     char *arg;
746     char *p = conf;
747 
748     if (!conf || !*conf)
749         return;
750 
751     while ((opt = getconf(&p, conf_keywords, &arg)) != -1) {
752         parse_conf_option(cfg, opt, arg);
753     }
754 }
755 
parse_conf_file(r_cfg_t * cfg,char const * path)756 static void parse_conf_file(r_cfg_t *cfg, char const *path)
757 {
758     if (!path || !*path || !strcmp(path, "null") || !strcmp(path, "0"))
759         return;
760 
761     char *conf = readconf(path);
762     parse_conf_text(cfg, conf);
763     //free(conf); // TODO: check no args are dangling, then use free
764 }
765 
parse_conf_try_default_files(r_cfg_t * cfg)766 static void parse_conf_try_default_files(r_cfg_t *cfg)
767 {
768     char **paths = compat_get_default_conf_paths();
769     for (int a = 0; paths[a]; a++) {
770         fprintf(stderr, "Trying conf file at \"%s\"...\n", paths[a]);
771         if (hasconf(paths[a])) {
772             fprintf(stderr, "Reading conf from \"%s\".\n", paths[a]);
773             parse_conf_file(cfg, paths[a]);
774             break;
775         }
776     }
777 }
778 
parse_conf_args(r_cfg_t * cfg,int argc,char * argv[])779 static void parse_conf_args(r_cfg_t *cfg, int argc, char *argv[])
780 {
781     int opt;
782 
783     optind = 1; // reset getopt
784     while ((opt = getopt(argc, argv, OPTSTRING)) != -1) {
785         if (opt == '?')
786             opt = optopt; // allow missing arguments
787         parse_conf_option(cfg, opt, optarg);
788     }
789 }
790 
parse_conf_option(r_cfg_t * cfg,int opt,char * arg)791 static void parse_conf_option(r_cfg_t *cfg, int opt, char *arg)
792 {
793     int n;
794     r_device *flex_device;
795 
796     if (arg && (!strcmp(arg, "help") || !strcmp(arg, "?"))) {
797         arg = NULL; // remove the arg if it's a request for the usage help
798     }
799 
800     switch (opt) {
801     case 'h':
802         usage(0);
803         break;
804     case 'V':
805         exit(0); // we already printed the version
806         break;
807     case 'v':
808         if (!arg)
809             cfg->verbosity++;
810         else
811             cfg->verbosity = atobv(arg, 1);
812         break;
813     case 'c':
814         parse_conf_file(cfg, arg);
815         break;
816     case 'd':
817         if (!arg)
818             help_device();
819 
820         cfg->dev_query = arg;
821         break;
822     case 't':
823         // this option changed, check and warn if old meaning is used
824         if (!arg || *arg == '-') {
825             fprintf(stderr, "test_mode (-t) is deprecated. Use -S none|all|unknown|known\n");
826             exit(1);
827         }
828         cfg->settings_str = arg;
829         break;
830     case 'f':
831         if (cfg->frequencies < MAX_FREQS) {
832             uint32_t sr = atouint32_metric(arg, "-f: ");
833             /* If the frequency is above 800MHz sample at 1MS/s */
834             if ((sr > FSK_PULSE_DETECTOR_LIMIT) && (cfg->samp_rate == DEFAULT_SAMPLE_RATE)) {
835                 cfg->samp_rate = 1000000;
836                 fprintf(stderr, "\nNew defaults active, use \"-Y classic -s 250k\" for the old defaults!\n\n");
837             }
838             cfg->frequency[cfg->frequencies++] = sr;
839         } else
840             fprintf(stderr, "Max number of frequencies reached %d\n", MAX_FREQS);
841         break;
842     case 'H':
843         if (cfg->hop_times < MAX_FREQS)
844             cfg->hop_time[cfg->hop_times++] = atoi_time(arg, "-H: ");
845         else
846             fprintf(stderr, "Max number of hop times reached %d\n", MAX_FREQS);
847         break;
848     case 'g':
849         if (!arg)
850             help_gain();
851 
852         free(cfg->gain_str);
853         cfg->gain_str = strdup(arg);
854         if (!cfg->gain_str)
855             FATAL_STRDUP("parse_conf_option()");
856         break;
857     case 'G':
858         if (atobv(arg, 1) == 4) {
859             fprintf(stderr, "\n\tUse -G for testing only. Enable protocols with -R if you really need them.\n\n");
860             cfg->no_default_devices = 1;
861             register_all_protocols(cfg, 1);
862         }
863         else {
864             fprintf(stderr, "\n\tUse -G for testing only. Enable with -G 4 if you really mean it.\n\n");
865             exit(1);
866         }
867         break;
868     case 'p':
869         cfg->ppm_error = atobv(arg, 0);
870         break;
871     case 's':
872         cfg->samp_rate = atouint32_metric(arg, "-s: ");
873         break;
874     case 'b':
875         cfg->out_block_size = atouint32_metric(arg, "-b: ");
876         break;
877     case 'l':
878         n = 1000;
879         if (arg && atoi(arg) > 0)
880             n = atoi(arg);
881         fprintf(stderr, "\n\tLevel limit has changed from \"-l %d\" to \"-Y level=%.1f\" in dB.\n\n", n, AMP_TO_DB(n));
882         exit(1);
883         break;
884     case 'n':
885         cfg->bytes_to_read = atouint32_metric(arg, "-n: ") * 2;
886         break;
887     case 'a':
888         if (atobv(arg, 1) == 4 && !cfg->demod->am_analyze) {
889             cfg->demod->am_analyze = am_analyze_create();
890         }
891         else {
892             fprintf(stderr, "\n\tUse -a for testing only. Enable with -a 4 if you really mean it.\n\n");
893             exit(1);
894         }
895         break;
896     case 'A':
897         cfg->demod->analyze_pulses = atobv(arg, 1);
898         break;
899     case 'I':
900         fprintf(stderr, "include_only (-I) is deprecated. Use -S none|all|unknown|known\n");
901         exit(1);
902         break;
903     case 'r':
904         if (!arg)
905             help_read();
906 
907         add_infile(cfg, arg);
908         // TODO: file_info_check_read()
909         break;
910     case 'w':
911         if (!arg)
912             help_write();
913 
914         add_dumper(cfg, arg, 0);
915         break;
916     case 'W':
917         if (!arg)
918             help_write();
919 
920         add_dumper(cfg, arg, 1);
921         break;
922     case 'S':
923         if (!arg)
924             usage(1);
925         if (strcasecmp(arg, "all") == 0)
926             cfg->grab_mode = 1;
927         else if (strcasecmp(arg, "unknown") == 0)
928             cfg->grab_mode = 2;
929         else if (strcasecmp(arg, "known") == 0)
930             cfg->grab_mode = 3;
931         else
932             cfg->grab_mode = atobv(arg, 1);
933         if (cfg->grab_mode && !cfg->demod->samp_grab)
934             cfg->demod->samp_grab = samp_grab_create(SIGNAL_GRABBER_BUFFER);
935         break;
936     case 'm':
937         fprintf(stderr, "sample mode option is deprecated.\n");
938         usage(1);
939         break;
940     case 'M':
941         if (!arg)
942             help_meta();
943 
944         if (!strncasecmp(arg, "time", 4)) {
945             char *p = arg_param(arg);
946             // time  time:1  time:on  time:yes
947             // time:0  time:off  time:no
948             // time:rel
949             // time:unix
950             // time:iso
951             // time:...:usec  time:...:sec
952             // time:...:utc  time:...:local
953             cfg->report_time = REPORT_TIME_DATE;
954             while (p && *p) {
955                 if (!strncasecmp(p, "0", 1) || !strncasecmp(p, "no", 2) || !strncasecmp(p, "off", 3))
956                     cfg->report_time = REPORT_TIME_OFF;
957                 else if (!strncasecmp(p, "1", 1) || !strncasecmp(p, "yes", 3) || !strncasecmp(p, "on", 2))
958                     cfg->report_time = REPORT_TIME_DATE;
959                 else if (!strncasecmp(p, "rel", 3))
960                     cfg->report_time = REPORT_TIME_SAMPLES;
961                 else if (!strncasecmp(p, "unix", 4))
962                     cfg->report_time = REPORT_TIME_UNIX;
963                 else if (!strncasecmp(p, "iso", 3))
964                     cfg->report_time = REPORT_TIME_ISO;
965                 else if (!strncasecmp(p, "usec", 4))
966                     cfg->report_time_hires = 1;
967                 else if (!strncasecmp(p, "sec", 3))
968                     cfg->report_time_hires = 0;
969                 else if (!strncasecmp(p, "tz", 2))
970                     cfg->report_time_tz = 1;
971                 else if (!strncasecmp(p, "notz", 4))
972                     cfg->report_time_tz = 0;
973                 else if (!strncasecmp(p, "utc", 3))
974                     cfg->report_time_utc = 1;
975                 else if (!strncasecmp(p, "local", 5))
976                     cfg->report_time_utc = 0;
977                 else {
978                     fprintf(stderr, "Unknown time format option: %s\n", p);
979                     help_meta();
980                 }
981 
982                 p = arg_param(p);
983             }
984             // fprintf(stderr, "time format: %d, usec:%d utc:%d\n", cfg->report_time, cfg->report_time_hires, cfg->report_time_utc);
985         }
986 
987         // TODO: old time options, remove someday
988         else if (!strcasecmp(arg, "reltime"))
989             cfg->report_time = REPORT_TIME_SAMPLES;
990         else if (!strcasecmp(arg, "notime"))
991             cfg->report_time = REPORT_TIME_OFF;
992         else if (!strcasecmp(arg, "hires"))
993             cfg->report_time_hires = 1;
994         else if (!strcasecmp(arg, "utc"))
995             cfg->report_time_utc = 1;
996         else if (!strcasecmp(arg, "noutc"))
997             cfg->report_time_utc = 0;
998 
999         else if (!strcasecmp(arg, "protocol"))
1000             cfg->report_protocol = 1;
1001         else if (!strcasecmp(arg, "noprotocol"))
1002             cfg->report_protocol = 0;
1003         else if (!strcasecmp(arg, "level"))
1004             cfg->report_meta = 1;
1005         else if (!strncasecmp(arg, "noise", 5))
1006             cfg->report_noise = atoiv(arg_param(arg), 10); // atoi_time_default()
1007         else if (!strcasecmp(arg, "bits"))
1008             cfg->verbose_bits = 1;
1009         else if (!strcasecmp(arg, "description"))
1010             cfg->report_description = 1;
1011         else if (!strcasecmp(arg, "newmodel"))
1012             fprintf(stderr, "newmodel option (-M) is deprecated.\n");
1013         else if (!strcasecmp(arg, "oldmodel"))
1014             fprintf(stderr, "oldmodel option (-M) is deprecated.\n");
1015         else if (!strncasecmp(arg, "stats", 5)) {
1016             // there also should be options to set wether to flush on report
1017             char *p = arg_param(arg);
1018             cfg->report_stats = atoiv(p, 1);
1019             cfg->stats_interval = atoiv(arg_param(p), 600); // atoi_time_default()
1020             time(&cfg->stats_time);
1021             cfg->stats_time += cfg->stats_interval;
1022         }
1023         else
1024             cfg->report_meta = atobv(arg, 1);
1025         break;
1026     case 'D':
1027         fprintf(stderr, "debug option (-D) is deprecated. See -v to increase verbosity\n");
1028         break;
1029     case 'z':
1030         fprintf(stderr, "override_short (-z) is deprecated.\n");
1031         break;
1032     case 'x':
1033         fprintf(stderr, "override_long (-x) is deprecated.\n");
1034         break;
1035     case 'R':
1036         if (!arg)
1037             help_protocols(cfg->devices, cfg->num_r_devices, 0);
1038 
1039         n = atoi(arg);
1040         if (n > cfg->num_r_devices || -n > cfg->num_r_devices) {
1041             fprintf(stderr, "Protocol number specified (%d) is larger than number of protocols\n\n", n);
1042             help_protocols(cfg->devices, cfg->num_r_devices, 1);
1043         }
1044         if ((n > 0 && cfg->devices[n - 1].disabled > 2) || (n < 0 && cfg->devices[-n - 1].disabled > 2)) {
1045             fprintf(stderr, "Protocol number specified (%d) is invalid\n\n", n);
1046             help_protocols(cfg->devices, cfg->num_r_devices, 1);
1047         }
1048 
1049         if (n < 0 && !cfg->no_default_devices) {
1050             register_all_protocols(cfg, 0); // register all defaults
1051         }
1052         cfg->no_default_devices = 1;
1053 
1054         if (n >= 1) {
1055             register_protocol(cfg, &cfg->devices[n - 1], arg_param(arg));
1056         }
1057         else if (n <= -1) {
1058             unregister_protocol(cfg, &cfg->devices[-n - 1]);
1059         }
1060         else {
1061             fprintf(stderr, "Disabling all device decoders.\n");
1062             list_clear(&cfg->demod->r_devs, (list_elem_free_fn)free_protocol);
1063         }
1064         break;
1065     case 'X':
1066         if (!arg)
1067             flex_create_device(NULL);
1068 
1069         flex_device = flex_create_device(arg);
1070         register_protocol(cfg, flex_device, "");
1071         break;
1072     case 'q':
1073         fprintf(stderr, "quiet option (-q) is default and deprecated. See -v to increase verbosity\n");
1074         break;
1075     case 'F':
1076         if (!arg)
1077             help_output();
1078 
1079         if (strncmp(arg, "json", 4) == 0) {
1080             add_json_output(cfg, arg_param(arg));
1081         }
1082         else if (strncmp(arg, "csv", 3) == 0) {
1083             add_csv_output(cfg, arg_param(arg));
1084         }
1085         else if (strncmp(arg, "kv", 2) == 0) {
1086             add_kv_output(cfg, arg_param(arg));
1087         }
1088         else if (strncmp(arg, "mqtt", 4) == 0) {
1089             add_mqtt_output(cfg, arg);
1090         }
1091         else if (strncmp(arg, "influx", 6) == 0) {
1092             add_influx_output(cfg, arg);
1093         }
1094         else if (strncmp(arg, "syslog", 6) == 0) {
1095             add_syslog_output(cfg, arg_param(arg));
1096         }
1097         else if (strncmp(optarg, "http", 4) == 0) {
1098             add_http_output(cfg, arg_param(optarg));
1099         }
1100         else if (strncmp(arg, "null", 4) == 0) {
1101             add_null_output(cfg, arg_param(arg));
1102         }
1103         else {
1104             fprintf(stderr, "Invalid output format %s\n", arg);
1105             usage(1);
1106         }
1107         break;
1108     case 'K':
1109         if (!arg)
1110             help_tags();
1111         add_data_tag(cfg, arg);
1112         break;
1113     case 'C':
1114         if (!arg)
1115             usage(1);
1116         if (strcmp(arg, "native") == 0) {
1117             cfg->conversion_mode = CONVERT_NATIVE;
1118         }
1119         else if (strcmp(arg, "si") == 0) {
1120             cfg->conversion_mode = CONVERT_SI;
1121         }
1122         else if (strcmp(arg, "customary") == 0) {
1123             cfg->conversion_mode = CONVERT_CUSTOMARY;
1124         }
1125         else {
1126             fprintf(stderr, "Invalid conversion mode %s\n", arg);
1127             usage(1);
1128         }
1129         break;
1130     case 'U':
1131         fprintf(stderr, "UTC mode option (-U) is deprecated. Please use \"-M utc\".\n");
1132         exit(1);
1133         break;
1134     case 'T':
1135         cfg->duration = atoi_time(arg, "-T: ");
1136         if (cfg->duration < 1) {
1137             fprintf(stderr, "Duration '%s' not a positive number; will continue indefinitely\n", arg);
1138         }
1139         break;
1140     case 'y':
1141         cfg->test_data = arg;
1142         break;
1143     case 'Y':
1144         if (!arg)
1145             usage(1);
1146         char const *p = arg;
1147         while (p && *p) {
1148             char const *val = NULL;
1149             if (kwargs_match(p, "autolevel", &val))
1150                 cfg->demod->auto_level = atoiv(val, 1); // arg_float_default(p + 9, "-Y autolevel: ");
1151             else if (kwargs_match(p, "squelch", &val))
1152                 cfg->demod->squelch_offset = atoiv(val, 1); // arg_float_default(p + 7, "-Y squelch: ");
1153             else if (kwargs_match(p, "auto", &val))
1154                 cfg->fsk_pulse_detect_mode = FSK_PULSE_DETECT_AUTO;
1155             else if (kwargs_match(p, "classic", &val))
1156                 cfg->fsk_pulse_detect_mode = FSK_PULSE_DETECT_OLD;
1157             else if (kwargs_match(p, "minmax", &val))
1158                 cfg->fsk_pulse_detect_mode = FSK_PULSE_DETECT_NEW;
1159             else if (kwargs_match(p, "ampest", &val))
1160                 cfg->demod->use_mag_est = 0;
1161             else if (kwargs_match(p, "verbose", &val))
1162                 cfg->demod->detect_verbosity++;
1163             else if (kwargs_match(p, "magest", &val))
1164                 cfg->demod->use_mag_est = 1;
1165             else if (kwargs_match(p, "level", &val))
1166                 cfg->demod->level_limit = arg_float(val, "-Y level: ");
1167             else if (kwargs_match(p, "minlevel", &val))
1168                 cfg->demod->min_level = arg_float(val, "-Y minlevel: ");
1169             else if (kwargs_match(p, "minsnr", &val))
1170                 cfg->demod->min_snr = arg_float(val, "-Y minsnr: ");
1171             else if (kwargs_match(p, "filter", &val))
1172                 cfg->demod->low_pass = arg_float(val, "-Y filter: ");
1173             else {
1174                 fprintf(stderr, "Unknown pulse detector setting: %s\n", p);
1175                 usage(1);
1176             }
1177             p = kwargs_skip(p);
1178         }
1179         break;
1180     case 'E':
1181         if (arg && !strcmp(arg, "hop")) {
1182             cfg->after_successful_events_flag = 2;
1183         }
1184         else if (arg && !strcmp(arg, "quit")) {
1185             cfg->after_successful_events_flag = 1;
1186         }
1187         else {
1188             cfg->after_successful_events_flag = atobv(arg, 1);
1189         }
1190         break;
1191     default:
1192         usage(1);
1193         break;
1194     }
1195 }
1196 
1197 static r_cfg_t g_cfg;
1198 
1199 // TODO: SIGINFO is not in POSIX...
1200 #ifndef SIGINFO
1201 #define SIGINFO 29
1202 #endif
1203 
1204 #ifdef _WIN32
1205 BOOL WINAPI
console_handler(int signum)1206 console_handler(int signum)
1207 {
1208     if (CTRL_C_EVENT == signum) {
1209         fprintf(stderr, "Signal caught, exiting!\n");
1210         g_cfg.exit_async = 1;
1211         sdr_stop(g_cfg.dev);
1212         return TRUE;
1213     }
1214     else if (CTRL_BREAK_EVENT == signum) {
1215         fprintf(stderr, "CTRL-BREAK detected, hopping to next frequency (-f). Use CTRL-C to quit.\n");
1216         g_cfg.hop_now = 1;
1217         return TRUE;
1218     }
1219     else if (signum == SIGALRM) {
1220         fprintf(stderr, "Async read stalled, exiting!\n");
1221         g_cfg.exit_code = 3;
1222         g_cfg.exit_async = 1;
1223         sdr_stop(g_cfg.dev);
1224         return TRUE;
1225     }
1226     return FALSE;
1227 }
1228 
1229 /* Only called for SIGALRM
1230  */
sighandler(int signum)1231 static void sighandler(int signum)
1232 {
1233   console_handler(signum);
1234 }
1235 
1236 #else
sighandler(int signum)1237 static void sighandler(int signum)
1238 {
1239     if (signum == SIGPIPE) {
1240         signal(SIGPIPE, SIG_IGN);
1241     }
1242     else if (signum == SIGINFO/* TODO: maybe SIGUSR1 */) {
1243         g_cfg.stats_now++;
1244         return;
1245     }
1246     else if (signum == SIGUSR1) {
1247         g_cfg.hop_now = 1;
1248         return;
1249     }
1250     else if (signum == SIGALRM) {
1251         fprintf(stderr, "Async read stalled, exiting!\n");
1252         g_cfg.exit_code = 3;
1253     }
1254     else {
1255         fprintf(stderr, "Signal caught, exiting!\n");
1256     }
1257     g_cfg.exit_async = 1;
1258     sdr_stop(g_cfg.dev);
1259 }
1260 #endif
1261 
sdr_handler(sdr_event_t * ev,void * ctx)1262 static void sdr_handler(sdr_event_t *ev, void *ctx)
1263 {
1264     r_cfg_t *cfg = ctx;
1265 
1266     data_t *data = NULL;
1267     if (ev->ev & SDR_EV_RATE) {
1268         cfg->samp_rate = ev->sample_rate;
1269         data = data_append(data,
1270                 "sample_rate", "", DATA_INT, ev->sample_rate,
1271                 NULL);
1272     }
1273     if (ev->ev & SDR_EV_CORR) {
1274         cfg->ppm_error = ev->freq_correction;
1275         data = data_append(data,
1276                 "freq_correction", "", DATA_INT, ev->freq_correction,
1277                 NULL);
1278     }
1279     if (ev->ev & SDR_EV_FREQ) {
1280         cfg->center_frequency = ev->center_frequency;
1281         data = data_append(data,
1282                 "center_frequency", "", DATA_INT, ev->center_frequency,
1283                 "frequencies", "", DATA_COND, cfg->frequencies > 1, DATA_ARRAY, data_array(cfg->frequencies, DATA_INT, cfg->frequency),
1284                 "hop_times", "", DATA_COND, cfg->frequencies > 1, DATA_ARRAY, data_array(cfg->hop_times, DATA_INT, cfg->hop_time),
1285                 NULL);
1286     }
1287     if (ev->ev & SDR_EV_GAIN) {
1288         data = data_append(data,
1289                 "gain", "", DATA_STRING, ev->gain_str,
1290                 NULL);
1291     }
1292     if (data) {
1293         for (size_t i = 0; i < cfg->output_handler.len; ++i) { // list might contain NULLs
1294             data_output_print(cfg->output_handler.elems[i], data);
1295         }
1296         data_free(data);
1297     }
1298 
1299     if (ev->ev == SDR_EV_DATA) {
1300         if (cfg->mgr) {
1301             int max_polls = 16;
1302             while (max_polls-- && mg_mgr_poll(cfg->mgr, 0));
1303         }
1304 
1305         if (!cfg->exit_async)
1306             sdr_callback((unsigned char *)ev->buf, ev->len, ctx);
1307     }
1308 
1309     if (cfg->exit_async)
1310         sdr_stop(cfg->dev);
1311 }
1312 
main(int argc,char ** argv)1313 int main(int argc, char **argv) {
1314 #ifndef _WIN32
1315     struct sigaction sigact;
1316 #endif
1317     FILE *in_file;
1318     int r = 0;
1319     struct dm_state *demod;
1320     r_cfg_t *cfg = &g_cfg;
1321 
1322     print_version(); // always print the version info
1323 
1324     r_init_cfg(cfg);
1325 
1326     setbuf(stdout, NULL);
1327     setbuf(stderr, NULL);
1328 
1329     demod = cfg->demod;
1330 
1331     // if there is no explicit conf file option look for default conf files
1332     if (!hasopt('c', argc, argv, OPTSTRING)) {
1333         parse_conf_try_default_files(cfg);
1334     }
1335 
1336     parse_conf_args(cfg, argc, argv);
1337     // apply hop defaults and set first frequency
1338     if (cfg->frequencies == 0) {
1339         cfg->frequency[0] = DEFAULT_FREQUENCY;
1340         cfg->frequencies  = 1;
1341     }
1342     cfg->center_frequency = cfg->frequency[cfg->frequency_index];
1343     if (cfg->frequencies > 1 && cfg->hop_times == 0) {
1344         cfg->hop_time[cfg->hop_times++] = DEFAULT_HOP_TIME;
1345     }
1346     // save sample rate, this should be a hop config too
1347     uint32_t sample_rate_0 = cfg->samp_rate;
1348 
1349     // add all remaining positional arguments as input files
1350     while (argc > optind) {
1351         add_infile(cfg, argv[optind++]);
1352     }
1353 
1354     pulse_detect_set_levels(demod->pulse_detect, demod->use_mag_est, demod->level_limit, demod->min_level, demod->min_snr, demod->detect_verbosity);
1355 
1356     if (demod->am_analyze) {
1357         demod->am_analyze->level_limit = DB_TO_AMP(demod->level_limit);
1358         demod->am_analyze->frequency   = &cfg->center_frequency;
1359         demod->am_analyze->samp_rate   = &cfg->samp_rate;
1360         demod->am_analyze->sample_size = &demod->sample_size;
1361     }
1362 
1363     if (demod->samp_grab) {
1364         demod->samp_grab->frequency   = &cfg->center_frequency;
1365         demod->samp_grab->samp_rate   = &cfg->samp_rate;
1366         demod->samp_grab->sample_size = &demod->sample_size;
1367     }
1368 
1369     if (cfg->report_time == REPORT_TIME_DEFAULT) {
1370         if (cfg->in_files.len)
1371             cfg->report_time = REPORT_TIME_SAMPLES;
1372         else
1373             cfg->report_time = REPORT_TIME_DATE;
1374     }
1375     if (cfg->report_time_utc) {
1376 #ifdef _WIN32
1377         putenv("TZ=UTC+0");
1378         _tzset();
1379 #else
1380         r = setenv("TZ", "UTC", 1);
1381         if (r != 0)
1382             fprintf(stderr, "Unable to set TZ to UTC; error code: %d\n", r);
1383 #endif
1384     }
1385 
1386     if (!cfg->output_handler.len) {
1387         add_kv_output(cfg, NULL);
1388     }
1389 
1390     // register default decoders if nothing is configured
1391     if (!cfg->no_default_devices) {
1392         register_all_protocols(cfg, 0); // register all defaults
1393     }
1394 
1395     // check if we need FM demod
1396     for (void **iter = demod->r_devs.elems; iter && *iter; ++iter) {
1397         r_device *r_dev = *iter;
1398         if (r_dev->modulation >= FSK_DEMOD_MIN_VAL) {
1399           demod->enable_FM_demod = 1;
1400           break;
1401         }
1402     }
1403 
1404     {
1405         char decoders_str[1024];
1406         decoders_str[0] = '\0';
1407         if (!cfg->verbosity) {
1408             abuf_t p = {0};
1409             abuf_init(&p, decoders_str, sizeof(decoders_str));
1410             // print registered decoder ranges
1411             abuf_printf(&p, " [");
1412             for (void **iter = demod->r_devs.elems; iter && *iter; ++iter) {
1413                 r_device *r_dev = *iter;
1414                 unsigned num = r_dev->protocol_num;
1415                 if (num == 0)
1416                     continue;
1417                 while (iter[1]
1418                         && r_dev->protocol_num + 1 == ((r_device *)iter[1])->protocol_num)
1419                     r_dev = *++iter;
1420                 if (num == r_dev->protocol_num)
1421                     abuf_printf(&p, " %u", num);
1422                 else
1423                     abuf_printf(&p, " %u-%u", num, r_dev->protocol_num);
1424             }
1425             abuf_printf(&p, " ]");
1426         }
1427         fprintf(stderr, "Registered %zu out of %u device decoding protocols%s\n",
1428                 demod->r_devs.len, cfg->num_r_devices, decoders_str);
1429     }
1430 
1431     char const **well_known = well_known_output_fields(cfg);
1432     start_outputs(cfg, well_known);
1433     free(well_known);
1434 
1435     if (cfg->out_block_size < MINIMAL_BUF_LENGTH ||
1436             cfg->out_block_size > MAXIMAL_BUF_LENGTH) {
1437         fprintf(stderr,
1438                 "Output block size wrong value, falling back to default\n");
1439         fprintf(stderr,
1440                 "Minimal length: %d\n", MINIMAL_BUF_LENGTH);
1441         fprintf(stderr,
1442                 "Maximal length: %d\n", MAXIMAL_BUF_LENGTH);
1443         cfg->out_block_size = DEFAULT_BUF_LENGTH;
1444     }
1445 
1446     // Special case for streaming test data
1447     if (cfg->test_data && (!strcasecmp(cfg->test_data, "-") || *cfg->test_data == '@')) {
1448         FILE *fp;
1449         char line[INPUT_LINE_MAX];
1450 
1451         if (*cfg->test_data == '@') {
1452             fprintf(stderr, "Reading test data from \"%s\"\n", &cfg->test_data[1]);
1453             fp = fopen(&cfg->test_data[1], "r");
1454         } else {
1455             fprintf(stderr, "Reading test data from stdin\n");
1456             fp = stdin;
1457         }
1458         if (!fp) {
1459             fprintf(stderr, "Failed to open %s\n", cfg->test_data);
1460             exit(1);
1461         }
1462 
1463         while (fgets(line, INPUT_LINE_MAX, fp)) {
1464             if (cfg->verbosity)
1465                 fprintf(stderr, "Processing test data \"%s\"...\n", line);
1466             r = 0;
1467             // test a single decoder?
1468             if (*line == '[') {
1469                 char *e = NULL;
1470                 unsigned d = (unsigned)strtol(&line[1], &e, 10);
1471                 if (!e || *e != ']') {
1472                     fprintf(stderr, "Bad protocol number %.5s.\n", line);
1473                     exit(1);
1474                 }
1475                 e++;
1476                 r_device *r_dev = NULL;
1477                 for (void **iter = demod->r_devs.elems; iter && *iter; ++iter) {
1478                     r_device *r_dev_i = *iter;
1479                     if (r_dev_i->protocol_num == d) {
1480                         r_dev = r_dev_i;
1481                         break;
1482                     }
1483                 }
1484                 if (!r_dev) {
1485                     fprintf(stderr, "Unknown protocol number %u.\n", d);
1486                     exit(1);
1487                 }
1488                 if (cfg->verbosity)
1489                     fprintf(stderr, "Verifying test data with device %s.\n", r_dev->name);
1490                 if (rfraw_check(e)) {
1491                     pulse_data_t pulse_data = {0};
1492                     rfraw_parse(&pulse_data, e);
1493                     list_t single_dev = {0};
1494                     list_push(&single_dev, r_dev);
1495                     if (!pulse_data.fsk_f2_est)
1496                         r += run_ook_demods(&single_dev, &pulse_data);
1497                     else
1498                         r += run_fsk_demods(&single_dev, &pulse_data);
1499                     list_free_elems(&single_dev, NULL);
1500                 } else
1501                 r += pulse_demod_string(e, r_dev);
1502                 continue;
1503             }
1504             // otherwise test all decoders
1505             if (rfraw_check(line)) {
1506                 pulse_data_t pulse_data = {0};
1507                 rfraw_parse(&pulse_data, line);
1508                 if (!pulse_data.fsk_f2_est)
1509                     r += run_ook_demods(&demod->r_devs, &pulse_data);
1510                 else
1511                     r += run_fsk_demods(&demod->r_devs, &pulse_data);
1512             } else
1513             for (void **iter = demod->r_devs.elems; iter && *iter; ++iter) {
1514                 r_device *r_dev = *iter;
1515                 if (cfg->verbosity)
1516                     fprintf(stderr, "Verifying test data with device %s.\n", r_dev->name);
1517                 r += pulse_demod_string(line, r_dev);
1518             }
1519         }
1520 
1521         if (*cfg->test_data == '@') {
1522             fclose(fp);
1523         }
1524 
1525         r_free_cfg(cfg);
1526         exit(!r);
1527     }
1528     // Special case for string test data
1529     if (cfg->test_data) {
1530         r = 0;
1531         if (rfraw_check(cfg->test_data)) {
1532             pulse_data_t pulse_data = {0};
1533             rfraw_parse(&pulse_data, cfg->test_data);
1534             if (!pulse_data.fsk_f2_est)
1535                 r += run_ook_demods(&demod->r_devs, &pulse_data);
1536             else
1537                 r += run_fsk_demods(&demod->r_devs, &pulse_data);
1538         } else
1539         for (void **iter = demod->r_devs.elems; iter && *iter; ++iter) {
1540             r_device *r_dev = *iter;
1541             if (cfg->verbosity)
1542                 fprintf(stderr, "Verifying test data with device %s.\n", r_dev->name);
1543             r += pulse_demod_string(cfg->test_data, r_dev);
1544         }
1545         r_free_cfg(cfg);
1546         exit(!r);
1547     }
1548 
1549     // Special case for in files
1550     if (cfg->in_files.len) {
1551         unsigned char *test_mode_buf = malloc(DEFAULT_BUF_LENGTH * sizeof(unsigned char));
1552         if (!test_mode_buf)
1553             FATAL_MALLOC("test_mode_buf");
1554         float *test_mode_float_buf = malloc(DEFAULT_BUF_LENGTH / sizeof(int16_t) * sizeof(float));
1555         if (!test_mode_float_buf)
1556             FATAL_MALLOC("test_mode_float_buf");
1557 
1558         if (cfg->duration > 0) {
1559             time(&cfg->stop_time);
1560             cfg->stop_time += cfg->duration;
1561         }
1562 
1563         for (void **iter = cfg->in_files.elems; iter && *iter; ++iter) {
1564             cfg->in_filename = *iter;
1565 
1566             file_info_clear(&demod->load_info); // reset all info
1567             file_info_parse_filename(&demod->load_info, cfg->in_filename);
1568             // apply file info or default
1569             cfg->samp_rate        = demod->load_info.sample_rate ? demod->load_info.sample_rate : sample_rate_0;
1570             cfg->center_frequency = demod->load_info.center_frequency ? demod->load_info.center_frequency : cfg->frequency[0];
1571 
1572             if (strcmp(demod->load_info.path, "-") == 0) { // read samples from stdin
1573                 in_file = stdin;
1574                 cfg->in_filename = "<stdin>";
1575             } else {
1576                 in_file = fopen(demod->load_info.path, "rb");
1577                 if (!in_file) {
1578                     fprintf(stderr, "Opening file: %s failed!\n", cfg->in_filename);
1579                     break;
1580                 }
1581             }
1582             fprintf(stderr, "Test mode active. Reading samples from file: %s\n", cfg->in_filename);  // Essential information (not quiet)
1583             if (demod->load_info.format == CU8_IQ
1584                     || demod->load_info.format == CS8_IQ
1585                     || demod->load_info.format == S16_AM
1586                     || demod->load_info.format == S16_FM) {
1587                 demod->sample_size = sizeof(uint8_t) * 2; // CU8, AM, FM
1588             } else if (demod->load_info.format == CS16_IQ
1589                     || demod->load_info.format == CF32_IQ) {
1590                 demod->sample_size = sizeof(int16_t) * 2; // CS16, CF32 (after conversion)
1591             } else if (demod->load_info.format == PULSE_OOK) {
1592                 // ignore
1593             } else {
1594                 fprintf(stderr, "Input format invalid: %s\n", file_info_string(&demod->load_info));
1595                 break;
1596             }
1597             if (cfg->verbosity) {
1598                 fprintf(stderr, "Input format: %s\n", file_info_string(&demod->load_info));
1599             }
1600             demod->sample_file_pos = 0.0;
1601 
1602             // special case for pulse data file-inputs
1603             if (demod->load_info.format == PULSE_OOK) {
1604                 while (!cfg->exit_async) {
1605                     pulse_data_load(in_file, &demod->pulse_data, cfg->samp_rate);
1606                     if (!demod->pulse_data.num_pulses)
1607                         break;
1608 
1609                     for (void **iter2 = demod->dumper.elems; iter2 && *iter2; ++iter2) {
1610                         file_info_t const *dumper = *iter2;
1611                         if (dumper->format == VCD_LOGIC) {
1612                             pulse_data_print_vcd(dumper->file, &demod->pulse_data, '\'');
1613                         } else if (dumper->format == PULSE_OOK) {
1614                             pulse_data_dump(dumper->file, &demod->pulse_data);
1615                         } else {
1616                             fprintf(stderr, "Dumper (%s) not supported on OOK input\n", dumper->spec);
1617                             exit(1);
1618                         }
1619                     }
1620 
1621                     if (demod->pulse_data.fsk_f2_est) {
1622                         run_fsk_demods(&demod->r_devs, &demod->pulse_data);
1623                     }
1624                     else {
1625                         int p_events = run_ook_demods(&demod->r_devs, &demod->pulse_data);
1626                         if (cfg->verbosity > 2)
1627                             pulse_data_print(&demod->pulse_data);
1628                         if (demod->analyze_pulses && (cfg->grab_mode <= 1 || (cfg->grab_mode == 2 && p_events == 0) || (cfg->grab_mode == 3 && p_events > 0))) {
1629                             pulse_analyzer(&demod->pulse_data, PULSE_DATA_OOK);
1630                         }
1631                     }
1632                 }
1633 
1634                 if (in_file != stdin)
1635                     fclose(in_file = stdin);
1636 
1637                 continue;
1638             }
1639 
1640             // default case for file-inputs
1641             int n_blocks = 0;
1642             unsigned long n_read;
1643             do {
1644                 // Convert CF32 file to CS16 buffer
1645                 if (demod->load_info.format == CF32_IQ) {
1646                     n_read = fread(test_mode_float_buf, sizeof(float), DEFAULT_BUF_LENGTH / 2, in_file);
1647                     // clamp float to [-1,1] and scale to Q0.15
1648                     for (unsigned long n = 0; n < n_read; n++) {
1649                         int s_tmp = test_mode_float_buf[n] * INT16_MAX;
1650                         if (s_tmp < -INT16_MAX)
1651                             s_tmp = -INT16_MAX;
1652                         else if (s_tmp > INT16_MAX)
1653                             s_tmp = INT16_MAX;
1654                         ((int16_t *)test_mode_buf)[n] = s_tmp;
1655                     }
1656                     n_read *= 2; // convert to byte count
1657                 } else {
1658                     n_read = fread(test_mode_buf, 1, DEFAULT_BUF_LENGTH, in_file);
1659 
1660                     // Convert CS8 file to CU8 buffer
1661                     if (demod->load_info.format == CS8_IQ) {
1662                         for (unsigned long n = 0; n < n_read; n++) {
1663                             test_mode_buf[n] = ((int8_t)test_mode_buf[n]) + 128;
1664                         }
1665                     }
1666                 }
1667                 if (n_read == 0) break;  // sdr_callback() will Segmentation Fault with len=0
1668                 demod->sample_file_pos = ((float)n_blocks * DEFAULT_BUF_LENGTH + n_read) / cfg->samp_rate / demod->sample_size;
1669                 n_blocks++; // this assumes n_read == DEFAULT_BUF_LENGTH
1670                 sdr_callback(test_mode_buf, n_read, cfg);
1671             } while (n_read != 0 && !cfg->exit_async);
1672 
1673             // Call a last time with cleared samples to ensure EOP detection
1674             if (demod->sample_size == 2) { // CU8
1675                 memset(test_mode_buf, 128, DEFAULT_BUF_LENGTH); // 128 is 0 in unsigned data
1676                 // or is 127.5 a better 0 in cu8 data?
1677                 //for (unsigned long n = 0; n < DEFAULT_BUF_LENGTH/2; n++)
1678                 //    ((uint16_t *)test_mode_buf)[n] = 0x807f;
1679             }
1680             else { // CF32, CS16
1681                     memset(test_mode_buf, 0, DEFAULT_BUF_LENGTH);
1682             }
1683             demod->sample_file_pos = ((float)n_blocks + 1) * DEFAULT_BUF_LENGTH / cfg->samp_rate / demod->sample_size;
1684             sdr_callback(test_mode_buf, DEFAULT_BUF_LENGTH, cfg);
1685             alarm(0); // cancel the watchdog timer
1686 
1687             //Always classify a signal at the end of the file
1688             if (demod->am_analyze)
1689                 am_analyze_classify(demod->am_analyze);
1690             if (cfg->verbosity) {
1691                 fprintf(stderr, "Test mode file issued %d packets\n", n_blocks);
1692             }
1693 
1694             if (in_file != stdin)
1695                 fclose(in_file = stdin);
1696         }
1697 
1698         close_dumpers(cfg);
1699         free(test_mode_buf);
1700         free(test_mode_float_buf);
1701         r_free_cfg(cfg);
1702         exit(0);
1703     }
1704 
1705     if (cfg->sr_filename) {
1706         fprintf(stderr, "SR writing not recommended for live input\n");
1707         exit(1);
1708     }
1709 
1710     // Normal case, no test data, no in files
1711     r = sdr_open(&cfg->dev, cfg->dev_query, cfg->verbosity);
1712     if (r < 0) {
1713         exit(2);
1714     }
1715     cfg->dev_info = sdr_get_dev_info(cfg->dev);
1716     demod->sample_size = sdr_get_sample_size(cfg->dev);
1717     //demod->sample_signed = sdr_get_sample_signed(cfg->dev);
1718 
1719 #ifndef _WIN32
1720     sigact.sa_handler = sighandler;
1721     sigemptyset(&sigact.sa_mask);
1722     sigact.sa_flags = 0;
1723     sigaction(SIGINT, &sigact, NULL);
1724     sigaction(SIGTERM, &sigact, NULL);
1725     sigaction(SIGQUIT, &sigact, NULL);
1726     sigaction(SIGPIPE, &sigact, NULL);
1727     sigaction(SIGUSR1, &sigact, NULL);
1728     sigaction(SIGINFO, &sigact, NULL);
1729 #else
1730     SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_handler, TRUE);
1731 #endif
1732     /* Set the sample rate */
1733     r = sdr_set_sample_rate(cfg->dev, cfg->samp_rate, 1); // always verbose
1734 
1735     if (cfg->verbosity || demod->level_limit < 0.0)
1736         fprintf(stderr, "Bit detection level set to %.1f%s.\n", demod->level_limit, (demod->level_limit < 0.0 ? "" : " (Auto)"));
1737 
1738     r = sdr_apply_settings(cfg->dev, cfg->settings_str, 1); // always verbose for soapy
1739 
1740     /* Enable automatic gain if gain_str empty (or 0 for RTL-SDR), set manual gain otherwise */
1741     r = sdr_set_tuner_gain(cfg->dev, cfg->gain_str, 1); // always verbose
1742 
1743     if (cfg->ppm_error)
1744         r = sdr_set_freq_correction(cfg->dev, cfg->ppm_error, 1); // always verbose
1745 
1746     /* Reset endpoint before we start reading from it (mandatory) */
1747     r = sdr_reset(cfg->dev, cfg->verbosity);
1748     if (r < 0)
1749         fprintf(stderr, "WARNING: Failed to reset buffers.\n");
1750     r = sdr_activate(cfg->dev);
1751 
1752     if (cfg->verbosity) {
1753         fprintf(stderr, "Reading samples in async mode...\n");
1754     }
1755     if (cfg->duration > 0) {
1756         time(&cfg->stop_time);
1757         cfg->stop_time += cfg->duration;
1758     }
1759 
1760     r = sdr_set_center_freq(cfg->dev, cfg->center_frequency, 1); // always verbose
1761 
1762         time(&cfg->hop_start_time);
1763         signal(SIGALRM, sighandler);
1764         alarm(3); // require callback to run every 3 second, abort otherwise
1765 
1766         r = sdr_start(cfg->dev, sdr_handler, (void *)cfg,
1767                 DEFAULT_ASYNC_BUF_NUMBER, cfg->out_block_size);
1768         if (r < 0) {
1769             fprintf(stderr, "WARNING: async read failed (%i).\n", r);
1770         }
1771 
1772         alarm(0); // cancel the watchdog timer
1773 
1774     if (cfg->report_stats > 0) {
1775         event_occurred_handler(cfg, create_report_data(cfg, cfg->report_stats));
1776         flush_report_data(cfg);
1777     }
1778 
1779     if (!cfg->exit_async) {
1780         fprintf(stderr, "\nLibrary error %d, exiting...\n", r);
1781         cfg->exit_code = r;
1782     }
1783 
1784     if (cfg->exit_code >= 0)
1785         r = cfg->exit_code;
1786     r_free_cfg(cfg);
1787 
1788     return r >= 0 ? r : -r;
1789 }
1790