/* * rtl_433, turns your Realtek RTL2832 based DVB dongle into a 433.92MHz generic data receiver * Copyright (C) 2012 by Benjamin Larsson * * Based on rtl_sdr * * Copyright (C) 2012 by Steve Markgraf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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 "rtl-sdr.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; uint32_t frequency[MAX_PROTOCOLS]; time_t rawtime_old; int flag; uint32_t samp_rate=DEFAULT_SAMPLE_RATE; 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; /* 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<>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) 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])); } } //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; }; struct dm_state { FILE *file; int save_data; int32_t level_limit; int32_t decimation_level; int16_t filter_buffer[MAXIMAL_BUF_LENGTH+FILTER_ORDER]; int16_t* f_buf; int analyze; int debug_mode; /* Signal grabber variables */ int signal_grabber; int8_t* sg_buf; int sg_index; int sg_len; /* Protocol states */ int r_dev_num; struct protocol_state *r_devs[MAX_PROTOCOLS]; }; 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); exit(1); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); return TRUE; } return FALSE; } #else static void sighandler(int signum) { 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); 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 (demod->r_dev_num > MAX_PROTOCOLS) fprintf(stderr, "Max number of protocols reached %d\n",MAX_PROTOCOLS); } static unsigned int counter = 0; static unsigned int print = 1; static unsigned int print2 = 0; static unsigned int pulses_found = 0; static unsigned int prev_pulse_start = 0; 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_pulse_counter = 0; 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; unsigned int a[3], b[2], a_cnt[3], a_new[3], b_new[2]; unsigned int signal_distance_data[4000] = {0}; struct protocol_state p = {0}; unsigned int signal_type; if (!signal_pulse_data[0][0]) return; 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], //signal_pulse_data[i][2]); if (signal_pulse_data[i][2] > max) max = signal_pulse_data[i][2]; if (signal_pulse_data[i][2] <= min) min = signal_pulse_data[i][2]; } } t=(max+min)/2; //fprintf(stderr, "\n\nMax: %d, Min: %d t:%d\n", max, min, t); 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++) { 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 { max_new = max_new + signal_pulse_data[i][2]; count_max++; } } } 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; 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++) { if (signal_pulse_data[i][0] > 0) { //fprintf(stderr, "%d\n", signal_pulse_data[i][1]); } } /* 50% decision limit */ if (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); signal_type = 1; } p_limit = (max+min)/2; /* Initial guesses */ a[0] = 1000000; a[2] = 0; 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]; } } 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; delta = 10000000; 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++) { 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]){ a_new[1] += signal_distance_data[i]; a_cnt[1]++; } else if (signal_distance_data[i] >= b[1]) { a_new[2] += signal_distance_data[i]; a_cnt[2]++; } } } // fprintf(stderr, "Iteration %d.", k); delta = 0; 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]); a[i] = a_new[i]; } // fprintf(stderr, " delta %d\n", delta); if (a[0] < min) { a[0] = min; // fprintf(stderr, "Fixing a[0] = %d\n", min); } if (a[2] > max) { a[0] = 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]); } // fprintf(stderr, "\n"); k++; } if (override_short) { p_limit = override_short; a[0] = override_short; } if (override_long) { 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); } } } demod_print_bits_packet(&p); } 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); } else { // fprintf(stderr, "1 [%d] %d > %d\n",i, signal_pulse_data[i][2], p_limit); demod_add_bit(&p, 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); } } } demod_print_bits_packet(&p); } for (i=0 ; i<1000 ; i++) { signal_pulse_data[i][0] = 0; signal_pulse_data[i][1] = 0; signal_pulse_data[i][2] = 0; signal_distance_data[i] = 0; } }; static void pwm_analyze(struct dm_state *demod, int16_t *buf, uint32_t len) { unsigned int i; for (i=0 ; i demod->level_limit) { if (!signal_start) signal_start = counter; if (print) { pulses_found++; pulse_start = counter; 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]); prev_pulse_start = pulse_start; print =0; print2 = 1; } } counter++; if (buf[i] < demod->level_limit) { 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", 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_counter++; if (signal_pulse_counter >= 4000) { signal_pulse_counter = 0; goto err; } } 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); pulses_found = 0; classify_signal(); signal_pulse_counter = 0; if (demod->sg_buf) { 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; if (sg_idx < 0) 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; fprintf(stderr, "restart_pos = %d\n", start_pos); } 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); } wlen = signal_bszie; if (start_pos + signal_bszie > SIGNAL_GRABBER_BUFFER) { wlen = SIGNAL_GRABBER_BUFFER - start_pos; wrest = signal_bszie - 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); } fclose(sgfp); } signal_start = 0; } } } return; err: fprintf(stderr, "To many pulses detected, probably bad input data or input parameters\n"); 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); } } } /* 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; // 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; // 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; // 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; // 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) { demod_add_bit(p, 1); } else if (p->pulse_length > p->short_limit) { demod_add_bit(p, 0); } 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; if (p->callback) events+=p->callback(p->bits_buffer); else demod_print_bits_packet(p); demod_reset_bits_packet(p); p->start_bit = 0; p->real_bits = 0; } } } /** 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); } /* 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]); } 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); } { 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); } } } 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); } } } } int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif char *filename = NULL; char *test_mode_file = NULL; FILE *test_mode; int n_read; int r, 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]; demod = malloc(sizeof(struct dm_state)); memset(demod,0,sizeof(struct dm_state)); /* initialize tables */ calc_squares(); demod->f_buf = &demod->filter_buffer[FILTER_ORDER]; demod->decimation_level = DEFAULT_DECIMATION_LEVEL; 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) { 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; } } /* 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(); } else { filename = argv[optind]; } 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"); fprintf(stderr, "Minimal length: %u\n", MINIMAL_BUF_LENGTH); fprintf(stderr, "Maximal length: %u\n", MAXIMAL_BUF_LENGTH); 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); } 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); 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); demod->save_data = 1; if (!filename) { demod->save_data = 0; } else if(strcmp(filename, "-") == 0) { /* Write samples to stdout */ demod->file = stdout; #ifdef _WIN32 _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; } } if (demod->signal_grabber) demod->sg_buf = malloc(SIGNAL_GRABBER_BUFFER); if (test_mode_file) { 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); i++; } //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]); exit(0); } /* Reset endpoint before we start reading from it (mandatory) */ r = rtlsdr_reset_buffer(dev); if (r < 0) fprintf(stderr, "WARNING: Failed to reset buffers.\n"); if (sync_mode) { fprintf(stderr, "Reading samples in sync mode...\n"); while (!do_exit) { r = rtlsdr_read_sync(dev, buffer, out_block_size, &n_read); if (r < 0) { fprintf(stderr, "WARNING: sync read failed.\n"); break; } 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) { fprintf(stderr, "Short write, samples lost, exiting!\n"); break; } if ((uint32_t)n_read < out_block_size) { fprintf(stderr, "Short read, samples lost, exiting!\n"); break; } if (bytes_to_read > 0) bytes_to_read -= n_read; } } else { if(frequencies==0) { frequency[0] = DEFAULT_FREQUENCY; frequencies=1; } else { time(&rawtime_old); } 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; frequency_current++; if(frequency_current>frequencies-1) frequency_current=0; } } if (do_exit) fprintf(stderr, "\nUser cancel, exiting...\n"); else fprintf(stderr, "\nLibrary error %d, exiting...\n", r); if (demod->file && (demod->file != stdout)) fclose(demod->file); for (i=0 ; ir_dev_num ; i++) free(demod->r_devs[i]); if (demod->signal_grabber) free(demod->sg_buf); if(demod) free(demod); rtlsdr_close(dev); free (buffer); out: return r >= 0 ? r : -r; }