From 2d77f9649d8e0c1f873e766549869cee0e0c7063 Mon Sep 17 00:00:00 2001 From: Leah Rowe Date: Fri, 13 Mar 2026 02:06:29 +0000 Subject: spkmodem-decode: edge detection *and* amplitude for pulses, we currently use amplitude detection. edge detection is better, because weak / low gain signals will be more reliable. if audio is coming in on/from a system that does automatic gain adjustment, this once again is more robust too. microphones and speakers (which people often use with spkmodem if nothing else available) often clamp amplitude, to an extent that this software may not detect those pulses reliably that way. so we detect slope edges instead. this causes very little performance penalty (use of abs(), that's about it) however, edge detection is inherently vulnerable to noise, so we will also detect amplitude. this acts as an effective noise filter, while still improving pulse detection. Signed-off-by: Leah Rowe --- util/spkmodem_decode/spkmodem-decode.c | 36 ++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/util/spkmodem_decode/spkmodem-decode.c b/util/spkmodem_decode/spkmodem-decode.c index 0f28bc6f..40581dd7 100644 --- a/util/spkmodem_decode/spkmodem-decode.c +++ b/util/spkmodem_decode/spkmodem-decode.c @@ -206,6 +206,9 @@ struct decoder_state { int freq_max; int freq_threshold; int learn_frames; + + /* previous sample used for edge detection */ + signed short prev_sample; }; static const char *argv0; @@ -445,9 +448,11 @@ decode_pulse(struct decoder_state *st) { unsigned char old_ring, old_sep; unsigned char new_pulse; + signed short sample; int ringpos; int sep_pos; - signed short sample; + int diff_edge; + int diff_amp; ringpos = st->ringpos; sep_pos = st->sep_pos; @@ -483,18 +488,33 @@ decode_pulse(struct decoder_state *st) sample = read_sample(st); /* - * Convert the waveform sample into a pulse (0 or 1). + * Avoid startup edge. Since + * it's zero at startup, this + * may wrongly produce a pulse + */ + if (st->sample_count == 0) + st->prev_sample = sample; + + /* + * Detect edges instead of amplitude. + * This is more tolerant of weak microphones + * and speaker distortion.. * - * The unsigned comparison creates a small dead zone near zero, - * suppressing small amplitude noise from microphones or - * cheap ADCs. Real PC speaker tones are far outside this - * range, so they still produce clean pulses. + * However, we check both slope edges and + * amplitude, to mitagate noise. */ - if ((unsigned)(sample + THRESHOLD) - > (unsigned)(2 * THRESHOLD)) + diff_amp = sample; + diff_edge = sample - st->prev_sample; + if (diff_edge < 0) + diff_edge = -diff_edge; + if (diff_amp < 0) + diff_amp = -diff_amp; + if (diff_edge > THRESHOLD && + diff_amp > THRESHOLD) new_pulse = 1; else new_pulse = 0; + st->prev_sample = sample; st->pulse[ringpos] = new_pulse; st->freq_separator += new_pulse; -- cgit v1.2.1