pulse_detect: Adding FSK demodulation

First implementation with OOK/FSK package determination.
Based on adaptive quantization with frequency estimators.
This commit is contained in:
Tommy Vestermark 2015-08-18 07:52:40 +02:00
parent 099ce87fbe
commit f24127c383
3 changed files with 120 additions and 40 deletions

View file

@ -14,6 +14,8 @@
#include <stdint.h>
#define PD_MAX_PULSES 1000 // Maximum number of pulses before forcing End Of Package
#define PD_MIN_PULSES 4 // Minimum number of pulses before declaring a proper package
#define PD_MIN_PULSE_SAMPLES 10 // Minimum number of samples in a pulse for proper detection
#define PD_MIN_GAP_MS 10 // Minimum gap size in milliseconds to exceed to declare End Of Package
#define PD_MAX_GAP_MS 100 // Maximum gap size in milliseconds to exceed to declare End Of Package
#define PD_MAX_GAP_RATIO 10 // Ratio gap/pulse width to exceed to declare End Of Package (heuristic)
@ -43,14 +45,19 @@ void pulse_data_clear(pulse_data_t *data); // Clear the struct
void pulse_data_print(const pulse_data_t *data);
/// Demodulate On/Off Keying from an envelope signal
/// Demodulate On/Off Keying (OOK) and Frequency Shift Keying (FSK) from an envelope signal
///
/// Function is stateful and can be called with chunks of input data
/// @param envelope_data: Samples with amplitude envelope of carrier
/// @param fm_data: Samples with frequency offset from center frequency
/// @param len: Number of samples in input buffers
/// @param samp_rate: Sample rate in samples per second
/// @param *pulses: Will return a pulse_data_t structure
/// @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, uint32_t samp_rate, pulse_data_t *pulses);
/// @param *fsk_pulses: Will return a pulse_data_t structure for FSK demodulated data
/// @return 0 if all input sample data is processed
/// @return 1 if OOK package is detected (but all sample data is still not completely processed)
/// @return 2 if FSK package is detected (but all sample data is still not completely processed)
int detect_pulse_package(const int16_t *envelope_data, const int16_t *fm_data, uint32_t len, int16_t level_limit, uint32_t samp_rate, pulse_data_t *pulses, pulse_data_t *fsk_pulses);
/// Analyze and print result

View file

@ -41,11 +41,23 @@ typedef struct {
unsigned int max_pulse; // Size of biggest pulse detected
unsigned int data_counter; // Counter for how much of data chunck is processed
unsigned int fsk_pulse_length; // Counter for internal pulse detection
enum {
PD_STATE_FSK_HIGH = 0,
PD_STATE_FSK_LOW = 1
} state_fsk;
int16_t fm_base_est; // Estimate for the FM base frequency for
int16_t fm_delta_est;
} pulse_state_t;
static pulse_state_t pulse_state;
#define FSK_EST_RATIO 32 // Constant for slowness of FSK estimators
#define FSK_DEFAULT_FM_DELTA 8000 // Default estimate for frequency delta
int detect_pulse_package(const int16_t *envelope_data, uint32_t len, int16_t level_limit, uint32_t samp_rate, pulse_data_t *pulses) {
int detect_pulse_package(const int16_t *envelope_data, const int16_t *fm_data, uint32_t len, int16_t level_limit, uint32_t samp_rate, pulse_data_t *pulses, pulse_data_t *fsk_pulses) {
const unsigned int samples_per_ms = samp_rate / 1000;
const int16_t HYSTERESIS = level_limit / 8; // ±12%
pulse_state_t *s = &pulse_state;
@ -57,6 +69,10 @@ int detect_pulse_package(const int16_t *envelope_data, uint32_t len, int16_t lev
s->pulse_length = 0;
s->max_pulse = 0;
if (envelope_data[s->data_counter] > (level_limit + HYSTERESIS)) {
s->fsk_pulse_length = 0;
s->fm_base_est = 0; // FM low frequency may be everywhere
s->fm_delta_est = FSK_DEFAULT_FM_DELTA; // FM delta default estimate
s->state_fsk = PD_STATE_FSK_HIGH; // Base frequency = high pulse
s->state = PD_STATE_PULSE;
}
break;
@ -68,16 +84,54 @@ int detect_pulse_package(const int16_t *envelope_data, uint32_t len, int16_t lev
pulses->pulse[pulses->num_pulses] = s->pulse_length; // Store pulse width
s->max_pulse = max(s->pulse_length, s->max_pulse); // Find largest pulse
// EOP if pulse is too long
if (s->pulse_length > (PD_MAX_PULSE_MS * samples_per_ms)) {
pulses->num_pulses++; // Store last pulse (with no gap)
// EOP if FSK modulation detected within pulse
if(fsk_pulses->num_pulses > PD_MIN_PULSES) {
if(s->state_fsk == PD_STATE_FSK_HIGH) {
fsk_pulses->pulse[fsk_pulses->num_pulses] = s->fsk_pulse_length; // Store last pulse
fsk_pulses->gap[fsk_pulses->num_pulses] = 0; // Zero gap at end
} else {
fsk_pulses->gap[fsk_pulses->num_pulses] = s->fsk_pulse_length; // Store last gap
}
fsk_pulses->num_pulses++;
s->state = PD_STATE_IDLE;
return 1; // End Of Package!!
return 2; // Signal FSK package
} else {
fsk_pulses->num_pulses = 0; // Clear pulses (should be more effective...)
}
s->pulse_length = 0;
s->state = PD_STATE_GAP;
}
} else {
// FSK demodulation is only relevant when a carrier is present
s->fsk_pulse_length++;
int16_t fm_n = fm_data[s->data_counter]; // Get current FM sample
int16_t fm_delta = abs(fm_n - s->fm_base_est); // Get delta from base frequency estimate
switch(s->state_fsk) {
case PD_STATE_FSK_HIGH:
if (s->pulse_length < PD_MIN_PULSE_SAMPLES) { // Initial samples in OOK pulse?
s->fm_base_est = s->fm_base_est - s->fm_base_est/2 + fm_n/2; // Quick initial estimator
} else if (fm_delta < s->fm_delta_est/2) { // Freq offset below delta threshold?
s->fm_base_est = s->fm_base_est - s->fm_base_est/FSK_EST_RATIO + fm_n/FSK_EST_RATIO; // Slow estimator
} else { // Above threshold
fsk_pulses->pulse[fsk_pulses->num_pulses] = s->fsk_pulse_length; // Store pulse width
s->fsk_pulse_length = 0;
s->state_fsk = PD_STATE_FSK_LOW;
}
break;
case PD_STATE_FSK_LOW:
if (fm_delta > s->fm_delta_est/2) { // Freq offset above delta threshold?
s->fm_delta_est = s->fm_delta_est - s->fm_delta_est/FSK_EST_RATIO + fm_delta/FSK_EST_RATIO; // Slow estimator
} else { // Below threshold
fsk_pulses->gap[fsk_pulses->num_pulses] = s->fsk_pulse_length; // Store gap width
fsk_pulses->num_pulses++;
s->fsk_pulse_length = 0;
s->state_fsk = PD_STATE_FSK_HIGH;
}
break;
default:
fprintf(stderr, "pulse_demod(): Unknown FSK state!!\n");
s->state_fsk = PD_STATE_FSK_HIGH;
} // switch(s->state_fsk)
} // if
break;
case PD_STATE_GAP:
s->pulse_length++;
@ -93,6 +147,10 @@ int detect_pulse_package(const int16_t *envelope_data, uint32_t len, int16_t lev
}
s->pulse_length = 0;
s->fsk_pulse_length = 0;
s->fm_base_est = 0; // FM low frequency may be everywhere
s->fm_delta_est = FSK_DEFAULT_FM_DELTA; // FM delta default estimate
s->state_fsk = PD_STATE_FSK_HIGH; // Base frequency = high pulse
s->state = PD_STATE_PULSE;
}

View file

@ -64,6 +64,7 @@ struct dm_state {
struct protocol_state *r_devs[MAX_PROTOCOLS];
pulse_data_t pulse_data;
pulse_data_t fsk_pulse_data;
};
void usage(r_device *devices) {
@ -667,35 +668,48 @@ static void rtlsdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx) {
}
}
// Detect a package and loop through demodulators with pulse data
while(detect_pulse_package(demod->am_buf, len/2, demod->level_limit, samp_rate, &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_PCM_RZ:
pulse_demod_pcm_rz(&demod->pulse_data, demod->r_devs[i]);
break;
case OOK_PULSE_PPM_RAW:
pulse_demod_ppm(&demod->pulse_data, demod->r_devs[i]);
break;
case OOK_PULSE_PWM_RAW:
pulse_demod_pwm(&demod->pulse_data, demod->r_devs[i]);
break;
case OOK_PULSE_PWM_TERNARY:
pulse_demod_pwm_ternary(&demod->pulse_data, demod->r_devs[i]);
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 > 1) pulse_data_print(&demod->pulse_data);
if(debug_output) pulse_analyzer(&demod->pulse_data);
pulse_data_clear(&demod->pulse_data);
int package_type = 1; // Just to get us started
while(package_type) {
package_type = detect_pulse_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(debug_output) fprintf(stderr, "Detected OOK package\n");
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_PCM_RZ:
pulse_demod_pcm_rz(&demod->pulse_data, demod->r_devs[i]);
break;
case OOK_PULSE_PPM_RAW:
pulse_demod_ppm(&demod->pulse_data, demod->r_devs[i]);
break;
case OOK_PULSE_PWM_RAW:
pulse_demod_pwm(&demod->pulse_data, demod->r_devs[i]);
break;
case OOK_PULSE_PWM_TERNARY:
pulse_demod_pwm_ternary(&demod->pulse_data, demod->r_devs[i]);
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 > 1) pulse_data_print(&demod->pulse_data);
if(debug_output) pulse_analyzer(&demod->pulse_data);
pulse_data_clear(&demod->pulse_data);
pulse_data_clear(&demod->fsk_pulse_data);
} else if (package_type == 2) {
if(debug_output) fprintf(stderr, "Detected FSK package\n");
if(debug_output > 1) pulse_data_print(&demod->fsk_pulse_data);
if(debug_output) pulse_analyzer(&demod->fsk_pulse_data);
pulse_data_clear(&demod->pulse_data);
pulse_data_clear(&demod->fsk_pulse_data);
}
}
}
@ -767,6 +781,7 @@ int main(int argc, char **argv) {
demod->level_limit = DEFAULT_LEVEL_LIMIT;
pulse_data_clear(&demod->pulse_data);
pulse_data_clear(&demod->fsk_pulse_data);
while ((opt = getopt(argc, argv, "x:z:p:Dtam:r:c:l:d:f:g:s:b:n:SR:")) != -1) {
switch (opt) {