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