Merge pull request from vestom/generic_demod

New packet based pulse demodulation infrastructure
This commit is contained in:
Benjamin Larsson 2015-07-09 21:45:31 +02:00
commit d96f8e3814
14 changed files with 701 additions and 145 deletions

View file

@ -35,6 +35,7 @@ if(CMAKE_COMPILER_IS_GNUCC AND NOT WIN32)
ADD_DEFINITIONS(-Wno-unused)
ADD_DEFINITIONS(-Wsign-compare)
ADD_DEFINITIONS(-g3 -O0)
ADD_DEFINITIONS(-std=gnu99)
#http://gcc.gnu.org/wiki/Visibility
add_definitions(-fvisibility=hidden)
endif()

47
include/bitbuffer.h Normal file
View file

@ -0,0 +1,47 @@
/**
* Bit buffer
*
* A two-dimensional bit buffer consisting of bytes
*
* Copyright (C) 2015 Tommy Vestermark
* 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.
*/
#ifndef INCLUDE_BITBUFFER_H_
#define INCLUDE_BITBUFFER_H_
#include <stdint.h>
#define BITBUF_COLS 34 // Number of bytes in a column
#define BITBUF_ROWS 50
/// Bit buffer
typedef struct {
int row_index; // Number of active rows - 1
int bit_col_index; // Bit index into byte (0 is MSB, 7 is LSB)
int16_t bits_per_row[BITBUF_ROWS];
uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS];
} bitbuffer_t;
/// Clear the content of the bitbuffer
void bitbuffer_clear(bitbuffer_t *bits);
/// Add a single bit at the end of the bitbuffer (MSB first)
void bitbuffer_add_bit(bitbuffer_t *bits, int bit);
/// Add a new row to the bitbuffer
void bitbuffer_add_row(bitbuffer_t *bits);
/// Invert all bits in the bitbuffer (do not invert the empty bits)
//void bitbuffer_invert(bitbuffer_t *bits);
/// Print the content of the bitbuffer
void bitbuffer_print(const bitbuffer_t *bits);
#endif /* INCLUDE_BITBUFFER_H_ */

46
include/pulse_demod.h Normal file
View file

@ -0,0 +1,46 @@
/**
* Pulse demodulation functions
*
* Binary demodulators (PWM/PPM/Manchester/...) using a pulse data structure as input
*
* Copyright (C) 2015 Tommy Vestermark
* 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.
*/
#ifndef INCLUDE_PULSE_DEMOD_H_
#define INCLUDE_PULSE_DEMOD_H_
#include <stdint.h>
#include "pulse_detect.h"
#include "rtl_433.h"
/// Demodulate a Pulse Width Modulation signal
///
/// Demodulate a Pulse Width Modulation (PWM) signal consisting of short and long high pulses.
/// Gap between pulses may be of fixed size or variable (e.g. fixed period)
/// - Short pulse will add a 1 bit
/// - Long pulse will add a 0 bit
/// @param start_bit = 0: Do not remove any startbits
/// @param start_bit = 1: First bit in each message is considered a startbit and not stored in bitbuffer
/// @return number of events processed
int pulse_demod_pwm(const pulse_data_t *pulses, struct protocol_state *device, int start_bit);
/// Demodulate a Manchester encoded signal with a hardcoded zerobit in front
///
/// Demodulate a Manchester encoded signal where first rising edge is counted as a databit
/// and therefore always will be zero (Most likely a hardcoded Oregon Scientific peculiarity)
///
/// Clock is recovered from the data based on pulse width. When time since last bit is more
/// than 1.5 times the clock half period (short_width) it is declared a data edge where:
/// - Rising edge means bit = 0
/// - Falling edge means bit = 1
/// @return number of events processed
int pulse_demod_manchester_zerobit(const pulse_data_t *pulses, struct protocol_state *device);
#endif /* INCLUDE_PULSE_DEMOD_H_ */

47
include/pulse_detect.h Normal file
View file

@ -0,0 +1,47 @@
/**
* Pulse detection functions
*
* Copyright (C) 2015 Tommy Vestermark
* 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.
*/
#ifndef INCLUDE_PULSE_DETECT_H_
#define INCLUDE_PULSE_DETECT_H_
#include <stdint.h>
#define PD_MAX_PULSES 1000 // Maximum number of pulses before forcing End Of Package
#define PD_MAX_GAP_RATIO 10 // Ratio gap/pulse width to exceed to declare End Of Package (heuristic)
#define PD_MAX_PULSE_LENGTH 25000 // Pulse width to exceed to declare End Of Package (e.g. for non OOK packages)
/// Data for a compact representation of generic pulse train
typedef struct {
unsigned int num_pulses;
unsigned int pulse[PD_MAX_PULSES]; // Contains width of a pulse (high)
unsigned int gap[PD_MAX_PULSES]; // Width of gaps between pulses (low)
} pulse_data_t;
/// Clear the content of a pulse_data_t structure
void pulse_data_clear(pulse_data_t *data); // Clear the struct
/// Print the content of a pulse_data_t structure (for debug)
void pulse_data_print(const pulse_data_t *data);
/// Demodulate On/Off Keying from an envelope signal
///
/// Function is stateful and can be called with chunks of input data
/// @return 0 if all input data is processed
/// @return 1 if package is detected (but data is still not completely processed)
int detect_pulse_package(const int16_t *envelope_data, uint32_t len, int16_t level_limit, pulse_data_t *pulses);
/// Analyze and print result
void pulse_analyzer(const pulse_data_t *data);
#endif /* INCLUDE_PULSE_DETECT_H_ */

View file

@ -37,10 +37,41 @@
/* Supported modulation types */
#define OOK_PWM_D 1 /* Pulses are of the same length, the distance varies (PPM) */
#define OOK_PWM_P 2 /* The length of the pulses varies */
#define OOK_MANCHESTER 3 /* Manchester code */
#define OOK_PWM_RAW 4 /* Pulse Width Modulation. No startbit removal. Short pulses = 1, Long = 0 */
#define OOK_PULSE_PWM_STARTBIT 3 // Pulse Width Modulation. Startbit removal. Short pulses = 1, Long = 0
#define OOK_PULSE_PWM_RAW 4 // Pulse Width Modulation. No startbit removal. Short pulses = 1, Long = 0
#define OOK_PULSE_MANCHESTER_ZEROBIT 5 // Manchester encoding. Hardcoded zerobit. Rising Edge = 0, Falling edge = 1
extern int debug_output;
int debug_callback(uint8_t buffer[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]);
struct protocol_state {
int (*callback)(uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]);
/* bits state */
int bits_col_idx;
int bits_row_idx;
int bits_bit_col_idx;
uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS];
int16_t bits_per_row[BITBUF_ROWS];
int bit_rows;
unsigned int modulation;
/* demod state */
int pulse_length;
int pulse_count;
int pulse_distance;
int sample_counter;
int start_c;
int packet_present;
int pulse_start;
int real_bits;
int start_bit;
/* pwm limits */
int short_limit;
int long_limit;
int reset_limit;
};
#endif /* INCLUDE_RTL_433_H_ */

View file

@ -18,6 +18,9 @@
########################################################################
add_executable(rtl_433
rtl_433.c
bitbuffer.c
pulse_demod.c
pulse_detect.c
devices/silvercrest.c
devices/rubicson.c
devices/prologue.c

110
src/bitbuffer.c Normal file
View file

@ -0,0 +1,110 @@
/**
* Bit buffer
*
* A two-dimensional bit buffer consisting of bytes
*
* Copyright (C) 2015 Tommy Vestermark
* 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.
*/
#include "bitbuffer.h"
#include <stdio.h>
#include <string.h>
void bitbuffer_clear(bitbuffer_t *bits) {
bits->row_index = 0;
bits->bit_col_index = 0;
memset(bits->bits_per_row, 0, BITBUF_ROWS*2);
memset(bits->bits_buffer, 0, BITBUF_ROWS * BITBUF_COLS);
}
void bitbuffer_add_bit(bitbuffer_t *bits, int bit) {
uint16_t col_index = bits->bits_per_row[bits->row_index]/8;
if((col_index < BITBUF_COLS)
&& (bits->row_index < BITBUF_ROWS)
) {
bits->bits_buffer[bits->row_index][col_index] |= bit << (7-bits->bit_col_index);
bits->bit_col_index++;
bits->bit_col_index %= 8; // Wrap around
bits->bits_per_row[bits->row_index]++;
}
else {
fprintf(stderr, "ERROR: bitbuffer:: Could not add more columns\n");
}
}
void bitbuffer_add_row(bitbuffer_t *bits) {
if(bits->row_index < BITBUF_ROWS) {
bits->row_index++;
bits->bit_col_index = 0;
}
else {
fprintf(stderr, "ERROR: bitbuffer:: Could not add more rows\n");
}
}
void bitbuffer_print(const bitbuffer_t *bits) {
fprintf(stderr, "bitbuffer:: row_index: %d, bit_col_index: %d\n", bits->row_index, bits->bit_col_index);
for (int row = 0; row <= bits->row_index; ++row) {
fprintf(stderr, "[%02d] {%d} ", row, bits->bits_per_row[row]);
for (int col = 0; col < (bits->bits_per_row[row]+7)/8; ++col) {
fprintf(stderr, "%02x ", bits->bits_buffer[row][col]);
}
fprintf(stderr, "\n");
}
}
// Test code
// gcc -I include/ -std=gnu11 -D _TEST src/bitbuffer.c
#ifdef _TEST
int main(int argc, char **argv) {
fprintf(stderr, "bitbuffer:: test\n");
bitbuffer_t bits = {0};
fprintf(stderr, "TEST: bitbuffer:: The empty buffer\n");
bitbuffer_print(&bits);
fprintf(stderr, "TEST: bitbuffer:: Add 1 bit\n");
bitbuffer_add_bit(&bits, 1);
bitbuffer_print(&bits);
fprintf(stderr, "TEST: bitbuffer:: Add 1 new row\n");
bitbuffer_add_row(&bits);
bitbuffer_print(&bits);
fprintf(stderr, "TEST: bitbuffer:: Fill row\n");
for (int i=0; i < BITBUF_COLS*8; ++i) {
bitbuffer_add_bit(&bits, i%2);
}
bitbuffer_print(&bits);
fprintf(stderr, "TEST: bitbuffer:: Add row and fill 1 column too many\n");
bitbuffer_add_row(&bits);
for (int i=0; i <= BITBUF_COLS*8; ++i) {
bitbuffer_add_bit(&bits, i%2);
}
bitbuffer_print(&bits);
fprintf(stderr, "TEST: bitbuffer:: Clear\n");
bitbuffer_clear(&bits);
bitbuffer_print(&bits);
fprintf(stderr, "TEST: bitbuffer:: Add 1 row too many\n");
for (int i=0; i <= BITBUF_ROWS; ++i) {
bitbuffer_add_row(&bits);
}
bitbuffer_add_bit(&bits, 1);
bitbuffer_print(&bits);
return 0;
}
#endif /* _TEST */

View file

@ -147,7 +147,7 @@ ambient_weather_callback (uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per
r_device ambient_weather = {
/* .id = */ 14,
/* .name = */ "Ambient Weather Temperature Sensor",
/* .modulation = */ OOK_MANCHESTER,
/* .modulation = */ OOK_PULSE_MANCHESTER_ZEROBIT,
/* .short_limit = */ 125,
/* .long_limit = */ 0, // not used
/* .reset_limit = */ 600,

View file

@ -61,10 +61,10 @@ static int fineoffset_WH2_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t
// Validate package
if (bits_per_row[0] >= 48 && // Dont waste time on a short package
bb[0][0] == 0xFF && // Preamble
bb[0][5] == crc8(&bb[0][1], 4, polynomial) // CRC (excluding preamble)
)
bb[0][5] == crc8(&bb[0][1], 4, polynomial) // CRC (excluding preamble)
)
{
// Nibble 3,4 contains ID
// Nibble 3,4 contains ID
ID = ((bb[0][1]&0x0F) << 4) | ((bb[0][2]&0xF0) >> 4);
// Nible 5,6,7 contains 12 bits of temperature
@ -99,7 +99,7 @@ static int fineoffset_WH2_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t
r_device fineoffset_WH2 = {
/* .id = */ 12,
/* .name = */ "Fine Offset Electronics, WH-2 Sensor",
/* .modulation = */ OOK_PWM_RAW,
/* .modulation = */ OOK_PULSE_PWM_RAW,
/* .short_limit = */ 200, // Short pulse 136, long pulse 381, fixed gap 259
/* .long_limit = */ 700, // Maximum pulse period (long pulse + fixed gap)
/* .reset_limit = */ 700, // We just want 1 package

View file

@ -207,9 +207,11 @@ static int lacrossetx_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],
r_device lacrossetx = {
/* .id = */11,
/* .name = */"LaCrosse TX Temperature / Humidity Sensor",
/* .modulation = */OOK_PWM_P,
///* .modulation = */OOK_PWM_P,
/* .modulation = */OOK_PULSE_PWM_STARTBIT,
/* .short_limit = */238,
/* .long_limit = */750,
/* .reset_limit = */8000,
///* .reset_limit = */8000,
/* .reset_limit = */2000,
/* .json_callback = */&lacrossetx_callback,
/* .disabled = */0, };

View file

@ -343,7 +343,7 @@ static int oregon_scientific_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int1
r_device oregon_scientific = {
/* .id = */ 11,
/* .name = */ "Oregon Scientific Weather Sensor",
/* .modulation = */ OOK_MANCHESTER,
/* .modulation = */ OOK_PULSE_MANCHESTER_ZEROBIT,
/* .short_limit = */ 125,
/* .long_limit = */ 0, // not used
/* .reset_limit = */ 600,

95
src/pulse_demod.c Normal file
View file

@ -0,0 +1,95 @@
/**
* Pulse demodulation functions
*
* Binary demodulators (PWM/PPM/Manchester/...) using a pulse data structure as input
*
* Copyright (C) 2015 Tommy Vestermark
* 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.
*/
#include "pulse_demod.h"
#include "bitbuffer.h"
#include <stdio.h>
int pulse_demod_pwm(const pulse_data_t *pulses, struct protocol_state *device, int start_bit) {
int events = 0;
int start_bit_detected = 0;
bitbuffer_t bits = {0};
for(unsigned n = 0; n < pulses->num_pulses; ++n) {
// Should we disregard startbit?
if(start_bit == 1 && start_bit_detected == 0) {
start_bit_detected = 1;
} else {
// Detect pulse width
if(pulses->pulse[n] <= (unsigned)device->short_limit) {
bitbuffer_add_bit(&bits, 1);
} else {
bitbuffer_add_bit(&bits, 0);
}
}
// End of Message?
if(pulses->gap[n] > (unsigned)device->reset_limit) {
if (device->callback) {
events += device->callback(bits.bits_buffer, bits.bits_per_row);
bitbuffer_clear(&bits);
start_bit_detected = 0;
} else {
bitbuffer_print(&bits);
}
// Check for new packet in multipacket
} else if(pulses->gap[n] > (unsigned)device->long_limit) {
bitbuffer_add_row(&bits);
start_bit_detected = 0;
}
}
return events;
}
int pulse_demod_manchester_zerobit(const pulse_data_t *pulses, struct protocol_state *device) {
int events = 0;
unsigned time_since_last = 0;
bitbuffer_t bits = {0};
// First rising edge is allways counted as a zero (Seems to be hardcoded policy for the Oregon Scientific sensors...)
bitbuffer_add_bit(&bits, 0);
for(unsigned n = 0; n < pulses->num_pulses; ++n) {
// Falling edge is on end of pulse
if(pulses->pulse[n] + time_since_last > (unsigned)(device->short_limit + (device->short_limit>>1))) {
// Last bit was recorded more than short_limit*1.5 samples ago
// so this pulse start must be a data edge (falling data edge means bit = 1)
bitbuffer_add_bit(&bits, 1);
time_since_last = 0;
} else {
time_since_last += pulses->pulse[n];
}
// End of Message?
if(pulses->gap[n] > (unsigned)device->reset_limit) {
if (device->callback) {
events += device->callback(bits.bits_buffer, bits.bits_per_row);
bitbuffer_clear(&bits);
bitbuffer_add_bit(&bits, 0); // Prepare for new message with hardcoded 0
time_since_last = 0;
} else {
bitbuffer_print(&bits);
}
// Rising edge is on end of gap
} else if(pulses->gap[n] + time_since_last > (unsigned)(device->short_limit + (device->short_limit>>1))) {
// Last bit was recorded more than short_limit*1.5 samples ago
// so this pulse end is a data edge (rising data edge means bit = 0)
bitbuffer_add_bit(&bits, 0);
time_since_last = 0;
} else {
time_since_last += pulses->gap[n];
}
}
return events;
}

275
src/pulse_detect.c Normal file
View file

@ -0,0 +1,275 @@
/**
* Pulse detection functions
*
* Copyright (C) 2015 Tommy Vestermark
* 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.
*/
#include "pulse_detect.h"
#include <stdio.h>
#include <stdlib.h>
void pulse_data_clear(pulse_data_t *data) {
data->num_pulses = 0;
for(unsigned n = 0; n < PD_MAX_PULSES; ++n) {
data->pulse[n] = 0;
data->gap[n] = 0;
}
}
void pulse_data_print(const pulse_data_t *data) {
fprintf(stderr, "Pulse data: %u pulses\n", data->num_pulses);
for(unsigned n = 0; n < data->num_pulses; ++n) {
fprintf(stderr, "[%3u] Pulse: %4u, Gap: %4u\n", n, data->pulse[n], data->gap[n]);
}
}
/// Internal state data for detect_pulse_package()
typedef struct {
enum {
PD_STATE_IDLE = 0,
PD_STATE_PULSE = 1,
PD_STATE_GAP = 2
} state;
unsigned int pulse_length; // Counter for internal pulse detection
unsigned int max_pulse; // Size of biggest pulse detected
unsigned int max_gap; // Size of biggest gap detected
unsigned int data_counter; // Counter for how much of data chunck is processed
} pulse_state_t;
static pulse_state_t pulse_state;
int detect_pulse_package(const int16_t *envelope_data, uint32_t len, int16_t level_limit, pulse_data_t *pulses) {
pulse_state_t *s = &pulse_state;
// Process all new samples
while(s->data_counter < len) {
switch (s->state) {
case PD_STATE_IDLE:
s->pulse_length = 0;
s->max_pulse = 0;
s->max_gap = 0;
if (envelope_data[s->data_counter] > level_limit) {
s->state = PD_STATE_PULSE;
}
break;
case PD_STATE_PULSE:
s->pulse_length++;
// End of pulse detected?
if (envelope_data[s->data_counter] < level_limit) { // Gap?
pulses->pulse[pulses->num_pulses] = s->pulse_length; // Store pulse width
// EOP if pulse is too long
if (s->pulse_length > PD_MAX_PULSE_LENGTH) {
pulses->num_pulses++; // Store last pulse (with no gap)
s->state = PD_STATE_IDLE;
return 1; // End Of Package!!
}
// Find largest pulse
if(s->pulse_length > s->max_pulse) {
s->max_pulse = s->pulse_length;
}
s->pulse_length = 0;
s->state = PD_STATE_GAP;
}
break;
case PD_STATE_GAP:
s->pulse_length++;
// New pulse detected?
if (envelope_data[s->data_counter] > level_limit) { // New pulse?
pulses->gap[pulses->num_pulses] = s->pulse_length; // Store gap width
pulses->num_pulses++; // Next pulse
// EOP if too many pulses
if (pulses->num_pulses >= PD_MAX_PULSES) {
s->state = PD_STATE_IDLE;
return 1; // End Of Package!!
}
// Find largest gap
if(s->pulse_length > s->max_gap) {
s->max_gap = s->pulse_length;
}
s->pulse_length = 0;
s->state = PD_STATE_PULSE;
}
// EOP if gap is too long
if ((s->pulse_length > (s->max_pulse * PD_MAX_GAP_RATIO))
// || (s->pulse_length > (s->max_gap * PD_MAX_GAP_RATIO) && s->max_gap !=0)
) {
pulses->gap[pulses->num_pulses] = s->pulse_length; // Store gap width
pulses->num_pulses++; // Store last pulse
s->state = PD_STATE_IDLE;
return 1; // End Of Package!!
}
break;
default:
fprintf(stderr, "demod_OOK(): Unknown state!!\n");
s->state = PD_STATE_IDLE;
} // switch
// Todo: check for too many pulses
s->data_counter++;
} // while
s->data_counter = 0;
return 0; // Out of data
}
#define MAX_HIST_BINS 16
/// Histogram data for single bin
typedef struct {
unsigned count;
unsigned sum;
unsigned mean;
unsigned min;
unsigned max;
} hist_bin_t;
/// Histogram data for all bins
typedef struct {
unsigned bins_count;
hist_bin_t bins[MAX_HIST_BINS];
} histogram_t;
// Helper macros
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
/// Generate a histogram (unsorted)
void histogram_sum(histogram_t *hist, const unsigned *data, unsigned len, float tolerance) {
unsigned bin; // Iterator will be used outside for!
for(unsigned n = 0; n < len; ++n) {
for(bin = 0; bin < hist->bins_count; ++bin) {
int bn = data[n];
int bm = hist->bins[bin].mean;
if (abs(bn - bm) < (tolerance * max(bn, bm))) {
hist->bins[bin].count++;
hist->bins[bin].sum += data[n];
hist->bins[bin].mean = hist->bins[bin].sum / hist->bins[bin].count;
hist->bins[bin].min = min(data[n], hist->bins[bin].min);
hist->bins[bin].max = max(data[n], hist->bins[bin].max);
break; // Match found!
}
}
// No match found?
if(bin == hist->bins_count && bin < MAX_HIST_BINS) {
hist->bins[bin].count = 1;
hist->bins[bin].sum = data[n];
hist->bins[bin].mean = data[n];
hist->bins[bin].min = data[n];
hist->bins[bin].max = data[n];
hist->bins_count++;
} // for bin
} // for data
}
/// Fuse histogram bins with means within tolerance
void histogram_fuse_bins(histogram_t *hist, float tolerance) {
hist_bin_t zerobin = {0};
if (hist->bins_count < 2) return; // Avoid underflow
// Compare all bins
for(unsigned n = 0; n < hist->bins_count-1; ++n) {
for(unsigned m = n+1; m < hist->bins_count; ++m) {
int bn = hist->bins[n].mean;
int bm = hist->bins[m].mean;
if (abs(bn - bm) < (tolerance * max(bn, bm))) {
// Fuse data for bin[n] and bin[m]
hist->bins[n].count += hist->bins[m].count;
hist->bins[n].sum += hist->bins[m].sum;
hist->bins[n].mean = hist->bins[n].sum / hist->bins[n].count;
hist->bins[n].min = min(hist->bins[n].min, hist->bins[m].min);
hist->bins[n].max = max(hist->bins[n].max, hist->bins[m].max);
// Delete bin[m]
for(unsigned l = m; l < hist->bins_count-1; ++l) {
hist->bins[l] = hist->bins[l+1];
}
hist->bins_count--;
hist->bins[hist->bins_count] = zerobin;
m--; // Compare new bin in same place!
} // if within tolerance
} // for m
} // for n
}
/// Print a histogram
void histogram_print(const histogram_t *hist) {
for(unsigned n = 0; n < hist->bins_count; ++n) {
fprintf(stderr, " [%2u] mean: %4u (%u/%u),\t count: %3u\n", n,
hist->bins[n].mean,
hist->bins[n].min,
hist->bins[n].max,
hist->bins[n].count);
}
}
#define TOLERANCE (0.2) // 20% tolerance should still discern between the pulse widths: 0.33, 0.66, 1.0
/// Analyze the statistics of a pulse data structure and print result
void pulse_analyzer(const pulse_data_t *data)
{
// Generate pulse period data
pulse_data_t pulse_periods = {0};
pulse_periods.num_pulses = data->num_pulses;
for(unsigned n = 0; n < pulse_periods.num_pulses; ++n) {
pulse_periods.pulse[n] = data->pulse[n] + data->gap[n];
}
histogram_t hist_pulses = {0};
histogram_t hist_gaps = {0};
histogram_t hist_periods = {0};
// Generate statistics
histogram_sum(&hist_pulses, data->pulse, data->num_pulses, TOLERANCE);
histogram_sum(&hist_gaps, data->gap, data->num_pulses-1, TOLERANCE); // Leave out last gap (end)
histogram_sum(&hist_periods, pulse_periods.pulse, pulse_periods.num_pulses-1, TOLERANCE); // Leave out last gap (end)
// Fuse overlapping bins
histogram_fuse_bins(&hist_pulses, TOLERANCE);
histogram_fuse_bins(&hist_gaps, TOLERANCE);
histogram_fuse_bins(&hist_periods, TOLERANCE);
fprintf(stderr, "\nAnalyzing pulses...\n");
fprintf(stderr, "Total number of pulses: %u\n", data->num_pulses);
fprintf(stderr, "Pulse width distribution:\n");
histogram_print(&hist_pulses);
fprintf(stderr, "Gap width distribution:\n");
histogram_print(&hist_gaps);
fprintf(stderr, "Pulse period distribution:\n");
histogram_print(&hist_periods);
fprintf(stderr, "Guessing modulation: ");
if(data->num_pulses == 1) {
fprintf(stderr, "Single pulse detected. Probably Frequency Shift Keying or just noise...\n");
} else if(hist_pulses.bins_count == 1 && hist_gaps.bins_count == 2 && hist_periods.bins_count == 2) {
fprintf(stderr, "Pulse Position Modulation with fixed pulse width\n");
} else if(hist_pulses.bins_count == 2 && hist_gaps.bins_count == 2 && hist_periods.bins_count == 1) {
fprintf(stderr, "Pulse Width Modulation with fixed period\n");
} else if(hist_pulses.bins_count == 2 && hist_gaps.bins_count == 1 && hist_periods.bins_count == 2) {
fprintf(stderr, "Pulse Width Modulation with fixed gap\n");
} else if(hist_pulses.bins_count == 2 && hist_gaps.bins_count == 2 && hist_periods.bins_count == 3) {
fprintf(stderr, "Manchester coding\n");
} else if(hist_pulses.bins_count == 3 && hist_gaps.bins_count == 3 && hist_periods.bins_count == 1) {
fprintf(stderr, "Pulse Width Modulation with startbit/delimiter\n");
} else {
fprintf(stderr, "No clue...\n");
}
fprintf(stderr, "\n");
}

View file

@ -22,6 +22,9 @@
#include "rtl-sdr.h"
#include "rtl_433.h"
#include "pulse_detect.h"
#include "pulse_demod.h"
static int do_exit = 0;
static int do_exit_async = 0, frequencies = 0, events = 0;
@ -93,37 +96,6 @@ int debug_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BI
return 0;
}
struct protocol_state {
int (*callback)(uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]);
/* bits state */
int bits_col_idx;
int bits_row_idx;
int bits_bit_col_idx;
uint8_t bits_buffer[BITBUF_ROWS][BITBUF_COLS];
int16_t bits_per_row[BITBUF_ROWS];
int bit_rows;
unsigned int modulation;
/* demod state */
int pulse_length;
int pulse_count;
int pulse_distance;
int sample_counter;
int start_c;
int packet_present;
int pulse_start;
int real_bits;
int start_bit;
/* pwm limits */
int short_limit;
int long_limit;
int reset_limit;
};
struct dm_state {
FILE *file;
int save_data;
@ -145,6 +117,7 @@ struct dm_state {
int r_dev_num;
struct protocol_state *r_devs[MAX_PROTOCOLS];
pulse_data_t pulse_data;
};
void usage(r_device *devices) {
@ -756,105 +729,6 @@ static void pwm_p_decode(struct dm_state *demod, struct protocol_state* p, int16
}
}
/* Machester Decode for Oregon Scientific Weather Sensors
Decode data streams sent by Oregon Scientific v2.1, and v3 weather sensors.
With manchester encoding, both the pulse width and pulse distance vary. Clock sync
is recovered from the data stream based on pulse widths and distances exceeding a
minimum threashold (short limit* 1.5).
*/
static void manchester_decode(struct dm_state *demod, struct protocol_state* p, int16_t *buf, uint32_t len) {
unsigned int i;
if (p->sample_counter == 0)
p->sample_counter = p->short_limit*2;
for (i=0 ; i<len ; i++) {
if (p->start_c)
p->sample_counter++; /* For this decode type, sample counter is count since last data bit recorded */
if (!p->pulse_count && (buf[i] > demod->level_limit)) { /* Pulse start (rising edge) */
p->pulse_count = 1;
if (p->sample_counter > (p->short_limit + (p->short_limit>>1))) {
/* Last bit was recorded more than short_limit*1.5 samples ago */
/* so this pulse start must be a data edge (rising data edge means bit = 0) */
demod_add_bit(p, 0);
p->sample_counter=1;
p->start_c++; // start_c counts number of bits received
}
}
if (p->pulse_count && (buf[i] <= demod->level_limit)) { /* Pulse end (falling edge) */
if (p->sample_counter > (p->short_limit + (p->short_limit>>1))) {
/* Last bit was recorded more than "short_limit*1.5" samples ago */
/* so this pulse end is a data edge (falling data edge means bit = 1) */
demod_add_bit(p, 1);
p->sample_counter=1;
p->start_c++;
}
p->pulse_count = 0;
}
if (p->sample_counter > p->reset_limit) {
//fprintf(stderr, "manchester_decode number of bits received=%d\n",p->start_c);
if (p->callback)
events+=p->callback(p->bits_buffer, p->bits_per_row);
else
demod_print_bits_packet(p);
demod_reset_bits_packet(p);
p->sample_counter = p->short_limit*2;
p->start_c = 0;
}
}
}
/* Pulse Width Modulation. No startbit removal */
static void pwm_raw_decode(struct dm_state *demod, struct protocol_state* p, int16_t *buf, uint32_t len) {
unsigned int i;
for (i = 0; i < len; i++) {
if (p->start_c) p->sample_counter++;
// Detect Pulse Start (leading edge)
if (!p->pulse_start && (buf[i] > demod->level_limit)) {
p->pulse_start = 1;
p->sample_counter = 0;
// Check for first bit in sequence
if(!p->start_c) {
p->start_c = 1;
}
}
// Detect Pulse End (trailing edge)
if (p->pulse_start && (buf[i] < demod->level_limit)) {
p->pulse_start = 0;
if (p->sample_counter <= p->short_limit) {
demod_add_bit(p, 1);
} else {
demod_add_bit(p, 0);
}
}
// Detect Pulse period overrun
if (p->sample_counter == p->long_limit) {
demod_next_bits_packet(p);
}
// Detect Pulse exceeding reset limit
if (p->sample_counter > p->reset_limit) {
p->sample_counter = 0;
p->start_c = 0;
p->pulse_start = 0;
if (p->callback)
events+=p->callback(p->bits_buffer, p->bits_per_row);
else
demod_print_bits_packet(p);
demod_reset_bits_packet(p);
}
}
}
/** Something that might look like a IIR lowpass filter
*
@ -923,6 +797,7 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) {
if (demod->analyze) {
pwm_analyze(demod, demod->f_buf, len / 2);
} else {
// Loop through all demodulators for all samples (CPU intensive!)
for (i = 0; i < demod->r_dev_num; i++) {
switch (demod->r_devs[i]->modulation) {
case OOK_PWM_D:
@ -931,16 +806,40 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) {
case OOK_PWM_P:
pwm_p_decode(demod, demod->r_devs[i], demod->f_buf, len / 2);
break;
case OOK_MANCHESTER:
manchester_decode(demod, demod->r_devs[i], demod->f_buf, len/2);
break;
case OOK_PWM_RAW:
pwm_raw_decode(demod, demod->r_devs[i], demod->f_buf, len / 2);
// Add pulse demodulators here
case OOK_PULSE_PWM_STARTBIT:
case OOK_PULSE_PWM_RAW:
case OOK_PULSE_MANCHESTER_ZEROBIT:
break;
default:
fprintf(stderr, "Unknown modulation %d in protocol!\n", demod->r_devs[i]->modulation);
}
}
// Detect a package and loop through demodulators with pulse data
while(detect_pulse_package(demod->f_buf, len/2, demod->level_limit, &demod->pulse_data)) {
for (i = 0; i < demod->r_dev_num; i++) {
switch (demod->r_devs[i]->modulation) {
// Old style decoders
case OOK_PWM_D:
case OOK_PWM_P:
break;
case OOK_PULSE_PWM_STARTBIT:
pulse_demod_pwm(&demod->pulse_data, demod->r_devs[i], 1);
break;
case OOK_PULSE_PWM_RAW:
pulse_demod_pwm(&demod->pulse_data, demod->r_devs[i], 0);
break;
case OOK_PULSE_MANCHESTER_ZEROBIT:
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) pulse_data_print(&demod->pulse_data);
if(debug_output) pulse_analyzer(&demod->pulse_data);
pulse_data_clear(&demod->pulse_data);
}
}
if (demod->save_data) {