X-Git-Url: https://git.rvb.name/rtl-433.git/blobdiff_plain/25b624169a19b22b938f5b80651e8f6cd7da092f..refs/heads/master:/src/rtl_433.c?ds=sidebyside diff --git a/src/rtl_433.c b/src/rtl_433.c index bee0e4d..945b532 100755 --- a/src/rtl_433.c +++ b/src/rtl_433.c @@ -20,761 +20,59 @@ * along with this program. If not, see . */ - -/* Currently this can decode the temperature and id from Rubicson sensors - * - * the sensor sends 36 bits 12 times pwm modulated - * the data is grouped into 9 nibles - * [id0] [id1], [unk0] [temp0], [temp1] [temp2], [unk1] [unk2], [unk3] - * - * The id changes when the battery is changed in the sensor. - * unk0 is always 1 0 0 0, most likely 2 channel bits as the sensor can recevice 3 channels - * unk1-3 changes and the meaning is unknown - * temp is 12 bit signed scaled by 10 - * - * The sensor can be bought at Kjell&Co - */ - -/* Prologue sensor protocol - * - * the sensor sends 36 bits 7 times, before the first packet there is a pulse sent - * the packets are pwm modulated - * - * the data is grouped in 9 nibles - * [id0] [rid0] [rid1] [data0] [temp0] [temp1] [temp2] [humi0] [humi1] - * - * id0 is always 1001,9 - * rid is a random id that is generated when the sensor starts, could include battery status - * the same batteries often generate the same id - * data(3) is 0 the battery status, 1 ok, 0 low, first reading always say low - * data(2) is 1 when the sensor sends a reading when pressing the button on the sensor - * data(1,0)+1 forms the channel number that can be set by the sensor (1-3) - * temp is 12 bit signed scaled by 10 - * humi0 is always 1100,c if no humidity sensor is available - * humi1 is always 1100,c if no humidity sensor is available - * - * The sensor can be bought at Clas Ohlson - */ - -#include -#include -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#else -#include -#include -#include -#include "getopt/getopt.h" -#endif +#include #include "rtl-sdr.h" +#include "rtl_433.h" +#include "baseband.h" +#include "pulse_detect.h" +#include "pulse_demod.h" +#include "data.h" +#include "util.h" -#define DEFAULT_SAMPLE_RATE 250000 -#define DEFAULT_FREQUENCY 433920000 -#define DEFAULT_HOP_TIME (60*10) -#define DEFAULT_HOP_EVENTS 2 -#define DEFAULT_ASYNC_BUF_NUMBER 32 -#define DEFAULT_BUF_LENGTH (16 * 16384) -#define DEFAULT_LEVEL_LIMIT 10000 -#define DEFAULT_DECIMATION_LEVEL 0 -#define MINIMAL_BUF_LENGTH 512 -#define MAXIMAL_BUF_LENGTH (256 * 16384) -#define FILTER_ORDER 1 -#define MAX_PROTOCOLS 10 -#define SIGNAL_GRABBER_BUFFER (12 * DEFAULT_BUF_LENGTH) -#define BITBUF_COLS 34 -#define BITBUF_ROWS 50 static int do_exit = 0; -static int do_exit_async=0, frequencies=0, events=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; +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 debug_output = 0; 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; -/* Supported modulation types */ -#define OOK_PWM_D 1 /* Pulses are of the same length, the distance varies */ -#define OOK_PWM_P 2 /* The length of the pulses varies */ - - -typedef struct { - unsigned int id; - char name[256]; - unsigned int modulation; - unsigned int short_limit; - unsigned int long_limit; - unsigned int reset_limit; - int (*json_callback)(uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS]) ; -} r_device; - -static int debug_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - int i,j,k; - fprintf(stderr, "\n"); - for (i=0 ; i=0 ; k--) { - if (bb[i][j] & 1<>= j; - fprintf(stderr,"%u", byte); - } - fprintf(stderr," "); - } - fprintf(stderr,"\n"); -} - -static int wh2_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - int i,j,k; - - uint8_t payload[4]; - int received_crc8,payload_crc8; - - int wh2_id; - float wh2_temp; - float wh2_humidity; - - if (bb[0][0] != 0xFE) return 0; - - payload[0] = bb[0][1]>>1; - payload[1] = bb[0][2]>>1 | ((bb[0][1]&1) << 7 ); - payload[2] = bb[0][3]>>1 | ((bb[0][2]&1) << 7 ); - payload[3] = bb[0][4]>>1 | ((bb[0][3]&1) << 7 ); - - received_crc8 = (bb[0][5]>>1) | ((bb[0][4]&1) << 7 ); - - payload_crc8 = crc8(payload,4); - - if (payload_crc8 != received_crc8) { - fprintf(stderr,"Bad WH2 payload CRC, skipping...\n"); - return 0; - } - - wh2_id = (payload[0] << 4) + (payload[1] >> 4); - wh2_temp = ((payload[1] & 0x7) << 8) + payload[2]; - if (payload[1] & 0x8) { - wh2_temp = -wh2_temp; - } - wh2_temp = wh2_temp/10; - - wh2_humidity = payload[3]; - - fprintf(stdout, "SENSOR:TYPE=WH2,ID=%X,HUMIDITY=%g,TEMPERATURE=%g\n",wh2_id,wh2_humidity,wh2_temp); - - return 1; - -} - -static int silvercrest_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - /* FIXME validate the received message better */ - if (bb[1][0] == 0xF8 && - bb[2][0] == 0xF8 && - bb[3][0] == 0xF8 && - bb[4][0] == 0xF8 && - bb[1][1] == 0x4d && - bb[2][1] == 0x4d && - bb[3][1] == 0x4d && - bb[4][1] == 0x4d) { - /* Pretty sure this is a Silvercrest remote */ - fprintf(stdout, "BUTTON:TYPE=SILVERCREST,CODE=%02x-%02x-%02x-%02x-%02x\n",bb[1][0],bb[0][1],bb[0][2],bb[0][3],bb[0][4]); - - if (debug_output) - debug_callback(bb); - - return 1; - } - return 0; -} - -static int rubicson_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - int temperature_before_dec; - int temperature_after_dec; - int16_t temp; - float ftemp; - - /* FIXME validate the received message better, figure out crc */ - if (bb[1][0] == bb[2][0] && bb[2][0] == bb[3][0] && bb[3][0] == bb[4][0] && - bb[4][0] == bb[5][0] && bb[5][0] == bb[6][0] && bb[6][0] == bb[7][0] && bb[7][0] == bb[8][0] && - bb[8][0] == bb[9][0] && (bb[5][0] != 0 && bb[5][1] != 0 && bb[5][2] != 0)) { - - /* Nible 3,4,5 contains 12 bits of temperature - * The temperature is signed and scaled by 10 */ - temp = (int16_t)((uint16_t)(bb[0][1] << 12) | (bb[0][2] << 4)); - temp = temp >> 4; - ftemp = (float)temp/10; - - fprintf(stdout, "SENSOR:TYPE=RUBICSON,ID=%X,TEMPERATURE=%f\n",bb[0][0],ftemp); - - if (debug_output) - debug_callback(bb); - - return 1; - } - return 0; -} - -static int prologue_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - int rid; - - int16_t temp2; - float ftemp; - - /* FIXME validate the received message better */ - if (((bb[1][0]&0xF0) == 0x90 && (bb[2][0]&0xF0) == 0x90 && (bb[3][0]&0xF0) == 0x90 && (bb[4][0]&0xF0) == 0x90 && - (bb[5][0]&0xF0) == 0x90 && (bb[6][0]&0xF0) == 0x90) || - ((bb[1][0]&0xF0) == 0x50 && (bb[2][0]&0xF0) == 0x50 && (bb[3][0]&0xF0) == 0x50 && (bb[4][0]&0xF0) == 0x50)) { - - /* Prologue sensor */ - temp2 = (int16_t)((uint16_t)(bb[1][2] << 8) | (bb[1][3]&0xF0)); - temp2 = temp2 >> 4; - ftemp = (float)temp2/10; - rid = ((bb[1][0]&0x0F)<<4)|(bb[1][1]&0xF0)>>4; - fprintf(stdout, - "SENSOR:TYPE=PROLOGUE,BUTTON=%d,BATTERY=%s,TEMPERATURE=%f,HUMIDITY=%d,CHANNEL=%d,ID=%d,RID=%02x\n", - bb[1][1]&0x04?1:0, - bb[1][1]&0x08?"Ok":"Low", - ftemp, - ((bb[1][3]&0x0F)<<4)|(bb[1][4]>>4), - (int)((bb[1][1]&0x03)+1), - (int)((bb[1][0]&0xF0)>>4), - rid); - - if (debug_output) - debug_callback(bb); - - return 1; - } - return 0; -} - -static int waveman_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - /* Two bits map to 2 states, 0 1 -> 0 and 1 1 -> 1 */ - int i; - uint8_t nb[3] = {0}; - - if (((bb[0][0]&0x55)==0x55) && ((bb[0][1]&0x55)==0x55) && ((bb[0][2]&0x55)==0x55) && ((bb[0][3]&0x55)==0x00)) { - for (i=0 ; i<3 ; i++) { - nb[i] |= ((bb[0][i]&0xC0)==0xC0) ? 0x00 : 0x01; - nb[i] |= ((bb[0][i]&0x30)==0x30) ? 0x00 : 0x02; - nb[i] |= ((bb[0][i]&0x0C)==0x0C) ? 0x00 : 0x04; - nb[i] |= ((bb[0][i]&0x03)==0x03) ? 0x00 : 0x08; - } - - fprintf(stdout, - "BUTTON:TYPE=WAVEMAN,ID=%c,CHANNEL=%d,BUTTON=%d,STATE=%s\n", - 'A'+nb[0], - (int)((nb[1]>>2)+1), - (int)((nb[1]&3)+1), - ((nb[2]==0xe) ? "on" : "off")); - - if (debug_output) - debug_callback(bb); - - return 1; - } - return 0; -} - -static int steffen_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - - if (bb[0][0]==0x00 && ((bb[1][0]&0x07)==0x07) && bb[1][0]==bb[2][0] && bb[2][0]==bb[3][0]) { - - fprintf(stdout, "BUTTON:TYPE=STEFFAN,CODE=%d%d%d%d%d,",(bb[1][0]&0x80)>>7, (bb[1][0]&0x40)>>6, (bb[1][0]&0x20)>>5, (bb[1][0]&0x10)>>4, (bb[1][0]&0x08)>>3); - - if ((bb[1][2]&0x0f)==0x0e) - fprintf(stdout, "BUTTON=A,"); - else if ((bb[1][2]&0x0f)==0x0d) - fprintf(stdout, "BUTTON=B,"); - else if ((bb[1][2]&0x0f)==0x0b) - fprintf(stdout, "BUTTON=C,"); - else if ((bb[1][2]&0x0f)==0x07) - fprintf(stdout, "BUTTON=D,"); - else if ((bb[1][2]&0x0f)==0x0f) - fprintf(stdout, "BUTTON=ALL,"); - else - fprintf(stdout, "BUTTON=UNKNOWN,"); - - if ((bb[1][2]&0xf0)==0xf0) { - fprintf(stdout, "STATE=OFF\n"); - } else { - fprintf(stdout, "STATE=ON\n"); - } - - if (debug_output) - debug_callback(bb); - - return 1; - } - return 0; -} - - -uint16_t AD_POP(uint8_t bb[BITBUF_COLS], uint8_t bits, uint8_t bit) { - uint16_t val = 0; - uint8_t i, byte_no, bit_no; - for (i=0;i=1&&dec[0]<=3?types[dec[0]-1]:"?",dec[1],dec[2],dec[3]|dec[4]<<8,dec[5]|dec[6]<<8,dec[7]|dec[8]<<8); - - return 1; -} - -static int ws2000_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - // based on http://www.dc3yc.privat.t-online.de/protocol.htm - uint8_t dec[13]; - uint8_t nibbles=0; - uint8_t bit=11; // preamble - char* types[]={"!AS3", "AS2000/ASH2000/S2000/S2001A/S2001IA/ASH2200/S300IA", "!S2000R", "!S2000W", "S2001I/S2001ID", "!S2500H", "!Pyrano", "!KS200/KS300"}; - uint8_t check_calculated=0, sum_calculated=0; - uint8_t i; - uint8_t stopbit; - uint8_t sum_received; - - dec[0] = AD_POP (bb[0], 4, bit); bit+=4; - stopbit= AD_POP (bb[0], 1, bit); bit+=1; - if (!stopbit) { -//fprintf(stderr, "!stopbit\n"); - return 0; - } - check_calculated ^= dec[0]; - sum_calculated += dec[0]; - - // read nibbles with stopbit ... - for (i = 1; i <= (dec[0]==4?12:8); i++) { - dec[i] = AD_POP (bb[0], 4, bit); bit+=4; - stopbit= AD_POP (bb[0], 1, bit); bit+=1; - if (!stopbit) { -//fprintf(stderr, "!stopbit %i\n", i); - return 0; - } - check_calculated ^= dec[i]; - sum_calculated += dec[i]; - nibbles++; - } - - if (check_calculated) { -//fprintf(stderr, "check_calculated (%d) != 0\n", check_calculated); - return 0; - } - - // Read sum - sum_received = AD_POP (bb[0], 4, bit); bit+=4; - sum_calculated+=5; - sum_calculated&=0xF; - if (sum_received != sum_calculated) { -//fprintf(stderr, "sum_received (%d) != sum_calculated (%d) ", sum_received, sum_calculated); - return 0; - } - -//for (i = 0; i < nibbles; i++) fprintf(stderr, "%02X ", dec[i]); fprintf(stderr, "\n"); - - fprintf(stdout, "SENSOR:TYPE=ELV-WS-2000,MODEL=%s,CODE=%d,TEMPERATURE=%s%d.%d,HUMIDITY=%d.%d",dec[0]<=7?types[dec[0]]:"?",dec[1]&7,dec[1]&8?"-":"",dec[4]*10+dec[3],dec[2],dec[7]*10+dec[6], dec[5]); - if(dec[0]==4) { - fprintf(stdout, "PRESSURE=%d\n", 200+dec[10]*100+dec[9]*10+dec[8]); - } else { - fprintf(stdout, "\n"); - } - - return 1; -} - -// ** Acurite 5n1 functions ** - -const float acurite_winddirections[] = - { 337.5, 315.0, 292.5, 270.0, 247.5, 225.0, 202.5, 180, - 157.5, 135.0, 112.5, 90.0, 67.5, 45.0, 22.5, 0.0 }; - -static int acurite_raincounter = 0; - -static int acurite_crc(uint8_t row[BITBUF_COLS], int cols) { - // sum of first n-1 bytes modulo 256 should equal nth byte - int i; - int sum = 0; - for ( i=0; i < cols; i++) - sum += row[i]; - if ( sum % 256 == row[cols] ) - return 1; - else - return 0; -} - -static int acurite_detect(uint8_t *pRow) { - int i; - if ( pRow[0] != 0x00 ) { - // invert bits due to wierd issue - for (i = 0; i < 8; i++) - pRow[i] = ~pRow[i] & 0xFF; - pRow[0] |= pRow[8]; // fix first byte that has mashed leading bit - - if (acurite_crc(pRow, 7)) - return 1; // passes crc check - } - return 0; -} - -static float acurite_getTemp (uint8_t highbyte, uint8_t lowbyte) { - // range -40 to 158 F - int highbits = (highbyte & 0x0F) << 7 ; - int lowbits = lowbyte & 0x7F; - int rawtemp = highbits | lowbits; - float temp = (rawtemp - 400) / 10.0; - return temp; -} - -static int acurite_getWindSpeed (uint8_t highbyte, uint8_t lowbyte) { - // range: 0 to 159 kph - int highbits = ( highbyte & 0x1F) << 3; - int lowbits = ( lowbyte & 0x70 ) >> 4; - int speed = highbits | lowbits; - return speed; -} - -static float acurite_getWindDirection (uint8_t byte) { - // 16 compass points, ccw from (NNW) to 15 (N) - int direction = byte & 0x0F; - return acurite_winddirections[direction]; -} - -static int acurite_getHumidity (uint8_t byte) { - // range: 1 to 99 %RH - int humidity = byte & 0x7F; - return humidity; -} - -static int acurite_getRainfallCounter (uint8_t highbyte, uint8_t lowbyte) { - // range: 0 to 99.99 in, 0.01 in incr., rolling counter? - int highbits = (highbyte & 0x3F) << 7 ; - int lowbits = lowbyte & 0x7F; - int raincounter = highbits | lowbits; - return raincounter; -} - -static int acurite5n1_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS]) { - // acurite 5n1 weather sensor decoding for rtl_433 - // Jens Jensen 2014 - int i; - uint8_t *buf = NULL; - // run through rows til we find one with good crc (brute force) - for (i=0; i < BITBUF_ROWS; i++) { - if (acurite_detect(bb[i])) { - buf = bb[i]; - break; // done - } - } - - if (buf) { - // decode packet here - fprintf(stdout, "SENSOR:TYPE=ACURITE"); - if (debug_output) { - for (i=0; i < 8; i++) - fprintf(stderr, "%02X ", buf[i]); - fprintf(stderr, "CRC OK\n"); - } - - if ((buf[2] & 0x0F) == 1) { - // wind speed, wind direction, rainfall - - float rainfall = 0.00; - int raincounter = 0; - if (acurite_raincounter > 0) { - // track rainfall difference after first run - raincounter = acurite_getRainfallCounter(buf[5], buf[6]); - rainfall = ( raincounter - acurite_raincounter ) * 0.01; - } else { - // capture starting counter - acurite_raincounter = raincounter; - } - - fprintf(stdout, ",WINDSPEED=%d", - acurite_getWindSpeed(buf[3], buf[4])); - fprintf(stdout, ",WINDDIRECTION=%0.1f", - acurite_getWindDirection(buf[4])); - fprintf(stdout, ",RAINGAUGE: %0.2f\n", rainfall); - - } else if ((buf[2] & 0x0F) == 8) { - // wind speed, temp, RH - fprintf(stdout, ",WINDSPEED=%d", - acurite_getWindSpeed(buf[3], buf[4])); - fprintf(stdout, ",TEMPERATURE=%2.1f", - acurite_getTemp(buf[4], buf[5])); - fprintf(stdout, ",HUMIDITY=%d\n", - acurite_getHumidity(buf[6])); - } else { - fprintf(stdout,",DATA=UNKNOWN"); - } - } - //if (debug_output) - // debug_callback(bb); - return 1; -} - - -// timings based on samp_rate=1024000 -r_device rubicson = { - /* .id = */ 1, - /* .name = */ "Rubicson Temperature Sensor", - /* .modulation = */ OOK_PWM_D, - /* .short_limit = */ 1744/4, - /* .long_limit = */ 3500/4, - /* .reset_limit = */ 5000/4, - /* .json_callback = */ &rubicson_callback, -}; - -r_device prologue = { - /* .id = */ 2, - /* .name = */ "Prologue Temperature Sensor", - /* .modulation = */ OOK_PWM_D, - /* .short_limit = */ 3500/4, - /* .long_limit = */ 7000/4, - /* .reset_limit = */ 15000/4, - /* .json_callback = */ &prologue_callback, -}; - -r_device silvercrest = { - /* .id = */ 3, - /* .name = */ "Silvercrest Remote Control", - /* .modulation = */ OOK_PWM_P, - /* .short_limit = */ 600/4, - /* .long_limit = */ 5000/4, - /* .reset_limit = */ 15000/4, - /* .json_callback = */ &silvercrest_callback, -}; - -r_device tech_line_fws_500 = { - /* .id = */ 4, - /* .name = */ "Tech Line FWS-500 Sensor", - /* .modulation = */ OOK_PWM_D, - /* .short_limit = */ 3500/4, - /* .long_limit = */ 7000/4, - /* .reset_limit = */ 15000/4, - /* .json_callback = */ &rubicson_callback, -}; - -r_device generic_hx2262 = { - /* .id = */ 5, - /* .name = */ "Window/Door sensor", - /* .modulation = */ OOK_PWM_P, - /* .short_limit = */ 1300/4, - /* .long_limit = */ 10000/4, - /* .reset_limit = */ 40000/4, - /* .json_callback = */ &silvercrest_callback, -}; - -r_device technoline_ws9118 = { - /* .id = */ 6, - /* .name = */ "Technoline WS9118", - /* .modulation = */ OOK_PWM_D, - /* .short_limit = */ 1800/4, - /* .long_limit = */ 3500/4, - /* .reset_limit = */ 15000/4, - /* .json_callback = */ &debug_callback, -}; - - -r_device elv_em1000 = { - /* .id = */ 7, - /* .name = */ "ELV EM 1000", - /* .modulation = */ OOK_PWM_D, - /* .short_limit = */ 750/4, - /* .long_limit = */ 7250/4, - /* .reset_limit = */ 30000/4, - /* .json_callback = */ &em1000_callback, -}; - -r_device elv_ws2000 = { - /* .id = */ 8, - /* .name = */ "ELV WS 2000", - /* .modulation = */ OOK_PWM_D, - /* .short_limit = */ (602+(1155-602)/2)/4, - /* .long_limit = */ ((1755635-1655517)/2)/4, // no repetitions - /* .reset_limit = */ ((1755635-1655517)*2)/4, - /* .json_callback = */ &ws2000_callback, -}; - -r_device waveman = { - /* .id = */ 6, - /* .name = */ "Waveman Switch Transmitter", - /* .modulation = */ OOK_PWM_P, - /* .short_limit = */ 1000/4, - /* .long_limit = */ 8000/4, - /* .reset_limit = */ 30000/4, - /* .json_callback = */ &waveman_callback, -}; - -r_device steffen = { - /* .id = */ 9, - /* .name = */ "Steffen Switch Transmitter", - /* .modulation = */ OOK_PWM_D, - /* .short_limit = */ 140, - /* .long_limit = */ 270, - /* .reset_limit = */ 1500, - /* .json_callback = */ &steffen_callback, -}; - -r_device acurite5n1 = { - /* .id = */ 10, - /* .name = */ "Acurite 5n1 Weather Station", - /* .modulation = */ OOK_PWM_P, - /* .short_limit = */ 75, - /* .long_limit = */ 220, - /* .reset_limit = */ 20000, - /* .json_callback = */ &acurite5n1_callback, -}; - -r_device wh2 = { - /* .id = */ 11, - /* .name = */ "WH2 Weather Station", - /* .modulation = */ OOK_PWM_P, - /* .short_limit = */ 150, - /* .long_limit = */ 400, - /* .reset_limit = */ 20000, - /* .json_callback = */ &wh2_callback, -}; - -struct protocol_state { - int (*callback)(uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS]); - - /* 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; - - -}; +typedef enum { + CONVERT_NATIVE, + CONVERT_SI, + CONVERT_CUSTOMARY +} conversion_mode_t; +static conversion_mode_t conversion_mode = CONVERT_NATIVE; +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 */ @@ -788,35 +86,71 @@ 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, - "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); +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= Tuner options =\n" + "\t[-d ] (default: 0)\n" + "\t[-g ] (default: 0 for auto)\n" + "\t[-f ] [-f...] Receive frequency(s) (default: %i Hz)\n" + "\t[-p ] Set sample rate (default: %i Hz)\n" + "\t[-S] Force sync output (default: async)\n" + "\t= Demodulator options =\n" + "\t[-R ] 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 ] Change detection level used to determine pulses [0-16384] (0 = auto) (default: %i)\n" + "\t[-z ] Override short value in data decoder\n" + "\t[-x ] Override long value in data decoder\n" + "\t[-n ] 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 ] Read data from input file instead of a receiver\n" + "\t[-m ] 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[] 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); } #ifdef _WIN32 BOOL WINAPI -sighandler(int signum) -{ +sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; @@ -826,117 +160,41 @@ sighandler(int signum) return FALSE; } #else -static void sighandler(int signum) -{ - fprintf(stderr, "Signal caught, exiting!\n"); +static void sighandler(int signum) { + if (signum == SIGPIPE) { + signal(SIGPIPE,SIG_IGN); + } else { + fprintf(stderr, "Signal caught, exiting!\n"); + } do_exit = 1; rtlsdr_cancel_async(dev); } #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<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<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 ; ibit_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<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->modulation = t_dev->modulation; - p->callback = t_dev->json_callback; - demod_reset_bits_packet(p); + struct protocol_state *p = calloc(1, sizeof (struct protocol_state)); + 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; + 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); + } } @@ -949,13 +207,63 @@ static unsigned int pulse_start = 0; static unsigned int pulse_end = 0; static unsigned int pulse_avg = 0; static unsigned int signal_start = 0; -static unsigned int signal_end = 0; -static unsigned int signal_pulse_data[4000][3] = {{0}}; +static unsigned int signal_end = 0; +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 i, k, max = 0, min = 1000000, t; unsigned int delta, count_min, count_max, min_new, max_new, p_limit; unsigned int a[3], b[2], a_cnt[3], a_new[3], b_new[2]; unsigned int signal_distance_data[4000] = {0}; @@ -965,7 +273,7 @@ static void classify_signal() { if (!signal_pulse_data[0][0]) return; - for (i=0 ; i<1000 ; i++) { + for (i = 0; i < 1000; i++) { if (signal_pulse_data[i][0] > 0) { //fprintf(stderr, "[%03d] s: %d\t e:\t %d\t l:%d\n", //i, signal_pulse_data[i][0], signal_pulse_data[i][1], @@ -976,96 +284,99 @@ static void classify_signal() { min = signal_pulse_data[i][2]; } } - t=(max+min)/2; + t = (max + min) / 2; //fprintf(stderr, "\n\nMax: %d, Min: %d t:%d\n", max, min, t); - delta = (max - min)*(max-min); + delta = (max - min)*(max - min); //TODO use Lloyd-Max quantizer instead - k=1; - while((k < 10) && (delta > 0)) { - min_new = 0; count_min = 0; - max_new = 0; count_max = 0; - - for (i=0 ; i < 1000 ; i++) { + k = 1; + while ((k < 10) && (delta > 0)) { + min_new = 0; + count_min = 0; + max_new = 0; + count_max = 0; + + for (i = 0; i < 1000; i++) { if (signal_pulse_data[i][0] > 0) { if (signal_pulse_data[i][2] < t) { min_new = min_new + signal_pulse_data[i][2]; count_min++; - } - else { + } else { max_new = max_new + signal_pulse_data[i][2]; count_max++; } } } - min_new = min_new / count_min; - max_new = max_new / count_max; + if (count_min != 0 && count_max != 0) { + min_new = min_new / count_min; + max_new = max_new / count_max; + } delta = (min - min_new)*(min - min_new) + (max - max_new)*(max - max_new); min = min_new; max = max_new; - t = (min + max)/2; + t = (min + max) / 2; - fprintf(stderr, "Iteration %d. t: %d min: %d (%d) max: %d (%d) delta %d\n", k,t, min, count_min, max, count_max, delta); + fprintf(stderr, "Iteration %d. t: %d min: %d (%d) max: %d (%d) delta %d\n", k, t, min, count_min, max, count_max, delta); k++; } - for (i=0 ; i<1000 ; i++) { + for (i = 0; i < 1000; i++) { if (signal_pulse_data[i][0] > 0) { //fprintf(stderr, "%d\n", signal_pulse_data[i][1]); } } /* 50% decision limit */ - if (max/min > 1) { + if (min != 0 && max / min > 1) { fprintf(stderr, "Pulse coding: Short pulse length %d - Long pulse length %d\n", min, max); signal_type = 2; } else { - fprintf(stderr, "Distance coding: Pulse length %d\n", (min+max)/2); + fprintf(stderr, "Distance coding: Pulse length %d\n", (min + max) / 2); signal_type = 1; } - p_limit = (max+min)/2; + p_limit = (max + min) / 2; /* Initial guesses */ a[0] = 1000000; a[2] = 0; - for (i=1 ; i<1000 ; i++) { + for (i = 1; i < 1000; i++) { if (signal_pulse_data[i][0] > 0) { -// fprintf(stderr, "[%03d] s: %d\t e:\t %d\t l:%d\t d:%d\n", -// i, signal_pulse_data[i][0], signal_pulse_data[i][1], -// signal_pulse_data[i][2], signal_pulse_data[i][0]-signal_pulse_data[i-1][1]); - signal_distance_data[i-1] = signal_pulse_data[i][0]-signal_pulse_data[i-1][1]; - if (signal_distance_data[i-1] > a[2]) - a[2] = signal_distance_data[i-1]; - if (signal_distance_data[i-1] <= a[0]) - a[0] = signal_distance_data[i-1]; + // fprintf(stderr, "[%03d] s: %d\t e:\t %d\t l:%d\t d:%d\n", + // i, signal_pulse_data[i][0], signal_pulse_data[i][1], + // signal_pulse_data[i][2], signal_pulse_data[i][0]-signal_pulse_data[i-1][1]); + signal_distance_data[i - 1] = signal_pulse_data[i][0] - signal_pulse_data[i - 1][1]; + if (signal_distance_data[i - 1] > a[2]) + a[2] = signal_distance_data[i - 1]; + if (signal_distance_data[i - 1] <= a[0]) + a[0] = signal_distance_data[i - 1]; } } min = a[0]; max = a[2]; - a[1] = (a[0]+a[2])/2; -// for (i=0 ; i<1 ; i++) { -// b[i] = (a[i]+a[i+1])/2; -// } - b[0] = (a[0]+a[1])/2; - b[1] = (a[1]+a[2])/2; -// fprintf(stderr, "a[0]: %d\t a[1]: %d\t a[2]: %d\t\n",a[0],a[1],a[2]); -// fprintf(stderr, "b[0]: %d\t b[1]: %d\n",b[0],b[1]); - - k=1; + a[1] = (a[0] + a[2]) / 2; + // for (i=0 ; i<1 ; i++) { + // b[i] = (a[i]+a[i+1])/2; + // } + b[0] = (a[0] + a[1]) / 2; + b[1] = (a[1] + a[2]) / 2; + // fprintf(stderr, "a[0]: %d\t a[1]: %d\t a[2]: %d\t\n",a[0],a[1],a[2]); + // fprintf(stderr, "b[0]: %d\t b[1]: %d\n",b[0],b[1]); + + k = 1; delta = 10000000; - while((k < 10) && (delta > 0)) { - for (i=0 ; i<3 ; i++) { + while ((k < 10) && (delta > 0)) { + for (i = 0; i < 3; i++) { a_new[i] = 0; a_cnt[i] = 0; } - for (i=0 ; i < 1000 ; i++) { + for (i = 0; i < 1000; i++) { if (signal_distance_data[i] > 0) { if (signal_distance_data[i] < b[0]) { a_new[0] += signal_distance_data[i]; a_cnt[0]++; - } else if (signal_distance_data[i] < b[1] && signal_distance_data[i] >= b[0]){ + } else if (signal_distance_data[i] < b[1] && signal_distance_data[i] >= b[0]) { a_new[1] += signal_distance_data[i]; a_cnt[1]++; } else if (signal_distance_data[i] >= b[1]) { @@ -1075,37 +386,37 @@ static void classify_signal() { } } -// fprintf(stderr, "Iteration %d.", k); + // fprintf(stderr, "Iteration %d.", k); delta = 0; - for (i=0 ; i<3 ; i++) { + for (i = 0; i < 3; i++) { if (a_cnt[i]) a_new[i] /= a_cnt[i]; - delta += (a[i]-a_new[i])*(a[i]-a_new[i]); -// fprintf(stderr, "\ta[%d]: %d (%d)", i, a_new[i], a[i]); + delta += (a[i] - a_new[i])*(a[i] - a_new[i]); + // fprintf(stderr, "\ta[%d]: %d (%d)", i, a_new[i], a[i]); a[i] = a_new[i]; } -// fprintf(stderr, " delta %d\n", delta); + // fprintf(stderr, " delta %d\n", delta); if (a[0] < min) { a[0] = min; -// fprintf(stderr, "Fixing a[0] = %d\n", min); + // fprintf(stderr, "Fixing a[0] = %d\n", min); } if (a[2] > max) { a[0] = max; -// fprintf(stderr, "Fixing a[2] = %d\n", max); + // fprintf(stderr, "Fixing a[2] = %d\n", max); } -// if (a[1] == 0) { -// a[1] = (a[2]+a[0])/2; -// fprintf(stderr, "Fixing a[1] = %d\n", a[1]); -// } - -// fprintf(stderr, "Iteration %d.", k); - for (i=0 ; i<2 ; i++) { -// fprintf(stderr, "\tb[%d]: (%d) ", i, b[i]); - b[i] = (a[i]+a[i+1])/2; -// fprintf(stderr, "%d ", b[i]); + // if (a[1] == 0) { + // a[1] = (a[2]+a[0])/2; + // fprintf(stderr, "Fixing a[1] = %d\n", a[1]); + // } + + // fprintf(stderr, "Iteration %d.", k); + for (i = 0; i < 2; i++) { + // fprintf(stderr, "\tb[%d]: (%d) ", i, b[i]); + b[i] = (a[i] + a[i + 1]) / 2; + // fprintf(stderr, "%d ", b[i]); } -// fprintf(stderr, "\n"); + // fprintf(stderr, "\n"); k++; } @@ -1118,59 +429,51 @@ static void classify_signal() { a[1] = override_long; } - if (a[1] 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); - } 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); - } 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); + 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); + 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); + 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); + 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) { + 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); + // fprintf(stderr, "0 [%d] %d < %d\n",i, signal_pulse_data[i][2], p_limit); + 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); + // fprintf(stderr, "1 [%d] %d > %d\n",i, signal_pulse_data[i][2], p_limit); + 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); + 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); + bitbuffer_add_row(&p.bits); } } } - demod_print_bits_packet(&p); + bitbuffer_print(&p.bits); } - for (i=0 ; i<1000 ; i++) { + for (i = 0; i < 1000; i++) { signal_pulse_data[i][0] = 0; signal_pulse_data[i][1] = 0; signal_pulse_data[i][2] = 0; @@ -1179,13 +482,12 @@ static void classify_signal() { }; - -static void pwm_analyze(struct dm_state *demod, int16_t *buf, uint32_t len) -{ +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 demod->level_limit) { + for (i = 0; i < len; i++) { + if (buf[i] > threshold) { if (!signal_start) signal_start = counter; if (print) { @@ -1194,24 +496,24 @@ static void pwm_analyze(struct dm_state *demod, int16_t *buf, uint32_t len) signal_pulse_data[signal_pulse_counter][0] = counter; signal_pulse_data[signal_pulse_counter][1] = -1; signal_pulse_data[signal_pulse_counter][2] = -1; - if (debug_output) fprintf(stderr, "pulse_distance %d\n",counter-pulse_end); - if (debug_output) fprintf(stderr, "pulse_start distance %d\n",pulse_start-prev_pulse_start); - if (debug_output) fprintf(stderr, "pulse_start[%d] found at sample %d, value = %d\n",pulses_found, counter, buf[i]); + if (debug_output) fprintf(stderr, "pulse_distance %d\n", counter - pulse_end); + if (debug_output) fprintf(stderr, "pulse_start distance %d\n", pulse_start - prev_pulse_start); + if (debug_output) fprintf(stderr, "pulse_start[%d] found at sample %d, value = %d\n", pulses_found, counter, buf[i]); prev_pulse_start = pulse_start; - print =0; + print = 0; print2 = 1; } } counter++; - if (buf[i] < demod->level_limit) { + if (buf[i] < threshold) { if (print2) { - pulse_avg += counter-pulse_start; + 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", - pulses_found, counter, counter-pulse_start, pulse_avg/pulses_found); + pulses_found, counter, counter - pulse_start, pulse_avg / pulses_found); pulse_end = counter; print2 = 0; signal_pulse_data[signal_pulse_counter][1] = counter; - signal_pulse_data[signal_pulse_counter][2] = counter-pulse_start; + signal_pulse_data[signal_pulse_counter][2] = counter - pulse_start; signal_pulse_counter++; if (signal_pulse_counter >= 4000) { signal_pulse_counter = 0; @@ -1221,37 +523,43 @@ static void pwm_analyze(struct dm_state *demod, int16_t *buf, uint32_t len) print = 1; if (signal_start && (pulse_end + 50000 < counter)) { signal_end = counter - 40000; - fprintf(stderr, "*** signal_start = %d, signal_end = %d\n",signal_start-10000, signal_end); - fprintf(stderr, "signal_len = %d, pulses = %d\n", signal_end-(signal_start-10000), pulses_found); + fprintf(stderr, "*** signal_start = %d, signal_end = %d\n", signal_start - 10000, signal_end); + fprintf(stderr, "signal_len = %d, pulses = %d\n", signal_end - (signal_start - 10000), pulses_found); pulses_found = 0; classify_signal(); signal_pulse_counter = 0; if (demod->sg_buf) { - int start_pos, signal_bszie, wlen, wrest=0, sg_idx, idx; + int start_pos, signal_bszie, wlen, wrest = 0, sg_idx, idx; char sgf_name[256] = {0}; FILE *sgfp; - sprintf(sgf_name, "gfile%03d.data",demod->signal_grabber); - demod->signal_grabber++; - signal_bszie = 2*(signal_end-(signal_start-10000)); - signal_bszie = (131072-(signal_bszie%131072)) + signal_bszie; - sg_idx = demod->sg_index-demod->sg_len; + 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; if (sg_idx < 0) - sg_idx = SIGNAL_GRABBER_BUFFER-demod->sg_len; - idx = (i-40000)*2; - start_pos = sg_idx+idx-signal_bszie; + sg_idx = SIGNAL_GRABBER_BUFFER - demod->sg_len; + idx = (i - 40000)*2; + start_pos = sg_idx + idx - signal_bszie; fprintf(stderr, "signal_bszie = %d - sg_index = %d\n", signal_bszie, demod->sg_index); fprintf(stderr, "start_pos = %d - buffer_size = %d\n", start_pos, SIGNAL_GRABBER_BUFFER); if (signal_bszie > SIGNAL_GRABBER_BUFFER) fprintf(stderr, "Signal bigger then buffer, signal = %d > buffer %d !!\n", signal_bszie, SIGNAL_GRABBER_BUFFER); if (start_pos < 0) { - start_pos = SIGNAL_GRABBER_BUFFER+start_pos; + start_pos = SIGNAL_GRABBER_BUFFER + start_pos; fprintf(stderr, "restart_pos = %d\n", start_pos); } - fprintf(stderr, "*** Saving signal to file %s\n",sgf_name); + fprintf(stderr, "*** Saving signal to file %s\n", sgf_name); sgfp = fopen(sgf_name, "wb"); if (!sgfp) { fprintf(stderr, "Failed to open %s\n", sgf_name); @@ -1261,12 +569,12 @@ static void pwm_analyze(struct dm_state *demod, int16_t *buf, uint32_t len) wlen = SIGNAL_GRABBER_BUFFER - start_pos; wrest = signal_bszie - wlen; } - fprintf(stderr, "*** Writing data from %d, len %d\n",start_pos, wlen); + fprintf(stderr, "*** Writing data from %d, len %d\n", start_pos, wlen); fwrite(&demod->sg_buf[start_pos], 1, wlen, sgfp); if (wrest) { - fprintf(stderr, "*** Writing data from %d, len %d\n",0, wrest); - fwrite(&demod->sg_buf[0], 1, wrest, sgfp); + fprintf(stderr, "*** Writing data from %d, len %d\n", 0, wrest); + fwrite(&demod->sg_buf[0], 1, wrest, sgfp); } fclose(sgfp); @@ -1284,468 +592,587 @@ 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 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); - else - demod_print_bits_packet(p); - demod_reset_bits_packet(p); - } +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]; + + 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], 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); + + // 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 (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); + } + } + + 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); } } -/* The length of pulses decodes into bits */ - -static void pwm_p_decode(struct dm_state *demod, struct protocol_state* p, int16_t *buf, uint32_t len) { - unsigned int i; - - for (i=0 ; i demod->level_limit && !p->start_bit) { - /* start bit detected */ - p->start_bit = 1; - p->start_c = 1; - p->sample_counter = 0; - if (debug_output) - fprintf(stderr, "start bit pulse start detected\n"); - } - - if (!p->real_bits && p->start_bit && (buf[i] < demod->level_limit)) { - /* end of startbit */ - p->real_bits = 1; - p->pulse_length = 0; - if (debug_output) - fprintf(stderr, "start bit pulse end detected\n"); - } - if (p->start_c) p->sample_counter++; - - - 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; - if (debug_output) - fprintf(stderr, "real bit pulse start detected\n"); - - } - - if (p->real_bits && p->pulse_start && (buf[i] < demod->level_limit)) { - /* end of pulse */ - - p->pulse_length = p->sample_counter-p->pulse_start; - if (debug_output) { - fprintf(stderr, "real bit pulse end detected %d\n", p->pulse_length); - fprintf(stderr, "space duration %d\n", p->sample_counter); - } - - if (p->pulse_length <= p->short_limit) { - if (debug_output) - fprintf(stderr, "1 received\n"); - demod_add_bit(p, 1); - } else { - if (debug_output) - fprintf(stderr, "0 received\n"); - 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)) { - if (debug_output) - fprintf(stderr, "End of packet\n"); - 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; - if (p->callback) - events+=p->callback(p->bits_buffer); - else - demod_print_bits_packet(p); - demod_reset_bits_packet(p); + csv_aux = data_csv_init(output_fields, num_output_fields); + free(output_fields); + return csv_aux; +} - p->start_bit = 0; - p->real_bits = 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<>1) + (b[0]*x_buf[0]>>1) + (b[1]*lp_xmem[0]>>1)) >> (F_SCALE-1); - for (i=1 ; i>1) + (b[0]*x_buf[i]>>1) + (b[1]*x_buf[i-1]>>1)) >> (F_SCALE-1); + 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) +void add_kv_output() { - 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); - } - { - for (i=0 ; ir_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; - 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); - } - } + 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) -{ +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; + 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)); + demod = malloc(sizeof (struct dm_state)); + memset(demod, 0, sizeof (struct dm_state)); /* initialize tables */ - calc_squares(); + baseband_init(); + + r_device devices[] = { +#define DECL(name) name, + DEVICES +#undef DECL + }; - demod->f_buf = &demod->filter_buffer[FILTER_ORDER]; - demod->decimation_level = DEFAULT_DECIMATION_LEVEL; - demod->level_limit = DEFAULT_LEVEL_LIMIT; + 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); - break; - case 'f': - if(frequencieslevel_limit = (uint32_t)atof(optarg); - break; - 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 'r': - test_mode_file = optarg; - break; - case 't': - demod->signal_grabber = 1; - break; - case 'm': - demod->debug_mode = atoi(optarg); - break; - case 'S': - sync_mode = 1; - break; - case 'D': - debug_output = 1; - break; - case 'z': - override_short = atoi(optarg); - break; - case 'x': - override_long = atoi(optarg); - break; - default: - usage(); - break; + case 'd': + dev_index = atoi(optarg); + break; + case 'f': + if (frequencies < MAX_PROTOCOLS) frequency[frequencies++] = (uint32_t) atof(optarg); + else fprintf(stderr, "Max number of frequencies reached %d\n", MAX_PROTOCOLS); + break; + 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; + case 's': + samp_rate = (uint32_t) atof(optarg); + break; + case 'b': + out_block_size = (uint32_t) atof(optarg); + break; + case 'l': + demod->level_limit = (uint32_t) atof(optarg); + break; + case 'n': + bytes_to_read = (uint32_t) atof(optarg) * 2; + break; + case 'a': + demod->analyze = 1; + break; + case 'A': + demod->analyze_pulses = 1; + break; + case 'I': + include_only = atoi(optarg); + break; + case 'r': + in_filename = optarg; + break; + case 't': + demod->signal_grabber = 1; + break; + case 'm': + demod->debug_mode = atoi(optarg); + break; + case 'S': + sync_mode = 1; + break; + case 'D': + debug_output++; + break; + case 'z': + override_short = atoi(optarg); + break; + 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(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, &wh2); - - if (argc <= optind-1) { - usage(); + if (argc <= optind - 1) { + 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(out_block_size < MINIMAL_BUF_LENGTH || - out_block_size > MAXIMAL_BUF_LENGTH ){ + 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, - "Output block size wrong value, falling back to default\n"); + "Output block size wrong value, falling back to default\n"); fprintf(stderr, - "Minimal length: %u\n", MINIMAL_BUF_LENGTH); + "Minimal length: %u\n", MINIMAL_BUF_LENGTH); fprintf(stderr, - "Maximal length: %u\n", MAXIMAL_BUF_LENGTH); + "Maximal length: %u\n", MAXIMAL_BUF_LENGTH); out_block_size = DEFAULT_BUF_LENGTH; } - buffer = malloc(out_block_size * sizeof(uint8_t)); + if (!in_filename) { + device_count = rtlsdr_get_device_count(); + if (!device_count) { + fprintf(stderr, "No supported devices found.\n"); + if (!in_filename) + exit(1); + } + + 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)); + } + + 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); +#else + 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 - device_count = rtlsdr_get_device_count(); - if (!device_count) { - fprintf(stderr, "No supported devices found.\n"); - if (!test_mode_file) - exit(1); - } + fprintf(stderr, "Bit detection level set to %d%s.\n", demod->level_limit, (demod->level_limit ? "" : " (Auto)")); - 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 (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"); - fprintf(stderr, "Using device %d: %s\n", - dev_index, rtlsdr_get_device_name(dev_index)); + /* 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_open(&dev, dev_index); - if (r < 0) { - fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); - if (!test_mode_file) - 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); -#else - 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); - } + 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 = ""; + } 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); } @@ -1755,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) { @@ -1763,44 +1198,56 @@ int main(int argc, char **argv) break; } - if ((bytes_to_read > 0) && (bytes_to_read < (uint32_t)n_read)) { + if ((bytes_to_read > 0) && (bytes_to_read < (uint32_t) n_read)) { n_read = bytes_to_read; 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; } - if ((uint32_t)n_read < out_block_size) { + if ((uint32_t) n_read < out_block_size) { fprintf(stderr, "Short read, samples lost, exiting!\n"); break; } + if (duration > 0) { + time(×tamp); + 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; - frequencies=1; + if (frequencies == 0) { + frequency[0] = DEFAULT_FREQUENCY; + frequencies = 1; } else { - time(&rawtime_old); + time(&rawtime_old); } - fprintf(stderr, "Reading samples in async mode...\n"); - while(!do_exit) { + 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]); if (r < 0) fprintf(stderr, "WARNING: Failed to set center freq.\n"); else fprintf(stderr, "Tuned to %u Hz.\n", rtlsdr_get_center_freq(dev)); - r = rtlsdr_read_async(dev, rtlsdr_callback, (void *)demod, - DEFAULT_ASYNC_BUF_NUMBER, out_block_size); - do_exit_async=0; + r = rtlsdr_read_async(dev, rtlsdr_callback, (void *) demod, + DEFAULT_ASYNC_BUF_NUMBER, out_block_size); + do_exit_async = 0; frequency_current++; - if(frequency_current>frequencies-1) frequency_current=0; + if (frequency_current > frequencies - 1) frequency_current = 0; } } @@ -1809,20 +1256,23 @@ 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 ; ir_dev_num ; i++) + for (i = 0; i < demod->r_dev_num; i++) free(demod->r_devs[i]); 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; }