From e726ddea078f8ce141b33dd752c466aa4640beed Mon Sep 17 00:00:00 2001 From: Benjamin Larsson Date: Tue, 8 Jan 2013 20:17:32 +0100 Subject: [PATCH] Working temp extraction from Rubicson sensors --- src/CMakeLists.txt | 8 + src/rtl_433.c | 562 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 570 insertions(+) create mode 100644 src/rtl_433.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b23395f..60dd1541 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,6 +72,7 @@ add_executable(rtl_test rtl_test.c) add_executable(rtl_fm rtl_fm.c) add_executable(rtl_eeprom rtl_eeprom.c) add_executable(rtl_adsb rtl_adsb.c) +add_executable(rtl_433 rtl_433.c) set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb) target_link_libraries(rtl_sdr rtlsdr_shared @@ -90,6 +91,10 @@ target_link_libraries(rtl_fm rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) +target_link_libraries(rtl_433 rtlsdr_shared + ${LIBUSB_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} +) target_link_libraries(rtl_eeprom rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} @@ -100,6 +105,7 @@ target_link_libraries(rtl_adsb rtlsdr_shared ) if(UNIX) target_link_libraries(rtl_fm m) +target_link_libraries(rtl_433 m) target_link_libraries(rtl_adsb m) if(APPLE) target_link_libraries(rtl_test m) @@ -113,12 +119,14 @@ target_link_libraries(rtl_sdr libgetopt_static) target_link_libraries(rtl_tcp libgetopt_static) target_link_libraries(rtl_test libgetopt_static) target_link_libraries(rtl_fm libgetopt_static) +target_link_libraries(rtl_433 libgetopt_static) target_link_libraries(rtl_eeprom libgetopt_static) target_link_libraries(rtl_adsb libgetopt_static) set_property(TARGET rtl_sdr APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_tcp APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_test APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_fm APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) +set_property(TARGET rtl_433 APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_eeprom APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_adsb APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) endif() diff --git a/src/rtl_433.c b/src/rtl_433.c new file mode 100644 index 00000000..688233a2 --- /dev/null +++ b/src/rtl_433.c @@ -0,0 +1,562 @@ +/* + * 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 sends36 bits 12 times + * 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 + * + * + * The sensor can be bought at Kjell&Co + */ + +#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 48000 +#define DEFAULT_FREQUENCY 433920000 +#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 + + +static int do_exit = 0; +static uint32_t bytes_to_read = 0; +static rtlsdr_dev_t *dev = NULL; + +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 bits_col_idx; + int bits_row_idx; + int bits_bit_col_idx; + uint8_t bits_buffer[12][5]; +}; + +void usage(void) +{ + fprintf(stderr, + "rtl_433, a 433.92MHz 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[-S force sync output (default: async)]\n" + "\t[-r read data from file instead of from a receiver]\n" + "\tfilename (a '-' dumps samples to stdout)\n\n"); + 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 + + +/** 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 ,sizeof(int32_t)*12*2); + demod->bits_col_idx = 0; + demod->bits_bit_col_idx = 7; + demod->bits_row_idx = 0; +} + +static void demod_add_bit(struct dm_state *demod, int bit) { + demod->bits_buffer[demod->bits_row_idx][demod->bits_col_idx] |= bit<bits_bit_col_idx; + demod->bits_bit_col_idx--; + if (demod->bits_bit_col_idx<0) { + demod->bits_bit_col_idx = 7; + demod->bits_col_idx++; + if (demod->bits_col_idx>4) { + demod->bits_col_idx = 4; + fprintf(stderr, "demod->bits_col_idx>4!\n"); + } + } +} + +static void demod_next_bits_packet(struct dm_state *demod) { + demod->bits_col_idx = 0; + demod->bits_row_idx++; + demod->bits_bit_col_idx = 7; + if (demod->bits_row_idx>11) { + demod->bits_row_idx = 11; + fprintf(stderr, "demod->bits_row_idx>11!\n"); + } +} + +static void demod_print_bits_packet(struct dm_state *demod) { + int i,j,k; + int temp_sign; + int temperature_before_dec; + int temperature_after_dec; + int16_t temp; + + fprintf(stderr, "\n"); + for (i=0 ; i<12 ; i++) { + for (j=0 ; j<5 ; j++) { + for (k=7 ; k>=0 ; k--) { + if (demod->bits_buffer[i][j] & 1<bits_buffer[i][j]); + fprintf(stderr, " "); + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); + fprintf(stderr, "%x %x %x %x %x\n",demod->bits_buffer[0][0],demod->bits_buffer[0][1],demod->bits_buffer[0][2],demod->bits_buffer[0][3],demod->bits_buffer[0][4]); + + /* Nible 3,4,5 contains 12 bits of temperature + * The temerature is signed and scaled by 10 */ + temp = (int16_t)((uint16_t)(demod->bits_buffer[0][1] << 12) | (demod->bits_buffer[0][2] << 4)); + temp = temp >> 4; + + temperature_before_dec = temp / 10; + temperature_after_dec = abs(temp % 10); + + fprintf(stderr, "id = %x\n",demod->bits_buffer[0][0]); + + fprintf(stderr, "temp = %d.%d\n",temperature_before_dec, temperature_after_dec); + + fprintf(stderr, "\n"); + +} + +static int pulse_length = 0; +static int pulse_count = 0; +static int pulse_distance = 0; +static int sample_counter = 0; +static int start_c = 0; + +/** + * Level 10000 -> + * Pulse length = 604 samples + * PW short = 1158 samples + * PW long = 2330 samples (2*PW short) + * PW next = 4670 samples (2*PW long) + */ + +static void level_detect(struct dm_state *demod, int16_t *buf, uint32_t len) +{ + unsigned int i; + + for (i=0 ; i demod->level_limit) { + pulse_count = 1; + start_c = 1; + } + if (pulse_count && (buf[i] < demod->level_limit)) { + pulse_length = 0; + pulse_distance = 1; + sample_counter = 0; + pulse_count = 0; + } + if (start_c) sample_counter++; + if (pulse_distance && (buf[i] > demod->level_limit)) { + if (sample_counter < 1744) { + demod_add_bit(demod, 0); + } else if (sample_counter < 3500) { + demod_add_bit(demod, 1); + } else { + demod_next_bits_packet(demod); + pulse_count = 0; + sample_counter = 0; + } + pulse_distance = 0; + } + if (sample_counter > 5000) { + start_c = 0; + sample_counter = 0; + pulse_distance = 0; + demod_print_bits_packet(demod); + demod_reset_bits_packet(demod); + } + } +} + + +/** 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; + if (demod->file || !demod->save_data) { + if (do_exit) + return; + + if ((bytes_to_read > 0) && (bytes_to_read < len)) { + len = bytes_to_read; + do_exit = 1; + rtlsdr_cancel_async(dev); + } + + envelope_detect(buf, len, demod->decimation_level); + low_pass_filter(sbuf, demod->f_buf, len>>(demod->decimation_level+1)); + level_detect(demod, demod->f_buf, len/2); + //pwm_demod(sbuf, len/2); + + 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; + } +} + +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; + struct dm_state* demod; + uint8_t *buffer; + uint32_t dev_index = 0; + uint32_t frequency = DEFAULT_FREQUENCY; + uint32_t samp_rate = DEFAULT_SAMPLE_RATE; + uint32_t out_block_size = DEFAULT_BUF_LENGTH; + int device_count; + char vendor[256], product[256], serial[256]; + + demod = malloc(sizeof(struct dm_state)); + demod->f_buf = &demod->filter_buffer[FILTER_ORDER]; + demod->decimation_level = DEFAULT_DECIMATION_LEVEL; + demod->level_limit = DEFAULT_LEVEL_LIMIT; + demod_reset_bits_packet(demod); + + while ((opt = getopt(argc, argv, "r:c:l:d:f:g:s:b:n:S::")) != -1) { + switch (opt) { + case 'd': + dev_index = atoi(optarg); + break; + case 'f': + frequency = (uint32_t)atof(optarg); + break; + case 'g': + gain = (int)(atof(optarg) * 10); /* tenths of a dB */ + 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 'c': + demod->decimation_level = (uint32_t)atof(optarg); + break; + case 'r': + test_mode_file = optarg; + break; + case 'S': + sync_mode = 1; + break; + default: + usage(); + break; + } + } + + 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"); + 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); + 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", samp_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); + + /* Set the frequency */ + r = rtlsdr_set_center_freq(dev, frequency); + if (r < 0) + fprintf(stderr, "WARNING: Failed to set center freq.\n"); + else + fprintf(stderr, "Tuned to %u Hz.\n", frequency); + + 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 { + /* 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); + } + + 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 (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"); + while(fread(test_mode_buf, 131072, 1, test_mode) != 0) { + rtlsdr_callback(test_mode_buf, 131072, demod); + i++; + } + 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 { + fprintf(stderr, "Reading samples in async mode...\n"); + r = rtlsdr_read_async(dev, rtlsdr_callback, (void *)demod, + DEFAULT_ASYNC_BUF_NUMBER, out_block_size); + } + + 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); + + rtlsdr_close(dev); + free (buffer); +out: + return r >= 0 ? r : -r; +}