From 3e30434a0c9d112e1026c41ef03cec484cfdff16 Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Tue, 11 May 2010 22:38:36 +0800 Subject: [PATCH] Fixed a typo in spandsp's msvc/inttypes.h Updated sig_tone processing in spandsp to the latest, to allow moy to proceed with his signaling work. --- libs/spandsp/src/sig_tone.c | 625 +++++++++----------- libs/spandsp/src/spandsp/private/sig_tone.h | 195 +++--- libs/spandsp/src/spandsp/sig_tone.h | 89 ++- libs/spandsp/tests/sig_tone_tests.c | 356 ++++++++--- 4 files changed, 698 insertions(+), 567 deletions(-) diff --git a/libs/spandsp/src/sig_tone.c b/libs/spandsp/src/sig_tone.c index 0183dadedb..5a4de1a298 100644 --- a/libs/spandsp/src/sig_tone.c +++ b/libs/spandsp/src/sig_tone.c @@ -1,8 +1,8 @@ /* * SpanDSP - a series of DSP components for telephony * - * sig_tone.c - Signalling tone processing for the 2280Hz, 2600Hz and similar - * signalling tone used in older protocols. + * sig_tone.c - Signalling tone processing for the 2280Hz, 2400Hz, 2600Hz + * and similar signalling tones used in older protocols. * * Written by Steve Underwood * @@ -23,7 +23,7 @@ * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: sig_tone.c,v 1.33 2009/09/04 14:38:46 steveu Exp $ + * $Id: sig_tone.c,v 1.39 2010/03/11 14:22:30 steveu Exp $ */ /*! \file */ @@ -53,6 +53,7 @@ #include "spandsp/saturated.h" #include "spandsp/vector_int.h" #include "spandsp/complex.h" +#include "spandsp/power_meter.h" #include "spandsp/dds.h" #include "spandsp/super_tone_rx.h" #include "spandsp/sig_tone.h" @@ -62,56 +63,64 @@ /*! PI */ #define PI 3.14159265358979323 -/* The coefficients for the data notch filter. This filter is also the - guard filter for tone detection. */ +enum +{ + NOTCH_COEFF_SET_2280HZ = 0, + NOTCH_COEFF_SET_2400HZ, + NOTCH_COEFF_SET_2600HZ +}; -sig_tone_descriptor_t sig_tones[4] = +/* The coefficients for the data notch filters. These filters are also the + guard filters for tone detection. */ +static const sig_tone_notch_coeffs_t notch_coeffs[3] = +{ + { /* 2280 Hz */ +#if defined(SPANDSP_USE_FIXED_POINT) + { 3600, 14397, 32767}, + { 0, -9425, -28954}, + { 0, 14196, 32767}, + { 0, -17393, -28954}, + 12, +#else + {0.878906f, 0.439362f, 1.0f}, + {0.0f, -0.287627f, -0.883605f}, + {0.0f, 0.433228f, 1.0f}, + {0.0f, -0.530792f, -0.883605f}, +#endif + }, + { /* 2400Hz */ +#if defined(SPANDSP_USE_FIXED_POINT) + { 3530, 20055, 32767}, + { 0, -14950, -28341}, + { 0, 20349, 32767}, + { 0, -22633, -28341}, + 12, +#else + {0.862000f, 0.612055f, 1.0f}, + {0.0f, -0.456264f, -0.864899f}, + {0.0f, 0.621021f, 1.0f}, + {0.0f, -0.690738f, -0.864899f}, +#endif + }, + { /* 2600Hz */ +#if defined(SPANDSP_USE_FIXED_POINT) + { 3530, 29569, 32767}, + { 0, -24010, -28341}, + { 0, 29844, 32767}, + { 0, -31208, -28341}, + 12, +#else + {0.862000f, 0.902374f, 1.0f}, + {0.0f, -0.732727f, -0.864899f}, + {0.0f, 0.910766f, 1.0f}, + {0.0f, -0.952393f, -0.864899f}, +#endif + } +}; + +static const sig_tone_flat_coeffs_t flat_coeffs[1] = { { - /* 2280Hz (e.g. AC15, and many other European protocols) */ - {2280, 0}, - {{-10, -20}, {0, 0}}, /* -10+-1 dBmO and -20+-1 dBm0 */ - ms_to_samples(400), /* 300ms to 550ms */ - - ms_to_samples(225), - - ms_to_samples(225), - TRUE, - - 24, - 64, - - 1, - { - { -#if defined(SPANDSP_USE_FIXED_POINT) - { 3600, 14397, 32767}, - { 0, -9425, -28954}, - { 0, 14196, 32767}, - { 0, -17393, -28954}, - 12, -#else - {0.878906f, 0.439362f, 1.0f}, - {0.0f, -0.287627f, -0.883605f}, - {0.0f, 0.433228f, 1.0f}, - {0.0f, -0.530792f, -0.883605f}, -#endif - }, - { -#if defined(SPANDSP_USE_FIXED_POINT) - { 0, 0, 0}, - { 0, 0, 0}, - { 0, 0, 0}, - { 0, 0, 0}, - 0, -#else - {0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f}, -#endif - } - }, #if defined(SPANDSP_USE_FIXED_POINT) { 12900, -16384, -16384}, { 0, -8578, -11796}, @@ -120,163 +129,96 @@ sig_tone_descriptor_t sig_tones[4] = {0.393676f, -0.5f, -0.5f}, {0.0f, -0.261778f, -0.359985f}, #endif + } +}; - 31744, - 1024, +static const sig_tone_descriptor_t sig_tones[3] = +{ + { + /* 2280Hz (e.g. AC15, and many other European protocols) */ + {2280, 0}, + {{-10, -20}, {0, 0}}, /* -10+-1 dBm0 and -20+-1 dBm0 */ + ms_to_samples(400), /* High to low timout - 300ms to 550ms */ + ms_to_samples(225), /* Sharp to flat timeout */ + ms_to_samples(225), /* Notch insertion timeout */ - 31744, - 187, - - 31744, - 187, - - -1, - -32, - - 57 + ms_to_samples(3), /* Tone on persistence check */ + ms_to_samples(8), /* Tone off persistence check */ + + 1, + { + ¬ch_coeffs[NOTCH_COEFF_SET_2280HZ], + NULL, + }, + &flat_coeffs[NOTCH_COEFF_SET_2280HZ], + + 13.0f, + -30.0f, + -30.0f }, { /* 2600Hz (e.g. many US protocols) */ {2600, 0}, {{-8, -8}, {0, 0}}, - ms_to_samples(400), - + ms_to_samples(0), + ms_to_samples(0), ms_to_samples(225), - ms_to_samples(225), - FALSE, - - 24, - 64, + ms_to_samples(3), + ms_to_samples(8), 1, { - { -#if defined(SPANDSP_USE_FIXED_POINT) - { 3539, 29569, 32767}, - { 0, -24010, -28341}, - { 0, 29844, 32767}, - { 0, -31208, -28341}, - 12, -#else - {0.864014f, 0.902374f, 1.0f}, - {0.0f, -0.732727f, -0.864899f}, - {0.0f, 0.910766f, 1.0f}, - {0.0f, -0.952393f, -0.864899f}, -#endif - }, - { -#if defined(SPANDSP_USE_FIXED_POINT) - { 0, 0, 0}, - { 0, 0, 0}, - { 0, 0, 0}, - { 0, 0, 0}, - 0, -#else - {0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f}, -#endif - } + ¬ch_coeffs[NOTCH_COEFF_SET_2600HZ], + NULL, }, -#if defined(SPANDSP_USE_FIXED_POINT) - { 32768, 0, 0}, - { 0, 0, 0}, - 15, -#else - {1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f}, -#endif + NULL, - 31744, - 1024, - - 31744, - 170, - - 31744, - 170, - - -1, - -32, - - 52 + 15.6f, + -30.0f, + -30.0f }, { /* 2400Hz/2600Hz (e.g. SS5 and SS5bis) */ - {2600, 2400}, + {2400, 2600}, {{-8, -8}, {-8, -8}}, - ms_to_samples(400), - + ms_to_samples(0), + ms_to_samples(0), ms_to_samples(225), - ms_to_samples(225), - FALSE, - - 24, - 64, + ms_to_samples(3), + ms_to_samples(8), 2, { - { -#if defined(SPANDSP_USE_FIXED_POINT) - { 3539, 29569, 32767}, - { 0, -24010, -28341}, - { 0, 29844, 32767}, - { 0, -31208, -28341}, - 12, -#else - {0.864014f, 0.902374f, 1.0f}, - {0.0f, -0.732727f, -0.864899f}, - {0.0f, 0.910766f, 1.0f}, - {0.0f, -0.952393f, -0.864899f}, -#endif - }, - { -#if defined(SPANDSP_USE_FIXED_POINT) - { 3539, 20349, 32767}, - { 0, -22075, -31856}, - { 0, 20174, 32767}, - { 0, -17832, -31836}, - 12, -#else - {0.864014f, 0.621007f, 1.0f}, - {0.0f, -0.673667f, -0.972167f}, - {0.0f, 0.615669f, 1.0f}, - {0.0f, -0.544180f, -0.971546f}, -#endif - } + ¬ch_coeffs[NOTCH_COEFF_SET_2400HZ], + ¬ch_coeffs[NOTCH_COEFF_SET_2600HZ] }, -#if defined(SPANDSP_USE_FIXED_POINT) - { 32768, 0, 0}, - { 0, 0, 0}, - 15, -#else - {1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f}, -#endif + NULL, - 31744, - 1024, - - 31744, - 170, - - 31744, - 170, - - -1, - -32, - - 52 + 15.6f, + -30.0f, + -30.0f } }; +static const int tone_present_bits[2] = +{ + SIG_TONE_1_PRESENT, + SIG_TONE_2_PRESENT +}; + +static const int tone_change_bits[2] = +{ + SIG_TONE_1_CHANGE, + SIG_TONE_2_CHANGE +}; + SPAN_DECLARE(int) sig_tone_tx(sig_tone_tx_state_t *s, int16_t amp[], int len) { int i; int j; + int k; int n; int16_t tone; int need_update; @@ -306,7 +248,7 @@ SPAN_DECLARE(int) sig_tone_tx(sig_tone_tx_state_t *s, int16_t amp[], int len) if (!(s->current_tx_tone & SIG_TONE_TX_PASSTHROUGH)) vec_zeroi16(&[i], n); /*endif*/ - if ((s->current_tx_tone & (SIG_TONE_1_PRESENT || SIG_TONE_2_PRESENT))) + if ((s->current_tx_tone & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT))) { /* Are we in the early phase (high tone energy level), or the sustaining phase (low tone energy level) of tone generation? */ @@ -325,26 +267,19 @@ SPAN_DECLARE(int) sig_tone_tx(sig_tone_tx_state_t *s, int16_t amp[], int len) high_low = 1; } /*endif*/ - if ((s->current_tx_tone & SIG_TONE_1_PRESENT) && s->phase_rate[0]) + for (k = 0; k < s->desc->tones; k++) { - for (j = i; j < i + n; j++) + if ((s->current_tx_tone & tone_present_bits[k]) && s->phase_rate[k]) { - tone = dds_mod(&(s->phase_acc[0]), s->phase_rate[0], s->tone_scaling[0][high_low], 0); - amp[j] = saturate(amp[j] + tone); + for (j = i; j < i + n; j++) + { + tone = dds_mod(&(s->phase_acc[k]), s->phase_rate[k], s->tone_scaling[k][high_low], 0); + amp[j] = saturate(amp[j] + tone); + } + /*endfor*/ } - /*endfor*/ + /*endif*/ } - /*endif*/ - if ((s->current_tx_tone & SIG_TONE_2_PRESENT) && s->phase_rate[1]) - { - for (j = i; j < i + n; j++) - { - tone = dds_mod(&(s->phase_acc[1]), s->phase_rate[1], s->tone_scaling[1][high_low], 0); - amp[j] = saturate(amp[j] + tone); - } - /*endfor*/ - } - /*endif*/ } /*endif*/ if (need_update && s->sig_update) @@ -421,82 +356,74 @@ SPAN_DECLARE(int) sig_tone_tx_free(sig_tone_tx_state_t *s) SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len) { #if defined(SPANDSP_USE_FIXED_POINT) - int32_t x; - int32_t notched_signal; - int32_t bandpass_signal; + int16_t x; + int32_t v; + int16_t notched_signal[2]; + int16_t bandpass_signal; #else float x; - float notched_signal; + float v; + float notched_signal[2]; float bandpass_signal; #endif int i; int j; - int32_t mown_notch[2]; - int32_t mown_bandpass; + int32_t notch_power[2]; + int32_t flat_power; for (i = 0; i < len; i++) { - if (s->signaling_state_duration < INT_MAX) - s->signaling_state_duration++; + if (s->signalling_state_duration < INT_MAX) + s->signalling_state_duration++; /*endif*/ - notched_signal = 0; for (j = 0; j < s->desc->tones; j++) { /* The notch filter is two cascaded biquads. */ - notched_signal = amp[i]; - #if defined(SPANDSP_USE_FIXED_POINT) - notched_signal *= s->desc->tone[j].notch_a1[0]; - notched_signal += s->tone[j].notch_z1[1]*s->desc->tone[j].notch_b1[1]; - notched_signal += s->tone[j].notch_z1[2]*s->desc->tone[j].notch_b1[2]; - x = notched_signal; - notched_signal += s->tone[j].notch_z1[1]*s->desc->tone[j].notch_a1[1]; - notched_signal += s->tone[j].notch_z1[2]*s->desc->tone[j].notch_a1[2]; - s->tone[j].notch_z1[2] = s->tone[j].notch_z1[1]; - s->tone[j].notch_z1[1] = x >> 15; - - notched_signal += s->tone[j].notch_z2[1]*s->desc->tone[j].notch_b2[1]; - notched_signal += s->tone[j].notch_z2[2]*s->desc->tone[j].notch_b2[2]; - x = notched_signal; - notched_signal += s->tone[j].notch_z2[1]*s->desc->tone[j].notch_a2[1]; - notched_signal += s->tone[j].notch_z2[2]*s->desc->tone[j].notch_a2[2]; - s->tone[j].notch_z2[2] = s->tone[j].notch_z2[1]; - s->tone[j].notch_z2[1] = x >> 15; - - notched_signal >>= s->desc->notch_postscale; + v = ((int32_t) amp[i]*s->desc->notch[j]->a1[0]) + + ((int32_t) s->tone[j].notch_z1[0]*s->desc->notch[j]->b1[1]) + + ((int32_t) s->tone[j].notch_z1[1]*s->desc->notch[j]->b1[2]); + x = v >> 15; + v += ((int32_t) s->tone[j].notch_z1[0]*s->desc->notch[j]->a1[1]) + + ((int32_t) s->tone[j].notch_z1[1]*s->desc->notch[j]->a1[2]); + s->tone[j].notch_z1[1] = s->tone[j].notch_z1[0]; + s->tone[j].notch_z1[0] = x; + v += ((int32_t) s->tone[j].notch_z2[0]*s->desc->notch[j]->b2[1]) + + ((int32_t) s->tone[j].notch_z2[1]*s->desc->notch[j]->b2[2]); + x = v >> 15; + v += ((int32_t) s->tone[j].notch_z2[0]*s->desc->notch[j]->a2[1]) + + ((int32_t) s->tone[j].notch_z2[1]*s->desc->notch[j]->a2[2]); + s->tone[j].notch_z2[1] = s->tone[j].notch_z2[0]; + s->tone[j].notch_z2[0] = x; + notched_signal[j] = v >> s->desc->notch[j]->postscale; #else - notched_signal *= s->desc->tone[j].notch_a1[0]; - notched_signal += s->tone[j].notch_z1[1]*s->desc->tone[j].notch_b1[1]; - notched_signal += s->tone[j].notch_z1[2]*s->desc->tone[j].notch_b1[2]; - x = notched_signal; - notched_signal += s->tone[j].notch_z1[1]*s->desc->tone[j].notch_a1[1]; - notched_signal += s->tone[j].notch_z1[2]*s->desc->tone[j].notch_a1[2]; - s->tone[j].notch_z1[2] = s->tone[j].notch_z1[1]; - s->tone[j].notch_z1[1] = x; - - notched_signal += s->tone[j].notch_z2[1]*s->desc->tone[j].notch_b2[1]; - notched_signal += s->tone[j].notch_z2[2]*s->desc->tone[j].notch_b2[2]; - x = notched_signal; - notched_signal += s->tone[j].notch_z2[1]*s->desc->tone[j].notch_a2[1]; - notched_signal += s->tone[j].notch_z2[2]*s->desc->tone[j].notch_a2[2]; - s->tone[j].notch_z2[2] = s->tone[j].notch_z2[1]; - s->tone[j].notch_z2[1] = x; + v = amp[i]*s->desc->notch[j]->a1[0] + + s->tone[j].notch_z1[0]*s->desc->notch[j]->b1[1] + + s->tone[j].notch_z1[1]*s->desc->notch[j]->b1[2]; + x = v; + v += s->tone[j].notch_z1[0]*s->desc->notch[j]->a1[1] + + s->tone[j].notch_z1[1]*s->desc->notch[j]->a1[2]; + s->tone[j].notch_z1[1] = s->tone[j].notch_z1[0]; + s->tone[j].notch_z1[0] = x; + v += s->tone[j].notch_z2[0]*s->desc->notch[j]->b2[1] + + s->tone[j].notch_z2[1]*s->desc->notch[j]->b2[2]; + x = v; + v += s->tone[j].notch_z2[0]*s->desc->notch[j]->a2[1] + + s->tone[j].notch_z2[1]*s->desc->notch[j]->a2[2]; + s->tone[j].notch_z2[1] = s->tone[j].notch_z2[0]; + s->tone[j].notch_z2[0] = x; + notched_signal[j] = v; #endif /* Modulus and leaky integrate the notched data. The result of - this isn't used in low tone detect mode, but we must keep notch_zl - rolling along. */ - s->tone[j].notch_zl = ((s->tone[j].notch_zl*s->desc->notch_slugi) >> 15) - + ((abs((int) notched_signal)*s->desc->notch_slugp) >> 15); - /* Mow the grass to weed out the noise! */ - mown_notch[j] = s->tone[0].notch_zl & s->desc->notch_threshold; + this isn't used in low tone detect mode, but we must keep the + power measurement rolling along. */ + notch_power[j] = power_meter_update(&s->tone[j].power, notched_signal[j]); } - if (s->tone_present) + if (s->tone[0].tone_present || s->tone[1].tone_present) { - if (s->flat_mode_timeout <= 0) + if (s->flat_mode_timeout && --s->flat_mode_timeout == 0) s->flat_mode = TRUE; - else - s->flat_mode_timeout--; /*endif*/ } else @@ -509,55 +436,51 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len) if (s->flat_mode) { /* Flat mode */ - - /* The bandpass filter is a single bi-quad stage */ bandpass_signal = amp[i]; -#if defined(SPANDSP_USE_FIXED_POINT) - bandpass_signal *= s->desc->broad_a[0]; - bandpass_signal += s->broad_z[1]*s->desc->broad_b[1]; - bandpass_signal += s->broad_z[2]*s->desc->broad_b[2]; - x = bandpass_signal; - bandpass_signal += s->broad_z[1]*s->desc->broad_a[1]; - bandpass_signal += s->broad_z[2]*s->desc->broad_a[2]; - s->broad_z[2] = s->broad_z[1]; - s->broad_z[1] = x >> 15; - bandpass_signal >>= s->desc->broad_postscale; -#else - bandpass_signal *= s->desc->broad_a[0]; - bandpass_signal += s->broad_z[1]*s->desc->broad_b[1]; - bandpass_signal += s->broad_z[2]*s->desc->broad_b[2]; - x = bandpass_signal; - bandpass_signal += s->broad_z[1]*s->desc->broad_a[1]; - bandpass_signal += s->broad_z[2]*s->desc->broad_a[2]; - s->broad_z[2] = s->broad_z[1]; - s->broad_z[1] = x; -#endif - /* Leaky integrate the bandpassed data */ - s->broad_zl = ((s->broad_zl*s->desc->broad_slugi) >> 15) - + ((abs((int) bandpass_signal)*s->desc->broad_slugp) >> 15); - - /* For the broad band receiver we use a simple linear threshold! */ - if (s->tone_present) + if (s->desc->flat) { - s->tone_present = (s->broad_zl > s->desc->broad_threshold); - if (!s->tone_present) + /* The bandpass filter is a single bi-quad stage */ +#if defined(SPANDSP_USE_FIXED_POINT) + v = ((int32_t) amp[i]*s->desc->flat->a[0]) + + ((int32_t) s->flat_z[0]*s->desc->flat->b[1]) + + ((int32_t) s->flat_z[1]*s->desc->flat->b[2]); + x = v >> 15; + v += ((int32_t) s->flat_z[0]*s->desc->flat->a[1]) + + ((int32_t) s->flat_z[1]*s->desc->flat->a[2]); + s->flat_z[1] = s->flat_z[0]; + s->flat_z[0] = x; + bandpass_signal = v >> s->desc->flat->postscale; +#else + v = amp[i]*s->desc->flat->a[0] + + s->flat_z[0]*s->desc->flat->b[1] + + s->flat_z[1]*s->desc->flat->b[2]; + x = v; + v += s->flat_z[0]*s->desc->flat->a[1] + + s->flat_z[1]*s->desc->flat->a[2]; + s->flat_z[1] = s->flat_z[0]; + s->flat_z[0] = x; + bandpass_signal = v; +#endif + } + flat_power = power_meter_update(&s->flat_power, bandpass_signal); + + /* For the flat receiver we use a simple power threshold! */ + if (s->tone[0].tone_present) + { + s->tone[0].tone_present = (flat_power > s->flat_detection_threshold); + if (!s->tone[0].tone_present) { - if (s->sig_update) - s->sig_update(s->user_data, SIG_TONE_1_CHANGE, 0, s->signaling_state_duration); - /*endif*/ - s->signaling_state_duration = 0; + s->signalling_state &= ~tone_present_bits[0]; + s->signalling_state |= tone_change_bits[0]; } /*endif*/ } else { - s->tone_present = (s->broad_zl > s->desc->broad_threshold); - if (s->tone_present) + s->tone[0].tone_present = (flat_power > s->flat_detection_threshold); + if (s->tone[0].tone_present) { - if (s->sig_update) - s->sig_update(s->user_data, SIG_TONE_1_CHANGE | SIG_TONE_1_PRESENT, 0, s->signaling_state_duration); - /*endif*/ - s->signaling_state_duration = 0; + s->signalling_state |= (tone_present_bits[0] | tone_change_bits[0]); } /*endif*/ } @@ -565,17 +488,14 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len) /* Notch insertion logic */ /* tone_present and tone_on are equivalent in flat mode */ - if (s->tone_present) + if (s->tone[0].tone_present) { - s->notch_enabled = s->desc->notch_allowed; s->notch_insertion_timeout = s->desc->notch_lag_time; } else { - if (s->notch_insertion_timeout > 0) + if (s->notch_insertion_timeout) s->notch_insertion_timeout--; - else - s->notch_enabled = FALSE; /*endif*/ } /*endif*/ @@ -583,91 +503,84 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len) else { /* Sharp mode */ + flat_power = power_meter_update(&s->flat_power, amp[i]); - /* Modulus and leaky integrate the data */ - s->broad_zl = ((s->broad_zl*s->desc->unfiltered_slugi) >> 15) - + ((abs((int) amp[i])*s->desc->unfiltered_slugp) >> 15); - - /* Mow the grass to weed out the noise! */ - mown_bandpass = s->broad_zl & s->desc->unfiltered_threshold; - - /* Persistence checking and notch insertion logic */ - if (!s->tone_present) + for (j = 0; j < s->desc->tones; j++) { - if (mown_notch[0] < mown_bandpass) + /* Persistence checking and notch insertion logic */ + if (s->tone[j].tone_present) { - /* Tone is detected this sample */ - if (s->tone_persistence_timeout <= 0) + if (flat_power < s->sharp_detection_threshold + || + (notch_power[j] >> 6)*s->detection_ratio > (flat_power >> 6)) { - s->tone_present = TRUE; - s->notch_enabled = s->desc->notch_allowed; - s->tone_persistence_timeout = s->desc->tone_off_check_time; - s->notch_insertion_timeout = s->desc->notch_lag_time; - if (s->sig_update) - s->sig_update(s->user_data, SIG_TONE_1_CHANGE | SIG_TONE_1_PRESENT, 0, s->signaling_state_duration); + /* Tone is not detected this sample */ + if (--s->tone[j].tone_persistence_timeout == 0) + { + /* Tone off is confirmed */ + s->tone[j].tone_present = FALSE; + s->tone[j].tone_persistence_timeout = s->desc->tone_on_check_time; + s->signalling_state &= ~tone_present_bits[j]; + s->signalling_state |= tone_change_bits[j]; + } /*endif*/ - s->signaling_state_duration = 0; } else { - s->tone_persistence_timeout--; - if (s->notch_insertion_timeout > 0) - s->notch_insertion_timeout--; - else - s->notch_enabled = FALSE; - /*endif*/ + s->tone[j].tone_persistence_timeout = s->desc->tone_off_check_time; } /*endif*/ } else { - s->tone_persistence_timeout = s->desc->tone_on_check_time; - if (s->notch_insertion_timeout > 0) + if (s->notch_insertion_timeout) s->notch_insertion_timeout--; - else - s->notch_enabled = FALSE; /*endif*/ - } - /*endif*/ - } - else - { - if (mown_notch[0] > mown_bandpass) - { - /* Tone is not detected this sample */ - if (s->tone_persistence_timeout <= 0) + if (flat_power > s->sharp_detection_threshold + && + (notch_power[j] >> 6)*s->detection_ratio < (flat_power >> 6)) { - s->tone_present = FALSE; - s->tone_persistence_timeout = s->desc->tone_on_check_time; - if (s->sig_update) - s->sig_update(s->user_data, SIG_TONE_1_CHANGE, 0, s->signaling_state_duration); + /* Tone is detected this sample */ + if (--s->tone[j].tone_persistence_timeout == 0) + { + /* Tone on is confirmed */ + s->tone[j].tone_present = TRUE; + s->tone[j].tone_persistence_timeout = s->desc->tone_off_check_time; + s->notch_insertion_timeout = s->desc->notch_lag_time; + s->signalling_state |= (tone_present_bits[j] | tone_change_bits[j]); + } /*endif*/ - s->signaling_state_duration = 0; } else { - s->tone_persistence_timeout--; + s->tone[j].tone_persistence_timeout = s->desc->tone_on_check_time; } /*endif*/ } - else - { - s->tone_persistence_timeout = s->desc->tone_off_check_time; - } /*endif*/ } + /*endfor*/ + } + /*endif*/ + if (s->signalling_state & (SIG_TONE_1_CHANGE | SIG_TONE_2_CHANGE)) + { + if (s->sig_update) + s->sig_update(s->user_data, s->signalling_state, 0, s->signalling_state_duration); /*endif*/ + s->signalling_state &= ~(SIG_TONE_1_CHANGE | SIG_TONE_2_CHANGE); + s->signalling_state_duration = 0; } /*endif*/ if ((s->current_rx_tone & SIG_TONE_RX_PASSTHROUGH)) { - if ((s->current_rx_tone & SIG_TONE_RX_FILTER_TONE) || s->notch_enabled) - amp[i] = (int16_t) notched_signal; + if ((s->current_rx_tone & SIG_TONE_RX_FILTER_TONE) || s->notch_insertion_timeout) + amp[i] = saturate16(notched_signal[0]); /*endif*/ } else { + /* Simply mute the media path */ amp[i] = 0; } /*endif*/ @@ -685,6 +598,11 @@ SPAN_DECLARE(void) sig_tone_rx_set_mode(sig_tone_rx_state_t *s, int mode, int du SPAN_DECLARE(sig_tone_rx_state_t *) sig_tone_rx_init(sig_tone_rx_state_t *s, int tone_type, tone_report_func_t sig_update, void *user_data) { +#if !defined(SPANDSP_USE_FIXED_POINT) + int i; + int j; +#endif + if (sig_update == NULL || tone_type < 1 || tone_type > 3) return NULL; /*endif*/ @@ -695,16 +613,31 @@ SPAN_DECLARE(sig_tone_rx_state_t *) sig_tone_rx_init(sig_tone_rx_state_t *s, int return NULL; } memset(s, 0, sizeof(*s)); +#if !defined(SPANDSP_USE_FIXED_POINT) + for (i = 0; i < 3; i++) + { + for (j = 0; j < 2; j++) + { + s->tone[j].notch_z1[i] = 0.0f; + s->tone[j].notch_z2[i] = 0.0f; + } + s->flat_z[i] = 0.0f; + } +#endif s->sig_update = sig_update; s->user_data = user_data; s->desc = &sig_tones[tone_type - 1]; - s->flat_mode_timeout = 0; - s->notch_insertion_timeout = 0; - s->tone_persistence_timeout = 0; - s->signaling_state_duration = 0; + power_meter_init(&s->tone[0].power, 5); + power_meter_init(&s->tone[1].power, 5); + power_meter_init(&s->flat_power, 5); + + s->flat_detection_threshold = power_meter_level_dbm0(s->desc->flat_detection_threshold); + s->sharp_detection_threshold = power_meter_level_dbm0(s->desc->sharp_detection_threshold); + s->detection_ratio = powf(10.0f, s->desc->detection_ratio/10.0f) + 1.0f; + return s; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/spandsp/private/sig_tone.h b/libs/spandsp/src/spandsp/private/sig_tone.h index edf7a68bb7..bb109f8309 100644 --- a/libs/spandsp/src/spandsp/private/sig_tone.h +++ b/libs/spandsp/src/spandsp/private/sig_tone.h @@ -2,7 +2,7 @@ * SpanDSP - a series of DSP components for telephony * * private/sig_tone.h - Signalling tone processing for the 2280Hz, 2400Hz, 2600Hz - * and similar signalling tone used in older protocols. + * and similar signalling tones used in older protocols. * * Written by Steve Underwood * @@ -23,116 +23,116 @@ * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: sig_tone.h,v 1.4 2009/09/04 14:38:47 steveu Exp $ + * $Id: sig_tone.h,v 1.9 2010/03/11 14:22:30 steveu Exp $ */ #if !defined(_SPANDSP_PRIVATE_SIG_TONE_H_) #define _SPANDSP_PRIVATE_SIG_TONE_H_ +/*! \brief The coefficient set for a pair of cascaded bi-quads that make a signalling notch filter. */ +typedef struct +{ +#if defined(SPANDSP_USE_FIXED_POINT) + int16_t a1[3]; + int16_t b1[3]; + int16_t a2[3]; + int16_t b2[3]; + int postscale; +#else + float a1[3]; + float b1[3]; + float a2[3]; + float b2[3]; +#endif +} sig_tone_notch_coeffs_t; + +/*! \brief The coefficient set for a bi-quad that makes a signalling flat filter. + Some signalling tone schemes require such a filter, and some don't. + It is termed a flat filter, to distinguish it from the sharp filter, + but obviously it is not actually flat. It is a broad band weighting + filter. */ +typedef struct +{ +#if defined(SPANDSP_USE_FIXED_POINT) + /*! \brief Flat mode bandpass bi-quad parameters */ + int16_t a[3]; + /*! \brief Flat mode bandpass bi-quad parameters */ + int16_t b[3]; + /*! \brief Post filter scaling */ + int postscale; +#else + /*! \brief Flat mode bandpass bi-quad parameters */ + float a[3]; + /*! \brief Flat mode bandpass bi-quad parameters */ + float b[3]; +#endif +} sig_tone_flat_coeffs_t; + /*! - Signaling tone descriptor. This defines the working state for a - single instance of the transmit and receive sides of a signaling + signalling tone descriptor. This defines the working state for a + single instance of the transmit and receive sides of a signalling tone processor. */ -struct sig_tone_descriptor_s +typedef struct { /*! \brief The tones used. */ int tone_freq[2]; - /*! \brief The high and low tone amplitudes for each of the tones. */ + /*! \brief The high and low tone amplitudes for each of the tones, in dBm0. */ int tone_amp[2][2]; /*! \brief The delay, in audio samples, before the high level tone drops - to a low level tone. */ + to a low level tone. Some signalling protocols require the + signalling tone be started at a high level, to ensure crisp + initial detection at the receiver, but require the tone + amplitude to drop by a number of dBs if it is sustained, + to reduce crosstalk levels. */ int high_low_timeout; - /*! \brief Some signaling tone detectors use a sharp initial filter, - changing to a broader band filter after some delay. This + /*! \brief Some signalling tone detectors use a sharp initial filter, + changing to a broader, flatter, filter after some delay. This parameter defines the delay. 0 means it never changes. */ int sharp_flat_timeout; /*! \brief Parameters to control the behaviour of the notch filter, used - to remove the tone from the voice path in some protocols. */ + to remove the tone from the voice path in some protocols. The + notch is applied as fast as possible, when the signalling tone + is detected. Its removal is delayed by this timeout, to avoid + clicky noises from repeated switching of the filter on rapid + pulses of signalling tone. */ int notch_lag_time; - /*! \brief TRUE if the notch may be used in the media flow. */ - int notch_allowed; /*! \brief The tone on persistence check, in audio samples. */ int tone_on_check_time; /*! \brief The tone off persistence check, in audio samples. */ int tone_off_check_time; - /*! \brief ??? */ + /*! \brief The number of tones used. */ int tones; /*! \brief The coefficients for the cascaded bi-quads notch filter. */ - struct - { -#if defined(SPANDSP_USE_FIXED_POINT) - int32_t notch_a1[3]; - int32_t notch_b1[3]; - int32_t notch_a2[3]; - int32_t notch_b2[3]; - int notch_postscale; -#else - float notch_a1[3]; - float notch_b1[3]; - float notch_a2[3]; - float notch_b2[3]; -#endif - } tone[2]; + const sig_tone_notch_coeffs_t *notch[2]; + /*! \brief The coefficients for the single bi-quad flat mode filter. */ + const sig_tone_flat_coeffs_t *flat; -#if defined(SPANDSP_USE_FIXED_POINT) - /*! \brief Flat mode bandpass bi-quad parameters */ - int32_t broad_a[3]; - /*! \brief Flat mode bandpass bi-quad parameters */ - int32_t broad_b[3]; - /*! \brief Post filter scaling */ - int broad_postscale; -#else - /*! \brief Flat mode bandpass bi-quad parameters */ - float broad_a[3]; - /*! \brief Flat mode bandpass bi-quad parameters */ - float broad_b[3]; -#endif - /*! \brief The coefficients for the post notch leaky integrator. */ - int32_t notch_slugi; - /*! \brief ??? */ - int32_t notch_slugp; - - /*! \brief The coefficients for the post modulus leaky integrator in the - unfiltered data path. The prescale value incorporates the - detection ratio. This is called the guard ratio in some - protocols. */ - int32_t unfiltered_slugi; - /*! \brief ??? */ - int32_t unfiltered_slugp; - - /*! \brief The coefficients for the post modulus leaky integrator in the - bandpass filter data path. */ - int32_t broad_slugi; - /*! \brief ??? */ - int32_t broad_slugp; - - /*! \brief Masks which effectively threshold the notched, weighted and - bandpassed data. */ - int32_t notch_threshold; - /*! \brief ??? */ - int32_t unfiltered_threshold; - /*! \brief ??? */ - int32_t broad_threshold; -}; + /*! \brief Minimum signalling tone to total power ratio, in dB */ + int16_t detection_ratio; + /*! \brief Minimum total power for detection in sharp mode, in dB */ + int16_t sharp_detection_threshold; + /*! \brief Minimum total power for detection in flat mode, in dB */ + int16_t flat_detection_threshold; +} sig_tone_descriptor_t; /*! - Signaling tone transmit state + Signalling tone transmit state */ struct sig_tone_tx_state_s { - /*! \brief The callback function used to handle signaling changes. */ + /*! \brief The callback function used to handle signalling changes. */ tone_report_func_t sig_update; /*! \brief A user specified opaque pointer passed to the callback function. */ void *user_data; /*! \brief Tone descriptor */ - sig_tone_descriptor_t *desc; + const sig_tone_descriptor_t *desc; /*! The phase rates for the one or two tones */ int32_t phase_rate[2]; @@ -148,22 +148,22 @@ struct sig_tone_tx_state_s int current_tx_tone; /*! \brief Current transmit timeout */ int current_tx_timeout; - /*! \brief Time in current signaling state, in samples. */ - int signaling_state_duration; + /*! \brief Time in current signalling state, in samples. */ + int signalling_state_duration; }; /*! - Signaling tone receive state + Signalling tone receive state */ struct sig_tone_rx_state_s { - /*! \brief The callback function used to handle signaling changes. */ + /*! \brief The callback function used to handle signalling changes. */ tone_report_func_t sig_update; /*! \brief A user specified opaque pointer passed to the callback function. */ void *user_data; /*! \brief Tone descriptor */ - sig_tone_descriptor_t *desc; + const sig_tone_descriptor_t *desc; /*! \brief The current receive tone */ int current_rx_tone; @@ -174,45 +174,54 @@ struct sig_tone_rx_state_s { #if defined(SPANDSP_USE_FIXED_POINT) /*! \brief The z's for the notch filter */ - int32_t notch_z1[3]; + int16_t notch_z1[2]; /*! \brief The z's for the notch filter */ - int32_t notch_z2[3]; + int16_t notch_z2[2]; #else /*! \brief The z's for the notch filter */ - float notch_z1[3]; + float notch_z1[2]; /*! \brief The z's for the notch filter */ - float notch_z2[3]; + float notch_z2[2]; #endif - /*! \brief The z's for the notch integrators. */ - int32_t notch_zl; + /*! \brief The power output of the notch. */ + power_meter_t power; + /*! \brief Persistence check for tone present */ + int tone_persistence_timeout; + /*! \brief TRUE if the tone is declared to be present */ + int tone_present; } tone[2]; #if defined(SPANDSP_USE_FIXED_POINT) /*! \brief The z's for the weighting/bandpass filter. */ - int32_t broad_z[3]; + int16_t flat_z[2]; #else /*! \brief The z's for the weighting/bandpass filter. */ - float broad_z[3]; + float flat_z[2]; #endif - /*! \brief The z for the broadband integrator. */ - int32_t broad_zl; + /*! \brief The output power of the flat (unfiltered or flat filtered) path. */ + power_meter_t flat_power; - /*! \brief ??? */ + /*! \brief The minimum reading from the power meter for detection in flat mode */ + int32_t flat_detection_threshold; + /*! \brief The minimum reading from the power meter for detection in sharp mode */ + int32_t sharp_detection_threshold; + /*! \brief The minimum ratio between notched power and total power for detection */ + int32_t detection_ratio; + + /*! \brief TRUE if in flat mode. FALSE if in sharp mode. */ int flat_mode; - /*! \brief ??? */ - int tone_present; - /*! \brief ??? */ + /*! \brief TRUE if the notch filter is enabled in the media path */ int notch_enabled; /*! \brief ??? */ int flat_mode_timeout; /*! \brief ??? */ int notch_insertion_timeout; - /*! \brief ??? */ - int tone_persistence_timeout; /*! \brief ??? */ - int signaling_state_duration; + int signalling_state; + /*! \brief ??? */ + int signalling_state_duration; }; #endif diff --git a/libs/spandsp/src/spandsp/sig_tone.h b/libs/spandsp/src/spandsp/sig_tone.h index 64bcd6f422..649f1e10a6 100644 --- a/libs/spandsp/src/spandsp/sig_tone.h +++ b/libs/spandsp/src/spandsp/sig_tone.h @@ -2,7 +2,7 @@ * SpanDSP - a series of DSP components for telephony * * sig_tone.h - Signalling tone processing for the 2280Hz, 2400Hz, 2600Hz - * and similar signalling tone used in older protocols. + * and similar signalling tones used in older protocols. * * Written by Steve Underwood * @@ -23,15 +23,15 @@ * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: sig_tone.h,v 1.20 2009/09/04 14:38:46 steveu Exp $ + * $Id: sig_tone.h,v 1.23 2010/03/09 13:43:04 steveu Exp $ */ /*! \file */ -/*! \page sig_tone_page The signaling tone processor +/*! \page sig_tone_page The 2280/2400/2600Hz signalling tone processor \section sig_tone_sec_1 What does it do? -The signaling tone processor handles the 2280Hz, 2400Hz and 2600Hz tones, used -in many analogue signaling procotols, and digital ones derived from them. +The signalling tone processor handles the 2280Hz, 2400Hz and 2600Hz tones, used +in many analogue signalling procotols, and digital ones derived from them. \section sig_tone_sec_2 How does it work? Most single and two voice frequency signalling systems share many features, as these @@ -57,30 +57,30 @@ least supervisory information may be heard. /* The optional tone sets */ enum { - /*! European 2280Hz signaling tone. Tone 1 is 2280Hz. Tone 2 is not used. */ + /*! European 2280Hz signalling tone. Tone 1 is 2280Hz. Tone 2 is not used. */ SIG_TONE_2280HZ = 1, - /*! US 2600Hz signaling tone. Tone 1 is 2600Hz. Tone 2 is not used. */ + /*! US 2600Hz signalling tone. Tone 1 is 2600Hz. Tone 2 is not used. */ SIG_TONE_2600HZ, - /*! US 2400Hz + 2600Hz signaling tones. Tone 1 is 2600Hz. Tone 2 is 2400Hz. */ + /*! US 2400Hz + 2600Hz signalling tones. Tone 1 is 2600Hz. Tone 2 is 2400Hz. */ SIG_TONE_2400HZ_2600HZ }; /* Mode control and report bits for transmit and receive */ enum { - /*! Signaling tone 1 is present */ + /*! Signalling tone 1 is present */ SIG_TONE_1_PRESENT = 0x001, - /*! Signaling tone 1 has changed state (ignored when setting tx mode) */ + /*! Signalling tone 1 has changed state (ignored when setting tx mode) */ SIG_TONE_1_CHANGE = 0x002, - /*! Signaling tone 2 is present */ + /*! Signalling tone 2 is present */ SIG_TONE_2_PRESENT = 0x004, - /*! Signaling tone 2 has changed state (ignored when setting tx mode) */ + /*! Signalling tone 2 has changed state (ignored when setting tx mode) */ SIG_TONE_2_CHANGE = 0x008, /*! The media signal is passing through. Tones might be added to it. */ SIG_TONE_TX_PASSTHROUGH = 0x010, /*! The media signal is passing through. Tones might be extracted from it, if detected. */ SIG_TONE_RX_PASSTHROUGH = 0x040, - /*! Force filtering of the signaling tone, whether signaling is being detected or not. + /*! Force filtering of the signalling tone, whether signalling is being detected or not. This is mostly useful for test purposes. */ SIG_TONE_RX_FILTER_TONE = 0x080, /*! Request an update of the transmit status, upon timeout of the previous status. */ @@ -89,13 +89,6 @@ enum SIG_TONE_RX_UPDATE_REQUEST = 0x200 }; -/*! - Signaling tone descriptor. This defines the working state for a - single instance of the transmit and receive sides of a signaling - tone processor. -*/ -typedef struct sig_tone_descriptor_s sig_tone_descriptor_t; - typedef struct sig_tone_tx_state_s sig_tone_tx_state_t; typedef struct sig_tone_rx_state_s sig_tone_rx_state_t; @@ -107,7 +100,7 @@ extern "C" /*! Process a block of received audio samples. \brief Process a block of received audio samples. - \param s The signaling tone context. + \param s The signalling tone context. \param amp The audio sample buffer. \param len The number of samples in the buffer. \return The number of samples unprocessed. */ @@ -115,36 +108,36 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len); /*! Set the receive mode. \brief Set the receive mode. - \param s The signaling tone context. + \param s The signalling tone context. \param mode The new mode for the receiver. \param duration The duration for this mode, before an update is requested. A duration of zero means forever. */ SPAN_DECLARE(void) sig_tone_rx_set_mode(sig_tone_rx_state_t *s, int mode, int duration); -/*! Initialise a signaling tone receiver context. - \brief Initialise a signaling tone context. - \param s The signaling tone context. - \param tone_type The type of signaling tone. - \param sig_update Callback function to handle signaling updates. +/*! Initialise a signalling tone receiver context. + \brief Initialise a signalling tone context. + \param s The signalling tone context. + \param tone_type The type of signalling tone. + \param sig_update Callback function to handle signalling updates. \param user_data An opaque pointer. \return A pointer to the signalling tone context, or NULL if there was a problem. */ SPAN_DECLARE(sig_tone_rx_state_t *) sig_tone_rx_init(sig_tone_rx_state_t *s, int tone_type, tone_report_func_t sig_update, void *user_data); -/*! Release a signaling tone receiver context. - \brief Release a signaling tone receiver context. - \param s The signaling tone context. +/*! Release a signalling tone receiver context. + \brief Release a signalling tone receiver context. + \param s The signalling tone context. \return 0 for OK */ SPAN_DECLARE(int) sig_tone_rx_release(sig_tone_rx_state_t *s); -/*! Free a signaling tone receiver context. - \brief Free a signaling tone receiver context. - \param s The signaling tone context. +/*! Free a signalling tone receiver context. + \brief Free a signalling tone receiver context. + \param s The signalling tone context. \return 0 for OK */ SPAN_DECLARE(int) sig_tone_rx_free(sig_tone_rx_state_t *s); -/*! Generate a block of signaling tone audio samples. - \brief Generate a block of signaling tone audio samples. - \param s The signaling tone context. +/*! Generate a block of signalling tone audio samples. + \brief Generate a block of signalling tone audio samples. + \param s The signalling tone context. \param amp The audio sample buffer. \param len The number of samples to be generated. \return The number of samples actually generated. */ @@ -152,30 +145,30 @@ SPAN_DECLARE(int) sig_tone_tx(sig_tone_tx_state_t *s, int16_t amp[], int len); /*! Set the tone mode. \brief Set the tone mode. - \param s The signaling tone context. + \param s The signalling tone context. \param mode The new mode for the transmitted tones. \param duration The duration for this mode, before an update is requested. A duration of zero means forever. */ SPAN_DECLARE(void) sig_tone_tx_set_mode(sig_tone_tx_state_t *s, int mode, int duration); -/*! Initialise a signaling tone transmitter context. - \brief Initialise a signaling tone context. - \param s The signaling tone context. - \param tone_type The type of signaling tone. - \param sig_update Callback function to handle signaling updates. +/*! Initialise a signalling tone transmitter context. + \brief Initialise a signalling tone context. + \param s The signalling tone context. + \param tone_type The type of signalling tone. + \param sig_update Callback function to handle signalling updates. \param user_data An opaque pointer. \return A pointer to the signalling tone context, or NULL if there was a problem. */ SPAN_DECLARE(sig_tone_tx_state_t *) sig_tone_tx_init(sig_tone_tx_state_t *s, int tone_type, tone_report_func_t sig_update, void *user_data); -/*! Release a signaling tone transmitter context. - \brief Release a signaling tone transmitter context. - \param s The signaling tone context. +/*! Release a signalling tone transmitter context. + \brief Release a signalling tone transmitter context. + \param s The signalling tone context. \return 0 for OK */ SPAN_DECLARE(int) sig_tone_tx_release(sig_tone_tx_state_t *s); -/*! Free a signaling tone transmitter context. - \brief Free a signaling tone transmitter context. - \param s The signaling tone context. +/*! Free a signalling tone transmitter context. + \brief Free a signalling tone transmitter context. + \param s The signalling tone context. \return 0 for OK */ SPAN_DECLARE(int) sig_tone_tx_free(sig_tone_tx_state_t *s); diff --git a/libs/spandsp/tests/sig_tone_tests.c b/libs/spandsp/tests/sig_tone_tests.c index e2afea4a3d..77433991ef 100644 --- a/libs/spandsp/tests/sig_tone_tests.c +++ b/libs/spandsp/tests/sig_tone_tests.c @@ -22,12 +22,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: sig_tone_tests.c,v 1.27 2009/09/23 16:02:59 steveu Exp $ + * $Id: sig_tone_tests.c,v 1.32 2010/03/11 14:22:30 steveu Exp $ */ /*! \file */ -/*! \page sig_tone_tests_page The signaling tone processor tests +/*! \page sig_tone_tests_page The 2280/2400/2600Hz signalling tone Rx/Tx tests \section sig_tone_tests_sec_1 What does it do? ???. @@ -55,17 +55,38 @@ #define SAMPLES_PER_CHUNK 160 +#define MITEL_DIR "../test-data/mitel/" +#define BELLCORE_DIR "../test-data/bellcore/" + +const char *bellcore_files[] = +{ + MITEL_DIR "mitel-cm7291-talkoff.wav", + BELLCORE_DIR "tr-tsy-00763-1.wav", + BELLCORE_DIR "tr-tsy-00763-2.wav", + BELLCORE_DIR "tr-tsy-00763-3.wav", + BELLCORE_DIR "tr-tsy-00763-4.wav", + BELLCORE_DIR "tr-tsy-00763-5.wav", + BELLCORE_DIR "tr-tsy-00763-6.wav", + "" +}; + +static int number_of_tones = 1; + static int sampleno = 0; static int tone_1_present = 0; static int tone_2_present = 0; static int tx_section = 0; static int dial_pulses = 0; +static int rx_handler_callbacks = 0; +static int tx_handler_callbacks = 0; + static void tx_handler(void *user_data, int what, int level, int duration) { sig_tone_tx_state_t *s; s = (sig_tone_tx_state_t *) user_data; + tx_handler_callbacks++; //printf("What - %d, duration - %d\n", what, duration); if ((what & SIG_TONE_TX_UPDATE_REQUEST)) { @@ -84,13 +105,19 @@ static void tx_handler(void *user_data, int what, int level, int duration) tx_section++; else tx_section--; + /*endif*/ sig_tone_tx_set_mode(s, 0, ms_to_samples(67)); break; case 2: tx_section++; - sig_tone_tx_set_mode(s, SIG_TONE_1_PRESENT, ms_to_samples(600)); + printf("600ms on - %d samples\n", ms_to_samples(600)); + if (number_of_tones == 2) + sig_tone_tx_set_mode(s, SIG_TONE_2_PRESENT, ms_to_samples(600)); + else + sig_tone_tx_set_mode(s, SIG_TONE_1_PRESENT, ms_to_samples(600)); break; case 3: + printf("End of sequence\n"); sig_tone_tx_set_mode(s, SIG_TONE_1_PRESENT | SIG_TONE_TX_PASSTHROUGH, 0); break; } @@ -104,6 +131,7 @@ static void rx_handler(void *user_data, int what, int level, int duration) { float ms; + rx_handler_callbacks++; ms = 1000.0f*(float) duration/(float) SAMPLE_RATE; printf("What - %d, duration - %d\n", what, duration); if ((what & SIG_TONE_1_CHANGE)) @@ -121,52 +149,184 @@ static void rx_handler(void *user_data, int what, int level, int duration) } /*- End of function --------------------------------------------------------*/ -static void map_frequency_response(sig_tone_rx_state_t *s) +static void map_frequency_response(sig_tone_rx_state_t *s, + double f1, + double f2, + double f3, + double f4) { - int16_t buf[8192]; + int16_t buf[SAMPLES_PER_CHUNK]; int i; int len; double sumin; double sumout; swept_tone_state_t *swept; + double freq; + double gain; /* Things like noise don't highlight the frequency response of the high Q notch very well. We use a slowly swept frequency to check it. */ + printf("Frequency response test\n"); + sig_tone_rx_set_mode(s, SIG_TONE_RX_PASSTHROUGH | SIG_TONE_RX_FILTER_TONE, 0); swept = swept_tone_init(NULL, 200.0f, 3900.0f, -10.0f, 120*SAMPLE_RATE, 0); for (;;) { if ((len = swept_tone(swept, buf, SAMPLES_PER_CHUNK)) <= 0) break; + /*endif*/ sumin = 0.0; for (i = 0; i < len; i++) sumin += (double) buf[i]*(double) buf[i]; + /*endfor*/ sig_tone_rx(s, buf, len); sumout = 0.0; for (i = 0; i < len; i++) sumout += (double) buf[i]*(double) buf[i]; /*endfor*/ - printf("%7.1f %f\n", swept_tone_current_frequency(swept), 10.0*log10(sumout/sumin)); + freq = swept_tone_current_frequency(swept); + if (sumin) + gain = 10.0*log10(sumout/sumin); + else + gain = 0.0; + printf("%7.1f Hz %f dBm0\n", freq, gain); + if (gain > 0.0 + || + (freq < f1 && gain < -1.0) + || + (freq > f2 && freq < f3 && gain > -30.0) + || + (freq > f4 && gain < -1.0)) + { + printf(" Failed\n"); + exit(2); + } + /*endif*/ } /*endfor*/ swept_tone_free(swept); + printf(" Passed\n"); } /*- End of function --------------------------------------------------------*/ -int main(int argc, char *argv[]) +static void speech_immunity_tests(sig_tone_rx_state_t *s) { + int j; + int total_hits; + SNDFILE *inhandle; + int16_t amp[SAMPLE_RATE]; + int frames; + + printf("Speech immunity test\n"); + total_hits = 0; + for (j = 0; bellcore_files[j][0]; j++) + { + /* Push some silence through, so we should be in the tone off state */ + vec_zeroi16(amp, SAMPLE_RATE); + sig_tone_rx(s, amp, SAMPLE_RATE); + rx_handler_callbacks = 0; + if ((inhandle = sf_open_telephony_read(bellcore_files[j], 1)) == NULL) + { + printf(" Cannot open speech file '%s'\n", bellcore_files[j]); + exit(2); + } + /*endif*/ + while ((frames = sf_readf_short(inhandle, amp, SAMPLE_RATE))) + { + sig_tone_rx(s, amp, frames); + } + /*endwhile*/ + if (sf_close(inhandle) != 0) + { + printf(" Cannot close speech file '%s'\n", bellcore_files[j]); + exit(2); + } + /*endif*/ + printf(" File %d gave %d false hits.\n", j + 1, rx_handler_callbacks); + total_hits += rx_handler_callbacks; + } + /*endfor*/ + printf(" %d hits in total\n", total_hits); + if (total_hits > 0) + { + printf(" Failed\n"); + exit(2); + } + /*endif*/ + printf(" Passed\n"); +} +/*- End of function --------------------------------------------------------*/ + +static void level_and_ratio_tests(sig_tone_rx_state_t *s, double pitch) +{ + awgn_state_t noise_source; + int32_t phase_rate; + uint32_t phase; + int16_t gain; + int16_t amp[SAMPLE_RATE]; + int i; + int j; + int k; + float noise_level; + float tone_level; + power_meter_t noise_meter; + power_meter_t tone_meter; + int16_t noise; + int16_t tone; + + printf("Acceptable level and ratio test\n"); + phase = 0; + phase_rate = dds_phase_rate(pitch); + for (k = -25; k > -60; k--) + { + noise_level = k; + awgn_init_dbm0(&noise_source, 1234567, noise_level); + tone_level = noise_level; + /* Push some silence through, so we should be in the tone off state */ + vec_zeroi16(amp, SAMPLE_RATE); + sig_tone_rx(s, amp, SAMPLE_RATE); + power_meter_init(&noise_meter, 6); + power_meter_init(&tone_meter, 6); + for (j = 0; j < 20; j++) + { + rx_handler_callbacks = 0; + gain = dds_scaling_dbm0(tone_level); + for (i = 0; i < SAMPLES_PER_CHUNK; i++) + { + noise = awgn(&noise_source); + tone = dds_mod(&phase, phase_rate, gain, 0); + power_meter_update(&noise_meter, noise); + power_meter_update(&tone_meter, tone); + amp[i] = noise + tone; + } + /*endfor*/ + sig_tone_rx(s, amp, SAMPLES_PER_CHUNK); + if (rx_handler_callbacks) + { + printf("Hit at tone = %fdBm0, noise = %fdBm0\n", tone_level, noise_level); + printf("Noise = %fdBm0, tone = %fdBm0\n", power_meter_current_dbm0(&noise_meter), power_meter_current_dbm0(&tone_meter)); + } + /*endif*/ + tone_level += 1.0f; + } + /*endfor*/ + } + /*endfor*/ + printf(" Passed\n"); +} +/*- End of function --------------------------------------------------------*/ + +static void sequence_tests(sig_tone_tx_state_t *tx_state, sig_tone_rx_state_t *rx_state, codec_munge_state_t *munge) +{ + int i; + awgn_state_t noise_source; + SNDFILE *outhandle; int16_t amp[SAMPLES_PER_CHUNK]; int16_t out_amp[2*SAMPLES_PER_CHUNK]; - SNDFILE *outhandle; int outframes; - int i; - int type; int rx_samples; int tx_samples; - sig_tone_tx_state_t tx_state; - sig_tone_rx_state_t rx_state; - awgn_state_t noise_source; - codec_munge_state_t *munge; + printf("Signalling sequence test\n"); if ((outhandle = sf_open_telephony_write(OUT_FILE_NAME, 2)) == NULL) { fprintf(stderr, " Cannot create audio file '%s'\n", OUT_FILE_NAME); @@ -175,76 +335,35 @@ int main(int argc, char *argv[]) /*endif*/ awgn_init_dbm0(&noise_source, 1234567, -20.0f); - - for (type = 1; type <= 3; type++) + for (sampleno = 0; sampleno < 60000; sampleno += SAMPLES_PER_CHUNK) { - sampleno = 0; - tone_1_present = 0; - tone_2_present = 0; - tx_section = 0; - munge = NULL; - switch (type) + if (sampleno == 8000) { - case 1: - printf("2280Hz tests.\n"); - munge = codec_munge_init(MUNGE_CODEC_ALAW, 0); - sig_tone_tx_init(&tx_state, SIG_TONE_2280HZ, tx_handler, &tx_state); - sig_tone_rx_init(&rx_state, SIG_TONE_2280HZ, rx_handler, &rx_state); - rx_state.current_rx_tone |= SIG_TONE_RX_PASSTHROUGH; - break; - case 2: - printf("2600Hz tests.\n"); - munge = codec_munge_init(MUNGE_CODEC_ULAW, 0); - sig_tone_tx_init(&tx_state, SIG_TONE_2600HZ, tx_handler, &tx_state); - sig_tone_rx_init(&rx_state, SIG_TONE_2600HZ, rx_handler, &rx_state); - rx_state.current_rx_tone |= SIG_TONE_RX_PASSTHROUGH; - break; - case 3: - printf("2400Hz/2600Hz tests.\n"); - munge = codec_munge_init(MUNGE_CODEC_ULAW, 0); - sig_tone_tx_init(&tx_state, SIG_TONE_2400HZ_2600HZ, tx_handler, &tx_state); - sig_tone_rx_init(&rx_state, SIG_TONE_2400HZ_2600HZ, rx_handler, &rx_state); - rx_state.current_rx_tone |= SIG_TONE_RX_PASSTHROUGH; - break; - } - /*endswitch*/ - /* Set to the default of hook condition */ - sig_tone_rx_set_mode(&rx_state, SIG_TONE_RX_PASSTHROUGH | SIG_TONE_RX_FILTER_TONE, 0); - sig_tone_tx_set_mode(&tx_state, SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT | SIG_TONE_TX_PASSTHROUGH, 0); - - map_frequency_response(&rx_state); - - sig_tone_rx_set_mode(&rx_state, SIG_TONE_RX_PASSTHROUGH, 0); - for (sampleno = 0; sampleno < 30000; sampleno += SAMPLES_PER_CHUNK) - { - if (sampleno == 8000) - { - /* 100ms seize */ - printf("100ms seize - %d samples\n", ms_to_samples(100)); - dial_pulses = 0; - sig_tone_tx_set_mode(&tx_state, 0, ms_to_samples(100)); - } - for (i = 0; i < SAMPLES_PER_CHUNK; i++) - amp[i] = awgn(&noise_source); - /*endfor*/ - tx_samples = sig_tone_tx(&tx_state, amp, SAMPLES_PER_CHUNK); - for (i = 0; i < tx_samples; i++) - out_amp[2*i] = amp[i]; - /*endfor*/ - codec_munge(munge, amp, tx_samples); - rx_samples = sig_tone_rx(&rx_state, amp, tx_samples); - for (i = 0; i < rx_samples; i++) - out_amp[2*i + 1] = amp[i]; - /*endfor*/ - outframes = sf_writef_short(outhandle, out_amp, rx_samples); - if (outframes != rx_samples) - { - fprintf(stderr, " Error writing audio file\n"); - exit(2); - } - /*endif*/ + /* 100ms seize */ + printf("100ms seize - %d samples\n", ms_to_samples(100)); + dial_pulses = 0; + sig_tone_tx_set_mode(tx_state, 0, ms_to_samples(100)); } + /*endif*/ + for (i = 0; i < SAMPLES_PER_CHUNK; i++) + amp[i] = awgn(&noise_source); /*endfor*/ + tx_samples = sig_tone_tx(tx_state, amp, SAMPLES_PER_CHUNK); + for (i = 0; i < tx_samples; i++) + out_amp[2*i] = amp[i]; + /*endfor*/ + codec_munge(munge, amp, tx_samples); + rx_samples = sig_tone_rx(rx_state, amp, tx_samples); + for (i = 0; i < rx_samples; i++) + out_amp[2*i + 1] = amp[i]; + /*endfor*/ + outframes = sf_writef_short(outhandle, out_amp, rx_samples); + if (outframes != rx_samples) + { + fprintf(stderr, " Error writing audio file\n"); + exit(2); + } + /*endif*/ } /*endfor*/ if (sf_close(outhandle) != 0) @@ -253,6 +372,83 @@ int main(int argc, char *argv[]) exit(2); } /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + int type; + sig_tone_tx_state_t tx_state; + sig_tone_rx_state_t rx_state; + codec_munge_state_t *munge; + double f1; + double f2; + double fc; + double f3; + double f4; + + for (type = 1; type <= 3; type++) + { + sampleno = 0; + tone_1_present = 0; + tone_2_present = 0; + tx_section = 0; + munge = NULL; + f1 = + f2 = + fc = + f3 = + f4 = 0.0; + switch (type) + { + case 1: + printf("2280Hz tests.\n"); + munge = codec_munge_init(MUNGE_CODEC_ALAW, 0); + sig_tone_tx_init(&tx_state, SIG_TONE_2280HZ, tx_handler, &tx_state); + sig_tone_rx_init(&rx_state, SIG_TONE_2280HZ, rx_handler, &rx_state); + number_of_tones = 1; + f1 = 2280.0 - 200.0; + f2 = 2280.0 - 20.0; + fc = 2280.0; + f3 = 2280.0 + 20.0; + f4 = 2280.0 + 200.0; + break; + case 2: + printf("2600Hz tests.\n"); + munge = codec_munge_init(MUNGE_CODEC_ULAW, 0); + sig_tone_tx_init(&tx_state, SIG_TONE_2600HZ, tx_handler, &tx_state); + sig_tone_rx_init(&rx_state, SIG_TONE_2600HZ, rx_handler, &rx_state); + number_of_tones = 1; + f1 = 2600.0 - 200.0; + f2 = 2600.0 - 20.0; + fc = 2600.0; + f3 = 2600.0 + 20.0; + f4 = 2600.0 + 200.0; + break; + case 3: + printf("2400Hz/2600Hz tests.\n"); + munge = codec_munge_init(MUNGE_CODEC_ULAW, 0); + sig_tone_tx_init(&tx_state, SIG_TONE_2400HZ_2600HZ, tx_handler, &tx_state); + sig_tone_rx_init(&rx_state, SIG_TONE_2400HZ_2600HZ, rx_handler, &rx_state); + number_of_tones = 2; + f1 = 2400.0 - 200.0; + f2 = 2400.0 - 20.0; + fc = 2400.0; + f3 = 2400.0 + 20.0; + f4 = 2400.0 + 200.0; + break; + } + /*endswitch*/ + /* Set to the default on hook condition */ + map_frequency_response(&rx_state, f1, f2, f3, f4); + speech_immunity_tests(&rx_state); + level_and_ratio_tests(&rx_state, fc); + + sig_tone_tx_set_mode(&tx_state, SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT | SIG_TONE_TX_PASSTHROUGH, 0); + sig_tone_rx_set_mode(&rx_state, SIG_TONE_RX_PASSTHROUGH, 0); + sequence_tests(&tx_state, &rx_state, munge); + } + /*endfor*/ printf("Tests completed.\n"); return 0;