X-Git-Url: https://git.rvb.name/rtl-433.git/blobdiff_plain/25b624169a19b22b938f5b80651e8f6cd7da092f..refs/heads/master:/src/rtl_433.c?ds=inline
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;
}