diff --git a/include/pulse_detect.h b/include/pulse_detect.h index 501bd886..e82b9292 100644 --- a/include/pulse_detect.h +++ b/include/pulse_detect.h @@ -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 diff --git a/src/pulse_detect.c b/src/pulse_detect.c index 7997dec8..43c42ebd 100644 --- a/src/pulse_detect.c +++ b/src/pulse_detect.c @@ -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; } diff --git a/src/rtl_433.c b/src/rtl_433.c index ea19a163..fda4cbf3 100644 --- a/src/rtl_433.c +++ b/src/rtl_433.c @@ -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) {