Редизайн на основе текущей ветки мейнстрима + новые устройства.
[rtl-433.git] / src / rtl_433.c
old mode 100644 (file)
new mode 100755 (executable)
index 52d54ab..945b532
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <stdbool.h>
+
 #include "rtl-sdr.h"
 #include "rtl_433.h"
-#include "rtl_433_devices.h"
+#include "baseband.h"
+#include "pulse_detect.h"
+#include "pulse_demod.h"
+#include "data.h"
+#include "util.h"
+
 
 static int do_exit = 0;
 static int do_exit_async = 0, frequencies = 0, events = 0;
 uint32_t frequency[MAX_PROTOCOLS];
 time_t rawtime_old;
+int duration = 0;
+time_t stop_time;
 int flag;
 uint32_t samp_rate = DEFAULT_SAMPLE_RATE;
+float sample_file_pos = -1;
 static uint32_t bytes_to_read = 0;
 static rtlsdr_dev_t *dev = NULL;
-static uint16_t scaled_squares[256];
 static int override_short = 0;
 static int override_long = 0;
+int include_only = 0;  // Option -I
 int debug_output = 0;
+int quiet_mode = 0;
+int utc_mode = 0;
+int overwrite_mode = 0;
 
-int debug_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
-    int i,j,k;
-    int rows_used[BITBUF_ROWS];
-    int col_max = 0;
-    int row_cnt = 0;
-
-    // determine what part of bb[][] has non-zero data to avoid
-    // outputting lots of empty rows
-    for (i=0 ; i<BITBUF_ROWS ; i++) {
-       for (j=BITBUF_COLS - 1 ; j > 0 ; j--) {
-           if (bb[i][j] != 0)
-               break;
-       }
-       if (j != 0) {
-           rows_used[i] = 1;
-           row_cnt++;
-           if (j > col_max)
-               col_max = j;
-       } else {
-           rows_used[i] = 0;
-       }
-    }
-
-    if (!row_cnt) {
-       fprintf(stderr, "debug_callback: empty data array\n");
-       return 0;
-    }
-
-    fprintf(stderr, "\n");
-    for (i=0 ; i<BITBUF_ROWS ; i++) {
-       if (!rows_used[i]) {
-           continue;
-       }
-
-        fprintf(stderr, "[%02d] ",i);
-        for (j=0 ; j<=col_max ; j++) {
-            fprintf(stderr, "%02x ", bb[i][j]);
-        }
-        fprintf(stderr, ": ");
-        for (j=0 ; j<=col_max ; j++) {
-            for (k=7 ; k>=0 ; k--) {
-                if (bb[i][j] & 1<<k)
-                    fprintf(stderr, "1");
-                else
-                    fprintf(stderr, "0");
-            }
-            fprintf(stderr, " ");
-        }
-        fprintf(stderr, "\n");
-    }
-    fprintf(stderr, "\n");
-
-    return 0;
-}
+typedef enum  {
+    CONVERT_NATIVE,
+    CONVERT_SI,
+    CONVERT_CUSTOMARY
+} conversion_mode_t;
+static conversion_mode_t conversion_mode = CONVERT_NATIVE;
 
-struct protocol_state {
-    int (*callback)(uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]);
-
-    /* bits state */
-    int bits_col_idx;
-    int bits_row_idx;
-    int bits_bit_col_idx;
-    uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS];
-    int16_t bits_per_row[BITBUF_ROWS];
-    int bit_rows;
-    unsigned int modulation;
-
-    /* demod state */
-    int pulse_length;
-    int pulse_count;
-    int pulse_distance;
-    int sample_counter;
-    int start_c;
-
-    int packet_present;
-    int pulse_start;
-    int real_bits;
-    int start_bit;
-    /* pwm limits */
-    int short_limit;
-    int long_limit;
-    int reset_limit;
-
-
-};
+int num_r_devices = 0;
 
 struct dm_state {
-    FILE *file;
-    int save_data;
+    FILE *out_file;
     int32_t level_limit;
-    int32_t decimation_level;
-    int16_t filter_buffer[MAXIMAL_BUF_LENGTH + FILTER_ORDER];
-    int16_t* f_buf;
+    int16_t am_buf[MAXIMAL_BUF_LENGTH];        // AM demodulated signal (for OOK decoding)
+    union {
+        // These buffers aren't used at the same time, so let's use a union to save some memory
+        int16_t fm_buf[MAXIMAL_BUF_LENGTH];    // FM demodulated signal (for FSK decoding)
+        uint16_t temp_buf[MAXIMAL_BUF_LENGTH]; // Temporary buffer (to be optimized out..)
+    };
+    FilterState lowpass_filter_state;
+    DemodFM_State demod_FM_state;
+    int enable_FM_demod;
     int analyze;
+    int analyze_pulses;
     int debug_mode;
 
     /* Signal grabber variables */
@@ -144,27 +86,65 @@ struct dm_state {
     int r_dev_num;
     struct protocol_state *r_devs[MAX_PROTOCOLS];
 
+       pulse_data_t    pulse_data;
+       pulse_data_t    fsk_pulse_data;
 };
 
-void usage(void) {
-    fprintf(stderr,
+void usage(r_device *devices) {
+       int i;
+       char disabledc;
+
+       fprintf(stderr,
             "rtl_433, an ISM band generic data receiver for RTL2832 based DVB-T receivers\n\n"
-            "Usage:\t[-d device_index (default: 0)]\n"
-            "\t[-g gain (default: 0 for auto)]\n"
-            "\t[-a analyze mode, print a textual description of the signal]\n"
-            "\t[-t signal auto save, use it together with analyze mode (-a -t)\n"
-            "\t[-l change the detection level used to determine pulses (0-3200) default: %i]\n"
-            "\t[-f [-f...] receive frequency[s], default: %i Hz]\n"
-            "\t[-s samplerate (default: %i Hz)]\n"
-            "\t[-S force sync output (default: async)]\n"
-            "\t[-r read data from file instead of from a receiver]\n"
-            "\t[-p ppm_error (default: 0)]\n"
-            "\t[-r test file name (indata)]\n"
-            "\t[-m test file mode (0 rtl_sdr data, 1 rtl_433 data)]\n"
-            "\t[-D print debug info on event\n"
-            "\t[-z override short value\n"
-            "\t[-x override long value\n"
-            "\tfilename (a '-' dumps samples to stdout)\n\n", DEFAULT_LEVEL_LIMIT, DEFAULT_FREQUENCY, DEFAULT_SAMPLE_RATE);
+            "Usage:\t= Tuner options =\n"
+            "\t[-d <RTL-SDR USB device index>] (default: 0)\n"
+            "\t[-g <gain>] (default: 0 for auto)\n"
+            "\t[-f <frequency>] [-f...] Receive frequency(s) (default: %i Hz)\n"
+            "\t[-p <ppm_error] Correct rtl-sdr tuner frequency offset error (default: 0)\n"
+            "\t[-s <sample rate>] Set sample rate (default: %i Hz)\n"
+            "\t[-S] Force sync output (default: async)\n"
+            "\t= Demodulator options =\n"
+            "\t[-R <device>] Enable only the specified device decoding protocol (can be used multiple times)\n"
+            "\t[-G] Enable all device protocols, included those disabled by default\n"
+            "\t[-l <level>] Change detection level used to determine pulses [0-16384] (0 = auto) (default: %i)\n"
+            "\t[-z <value>] Override short value in data decoder\n"
+            "\t[-x <value>] Override long value in data decoder\n"
+            "\t[-n <value>] Specify number of samples to take (each sample is 2 bytes: 1 each of I & Q)\n"
+            "\t= Analyze/Debug options =\n"
+            "\t[-a] Analyze mode. Print a textual description of the signal. Disables decoding\n"
+            "\t[-A] Pulse Analyzer. Enable pulse analyzis and decode attempt\n"
+            "\t[-I] Include only: 0 = all (default), 1 = unknown devices, 2 = known devices\n"
+            "\t[-D] Print debug info on event (repeat for more info)\n"
+            "\t[-q] Quiet mode, suppress non-data messages\n"
+            "\t[-W] Overwrite mode, disable checks to prevent files from being overwritten\n"
+            "\t= File I/O options =\n"
+            "\t[-t] Test signal auto save. Use it together with analyze mode (-a -t). Creates one file per signal\n"
+            "\t\t Note: Saves raw I/Q samples (uint8 pcm, 2 channel). Preferred mode for generating test files\n"
+            "\t[-r <filename>] Read data from input file instead of a receiver\n"
+            "\t[-m <mode>] Data file mode for input / output file (default: 0)\n"
+            "\t\t 0 = Raw I/Q samples (uint8, 2 channel)\n"
+            "\t\t 1 = AM demodulated samples (int16 pcm, 1 channel)\n"
+            "\t\t 2 = FM demodulated samples (int16) (experimental)\n"
+            "\t\t 3 = Raw I/Q samples (cf32, 2 channel)\n"
+            "\t\t Note: If output file is specified, input will always be I/Q\n"
+            "\t[-F] kv|json|csv Produce decoded output in given format. Not yet supported by all drivers.\n"
+            "\t[-C] native|si|customary Convert units in decoded output.\n"
+            "\t[-T] specify number of seconds to run\n"
+            "\t[-U] Print timestamps in UTC (this may also be accomplished by invocation with TZ environment variable set).\n"
+            "\t[<filename>] Save data stream to output file (a '-' dumps samples to stdout)\n\n",
+            DEFAULT_FREQUENCY, DEFAULT_SAMPLE_RATE, DEFAULT_LEVEL_LIMIT);
+
+    fprintf(stderr, "Supported device protocols:\n");
+    for (i = 0; i < num_r_devices; i++) {
+       if (devices[i].disabled)
+           disabledc = '*';
+       else
+           disabledc = ' ';
+
+        fprintf(stderr, "    [%02d]%c %s\n", i + 1, disabledc, devices[i].name);
+    }
+    fprintf(stderr, "\n* Disabled by default, use -R n or -G\n");
+
     exit(1);
 }
 
@@ -191,108 +171,30 @@ static void sighandler(int signum) {
 }
 #endif
 
-/* precalculate lookup table for envelope detection */
-static void calc_squares() {
-    int i;
-    for (i = 0; i < 256; i++)
-        scaled_squares[i] = (128 - i) * (128 - i);
-}
-
-/** This will give a noisy envelope of OOK/ASK signals
- *  Subtract the bias (-128) and get an envelope estimation
- *  The output will be written in the input buffer
- *  @returns   pointer to the input buffer
- */
-
-static void envelope_detect(unsigned char *buf, uint32_t len, int decimate) {
-    uint16_t* sample_buffer = (uint16_t*) buf;
-    unsigned int i;
-    unsigned op = 0;
-    unsigned int stride = 1 << decimate;
-
-    for (i = 0; i < len / 2; i += stride) {
-        sample_buffer[op++] = scaled_squares[buf[2 * i ]] + scaled_squares[buf[2 * i + 1]];
-    }
-}
-
-static void demod_reset_bits_packet(struct protocol_state* p) {
-    memset(p->bits_buffer, 0, BITBUF_ROWS * BITBUF_COLS);
-    memset(p->bits_per_row, 0, BITBUF_ROWS);
-    p->bits_col_idx = 0;
-    p->bits_bit_col_idx = 7;
-    p->bits_row_idx = 0;
-    p->bit_rows = 0;
-}
-
-static void demod_add_bit(struct protocol_state* p, int bit) {
-    p->bits_buffer[p->bits_row_idx][p->bits_col_idx] |= bit << p->bits_bit_col_idx;
-    p->bits_bit_col_idx--;
-    if (p->bits_bit_col_idx < 0) {
-        p->bits_bit_col_idx = 7;
-        p->bits_col_idx++;
-        if (p->bits_col_idx > BITBUF_COLS - 1) {
-            p->bits_col_idx = BITBUF_COLS - 1;
-            //            fprintf(stderr, "p->bits_col_idx>%i!\n", BITBUF_COLS-1);
-        }
-    }
-    p->bits_per_row[p->bit_rows]++;
-}
-
-static void demod_next_bits_packet(struct protocol_state* p) {
-    p->bits_col_idx = 0;
-    p->bits_row_idx++;
-    p->bits_bit_col_idx = 7;
-    if (p->bits_row_idx > BITBUF_ROWS - 1) {
-        p->bits_row_idx = BITBUF_ROWS - 1;
-        //fprintf(stderr, "p->bits_row_idx>%i!\n", BITBUF_ROWS-1);
-    }
-    p->bit_rows++;
-    if (p->bit_rows > BITBUF_ROWS - 1)
-        p->bit_rows -= 1;
-}
-
-static void demod_print_bits_packet(struct protocol_state* p) {
-    int i, j, k;
-
-    fprintf(stderr, "\n");
-    for (i = 0; i < p->bit_rows + 1; i++) {
-        fprintf(stderr, "[%02d] {%d} ", i, p->bits_per_row[i]);
-        for (j = 0; j < ((p->bits_per_row[i] + 8) / 8); j++) {
-            fprintf(stderr, "%02x ", p->bits_buffer[i][j]);
-        }
-        fprintf(stderr, ": ");
-        for (j = 0; j < ((p->bits_per_row[i] + 8) / 8); j++) {
-            for (k = 7; k >= 0; k--) {
-                if (p->bits_buffer[i][j] & 1 << k)
-                    fprintf(stderr, "1");
-                else
-                    fprintf(stderr, "0");
-            }
-            //            fprintf(stderr, "=0x%x ",demod->bits_buffer[i][j]);
-            fprintf(stderr, " ");
-        }
-        fprintf(stderr, "\n");
-    }
-    fprintf(stderr, "\n");
-    return;
-}
 
 static void register_protocol(struct dm_state *demod, r_device *t_dev) {
     struct protocol_state *p = calloc(1, sizeof (struct protocol_state));
-    p->short_limit = (float) t_dev->short_limit / ((float) DEFAULT_SAMPLE_RATE / (float) samp_rate);
-    p->long_limit = (float) t_dev->long_limit / ((float) DEFAULT_SAMPLE_RATE / (float) samp_rate);
-    p->reset_limit = (float) t_dev->reset_limit / ((float) DEFAULT_SAMPLE_RATE / (float) samp_rate);
+    p->short_limit = (float) t_dev->short_limit / ((float) 1000000 / (float) samp_rate);
+    p->long_limit = (float) t_dev->long_limit / ((float) 1000000 / (float) samp_rate);
+    p->reset_limit = (float) t_dev->reset_limit / ((float) 1000000 / (float) samp_rate);
     p->modulation = t_dev->modulation;
     p->callback = t_dev->json_callback;
-    demod_reset_bits_packet(p);
+    p->name = t_dev->name;
+    p->demod_arg = t_dev->demod_arg;
+    bitbuffer_clear(&p->bits);
 
     demod->r_devs[demod->r_dev_num] = p;
     demod->r_dev_num++;
 
-    fprintf(stderr, "Registering protocol[%02d] %s\n", demod->r_dev_num, t_dev->name);
+    if (!quiet_mode) {
+       fprintf(stderr, "Registering protocol [%d] \"%s\"\n", demod->r_dev_num, t_dev->name);
+    }
 
-    if (demod->r_dev_num > MAX_PROTOCOLS)
-        fprintf(stderr, "Max number of protocols reached %d\n", MAX_PROTOCOLS);
+    if (demod->r_dev_num > MAX_PROTOCOLS) {
+        fprintf(stderr, "\n\nMax number of protocols reached %d\n", MAX_PROTOCOLS);
+       fprintf(stderr, "Increase MAX_PROTOCOLS and recompile\n");
+       exit(-1);
+    }
 }
 
 
@@ -310,6 +212,56 @@ static unsigned int signal_pulse_data[4000][3] = {
     {0}};
 static unsigned int signal_pulse_counter = 0;
 
+typedef struct output_handler {
+    /*data_printer_t*/ void *printer;
+    void (*aux_free)(void *aux);
+    FILE *file;
+    void *aux;
+    struct output_handler *next;
+} output_handler_t;
+static output_handler_t *output_handler = NULL;
+static output_handler_t **next_output_handler = &output_handler;
+
+/* handles incoming structured data by dumping it */
+void data_acquired_handler(data_t *data)
+{
+    if (conversion_mode == CONVERT_SI) {
+        for (data_t *d = data; d; d = d->next) {
+            if ((d->type == DATA_DOUBLE) &&
+                !strcmp(d->key, "temperature_F")) {
+                    *(double*)d->value = fahrenheit2celsius(*(double*)d->value);
+                                       free(d->key);
+                    d->key = strdup("temperature_C");
+                    char *pos;
+                    if (d->format &&
+                        (pos = strrchr(d->format, 'F'))) {
+                        *pos = 'C';
+                    }
+            }
+        }
+    }
+    if (conversion_mode == CONVERT_CUSTOMARY) {
+        for (data_t *d = data; d; d = d->next) {
+            if ((d->type == DATA_DOUBLE) &&
+                !strcmp(d->key, "temperature_C")) {
+                    *(double*)d->value = celsius2fahrenheit(*(double*)d->value);
+                                       free(d->key);
+                    d->key = strdup("temperature_F");
+                    char *pos;
+                    if (d->format &&
+                        (pos = strrchr(d->format, 'C'))) {
+                        *pos = 'F';
+                    }
+            }
+        }
+    }
+
+    for (output_handler_t *output = output_handler; output; output = output->next) {
+        data_print(data, output->file, output->printer, output->aux);
+    }
+    data_free(data);
+}
+
 static void classify_signal() {
     unsigned int i, k, max = 0, min = 1000000, t;
     unsigned int delta, count_min, count_max, min_new, max_new, p_limit;
@@ -477,56 +429,48 @@ static void classify_signal() {
         a[1] = override_long;
     }
 
-    if (a[1]<a[0]) {
-        a[1]=a[0]+1;
-    }
-                                  
-    if (a[2]<a[1]*2) {
-        a[2] = a[1]*2;
-    }
-                                                                     
     fprintf(stderr, "\nShort distance: %d, long distance: %d, packet distance: %d\n", a[0], a[1], a[2]);
     fprintf(stderr, "\np_limit: %d\n", p_limit);
 
-    demod_reset_bits_packet(&p);
+    bitbuffer_clear(&p.bits);
     if (signal_type == 1) {
         for (i = 0; i < 1000; i++) {
             if (signal_distance_data[i] > 0) {
                 if (signal_distance_data[i] < (a[0] + a[1]) / 2) {
                     //                     fprintf(stderr, "0 [%d] %d < %d\n",i, signal_distance_data[i], (a[0]+a[1])/2);
-                    demod_add_bit(&p, 0);
+                    bitbuffer_add_bit(&p.bits, 0);
                 } else if ((signal_distance_data[i] > (a[0] + a[1]) / 2) && (signal_distance_data[i] < (a[1] + a[2]) / 2)) {
                     //                     fprintf(stderr, "0 [%d] %d > %d\n",i, signal_distance_data[i], (a[0]+a[1])/2);
-                    demod_add_bit(&p, 1);
+                    bitbuffer_add_bit(&p.bits, 1);
                 } else if (signal_distance_data[i] > (a[1] + a[2]) / 2) {
                     //                     fprintf(stderr, "0 [%d] %d > %d\n",i, signal_distance_data[i], (a[1]+a[2])/2);
-                    demod_next_bits_packet(&p);
+                    bitbuffer_add_row(&p.bits);
                 }
 
             }
 
         }
-        demod_print_bits_packet(&p);
+        bitbuffer_print(&p.bits);
     }
     if (signal_type == 2) {
         for (i = 0; i < 1000; i++) {
             if (signal_pulse_data[i][2] > 0) {
                 if (signal_pulse_data[i][2] < p_limit) {
                     //                     fprintf(stderr, "0 [%d] %d < %d\n",i, signal_pulse_data[i][2], p_limit);
-                    demod_add_bit(&p, 0);
+                    bitbuffer_add_bit(&p.bits, 0);
                 } else {
                     //                     fprintf(stderr, "1 [%d] %d > %d\n",i, signal_pulse_data[i][2], p_limit);
-                    demod_add_bit(&p, 1);
+                    bitbuffer_add_bit(&p.bits, 1);
                 }
                 if ((signal_distance_data[i] >= (a[1] + a[2]) / 2)) {
                     //                     fprintf(stderr, "\\n [%d] %d > %d\n",i, signal_distance_data[i], (a[1]+a[2])/2);
-                    demod_next_bits_packet(&p);
+                    bitbuffer_add_row(&p.bits);
                 }
 
 
             }
         }
-        demod_print_bits_packet(&p);
+        bitbuffer_print(&p.bits);
     }
 
     for (i = 0; i < 1000; i++) {
@@ -540,9 +484,10 @@ static void classify_signal() {
 
 static void pwm_analyze(struct dm_state *demod, int16_t *buf, uint32_t len) {
     unsigned int i;
+    int32_t threshold = (demod->level_limit ? demod->level_limit : 8000);      // Does not support auto level. Use old default instead.
 
     for (i = 0; i < len; i++) {
-        if (buf[i] > demod->level_limit) {
+        if (buf[i] > threshold) {
             if (!signal_start)
                 signal_start = counter;
             if (print) {
@@ -560,7 +505,7 @@ static void pwm_analyze(struct dm_state *demod, int16_t *buf, uint32_t len) {
             }
         }
         counter++;
-        if (buf[i] < demod->level_limit) {
+        if (buf[i] < threshold) {
             if (print2) {
                 pulse_avg += counter - pulse_start;
                 if (debug_output) fprintf(stderr, "pulse_end  [%d] found at sample %d, pulse length = %d, pulse avg length = %d\n",
@@ -589,8 +534,14 @@ static void pwm_analyze(struct dm_state *demod, int16_t *buf, uint32_t len) {
                     char sgf_name[256] = {0};
                     FILE *sgfp;
 
-                    sprintf(sgf_name, "gfile%03d.data", demod->signal_grabber);
-                    demod->signal_grabber++;
+                   while (1) {
+                       sprintf(sgf_name, "gfile%03d.data", demod->signal_grabber);
+                       demod->signal_grabber++;
+                       if (access(sgf_name, F_OK) == -1 || overwrite_mode) {
+                           break;
+                       }
+                   }
+
                     signal_bszie = 2 * (signal_end - (signal_start - 10000));
                     signal_bszie = (131072 - (signal_bszie % 131072)) + signal_bszie;
                     sg_idx = demod->sg_index - demod->sg_len;
@@ -641,310 +592,285 @@ err:
     return;
 }
 
-/* The distance between pulses decodes into bits */
-
-static void pwm_d_decode(struct dm_state *demod, struct protocol_state* p, int16_t *buf, uint32_t len) {
-    unsigned int i;
-
-    for (i = 0; i < len; i++) {
-        if (buf[i] > demod->level_limit) {
-            p->pulse_count = 1;
-            p->start_c = 1;
-        }
-        if (p->pulse_count && (buf[i] < demod->level_limit)) {
-            p->pulse_length = 0;
-            p->pulse_distance = 1;
-            p->sample_counter = 0;
-            p->pulse_count = 0;
-        }
-        if (p->start_c) p->sample_counter++;
-        if (p->pulse_distance && (buf[i] > demod->level_limit)) {
-            if (p->sample_counter < p->short_limit) {
-                demod_add_bit(p, 0);
-            } else if (p->sample_counter < p->long_limit) {
-                demod_add_bit(p, 1);
-            } else {
-                demod_next_bits_packet(p);
-                p->pulse_count = 0;
-                p->sample_counter = 0;
-            }
-            p->pulse_distance = 0;
-        }
-        if (p->sample_counter > p->reset_limit) {
-            p->start_c = 0;
-            p->sample_counter = 0;
-            p->pulse_distance = 0;
-            if (p->callback)
-                events += p->callback(p->bits_buffer, p->bits_per_row);
-            else
-                demod_print_bits_packet(p);
 
-            demod_reset_bits_packet(p);
-        }
-    }
-}
-
-/* The length of pulses decodes into bits */
+static void rtlsdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx) {
+    struct dm_state *demod = ctx;
+    int i;
+    char time_str[LOCAL_TIME_BUFLEN];
 
-static void pwm_p_decode(struct dm_state *demod, struct protocol_state* p, int16_t *buf, uint32_t len) {
-    unsigned int i;
+       if (do_exit || do_exit_async)
+               return;
 
-    for (i = 0; i < len; i++) {
-        if (buf[i] > demod->level_limit && !p->start_bit) {
-            /* start bit detected */
-            p->start_bit = 1;
-            p->start_c = 1;
-            p->sample_counter = 0;
-            //            fprintf(stderr, "start bit pulse start detected\n");
-        }
+       if ((bytes_to_read > 0) && (bytes_to_read < len)) {
+               len = bytes_to_read;
+               do_exit = 1;
+               rtlsdr_cancel_async(dev);
+       }
 
-        if (!p->real_bits && p->start_bit && (buf[i] < demod->level_limit)) {
-            /* end of startbit */
-            p->real_bits = 1;
-            p->pulse_length = 0;
-            p->sample_counter = 0;
-            //            fprintf(stderr, "start bit pulse end detected\n");
-        }
-        if (p->start_c) p->sample_counter++;
+       if (demod->signal_grabber) {
+               //fprintf(stderr, "[%d] sg_index - len %d\n", demod->sg_index, len );
+               memcpy(&demod->sg_buf[demod->sg_index], iq_buf, len);
+               demod->sg_len = len;
+               demod->sg_index += len;
+               if (demod->sg_index + len > SIGNAL_GRABBER_BUFFER)
+                       demod->sg_index = 0;
+       }
 
+       // AM demodulation
+       envelope_detect(iq_buf, demod->temp_buf, len/2);
+       baseband_low_pass_filter(demod->temp_buf, demod->am_buf, len/2, &demod->lowpass_filter_state);
 
-        if (!p->pulse_start && p->real_bits && (buf[i] > demod->level_limit)) {
-            /* save the pulse start, it will never be zero */
-            p->pulse_start = p->sample_counter;
-            //           fprintf(stderr, "real bit pulse start detected\n");
+       // FM demodulation
+       if (demod->enable_FM_demod) {
+               baseband_demod_FM(iq_buf, demod->fm_buf, len/2, &demod->demod_FM_state);
+       }
 
-        }
+       // Handle special input formats
+       if(!demod->out_file) {                          // If output file is specified we always assume I/Q input
+               if (demod->debug_mode == 1) {   // The IQ buffer is really AM demodulated data
+                       memcpy(demod->am_buf, iq_buf, len);
+               } else if (demod->debug_mode == 2) {    // The IQ buffer is really FM demodulated data
+                       fprintf(stderr, "Reading FM modulated data not implemented yet!\n");
+               }
+       }
 
-        if (p->real_bits && p->pulse_start && (buf[i] < demod->level_limit)) {
-            /* end of pulse */
+       if (demod->analyze || (demod->out_file == stdout)) {    // We don't want to decode devices when outputting to stdout
+               pwm_analyze(demod, demod->am_buf, len / 2);
+       } else {
+               // Detect a package and loop through demodulators with pulse data
+               int package_type = 1;   // Just to get us started
+               while(package_type) {
+                       int p_events = 0;       // Sensor events successfully detected per package
+                       package_type = pulse_detect_package(demod->am_buf, demod->fm_buf, len/2, demod->level_limit, samp_rate, &demod->pulse_data, &demod->fsk_pulse_data);
+                       if (package_type == 1) {
+                               if(demod->analyze_pulses) fprintf(stderr, "Detected OOK package\t@ %s\n", local_time_str(0, time_str));
+                               for (i = 0; i < demod->r_dev_num; i++) {
+                                       switch (demod->r_devs[i]->modulation) {
+                                               case OOK_PULSE_PCM_RZ:
+                                                       p_events += pulse_demod_pcm(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case OOK_PULSE_PPM_RAW:
+                                                       p_events += pulse_demod_ppm(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case OOK_PULSE_PWM_PRECISE:
+                                                       p_events += pulse_demod_pwm_precise(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case OOK_PULSE_PWM_RAW:
+                                                       p_events += pulse_demod_pwm(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case OOK_PULSE_PWM_TERNARY:
+                                                       p_events += pulse_demod_pwm_ternary(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case OOK_PULSE_MANCHESTER_ZEROBIT:
+                                                       p_events += pulse_demod_manchester_zerobit(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case OOK_PULSE_CLOCK_BITS:
+                                                       p_events += pulse_demod_clock_bits(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case OOK_PULSE_PWM_OSV1:
+                                                       p_events += pulse_demod_osv1(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               // FSK decoders
+                                               case FSK_PULSE_PCM:
+                                               case FSK_PULSE_PWM_RAW:
+                                                       break;
+                                               case FSK_PULSE_MANCHESTER_ZEROBIT:
+                                                       p_events += pulse_demod_manchester_zerobit(&demod->pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               default:
+                                                       fprintf(stderr, "Unknown modulation %d in protocol!\n", demod->r_devs[i]->modulation);
+                                       }
+                               } // for demodulators
+                               if(debug_output > 1) pulse_data_print(&demod->pulse_data);
+                               if(demod->analyze_pulses && (include_only == 0 || (include_only == 1 && p_events == 0) || (include_only == 2 && p_events > 0)) ) { 
+                                       pulse_analyzer(&demod->pulse_data, samp_rate);
+                               }
+                       } else if (package_type == 2) {
+                               if(demod->analyze_pulses) fprintf(stderr, "Detected FSK package\t@ %s\n", local_time_str(0, time_str));
+                               for (i = 0; i < demod->r_dev_num; i++) {
+                                       switch (demod->r_devs[i]->modulation) {
+                                               // OOK decoders
+                                               case OOK_PULSE_PCM_RZ:
+                                               case OOK_PULSE_PPM_RAW:
+                                               case OOK_PULSE_PWM_PRECISE:
+                                               case OOK_PULSE_PWM_RAW:
+                                               case OOK_PULSE_PWM_TERNARY:
+                                               case OOK_PULSE_MANCHESTER_ZEROBIT:
+                                               case OOK_PULSE_CLOCK_BITS:
+                                               case OOK_PULSE_PWM_OSV1:
+                                                       break;
+                                               case FSK_PULSE_PCM:
+                                                       p_events += pulse_demod_pcm(&demod->fsk_pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case FSK_PULSE_PWM_RAW:
+                                                       p_events += pulse_demod_pwm(&demod->fsk_pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               case FSK_PULSE_MANCHESTER_ZEROBIT:
+                                                       p_events += pulse_demod_manchester_zerobit(&demod->fsk_pulse_data, demod->r_devs[i]);
+                                                       break;
+                                               default:
+                                                       fprintf(stderr, "Unknown modulation %d in protocol!\n", demod->r_devs[i]->modulation);
+                                       }
+                               } // for demodulators
+                               if(debug_output > 1) pulse_data_print(&demod->fsk_pulse_data);
+                               if(demod->analyze_pulses && (include_only == 0 || (include_only == 1 && p_events == 0) || (include_only == 2 && p_events > 0)) ) { 
+                                       pulse_analyzer(&demod->fsk_pulse_data, samp_rate);
+                               }
+                       } // if (package_type == ...
+               } // while(package_type)...
+       } // if (demod->analyze...
+
+       if (demod->out_file) {
+               uint8_t* out_buf = iq_buf;                              // Default is to dump IQ samples
+               if (demod->debug_mode == 1) {                   // AM data
+                       out_buf = (uint8_t*)demod->am_buf;
+               } else if (demod->debug_mode == 2) {    // FM data
+                       out_buf = (uint8_t*)demod->fm_buf;
+               }
+               if (fwrite(out_buf, 1, len, demod->out_file) != len) {
+                       fprintf(stderr, "Short write, samples lost, exiting!\n");
+                       rtlsdr_cancel_async(dev);
+               }
+       }
 
-            p->pulse_length = p->sample_counter - p->pulse_start;
-            //           fprintf(stderr, "real bit pulse end detected %d\n", p->pulse_length);
-            //           fprintf(stderr, "space duration %d\n", p->sample_counter);
+       if (bytes_to_read > 0)
+               bytes_to_read -= len;
+
+        time_t rawtime;
+        time(&rawtime);
+       if (frequencies > 1) {
+               if (difftime(rawtime, rawtime_old) > DEFAULT_HOP_TIME || events >= DEFAULT_HOP_EVENTS) {
+                       rawtime_old = rawtime;
+                       events = 0;
+                       do_exit_async = 1;
+                       rtlsdr_cancel_async(dev);
+               }
+       }
+    if (duration > 0 && rawtime >= stop_time) {
+      do_exit_async = do_exit = 1;
+      fprintf(stderr, "Time expired, exiting!\n");
+      rtlsdr_cancel_async(dev);
+    }
+}
 
-            if (p->pulse_length <= p->short_limit) {
-                demod_add_bit(p, 1);
-            } else if (p->pulse_length > p->short_limit) {
-                demod_add_bit(p, 0);
+// find the fields output for CSV
+void *determine_csv_fields(r_device* devices, int num_devices)
+{
+    int i, j;
+    int cur_output_fields = 0;
+    int num_output_fields = 0;
+    void *csv_aux;
+    const char **output_fields = NULL;
+    for (i = 0; i < num_devices; i++)
+        if (!devices[i].disabled) {
+           if (devices[i].fields)
+               for (int c = 0; devices[i].fields[c]; ++c)
+                   ++num_output_fields;
+           else
+               fprintf(stderr, "rtl_433: warning: %d \"%s\" does not support CSV output\n",
+                       i, devices[i].name);
+       }
+    output_fields = calloc(num_output_fields + 1, sizeof(char*));
+    for (i = 0; i < num_devices; i++) {
+        if (!devices[i].disabled && devices[i].fields) {
+            for (int c = 0; devices[i].fields[c]; ++c) {
+                output_fields[cur_output_fields] = devices[i].fields[c];
+                ++cur_output_fields;
             }
-            p->sample_counter = 0;
-            p->pulse_start = 0;
-        }
-
-        if (p->real_bits && (p->pulse_length > p->long_limit)) {
-            demod_next_bits_packet(p);
-
-            p->start_bit = 0;
-            p->real_bits = 0;
-        }
-
-        if (p->sample_counter > p->reset_limit) {
-            p->start_c = 0;
-            p->sample_counter = 0;
-            //demod_print_bits_packet(p);
-            if (p->callback)
-                events += p->callback(p->bits_buffer, p->bits_per_row);
-            else
-                demod_print_bits_packet(p);
-            demod_reset_bits_packet(p);
-
-            p->start_bit = 0;
-            p->real_bits = 0;
         }
     }
-}
 
-/*  Machester Decode for Oregon Scientific Weather Sensors
-   Decode data streams sent by Oregon Scientific v2.1, and v3 weather sensors.
-   With manchester encoding, both the pulse width and pulse distance vary.  Clock sync
-   is recovered from the data stream based on pulse widths and distances exceeding a
-   minimum threashold (short limit* 1.5).
- */
-static void manchester_decode(struct dm_state *demod, struct protocol_state* p, int16_t *buf, uint32_t len) {
-    unsigned int i;
-
-       if (p->sample_counter == 0)
-           p->sample_counter = p->short_limit*2;
-
-    for (i=0 ; i<len ; i++) {
-
-           if (p->start_c)
-                   p->sample_counter++; /* For this decode type, sample counter is count since last data bit recorded */
-
-        if (!p->pulse_count && (buf[i] > demod->level_limit)) { /* Pulse start (rising edge) */
-            p->pulse_count = 1;
-                       if (p->sample_counter  > (p->short_limit + (p->short_limit>>1))) {
-                          /* Last bit was recorded more than short_limit*1.5 samples ago */
-                          /* so this pulse start must be a data edge (rising data edge means bit = 0) */
-               demod_add_bit(p, 0);
-                          p->sample_counter=1;
-                          p->start_c++; // start_c counts number of bits received
-                       }
-        }
-        if (p->pulse_count && (buf[i] <= demod->level_limit)) { /* Pulse end (falling edge) */
-                   if (p->sample_counter > (p->short_limit + (p->short_limit>>1))) {
-                      /* Last bit was recorded more than "short_limit*1.5" samples ago */
-                          /* so this pulse end is a data edge (falling data edge means bit = 1) */
-               demod_add_bit(p, 1);
-                          p->sample_counter=1;
-                          p->start_c++;
-                       }
-            p->pulse_count = 0;
-        }
+    csv_aux = data_csv_init(output_fields, num_output_fields);
+    free(output_fields);
+    return csv_aux;
+}
 
-        if (p->sample_counter > p->reset_limit) {
-       //fprintf(stderr, "manchester_decode number of bits received=%d\n",p->start_c);
-                  if (p->callback)
-              events+=p->callback(p->bits_buffer, p->bits_per_row);
-           else
-              demod_print_bits_packet(p);
-                       demod_reset_bits_packet(p);
-               p->sample_counter = p->short_limit*2;
-                       p->start_c = 0;
-        }
+void add_json_output()
+{
+    output_handler_t *output = calloc(1, sizeof(output_handler_t));
+    if (!output) {
+        fprintf(stderr, "rtl_433: failed to allocate memory for output handler\n");
+        exit(1);
     }
+    output->printer = &data_json_printer;
+    output->file = stdout;
+    *next_output_handler = output;
+    next_output_handler = &output->next;
 }
 
-/** Something that might look like a IIR lowpass filter
- *
- *  [b,a] = butter(1, 0.01) ->  quantizes nicely thus suitable for fixed point
- *  Q1.15*Q15.0 = Q16.15
- *  Q16.15>>1 = Q15.14
- *  Q15.14 + Q15.14 + Q15.14 could possibly overflow to 17.14
- *  but the b coeffs are small so it wont happen
- *  Q15.14>>14 = Q15.0 \o/
- */
-
-static uint16_t lp_xmem[FILTER_ORDER] = {0};
-
-#define F_SCALE 15
-#define S_CONST (1<<F_SCALE)
-#define FIX(x) ((int)(x*S_CONST))
-
-int a[FILTER_ORDER + 1] = {FIX(1.00000), FIX(0.96907)};
-int b[FILTER_ORDER + 1] = {FIX(0.015466), FIX(0.015466)};
-
-static void low_pass_filter(uint16_t *x_buf, int16_t *y_buf, uint32_t len) {
-    unsigned int i;
-
-    /* Calculate first sample */
-    y_buf[0] = ((a[1] * y_buf[-1] >> 1) + (b[0] * x_buf[0] >> 1) + (b[1] * lp_xmem[0] >> 1)) >> (F_SCALE - 1);
-    for (i = 1; i < len; i++) {
-        y_buf[i] = ((a[1] * y_buf[i - 1] >> 1) + (b[0] * x_buf[i] >> 1) + (b[1] * x_buf[i - 1] >> 1)) >> (F_SCALE - 1);
+void add_csv_output(void *aux_data)
+{
+    if (!aux_data) {
+        fprintf(stderr, "rtl_433: failed to allocate memory for CSV auxiliary data\n");
+        exit(1);
     }
-
-    /* Save last sample */
-    memcpy(lp_xmem, &x_buf[len - 1 - FILTER_ORDER], FILTER_ORDER * sizeof (int16_t));
-    memcpy(&y_buf[-FILTER_ORDER], &y_buf[len - 1 - FILTER_ORDER], FILTER_ORDER * sizeof (int16_t));
-    //fprintf(stderr, "%d\n", y_buf[0]);
+    output_handler_t *output = calloc(1, sizeof(output_handler_t));
+    if (!output) {
+        fprintf(stderr, "rtl_433: failed to allocate memory for output handler\n");
+        exit(1);
+    }
+    output->printer = &data_csv_printer;
+    output->aux_free = &data_csv_free;
+    output->file = stdout;
+    output->aux = aux_data;
+    *next_output_handler = output;
+    next_output_handler = &output->next;
 }
 
-static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) {
-    struct dm_state *demod = ctx;
-    uint16_t* sbuf = (uint16_t*) buf;
-    int i;
-    if (demod->file || !demod->save_data) {
-        if (do_exit || do_exit_async)
-            return;
-
-        if ((bytes_to_read > 0) && (bytes_to_read < len)) {
-            len = bytes_to_read;
-            do_exit = 1;
-            rtlsdr_cancel_async(dev);
-        }
-
-        if (demod->signal_grabber) {
-            //fprintf(stderr, "[%d] sg_index - len %d\n", demod->sg_index, len );
-            memcpy(&demod->sg_buf[demod->sg_index], buf, len);
-            demod->sg_len = len;
-            demod->sg_index += len;
-            if (demod->sg_index + len > SIGNAL_GRABBER_BUFFER)
-                demod->sg_index = 0;
-        }
-
-
-        if (demod->debug_mode == 0) {
-            envelope_detect(buf, len, demod->decimation_level);
-            low_pass_filter(sbuf, demod->f_buf, len >> (demod->decimation_level + 1));
-        } else if (demod->debug_mode == 1) {
-            memcpy(demod->f_buf, buf, len);
-        }
-        if (demod->analyze) {
-            pwm_analyze(demod, demod->f_buf, len / 2);
-        } else {
-            for (i = 0; i < demod->r_dev_num; i++) {
-                switch (demod->r_devs[i]->modulation) {
-                    case OOK_PWM_D:
-                        pwm_d_decode(demod, demod->r_devs[i], demod->f_buf, len / 2);
-                        break;
-                    case OOK_PWM_P:
-                        pwm_p_decode(demod, demod->r_devs[i], demod->f_buf, len / 2);
-                        break;
-                    case OOK_MANCHESTER:
-                        manchester_decode(demod, demod->r_devs[i], demod->f_buf, len/2);
-                        break;
-                    default:
-                        fprintf(stderr, "Unknown modulation %d in protocol!\n", demod->r_devs[i]->modulation);
-                }
-            }
-            fflush(stdout);
-        }
-
-        if (demod->save_data) {
-            if (fwrite(demod->f_buf, 1, len >> demod->decimation_level, demod->file) != len >> demod->decimation_level) {
-                fprintf(stderr, "Short write, samples lost, exiting!\n");
-                rtlsdr_cancel_async(dev);
-            }
-        }
-
-        if (bytes_to_read > 0)
-            bytes_to_read -= len;
-
-        if (frequencies > 1) {
-            time_t rawtime;
-            time(&rawtime);
-            if (difftime(rawtime, rawtime_old) > DEFAULT_HOP_TIME || events >= DEFAULT_HOP_EVENTS) {
-                rawtime_old = rawtime;
-                events = 0;
-                do_exit_async = 1;
-                rtlsdr_cancel_async(dev);
-            }
-        }
+void add_kv_output()
+{
+    output_handler_t *output = calloc(1, sizeof(output_handler_t));
+    if (!output) {
+        fprintf(stderr, "rtl_433: failed to allocate memory for output handler\n");
+        exit(1);
     }
+    output->printer = &data_kv_printer;
+    output->file = stdout;
+    *next_output_handler = output;
+    next_output_handler = &output->next;
 }
 
 int main(int argc, char **argv) {
 #ifndef _WIN32
     struct sigaction sigact;
 #endif
-    char *filename = NULL;
-    char *test_mode_file = NULL;
-    FILE *test_mode;
+    char *out_filename = NULL;
+    char *in_filename = NULL;
+    FILE *in_file;
     int n_read;
-    int r, opt;
+    int r = 0, opt;
     int i, gain = 0;
     int sync_mode = 0;
     int ppm_error = 0;
     struct dm_state* demod;
-    uint8_t *buffer;
     uint32_t dev_index = 0;
     int frequency_current = 0;
     uint32_t out_block_size = DEFAULT_BUF_LENGTH;
     int device_count;
     char vendor[256], product[256], serial[256];
+    int have_opt_R = 0;
+    int register_all = 0;
+
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
 
     demod = malloc(sizeof (struct dm_state));
     memset(demod, 0, sizeof (struct dm_state));
 
     /* initialize tables */
-    calc_squares();
+    baseband_init();
 
-    demod->f_buf = &demod->filter_buffer[FILTER_ORDER];
-    demod->decimation_level = DEFAULT_DECIMATION_LEVEL;
-    demod->level_limit = DEFAULT_LEVEL_LIMIT;
+       r_device devices[] = {
+#define DECL(name) name,
+                       DEVICES
+#undef DECL
+                       };
 
+    num_r_devices = sizeof(devices)/sizeof(*devices);
+
+    demod->level_limit = DEFAULT_LEVEL_LIMIT;
 
-    while ((opt = getopt(argc, argv, "x:z:p:Dtam:r:c:l:d:f:g:s:b:n:S::")) != -1) {
+    while ((opt = getopt(argc, argv, "x:z:p:DtaAI:qm:r:l:d:f:g:s:b:n:SR:F:C:T:UWG")) != -1) {
         switch (opt) {
             case 'd':
                 dev_index = atoi(optarg);
@@ -956,6 +882,9 @@ int main(int argc, char **argv) {
             case 'g':
                 gain = (int) (atof(optarg) * 10); /* tenths of a dB */
                 break;
+            case 'G':
+                register_all = 1;
+                break;
             case 'p':
                 ppm_error = atoi(optarg);
                 break;
@@ -971,14 +900,17 @@ int main(int argc, char **argv) {
             case 'n':
                 bytes_to_read = (uint32_t) atof(optarg) * 2;
                 break;
-            case 'c':
-                demod->decimation_level = (uint32_t) atof(optarg);
-                break;
             case 'a':
                 demod->analyze = 1;
                 break;
+            case 'A':
+                demod->analyze_pulses = 1;
+                break;
+            case 'I':
+                include_only = atoi(optarg);
+                break;
             case 'r':
-                test_mode_file = optarg;
+                in_filename = optarg;
                 break;
             case 't':
                 demod->signal_grabber = 1;
@@ -990,7 +922,7 @@ int main(int argc, char **argv) {
                 sync_mode = 1;
                 break;
             case 'D':
-                debug_output = 1;
+                debug_output++;
                 break;
             case 'z':
                 override_short = atoi(optarg);
@@ -998,40 +930,96 @@ int main(int argc, char **argv) {
             case 'x':
                 override_long = atoi(optarg);
                 break;
+            case 'R':
+                if (!have_opt_R) {
+                    for (i = 0; i < num_r_devices; i++) {
+                        devices[i].disabled = 1;
+                    }
+                    have_opt_R = 1;
+                }
+
+                i = atoi(optarg);
+                if (i > num_r_devices) {
+                    fprintf(stderr, "Remote device number specified larger than number of devices\n\n");
+                    usage(devices);
+                }
+
+                devices[i - 1].disabled = 0;
+                break;
+           case 'q':
+               quiet_mode = 1;
+               break;
+           case 'F':
+               if (strcmp(optarg, "json") == 0) {
+            add_json_output();
+               } else if (strcmp(optarg, "csv") == 0) {
+            add_csv_output(determine_csv_fields(devices, num_r_devices));
+               } else if (strcmp(optarg, "kv") == 0) {
+            add_kv_output();
+               } else {
+                    fprintf(stderr, "Invalid output format %s\n", optarg);
+                    usage(devices);
+               }
+               break;
+        case 'C':
+        if (strcmp(optarg, "native") == 0) {
+            conversion_mode = CONVERT_NATIVE;
+        } else if (strcmp(optarg, "si") == 0) {
+            conversion_mode = CONVERT_SI;
+        } else if (strcmp(optarg, "customary") == 0) {
+            conversion_mode = CONVERT_CUSTOMARY;
+        } else {
+                    fprintf(stderr, "Invalid conversion mode %s\n", optarg);
+                    usage(devices);
+        }
+        break;
+        case 'U':
+        #if !defined(__MINGW32__)
+          utc_mode = setenv("TZ", "UTC", 1);
+          if(utc_mode != 0) fprintf(stderr, "Unable to set TZ to UTC; error code: %d\n", utc_mode);
+        #endif
+        break;
+            case 'W':
+            overwrite_mode = 1;
+        break;
+        case 'T':
+          time(&stop_time);
+          duration = atoi(optarg);
+          if (duration < 1) {
+            fprintf(stderr, "Duration '%s' was not positive integer; will continue indefinitely\n", optarg);
+          } else {
+            stop_time += duration;
+          }
+          break;
             default:
-                usage();
+                usage(devices);
                 break;
         }
     }
 
-    /* init protocols somewhat ok */
-    register_protocol(demod, &rubicson);
-    register_protocol(demod, &prologue);
-    register_protocol(demod, &silvercrest);
-    //    register_protocol(demod, &generic_hx2262);
-    //    register_protocol(demod, &technoline_ws9118);
-    register_protocol(demod, &elv_em1000);
-    register_protocol(demod, &elv_ws2000);
-    register_protocol(demod, &waveman);
-    register_protocol(demod, &steffen);
-    register_protocol(demod, &acurite5n1);
-    register_protocol(demod, &acurite_th);
-    register_protocol(demod, &acurite_rain_gauge);
-    register_protocol(demod, &lacrossetx);
-    register_protocol(demod, &oregon_scientific);
-    register_protocol(demod, &newkaku);
-    register_protocol(demod, &alectov1);
-    register_protocol(demod, &intertechno);
-    register_protocol(demod, &mebus433);
-    register_protocol(demod, &wh2);
-    register_protocol(demod, &leak);
-
     if (argc <= optind - 1) {
-        usage();
+        usage(devices);
     } else {
-        filename = argv[optind];
+        out_filename = argv[optind];
+    }
+
+    if (!output_handler) {
+        add_kv_output();
     }
 
+    for (i = 0; i < num_r_devices; i++) {
+        if (!devices[i].disabled || register_all) {
+            register_protocol(demod, &devices[i]);
+            if(devices[i].modulation >= FSK_DEMOD_MIN_VAL) {
+              demod->enable_FM_demod = 1;
+            }
+        }
+    }
+
+    if (!quiet_mode)
+       fprintf(stderr,"Registered %d out of %d device decoding protocols\n",
+               demod->r_dev_num, num_r_devices);
+
     if (out_block_size < MINIMAL_BUF_LENGTH ||
             out_block_size > MAXIMAL_BUF_LENGTH) {
         fprintf(stderr,
@@ -1043,113 +1031,148 @@ int main(int argc, char **argv) {
         out_block_size = DEFAULT_BUF_LENGTH;
     }
 
-    buffer = malloc(out_block_size * sizeof (uint8_t));
-
-    device_count = rtlsdr_get_device_count();
-    if (!device_count) {
-        fprintf(stderr, "No supported devices found.\n");
-        if (!test_mode_file)
-            exit(1);
-    }
+    if (!in_filename) {
+       device_count = rtlsdr_get_device_count();
+       if (!device_count) {
+           fprintf(stderr, "No supported devices found.\n");
+           if (!in_filename)
+               exit(1);
+       }
 
-    fprintf(stderr, "Found %d device(s):\n", device_count);
-    for (i = 0; i < device_count; i++) {
-        rtlsdr_get_device_usb_strings(i, vendor, product, serial);
-        fprintf(stderr, "  %d:  %s, %s, SN: %s\n", i, vendor, product, serial);
-    }
-    fprintf(stderr, "\n");
+       if (!quiet_mode) {
+           fprintf(stderr, "Found %d device(s):\n", device_count);
+           for (i = 0; i < device_count; i++) {
+               rtlsdr_get_device_usb_strings(i, vendor, product, serial);
+               fprintf(stderr, "  %d:  %s, %s, SN: %s\n", i, vendor, product, serial);
+           }
+           fprintf(stderr, "\n");
 
-    fprintf(stderr, "Using device %d: %s\n",
-            dev_index, rtlsdr_get_device_name(dev_index));
+           fprintf(stderr, "Using device %d: %s\n",
+                   dev_index, rtlsdr_get_device_name(dev_index));
+       }
 
-    r = rtlsdr_open(&dev, dev_index);
-    if (r < 0) {
-        fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index);
-        if (!test_mode_file)
-            exit(1);
-    }
+       r = rtlsdr_open(&dev, dev_index);
+       if (r < 0) {
+           fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index);
+           exit(1);
+       }
 #ifndef _WIN32
-    sigact.sa_handler = sighandler;
-    sigemptyset(&sigact.sa_mask);
-    sigact.sa_flags = 0;
-    sigaction(SIGINT, &sigact, NULL);
-    sigaction(SIGTERM, &sigact, NULL);
-    sigaction(SIGQUIT, &sigact, NULL);
-    sigaction(SIGPIPE, &sigact, NULL);
+       sigact.sa_handler = sighandler;
+       sigemptyset(&sigact.sa_mask);
+       sigact.sa_flags = 0;
+       sigaction(SIGINT, &sigact, NULL);
+       sigaction(SIGTERM, &sigact, NULL);
+       sigaction(SIGQUIT, &sigact, NULL);
+       sigaction(SIGPIPE, &sigact, NULL);
 #else
-    SetConsoleCtrlHandler((PHANDLER_ROUTINE) sighandler, TRUE);
+       SetConsoleCtrlHandler((PHANDLER_ROUTINE) sighandler, TRUE);
 #endif
-    /* Set the sample rate */
-    r = rtlsdr_set_sample_rate(dev, samp_rate);
-    if (r < 0)
-        fprintf(stderr, "WARNING: Failed to set sample rate.\n");
-    else
-        fprintf(stderr, "Sample rate set to %d.\n", rtlsdr_get_sample_rate(dev)); // Unfortunately, doesn't return real rate
-
-    fprintf(stderr, "Sample rate decimation set to %d. %d->%d\n", demod->decimation_level, samp_rate, samp_rate >> demod->decimation_level);
-    fprintf(stderr, "Bit detection level set to %d.\n", demod->level_limit);
-
-    if (0 == gain) {
-        /* Enable automatic gain */
-        r = rtlsdr_set_tuner_gain_mode(dev, 0);
-        if (r < 0)
-            fprintf(stderr, "WARNING: Failed to enable automatic gain.\n");
-        else
-            fprintf(stderr, "Tuner gain set to Auto.\n");
-    } else {
-        /* Enable manual gain */
-        r = rtlsdr_set_tuner_gain_mode(dev, 1);
-        if (r < 0)
-            fprintf(stderr, "WARNING: Failed to enable manual gain.\n");
-
-        /* Set the tuner gain */
-        r = rtlsdr_set_tuner_gain(dev, gain);
-        if (r < 0)
-            fprintf(stderr, "WARNING: Failed to set tuner gain.\n");
-        else
-            fprintf(stderr, "Tuner gain set to %f dB.\n", gain / 10.0);
-    }
+       /* Set the sample rate */
+       r = rtlsdr_set_sample_rate(dev, samp_rate);
+       if (r < 0)
+           fprintf(stderr, "WARNING: Failed to set sample rate.\n");
+       else
+           fprintf(stderr, "Sample rate set to %d.\n", rtlsdr_get_sample_rate(dev)); // Unfortunately, doesn't return real rate
+
+       fprintf(stderr, "Bit detection level set to %d%s.\n", demod->level_limit, (demod->level_limit ? "" : " (Auto)"));
+
+       if (0 == gain) {
+           /* Enable automatic gain */
+           r = rtlsdr_set_tuner_gain_mode(dev, 0);
+           if (r < 0)
+               fprintf(stderr, "WARNING: Failed to enable automatic gain.\n");
+           else
+               fprintf(stderr, "Tuner gain set to Auto.\n");
+       } else {
+           /* Enable manual gain */
+           r = rtlsdr_set_tuner_gain_mode(dev, 1);
+           if (r < 0)
+               fprintf(stderr, "WARNING: Failed to enable manual gain.\n");
+
+           /* Set the tuner gain */
+           r = rtlsdr_set_tuner_gain(dev, gain);
+           if (r < 0)
+               fprintf(stderr, "WARNING: Failed to set tuner gain.\n");
+           else
+               fprintf(stderr, "Tuner gain set to %f dB.\n", gain / 10.0);
+       }
+
+       r = rtlsdr_set_freq_correction(dev, ppm_error);
 
-    r = rtlsdr_set_freq_correction(dev, ppm_error);
+    }
 
-    demod->save_data = 1;
-    if (!filename) {
-        demod->save_data = 0;
-    } else if (strcmp(filename, "-") == 0) { /* Write samples to stdout */
-        demod->file = stdout;
+       if (out_filename) {
+               if (strcmp(out_filename, "-") == 0) { /* Write samples to stdout */
+                       demod->out_file = stdout;
 #ifdef _WIN32
-        _setmode(_fileno(stdin), _O_BINARY);
+                       _setmode(_fileno(stdin), _O_BINARY);
 #endif
-    } else {
-        demod->file = fopen(filename, "wb");
-        if (!demod->file) {
-            fprintf(stderr, "Failed to open %s\n", filename);
-            goto out;
-        }
-    }
+               } else {
+                       if (access(out_filename, F_OK) == 0 && !overwrite_mode) {
+                           fprintf(stderr, "Output file %s already exists, exiting\n", out_filename);
+                           goto out;
+                       }
+                       demod->out_file = fopen(out_filename, "wb");
+                       if (!demod->out_file) {
+                               fprintf(stderr, "Failed to open %s\n", out_filename);
+                               goto out;
+                       }
+               }
+       }
 
     if (demod->signal_grabber)
         demod->sg_buf = malloc(SIGNAL_GRABBER_BUFFER);
 
-    if (test_mode_file) {
+    if (in_filename) {
         int i = 0;
         unsigned char test_mode_buf[DEFAULT_BUF_LENGTH];
-        fprintf(stderr, "Test mode active. Reading samples from file: %s\n", test_mode_file);
-        test_mode = fopen(test_mode_file, "r");
-        if (!test_mode) {
-            fprintf(stderr, "Opening file: %s failed!\n", test_mode_file);
-            goto out;
-        }
-        while (fread(test_mode_buf, 131072, 1, test_mode) != 0) {
-            rtlsdr_callback(test_mode_buf, 131072, demod);
+        float test_mode_float_buf[DEFAULT_BUF_LENGTH];
+       if (strcmp(in_filename, "-") == 0) { /* read samples from stdin */
+           in_file = stdin;
+           in_filename = "<stdin>";
+       } else {
+           in_file = fopen(in_filename, "rb");
+           if (!in_file) {
+               fprintf(stderr, "Opening file: %s failed!\n", in_filename);
+               goto out;
+           }
+       }
+       fprintf(stderr, "Test mode active. Reading samples from file: %s\n", in_filename);      // Essential information (not quiet)
+       if (!quiet_mode) {
+           fprintf(stderr, "Input format: %s\n", (demod->debug_mode == 3) ? "cf32" : "uint8");
+       }
+       sample_file_pos = 0.0;
+
+        int n_read, cf32_tmp;
+        do {
+           if (demod->debug_mode == 3) {
+               n_read = fread(test_mode_float_buf, sizeof(float), 131072, in_file);
+               for(int n = 0; n < n_read; n++) {
+                   cf32_tmp = test_mode_float_buf[n]*127 + 127;
+                       if (cf32_tmp < 0)
+                           cf32_tmp = 0;
+                       else if (cf32_tmp > 255)
+                           cf32_tmp = 255;
+                       test_mode_buf[n] = (uint8_t)cf32_tmp;
+               }
+            } else {
+                n_read = fread(test_mode_buf, 1, 131072, in_file);
+            }
+            if (n_read == 0) break;    // rtlsdr_callback() will Segmentation Fault with len=0
+            rtlsdr_callback(test_mode_buf, n_read, demod);
             i++;
-        }
+           sample_file_pos = (float)i * n_read / samp_rate;
+        } while (n_read != 0);
+
+        // Call a last time with cleared samples to ensure EOP detection
+        memset(test_mode_buf, 128, DEFAULT_BUF_LENGTH);     // 128 is 0 in unsigned data
+        rtlsdr_callback(test_mode_buf, 131072, demod);      // Why the magic value 131072?
+
         //Always classify a signal at the end of the file
         classify_signal();
-        fprintf(stderr, "Test mode file issued %d packets\n", i);
-        fprintf(stderr, "Filter coeffs used:\n");
-        fprintf(stderr, "a: %d %d\n", a[0], a[1]);
-        fprintf(stderr, "b: %d %d\n", b[0], b[1]);
+       if (!quiet_mode) {
+           fprintf(stderr, "Test mode file issued %d packets\n", i);
+       }
         exit(0);
     }
 
@@ -1159,7 +1182,15 @@ int main(int argc, char **argv) {
         fprintf(stderr, "WARNING: Failed to reset buffers.\n");
 
     if (sync_mode) {
-        fprintf(stderr, "Reading samples in sync mode...\n");
+        if (!demod->out_file) {
+            fprintf(stderr, "Specify an output file for sync mode.\n");
+            exit(0);
+        }
+
+       fprintf(stderr, "Reading samples in sync mode...\n");
+       uint8_t *buffer = malloc(out_block_size * sizeof (uint8_t));
+
+      time_t timestamp;
         while (!do_exit) {
             r = rtlsdr_read_sync(dev, buffer, out_block_size, &n_read);
             if (r < 0) {
@@ -1172,7 +1203,7 @@ int main(int argc, char **argv) {
                 do_exit = 1;
             }
 
-            if (fwrite(buffer, 1, n_read, demod->file) != (size_t) n_read) {
+            if (fwrite(buffer, 1, n_read, demod->out_file) != (size_t) n_read) {
                 fprintf(stderr, "Short write, samples lost, exiting!\n");
                 break;
             }
@@ -1182,9 +1213,19 @@ int main(int argc, char **argv) {
                 break;
             }
 
+        if (duration > 0) {
+          time(&timestamp);
+          if (timestamp >= stop_time) {
+            do_exit = 1;
+            fprintf(stderr, "Time expired, exiting!\n");
+          }
+        }
+
             if (bytes_to_read > 0)
                 bytes_to_read -= n_read;
         }
+
+       free(buffer);
     } else {
         if (frequencies == 0) {
             frequency[0] = DEFAULT_FREQUENCY;
@@ -1192,7 +1233,9 @@ int main(int argc, char **argv) {
         } else {
             time(&rawtime_old);
         }
-        fprintf(stderr, "Reading samples in async mode...\n");
+       if (!quiet_mode) {
+           fprintf(stderr, "Reading samples in async mode...\n");
+       }
         while (!do_exit) {
             /* Set the frequency */
             r = rtlsdr_set_center_freq(dev, frequency[frequency_current]);
@@ -1213,8 +1256,8 @@ int main(int argc, char **argv) {
     else
         fprintf(stderr, "\nLibrary error %d, exiting...\n", r);
 
-    if (demod->file && (demod->file != stdout))
-        fclose(demod->file);
+    if (demod->out_file && (demod->out_file != stdout))
+        fclose(demod->out_file);
 
     for (i = 0; i < demod->r_dev_num; i++)
         free(demod->r_devs[i]);
@@ -1222,11 +1265,14 @@ int main(int argc, char **argv) {
     if (demod->signal_grabber)
         free(demod->sg_buf);
 
-    if (demod)
-        free(demod);
+    free(demod);
 
     rtlsdr_close(dev);
-    free(buffer);
 out:
+    for (output_handler_t *output = output_handler; output; output = output->next) {
+        if (output->aux_free) {
+            output->aux_free(output->aux);
+        }
+    }
     return r >= 0 ? r : -r;
 }