parent
44247ffb53
commit
9e75e64787
@ -0,0 +1,259 @@ |
||||
/* DTMF coder
|
||||
* |
||||
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu> |
||||
* All Rights Reserved |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
#include <math.h> |
||||
#include "../libsample/sample.h" |
||||
#include "dtmf_decode.h" |
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define level2db(level) (20 * log10(level)) |
||||
#define db2level(db) pow(10, (double)db / 20.0) |
||||
|
||||
#define DTMF_LOW_1 697.0 |
||||
#define DTMF_LOW_2 770.0 |
||||
#define DTMF_LOW_3 852.0 |
||||
#define DTMF_LOW_4 941.0 |
||||
#define DTMF_HIGH_1 1209.0 |
||||
#define DTMF_HIGH_2 1336.0 |
||||
#define DTMF_HIGH_3 1477.0 |
||||
#define DTMF_HIGH_4 1633.0 |
||||
|
||||
static const char dtmf_digit[] = " 123A456B789C*0#D"; |
||||
|
||||
#ifdef DEBUG |
||||
const char *_debug_amplitude(double level) |
||||
{ |
||||
static char text[42]; |
||||
|
||||
strcpy(text, " : "); |
||||
if (level > 1.0) |
||||
level = 1.0; |
||||
if (level < -1.0) |
||||
level = -1.0; |
||||
text[20 + (int)(level * 20)] = '*'; |
||||
|
||||
return text; |
||||
} |
||||
#endif |
||||
|
||||
int dtmf_decode_init(dtmf_dec_t *dtmf, void *priv, void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas), int samplerate, double max_amplitude, double min_amplitude) |
||||
{ |
||||
int rc; |
||||
|
||||
memset(dtmf, 0, sizeof(*dtmf)); |
||||
dtmf->priv = priv; |
||||
dtmf->recv_digit = recv_digit; |
||||
dtmf->samplerate = samplerate; |
||||
dtmf->freq_tollerance = 3.0; |
||||
dtmf->max_amplitude = max_amplitude; |
||||
dtmf->min_amplitude = min_amplitude; |
||||
dtmf->forward_twist = db2level(4.0); |
||||
dtmf->reverse_twist = db2level(8.0); |
||||
dtmf->time_detect = (int)(0.025 * (double)samplerate); |
||||
dtmf->time_meas = (int)(0.015 * (double)samplerate); |
||||
dtmf->time_pause = (int)(0.010 * (double)samplerate); |
||||
|
||||
/* init fm demodulator */ |
||||
rc = fm_demod_init(&dtmf->demod_low, (double)samplerate, (DTMF_LOW_1 + DTMF_LOW_4) / 2.0, DTMF_LOW_4 - DTMF_LOW_1); |
||||
if (rc < 0) |
||||
goto error; |
||||
rc = fm_demod_init(&dtmf->demod_high, (double)samplerate, (DTMF_HIGH_1 + DTMF_HIGH_4) / 2.0, DTMF_HIGH_4 - DTMF_HIGH_1); |
||||
if (rc < 0) |
||||
goto error; |
||||
|
||||
/* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */ |
||||
iir_lowpass_init(&dtmf->freq_lp[0], 100.0, samplerate, 2); |
||||
iir_lowpass_init(&dtmf->freq_lp[1], 100.0, samplerate, 2); |
||||
|
||||
return 0; |
||||
|
||||
error: |
||||
dtmf_decode_exit(dtmf); |
||||
return rc; |
||||
} |
||||
|
||||
void dtmf_decode_exit(dtmf_dec_t *dtmf) |
||||
{ |
||||
fm_demod_exit(&dtmf->demod_low); |
||||
fm_demod_exit(&dtmf->demod_high); |
||||
} |
||||
|
||||
void dtmf_decode_filter(dtmf_dec_t *dtmf, sample_t *samples, int length, sample_t *frequency_low, sample_t *frequency_high, sample_t *amplitude_low, sample_t *amplitude_high) |
||||
{ |
||||
sample_t I_low[length], Q_low[length]; |
||||
sample_t I_high[length], Q_high[length]; |
||||
int i; |
||||
|
||||
fm_demodulate_real(&dtmf->demod_low, frequency_low, length, samples, I_low, Q_low); |
||||
fm_demodulate_real(&dtmf->demod_high, frequency_high, length, samples, I_high, Q_high); |
||||
/* peak amplitude is the length of I/Q vector
|
||||
* since we filter out the unwanted modulation product, the vector is only half of length */ |
||||
for (i = 0; i < length; i++) { |
||||
amplitude_low[i] = sqrt(I_low[i] * I_low[i] + Q_low[i] * Q_low[i]) * 2.0; |
||||
amplitude_high[i] = sqrt(I_high[i] * I_high[i] + Q_high[i] * Q_high[i]) * 2.0; |
||||
} |
||||
iir_process(&dtmf->freq_lp[0], frequency_low, length); |
||||
iir_process(&dtmf->freq_lp[1], frequency_high, length); |
||||
} |
||||
void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length) |
||||
{ |
||||
sample_t frequency_low[length], amplitude_low[length]; |
||||
sample_t frequency_high[length], amplitude_high[length]; |
||||
double tollerance, min_amplitude, max_amplitude, forward_twist, reverse_twist, f1, f2; |
||||
int time_detect, time_meas, time_pause; |
||||
int low = 0, high = 0; |
||||
char detected, digit; |
||||
int count; |
||||
int aplitude_ok, twist_ok; |
||||
int i; |
||||
|
||||
tollerance = dtmf->freq_tollerance; |
||||
min_amplitude = dtmf->min_amplitude; |
||||
max_amplitude = dtmf->max_amplitude; |
||||
forward_twist = dtmf->forward_twist; |
||||
reverse_twist = dtmf->reverse_twist; |
||||
time_detect = dtmf->time_detect; |
||||
time_meas = dtmf->time_meas; |
||||
time_pause = dtmf->time_pause; |
||||
detected = dtmf->detected; |
||||
count = dtmf->count; |
||||
|
||||
/* FM/AM demod */ |
||||
dtmf_decode_filter(dtmf, samples, length, frequency_low, frequency_high, amplitude_low, amplitude_high); |
||||
|
||||
for (i = 0; i < length; i++) { |
||||
#ifdef DEBUG |
||||
printf("%s %.5f\n", _debug_amplitude(samples[i]/2.0), samples[i]/2.0); |
||||
#endif |
||||
/* get frequency of low frequencies, correct amplitude drop at cutoff point */ |
||||
f1 = frequency_low[i] + (DTMF_LOW_1 + DTMF_LOW_4) / 2.0; |
||||
if (f1 >= DTMF_LOW_1 - tollerance && f1 <= DTMF_LOW_1 + tollerance) { |
||||
/* cutoff point */ |
||||
amplitude_low[i] /= 0.7071; |
||||
low = 1; |
||||
f1 -= DTMF_LOW_1; |
||||
} else |
||||
if (f1 >= DTMF_LOW_2 - tollerance && f1 <= DTMF_LOW_2 + tollerance) { |
||||
amplitude_low[i] /= 1.0734; |
||||
low = 2; |
||||
f1 -= DTMF_LOW_2; |
||||
} else |
||||
if (f1 >= DTMF_LOW_3 - tollerance && f1 <= DTMF_LOW_3 + tollerance) { |
||||
amplitude_low[i] /= 1.0389; |
||||
low = 3; |
||||
f1 -= DTMF_LOW_3; |
||||
} else |
||||
if (f1 >= DTMF_LOW_4 - tollerance && f1 <= DTMF_LOW_4 + tollerance) { |
||||
/* cutoff point */ |
||||
amplitude_low[i] /= 0.7071; |
||||
low = 4; |
||||
f1 -= DTMF_LOW_4; |
||||
} else |
||||
low = 0; |
||||
/* get frequency of high frequencies, correct amplitude drop at cutoff point */ |
||||
f2 = frequency_high[i] + (DTMF_HIGH_1 + DTMF_HIGH_4) / 2.0; |
||||
if (f2 >= DTMF_HIGH_1 - tollerance && f2 <= DTMF_HIGH_1 + tollerance) { |
||||
/* cutoff point */ |
||||
amplitude_high[i] /= 0.7071; |
||||
high = 1; |
||||
f2 -= DTMF_HIGH_1; |
||||
} else |
||||
if (f2 >= DTMF_HIGH_2 - tollerance && f2 <= DTMF_HIGH_2 + tollerance) { |
||||
amplitude_high[i] /= 1.0731; |
||||
high = 2; |
||||
f2 -= DTMF_HIGH_2; |
||||
} else |
||||
if (f2 >= DTMF_HIGH_3 - tollerance && f2 <= DTMF_HIGH_3 + tollerance) { |
||||
amplitude_high[i] /= 1.0372; |
||||
high = 3; |
||||
f2 -= DTMF_HIGH_3; |
||||
} else |
||||
if (f2 >= DTMF_HIGH_4 - tollerance && f2 <= DTMF_HIGH_4 + tollerance) { |
||||
/* cutoff point */ |
||||
amplitude_high[i] /= 0.7071; |
||||
high = 4; |
||||
f2 -= DTMF_HIGH_4; |
||||
} else |
||||
high = 0; |
||||
digit = 0; |
||||
aplitude_ok = 0; |
||||
twist_ok = 0; |
||||
if (low && high) { |
||||
digit = dtmf_digit[low*4+high]; |
||||
/* check for limits */ |
||||
if (amplitude_low[i] <= max_amplitude && amplitude_low[i] >= min_amplitude && amplitude_high[i] <= max_amplitude && amplitude_high[i] >= min_amplitude) { |
||||
aplitude_ok = 1; |
||||
#ifdef DEBUG |
||||
printf("%.5f %.5f %.1f\n", amplitude_low[i], amplitude_high[i], level2db(amplitude_high[i] / amplitude_low[i])); |
||||
#endif |
||||
if (amplitude_high[i] / amplitude_low[i] <= forward_twist && amplitude_low[i] / amplitude_high[i] <= reverse_twist) |
||||
twist_ok = 1; |
||||
} |
||||
} |
||||
|
||||
if (!detected) { |
||||
if (digit && aplitude_ok && twist_ok) { |
||||
if (count == 0) { |
||||
memset(&dtmf->meas, 0, sizeof(dtmf->meas)); |
||||
} |
||||
if (count >= time_meas) { |
||||
dtmf->meas.frequency_low += f1; |
||||
dtmf->meas.frequency_high += f2; |
||||
dtmf->meas.amplitude_low += amplitude_low[i]; |
||||
dtmf->meas.amplitude_high += amplitude_high[i]; |
||||
dtmf->meas.count++; |
||||
} |
||||
count++; |
||||
if (count >= time_detect) { |
||||
detected = digit; |
||||
dtmf->meas.frequency_low /= dtmf->meas.count; |
||||
dtmf->meas.frequency_high /= dtmf->meas.count; |
||||
dtmf->meas.amplitude_low /= dtmf->meas.count; |
||||
dtmf->meas.amplitude_high /= dtmf->meas.count; |
||||
dtmf->meas.count = 1; |
||||
dtmf->recv_digit(dtmf->priv, digit, &dtmf->meas); |
||||
} |
||||
} else |
||||
count = 0; |
||||
} else { |
||||
if (!digit || digit != detected || !aplitude_ok || !twist_ok) { |
||||
count++; |
||||
if (count >= time_pause) { |
||||
detected = 0; |
||||
#ifdef DEBUG |
||||
printf("lost!\n"); |
||||
#endif |
||||
} |
||||
} else |
||||
count = 0; |
||||
} |
||||
#ifdef DEBUG |
||||
if (digit) |
||||
printf("DTMF tone='%c' diff frequency=%.1f %.1f amplitude=%.1f %.1f dB (%s) twist=%.1f dB (%s)\n", digit, f1, f2, level2db(amplitude_low[i]), level2db(amplitude_high[i]), (aplitude_ok) ? "OK" : "nok", level2db(amplitude_high[i] / amplitude_low[i]), (twist_ok) ? "OK" : "nok"); |
||||
#endif |
||||
|
||||
dtmf->detected = detected; |
||||
dtmf->count = count; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,35 @@ |
||||
#include "../libfm/fm.h" |
||||
|
||||
typedef struct ftmf_meas { |
||||
double frequency_low; |
||||
double frequency_high; |
||||
double amplitude_low; |
||||
double amplitude_high; |
||||
int count; |
||||
} dtmf_meas_t; |
||||
|
||||
typedef struct dtmf_dec { |
||||
void *priv; |
||||
void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas); |
||||
int samplerate; /* samplerate */ |
||||
double freq_tollerance; /* +- limit of frequency deviation to allow */ |
||||
double min_amplitude; /* minimum amplitude relative to 0 dBm */ |
||||
double max_amplitude; /* maximum amplitude relative to 0 dBm */ |
||||
double forward_twist; /* how much do higher frequencies are louder than lower frequencies */ |
||||
double reverse_twist; /* how much do lower frequencies are louder than higher frequencies */ |
||||
int time_detect; |
||||
int time_meas; |
||||
int time_pause; |
||||
fm_demod_t demod_low; /* demodulator for low frequencies */ |
||||
fm_demod_t demod_high; /* demodulator for high frequencies */ |
||||
iir_filter_t freq_lp[2]; /* low pass to filter the frequency result */ |
||||
char detected; /* currently detected DTMF digit or 0 for no detection */ |
||||
int count; /* counter to count detection or loss (pause) of signal */ |
||||
dtmf_meas_t meas; /* measurements */ |
||||
} dtmf_dec_t; |
||||
|
||||
int dtmf_decode_init(dtmf_dec_t *dtmf, void *priv, void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas), int samplerate, double max_amplitude, double min_amplitude); |
||||
void dtmf_decode_exit(dtmf_dec_t *dtmf); |
||||
void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length); |
||||
void dtmf_decode_filter(dtmf_dec_t *dtmf, sample_t *samples, int length, sample_t *frequency_low, sample_t *frequency_high, sample_t *amplitude_low, sample_t *amplitude_high); |
||||
|
@ -0,0 +1,114 @@ |
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <math.h> |
||||
#include <string.h> |
||||
#include "../libdebug/debug.h" |
||||
#include "../libsample/sample.h" |
||||
#include "../libdtmf/dtmf_decode.h" |
||||
#include "../libdtmf/dtmf_encode.h" |
||||
|
||||
#define level2db(level) (20 * log10(level)) |
||||
#define db2level(db) pow(10, (double)db / 20.0) |
||||
|
||||
#define SAMPLERATE 8000 |
||||
|
||||
static double test_frequency[8] = { 697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0 }; |
||||
static const char *test_digits = "*#0123456789ABCD"; |
||||
|
||||
static sample_t samples[SAMPLERATE]; |
||||
|
||||
/* generate samples with two tones */ |
||||
static void generate_test_sample(double frequency1, double frequency2, double amplitude1, double amplitude2) |
||||
{ |
||||
int i; |
||||
double value; |
||||
|
||||
for (i = 0; i < SAMPLERATE; i++) { |
||||
value = cos(2.0 * M_PI * frequency1 / (double)SAMPLERATE * i) * amplitude1; |
||||
value += cos(2.0 * M_PI * frequency2 / (double)SAMPLERATE * i) * amplitude2; |
||||
samples[i] = value; |
||||
} |
||||
} |
||||
|
||||
static void check_level(sample_t *samples, const char *desc, double target, int when1, int when2) |
||||
{ |
||||
int i; |
||||
double amplitude = 0.0, diff; |
||||
|
||||
for (i = when1; i < when2; i++) { |
||||
amplitude += samples[i]; |
||||
} |
||||
amplitude = amplitude / (when2 - when1); |
||||
diff = fabs(amplitude - target); |
||||
printf("%s: amplitude between %d and %d ms is %.4f / %.4f db (expected %.4f)\n", desc, when1 * 1000 / SAMPLERATE, when2 * 1000 / SAMPLERATE, amplitude, level2db(amplitude), level2db(target)); |
||||
if (diff < -0.1 || diff > 0.1) |
||||
printf("**** ERROR: we expected a diff close to 0.0\n"); |
||||
else |
||||
printf("OK!\n"); |
||||
} |
||||
|
||||
static char got_digit; |
||||
|
||||
static void recv_digit(void *inst, char digit, dtmf_meas_t *meas) |
||||
{ |
||||
printf("decoded digit '%c' frequency %.1f %.1f amplitude %.1f %.1f dB\n", digit, meas->frequency_low, meas->frequency_high, level2db(meas->amplitude_low), level2db(meas->amplitude_high)); |
||||
got_digit = digit; |
||||
} |
||||
|
||||
int main(void) |
||||
{ |
||||
dtmf_dec_t dtmf_dec; |
||||
dtmf_enc_t dtmf_enc; |
||||
sample_t frequency1[SAMPLERATE], frequency2[SAMPLERATE], amplitude1[SAMPLERATE], amplitude2[SAMPLERATE]; |
||||
int f, i; |
||||
double target; |
||||
|
||||
dtmf_decode_init(&dtmf_dec, NULL, recv_digit, SAMPLERATE, db2level(0), db2level(-30.0)); |
||||
|
||||
for (f = 0; f < 8; f++) { |
||||
printf("Testing filter with frequency %.0f Hz:\n", test_frequency[f]); |
||||
generate_test_sample(test_frequency[f], 0.0, 1.0, 0.0); |
||||
|
||||
dtmf_decode_filter(&dtmf_dec, samples, SAMPLERATE, frequency1, frequency2, amplitude1, amplitude2); |
||||
|
||||
if (f == 0 || f == 3) |
||||
target = sqrt(0.5); |
||||
else if (f == 1 || f == 2) |
||||
target = 1.0; |
||||
else |
||||
target = 0.0; |
||||
check_level(amplitude1, "frequency level", target, 900 * SAMPLERATE / 1000, 1000 * SAMPLERATE / 1000); |
||||
if (f == 4 || f == 7) |
||||
target = sqrt(0.5); |
||||
else if (f == 5 || f == 6) |
||||
target = 1.0; |
||||
else |
||||
target = 0.0; |
||||
check_level(amplitude2, "frequency level", target, 900 * SAMPLERATE / 1000, 1000 * SAMPLERATE / 1000); |
||||
|
||||
puts(""); |
||||
} |
||||
|
||||
dtmf_encode_init(&dtmf_enc, SAMPLERATE, 1.0); |
||||
|
||||
for (i = 0; i < 16; i++) { |
||||
printf("Testing digit '%c' encoding and decoding:\n", test_digits[i]); |
||||
memset(samples, 0, sizeof(samples[0]) * SAMPLERATE); |
||||
dtmf_encode_set_tone(&dtmf_enc, test_digits[i]); |
||||
dtmf_encode(&dtmf_enc, samples + SAMPLERATE / 10, SAMPLERATE / 20); |
||||
got_digit = 0; |
||||
dtmf_decode(&dtmf_dec, samples, SAMPLERATE); |
||||
if (got_digit == 0) |
||||
printf("**** ERROR: we expected to decode digit '%c', but nothing was decoded\n", test_digits[i]); |
||||
else if (got_digit != test_digits[i]) |
||||
printf("**** ERROR: we expected to decode digit '%c', but we decoded digit '%c'\n", test_digits[i], got_digit); |
||||
else |
||||
printf("OK!\n"); |
||||
puts(""); |
||||
} |
||||
|
||||
dtmf_decode_exit(&dtmf_dec); |
||||
|
||||
return 0; |
||||
} |
||||
|
Loading…
Reference in new issue