pulse_detect: Adding FSK demodulation
First implementation with OOK/FSK package determination. Based on adaptive quantization with frequency estimators.
This commit is contained in:
parent
099ce87fbe
commit
f24127c383
3 changed files with 120 additions and 40 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue