Skip to content

Commit f2ad82c

Browse files
committed
Add pulse analyzer as filter to sample grabber
1 parent dbf5609 commit f2ad82c

4 files changed

Lines changed: 86 additions & 2 deletions

File tree

include/pulse_analyzer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
struct r_device;
1818

19+
/// Check the statistics of a pulse data structure and return a summary.
20+
int pulse_analyzer_check(pulse_data_t *data, int package_type, struct r_device *device);
21+
1922
/// Analyze and print result.
2023
void pulse_analyzer(pulse_data_t *data, int package_type, struct r_device *device);
2124

include/r_private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct dm_state {
5555
pulse_data_t pulse_data;
5656
pulse_data_t fsk_pulse_data;
5757
unsigned frame_event_count;
58+
int frame_quality;
5859
unsigned frame_start_ago;
5960
unsigned frame_end_ago;
6061
struct timeval now;

src/pulse_analyzer.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,71 @@ static void hexstr_print(hexstr_t *h, FILE *out)
201201

202202
#define TOLERANCE (0.2f) // 20% tolerance should still discern between the pulse widths: 0.33, 0.66, 1.0
203203

204+
/// Check the statistics of a pulse data structure and return a summary.
205+
int pulse_analyzer_check(pulse_data_t *data, int package_type, r_device *device)
206+
{
207+
(void)package_type;
208+
(void)device; // TODO: use device for logging
209+
210+
if (data->num_pulses == 0) {
211+
fprintf(stderr, "Skipping frame without pulses\n");
212+
return 0;
213+
}
214+
215+
// Generate pulse period data (pulse + gap, trailing gap)
216+
pulse_data_t pulse_periods_pg = {0};
217+
pulse_periods_pg.num_pulses = data->num_pulses;
218+
for (unsigned n = 0; n < pulse_periods_pg.num_pulses; ++n) {
219+
pulse_periods_pg.pulse[n] = data->pulse[n] + data->gap[n];
220+
}
221+
// Generate pulse period data (gap + pulse, leading gap)
222+
pulse_data_t pulse_periods_gp = {0};
223+
pulse_periods_gp.num_pulses = data->num_pulses;
224+
pulse_periods_gp.pulse[0] = data->pulse[0];
225+
for (unsigned n = 1; n < pulse_periods_gp.num_pulses; ++n) {
226+
pulse_periods_gp.pulse[n] = data->pulse[n] + data->gap[n - 1];
227+
}
228+
229+
histogram_t hist_pulses = {0};
230+
histogram_t hist_gaps = {0};
231+
histogram_t hist_periods_pg = {0}; // Pulse+Gap periods
232+
histogram_t hist_periods_gp = {0}; // Gap+Pulse periods
233+
histogram_t hist_timings = {0};
234+
235+
// Generate statistics
236+
histogram_sum(&hist_pulses, data->pulse, data->num_pulses, TOLERANCE);
237+
histogram_sum(&hist_gaps, data->gap, data->num_pulses - 1, TOLERANCE); // Leave out last gap (end)
238+
histogram_sum(&hist_periods_pg, pulse_periods_pg.pulse, pulse_periods_pg.num_pulses - 1, TOLERANCE); // Leave out last gap (end)
239+
histogram_sum(&hist_periods_gp, pulse_periods_gp.pulse, pulse_periods_gp.num_pulses, TOLERANCE);
240+
histogram_sum(&hist_timings, data->pulse, data->num_pulses, TOLERANCE);
241+
histogram_sum(&hist_timings, data->gap, data->num_pulses, TOLERANCE);
242+
243+
// Fuse overlapping bins
244+
histogram_fuse_bins(&hist_pulses, TOLERANCE);
245+
histogram_fuse_bins(&hist_gaps, TOLERANCE);
246+
histogram_fuse_bins(&hist_periods_pg, TOLERANCE);
247+
histogram_fuse_bins(&hist_timings, TOLERANCE);
248+
249+
histogram_sort_mean(&hist_pulses); // Easier to work with sorted data
250+
histogram_sort_mean(&hist_gaps);
251+
if (hist_pulses.bins[0].mean == 0) {
252+
histogram_delete_bin(&hist_pulses, 0);
253+
} // Remove FSK initial zero-bin
254+
255+
// Attempt to find a matching modulation
256+
if (data->num_pulses == 1) {
257+
fprintf(stderr, "Skipping frame with single pulse\n");
258+
return 0;
259+
}
260+
else if (hist_pulses.bins_count == 1 && hist_gaps.bins_count == 1) {
261+
fprintf(stderr, "Skipping frame with no data\n");
262+
return 0;
263+
}
264+
265+
fprintf(stderr, "Frame with %u bins found\n", hist_pulses.bins_count + hist_gaps.bins_count);
266+
return 1;
267+
}
268+
204269
/// Analyze the statistics of a pulse data structure and print result
205270
void pulse_analyzer(pulse_data_t *data, int package_type, r_device* device)
206271
{

src/rtl_433.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ static void reset_sdr_callback(r_cfg_t *cfg)
407407
demod->frame_start_ago = 0;
408408
demod->frame_end_ago = 0;
409409
demod->frame_event_count = 0;
410+
demod->frame_quality = 0;
410411

411412
demod->min_level_auto = 0.0f;
412413
demod->noise_level = 0.0f;
@@ -594,6 +595,11 @@ static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
594595
r_device device = {.log_fn = log_device_handler, .output_ctx = cfg};
595596
pulse_analyzer(&demod->pulse_data, package_type, &device);
596597
}
598+
if (cfg->grab_mode == 4 && p_events == 0) {
599+
r_device device = {.log_fn = log_device_handler, .output_ctx = cfg};
600+
int p_quality = pulse_analyzer_check(&demod->pulse_data, package_type, &device);
601+
demod->frame_quality = p_quality > demod->frame_quality ? p_quality : demod->frame_quality;
602+
}
597603

598604
} else if (package_type == PULSE_DATA_FSK) {
599605
calc_rssi_snr(cfg, &demod->fsk_pulse_data);
@@ -621,6 +627,11 @@ static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
621627
r_device device = {.log_fn = log_device_handler, .output_ctx = cfg};
622628
pulse_analyzer(&demod->fsk_pulse_data, package_type, &device);
623629
}
630+
if (cfg->grab_mode == 4 && p_events == 0) {
631+
r_device device = {.log_fn = log_device_handler, .output_ctx = cfg};
632+
int p_quality = pulse_analyzer_check(&demod->fsk_pulse_data, package_type, &device);
633+
demod->frame_quality = p_quality > demod->frame_quality ? p_quality : demod->frame_quality;
634+
}
624635
} // if (package_type == ...
625636
d_events += p_events;
626637
} // while (package_type)...
@@ -633,16 +644,18 @@ static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
633644
if (demod->samp_grab) {
634645
if (cfg->grab_mode == 1
635646
|| (cfg->grab_mode == 2 && demod->frame_event_count == 0)
636-
|| (cfg->grab_mode == 3 && demod->frame_event_count > 0)) {
647+
|| (cfg->grab_mode == 3 && demod->frame_event_count > 0)
648+
|| (cfg->grab_mode == 4 && demod->frame_event_count == 0 && demod->frame_quality > 0)) {
637649
unsigned frame_pad = n_samples / 8; // this could also be a fixed value, e.g. 10000 samples
638650
unsigned start_padded = demod->frame_start_ago + frame_pad;
639651
unsigned end_padded = demod->frame_end_ago - frame_pad;
640652
unsigned len_padded = start_padded - end_padded;
641653
samp_grab_write(demod->samp_grab, len_padded, end_padded);
642654
}
643655
}
644-
demod->frame_start_ago = 0;
656+
demod->frame_start_ago = 0;
645657
demod->frame_event_count = 0;
658+
demod->frame_quality = 0;
646659
}
647660

648661
// dump partial pulse_data for this buffer
@@ -1060,6 +1073,8 @@ static void parse_conf_option(r_cfg_t *cfg, int opt, char *arg)
10601073
cfg->grab_mode = 2;
10611074
else if (strcasecmp(arg, "known") == 0)
10621075
cfg->grab_mode = 3;
1076+
else if (strcasecmp(arg, "undecoded") == 0)
1077+
cfg->grab_mode = 4;
10631078
else
10641079
cfg->grab_mode = atobv(arg, 1);
10651080
if (cfg->grab_mode && !cfg->demod->samp_grab)

0 commit comments

Comments
 (0)