/* * SpanDSP - a series of DSP components for telephony * * sig_tone.c - Signalling tone processing for the 2280Hz, 2400Hz, 2600Hz * and similar signalling tones used in older protocols. * * Written by Steve Underwood * * Copyright (C) 2004 Steve Underwood * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 2.1, * as published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * 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.40 2010/05/12 15:32:41 steveu Exp $ */ /*! \file */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #if defined(HAVE_TGMATH_H) #include #endif #if defined(HAVE_MATH_H) #include #endif #include "floating_fudge.h" #include #include #include #include "spandsp/telephony.h" #include "spandsp/fast_convert.h" #include "spandsp/dc_restore.h" #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" #include "spandsp/private/sig_tone.h" /*! PI */ #define PI 3.14159265358979323 enum { NOTCH_COEFF_SET_2280HZ = 0, NOTCH_COEFF_SET_2400HZ, NOTCH_COEFF_SET_2600HZ }; /* 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] = { { #if defined(SPANDSP_USE_FIXED_POINT) { 12900, -16384, -16384}, { 0, -8578, -11796}, 15, #else {0.393676f, -0.5f, -0.5f}, {0.0f, -0.261778f, -0.359985f}, #endif } }; 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 */ 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(0), ms_to_samples(0), ms_to_samples(225), ms_to_samples(3), ms_to_samples(8), 1, { ¬ch_coeffs[NOTCH_COEFF_SET_2600HZ], NULL, }, NULL, 15.6f, -30.0f, -30.0f }, { /* 2400Hz/2600Hz (e.g. SS5 and SS5bis) */ {2400, 2600}, {{-8, -8}, {-8, -8}}, ms_to_samples(0), ms_to_samples(0), ms_to_samples(225), ms_to_samples(3), ms_to_samples(8), 2, { ¬ch_coeffs[NOTCH_COEFF_SET_2400HZ], ¬ch_coeffs[NOTCH_COEFF_SET_2600HZ] }, NULL, 15.6f, -30.0f, -30.0f } }; static const int tone_present_bits[3] = { SIG_TONE_1_PRESENT, SIG_TONE_2_PRESENT, SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT }; static const int tone_change_bits[3] = { SIG_TONE_1_CHANGE, SIG_TONE_2_CHANGE, SIG_TONE_1_CHANGE | SIG_TONE_2_CHANGE }; static const int coeff_sets[3] = { 0, 1, 0 }; 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; int high_low; for (i = 0; i < len; i += n) { if (s->current_tx_timeout) { if (s->current_tx_timeout <= len - i) { n = s->current_tx_timeout; need_update = TRUE; } else { n = len - i; need_update = FALSE; } s->current_tx_timeout -= n; } else { n = len - i; need_update = FALSE; } 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))) { /* Are we in the early phase (high tone energy level), or the sustaining phase (low tone energy level) of tone generation? */ /* This doesn't try to get the high/low timing precise, as there is no value in doing so. It works block by block, and the blocks are normally quite short. */ if (s->high_low_timer > 0) { if (n > s->high_low_timer) n = s->high_low_timer; s->high_low_timer -= n; high_low = 0; } else { high_low = 1; } /*endif*/ for (k = 0; k < s->desc->tones; k++) { if ((s->current_tx_tone & tone_present_bits[k]) && s->phase_rate[k]) { 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] = saturated_add16(amp[j], tone); } /*endfor*/ } /*endif*/ } } /*endif*/ if (need_update && s->sig_update) s->sig_update(s->user_data, SIG_TONE_TX_UPDATE_REQUEST, 0, 0); /*endif*/ } /*endfor*/ return len; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(void) sig_tone_tx_set_mode(sig_tone_tx_state_t *s, int mode, int duration) { int old_tones; int new_tones; old_tones = s->current_tx_tone & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT); new_tones = mode & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT); if (new_tones && old_tones != new_tones) s->high_low_timer = s->desc->high_low_timeout; /*endif*/ /* If a tone is being turned on, let's start the phase from zero */ if ((mode & SIG_TONE_1_PRESENT) && !(s->current_tx_tone & SIG_TONE_1_PRESENT)) s->phase_acc[0] = 0; if ((mode & SIG_TONE_2_PRESENT) && !(s->current_tx_tone & SIG_TONE_2_PRESENT)) s->phase_acc[1] = 0; s->current_tx_tone = mode; s->current_tx_timeout = duration; } /*- End of function --------------------------------------------------------*/ 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) { int i; if (sig_update == NULL || tone_type < 1 || tone_type > 3) return NULL; /*endif*/ if (s == NULL) { if ((s = (sig_tone_tx_state_t *) malloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); s->sig_update = sig_update; s->user_data = user_data; s->desc = &sig_tones[tone_type - 1]; for (i = 0; i < 2; i++) { if (s->desc->tone_freq[i]) s->phase_rate[i] = dds_phase_rate((float) s->desc->tone_freq[i]); else s->phase_rate[i] = 0; s->tone_scaling[i][0] = dds_scaling_dbm0((float) s->desc->tone_amp[i][0]); s->tone_scaling[i][1] = dds_scaling_dbm0((float) s->desc->tone_amp[i][1]); } return s; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) sig_tone_tx_release(sig_tone_tx_state_t *s) { return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) sig_tone_tx_free(sig_tone_tx_state_t *s) { if (s) free(s); return 0; } /*- End of function --------------------------------------------------------*/ int nnn = 0; SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len) { #if defined(SPANDSP_USE_FIXED_POINT) int16_t x; int32_t v; int16_t notched_signal[3]; int16_t bandpass_signal; int16_t signal; #else float x; float v; float notched_signal[3]; float bandpass_signal; float signal; #endif int i; int j; int k; int l; int m; int32_t notch_power[3]; int32_t flat_power; int immediate; l = s->desc->tones; if (l == 2) l = 3; notch_power[1] = notch_power[2] = INT32_MAX; for (i = 0; i < len; i++) { if (s->signalling_state_duration < INT_MAX) s->signalling_state_duration++; /*endif*/ signal = amp[i]; for (j = 0; j < l; j++) { k = coeff_sets[j]; /* The notch filter is two cascaded biquads. */ #if defined(SPANDSP_USE_FIXED_POINT) v = ((int32_t) signal*s->desc->notch[k]->a1[0]) + ((int32_t) s->tone[j].notch_z1[0]*s->desc->notch[k]->b1[1]) + ((int32_t) s->tone[j].notch_z1[1]*s->desc->notch[k]->b1[2]); x = v >> 15; v += ((int32_t) s->tone[j].notch_z1[0]*s->desc->notch[k]->a1[1]) + ((int32_t) s->tone[j].notch_z1[1]*s->desc->notch[k]->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[k]->b2[1]) + ((int32_t) s->tone[j].notch_z2[1]*s->desc->notch[k]->b2[2]); x = v >> 15; v += ((int32_t) s->tone[j].notch_z2[0]*s->desc->notch[k]->a2[1]) + ((int32_t) s->tone[j].notch_z2[1]*s->desc->notch[k]->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[k]->postscale; #else v = signal*s->desc->notch[k]->a1[0] + s->tone[j].notch_z1[0]*s->desc->notch[k]->b1[1] + s->tone[j].notch_z1[1]*s->desc->notch[k]->b1[2]; x = v; v += s->tone[j].notch_z1[0]*s->desc->notch[k]->a1[1] + s->tone[j].notch_z1[1]*s->desc->notch[k]->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[k]->b2[1] + s->tone[j].notch_z2[1]*s->desc->notch[k]->b2[2]; x = v; v += s->tone[j].notch_z2[0]*s->desc->notch[k]->a2[1] + s->tone[j].notch_z2[1]*s->desc->notch[k]->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 the power measurement rolling along. */ notch_power[j] = power_meter_update(&s->tone[j].power, notched_signal[j]); if (j == 1) signal = notched_signal[j]; } if ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT))) { if (s->flat_mode_timeout && --s->flat_mode_timeout == 0) s->flat_mode = TRUE; /*endif*/ } else { s->flat_mode_timeout = s->desc->sharp_flat_timeout; s->flat_mode = FALSE; } /*endif*/ immediate = -1; if (s->flat_mode) { //printf("Flat mode %d %d\n", s->flat_mode_timeout, s->desc->sharp_flat_timeout); /* Flat mode */ bandpass_signal = amp[i]; if (s->desc->flat) { /* 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->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT))) { if (flat_power < s->flat_detection_threshold) { s->signalling_state &= ~tone_present_bits[0]; s->signalling_state |= tone_change_bits[0]; } /*endif*/ } else { if (flat_power > s->flat_detection_threshold) s->signalling_state |= (tone_present_bits[0] | tone_change_bits[0]); /*endif*/ } /*endif*/ /* Notch insertion logic */ /* tone_present and tone_on are equivalent in flat mode */ if ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT))) { s->notch_insertion_timeout = s->desc->notch_lag_time; } else { if (s->notch_insertion_timeout) s->notch_insertion_timeout--; /*endif*/ } /*endif*/ } else { /* Sharp mode */ flat_power = power_meter_update(&s->flat_power, amp[i]); /* Persistence checking and notch insertion logic */ if (flat_power >= s->sharp_detection_threshold) { /* Which is the better of the single tone responses? */ m = (notch_power[0] < notch_power[1]) ? 0 : 1; /* Single tone has precedence. If the better one fails to detect, try for a dual tone signal. */ if ((notch_power[m] >> 6)*s->detection_ratio < (flat_power >> 6)) immediate = m; else if ((notch_power[2] >> 6)*s->detection_ratio < (flat_power >> 7)) immediate = 2; } //printf("Immediate = %d %d %d\n", immediate, s->signalling_state, s->tone_persistence_timeout); if ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT))) { if (immediate != s->current_notch_filter) { /* No tone is detected this sample */ if (--s->tone_persistence_timeout == 0) { /* Tone off is confirmed */ s->tone_persistence_timeout = s->desc->tone_on_check_time; s->signalling_state |= ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT)) << 1); s->signalling_state &= ~(SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT); } /*endif*/ } else { s->tone_persistence_timeout = s->desc->tone_off_check_time; } /*endif*/ } else { if (s->notch_insertion_timeout) s->notch_insertion_timeout--; /*endif*/ if (immediate >= 0 && immediate == s->last_sample_tone_present) { /* Consistent tone detected this sample */ if (--s->tone_persistence_timeout == 0) { /* Tone on is confirmed */ s->tone_persistence_timeout = s->desc->tone_off_check_time; s->notch_insertion_timeout = s->desc->notch_lag_time; s->signalling_state |= (tone_present_bits[immediate] | tone_change_bits[immediate]); s->current_notch_filter = immediate; } /*endif*/ } else { s->tone_persistence_timeout = s->desc->tone_on_check_time; } /*endif*/ } /*endif*/ //printf("XXX %d %d %d %d %d %d\n", nnn++, notch_power[0], notch_power[1], notch_power[2], flat_power, immediate*10000000); } /*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_insertion_timeout) #if defined(SPANDSP_USE_FIXED_POINT) amp[i] = saturate16(notched_signal[s->current_notch_filter]); #else amp[i] = fsaturatef(notched_signal[s->current_notch_filter]); #endif /*endif*/ } else { /* Simply mute the media path */ amp[i] = 0; } /*endif*/ s->last_sample_tone_present = immediate; } /*endfor*/ return len; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(void) sig_tone_rx_set_mode(sig_tone_rx_state_t *s, int mode, int duration) { s->current_rx_tone = mode; } /*- End of function --------------------------------------------------------*/ 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*/ if (s == NULL) { if ((s = (sig_tone_rx_state_t *) malloc(sizeof(*s))) == NULL) 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; } } for (i = 0; i < 2; i++) s->flat_z[i] = 0.0f; #endif s->last_sample_tone_present = -1; s->sig_update = sig_update; s->user_data = user_data; s->desc = &sig_tones[tone_type - 1]; for (i = 0; i < 3; i++) power_meter_init(&s->tone[i].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 --------------------------------------------------------*/ SPAN_DECLARE(int) sig_tone_rx_release(sig_tone_rx_state_t *s) { return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) sig_tone_rx_free(sig_tone_rx_state_t *s) { if (s) free(s); return 0; } /*- End of function --------------------------------------------------------*/ /*- End of file ------------------------------------------------------------*/