diff spandsp-0.0.3/spandsp-0.0.3/src/sig_tone.c @ 5:f762bf195c4b

import spandsp-0.0.3
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 16:00:21 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spandsp-0.0.3/spandsp-0.0.3/src/sig_tone.c	Fri Jun 25 16:00:21 2010 +0200
@@ -0,0 +1,524 @@
+/*
+ * 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.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * 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 General Public License version 2, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.15 2006/11/19 14:07:25 steveu Exp $
+ */
+
+/*! \file */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include <memory.h>
+#include <string.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/dc_restore.h"
+#include "spandsp/dds.h"
+#include "spandsp/sig_tone.h"
+
+#define PI 3.14159265358979323
+
+/* The coefficients for the data notch filter. This filter is also the
+   guard filter for tone detection. */
+
+sig_tone_descriptor_t sig_tones[4] =
+{
+    {
+        /* 2280Hz (e.g. AC15, and many other European protocols) */
+        {2280,  0},
+        {-10, -20},
+        400*8,
+    
+        225*8,
+    
+        225*8,
+        TRUE,
+    
+        24,
+        64,
+    
+        {  3600,  14397,  32767},
+        {     0,  -9425, -28954},
+        {     0,  14196,  32767},
+        {     0, -17393, -28954},
+        12,
+/*
+    0.1098633,  0.4393615,   1.0,
+    0,         -0.2876274,  -0.8836059,
+    0,          0.4332275,   1.0,
+    0,         -0.5307922,  -0.8836059,
+*/
+        { 12900, -16384, -16384}, 
+        {     0,  -8578, -11796},
+        15,
+
+        31744,
+        1024,
+    
+        31744,
+        187,
+    
+        31744,
+        187,
+    
+        -1,
+        -32,
+    
+        57
+    },
+    {
+        /* 2600Hz (e.g. many US protocols) */
+        {2600, 0},
+        {-8, -8},
+        400*8,
+    
+        225*8,
+    
+        225*8,
+        FALSE,
+    
+        24,
+        64,
+    
+        {  3539,  29569,  32767},
+        {     0, -24010, -28341},
+        {     0,  29844,  32767},
+        {     0, -31208, -28341},
+        12,
+/*
+    0.1080017,  0.9023743,   1.0,
+    0,         -0.7327271,  -0.86489868,
+    0,          0.9107666,   1.0,
+    0,         -0.9523925,  -0.86489868,
+*/
+        { 32768,      0,      0},
+        {     0,      0,      0},
+        15,
+    
+        31744,
+        1024,
+    
+        31744,
+        170,
+    
+        31744,
+        170,
+    
+        -1,
+        -32,
+    
+        52
+    },
+    {
+        /* 2400Hz / 2600Hz (e.g. SS5 and SS5bis) */
+        {2400, 2600},
+        {-8, -8},
+        400*8,
+    
+        225*8,
+    
+        225*8,
+        FALSE,
+    
+        24,
+        64,
+    
+        {  3539,  20349,  32767},
+        {     0, -22075, -31856},
+        {     0,  20174,  32767},
+        {     0, -17832, -31836},
+        12,
+/*
+    0.1080017,  0.6210065,   1.0,
+    0,         -0.6736673,  -0.9721678,
+    0,          0.6156693,   1.0,
+    0,         -0.5441804,  -0.9715460,
+*/
+    
+        { 32768,      0,      0},
+        {     0,      0,      0},
+        15,
+    
+        31744,
+        1024,
+    
+        31744,
+        170,
+    
+        31744,
+        170,
+    
+        -1,
+        -32,
+    
+        52
+    }
+};
+
+int sig_tone_rx(sig_tone_state_t *s, int16_t amp[], int len)
+{
+    int32_t x;
+    int32_t notched_signal;
+    int32_t bandpass_signal;
+    int i;
+
+    for (i = 0;  i < len;  i++)
+    {
+        if (s->signaling_state_duration < 0xFFFF)
+            s->signaling_state_duration++;
+        /*endif*/
+        /* The notch filter is two cascaded biquads. */
+        notched_signal = amp[i];
+        
+        notched_signal *= s->desc->notch_a1[0];
+        notched_signal += s->notch_z1[1]*s->desc->notch_b1[1];
+        notched_signal += s->notch_z1[2]*s->desc->notch_b1[2];
+        x = notched_signal;
+        notched_signal += s->notch_z1[1]*s->desc->notch_a1[1];
+        notched_signal += s->notch_z1[2]*s->desc->notch_a1[2];
+        s->notch_z1[2] = s->notch_z1[1];
+        s->notch_z1[1] = x >> 15;
+        
+        notched_signal += s->notch_z2[1]*s->desc->notch_b2[1];
+        notched_signal += s->notch_z2[2]*s->desc->notch_b2[2];
+        x = notched_signal;
+        notched_signal += s->notch_z2[1]*s->desc->notch_a2[1];
+        notched_signal += s->notch_z2[2]*s->desc->notch_a2[2];
+        s->notch_z2[2] = s->notch_z2[1];
+        s->notch_z2[1] = x >> 15;
+        
+        notched_signal >>= s->desc->notch_postscale;
+
+        /* 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->notch_zl = ((s->notch_zl*s->desc->notch_slugi) >> 15)
+                    + ((abs(notched_signal)*s->desc->notch_slugp) >> 15);
+
+        /* Mow the grass to weed out the noise! */
+        s->mown_notch = s->notch_zl & s->desc->notch_threshold;
+
+        if (s->tone_present)
+        {
+            if (s->flat_mode_timeout <= 0)
+                s->flat_mode = TRUE;
+            else
+                s->flat_mode_timeout--;
+            /*endif*/
+        }
+        else
+        {
+            s->flat_mode_timeout = s->desc->sharp_flat_timeout;
+            s->flat_mode = FALSE;
+        }
+        /*endif*/
+
+        if (s->flat_mode)
+        {
+            /* Flat mode */
+    
+            /* The bandpass filter is a single bi-quad stage */
+            bandpass_signal = amp[i];
+            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;
+    
+            /* Leaky integrate the bandpassed data */
+            s->broad_zl = ((s->broad_zl*s->desc->broad_slugi) >> 15)
+                        + ((abs(bandpass_signal)*s->desc->broad_slugp) >> 15);
+    
+            /* For the broad band receiver we use a simple linear threshold! */
+            if (s->tone_present)
+            {
+                s->tone_present = (s->broad_zl > s->desc->broad_threshold);
+                if (!s->tone_present)
+                {
+                    if (s->sig_update)
+                        s->sig_update(s->user_data, SIG_TONE_1_CHANGE | (s->signaling_state_duration << 16));
+                    /*endif*/
+                    s->signaling_state_duration = 0;
+                }
+                /*endif*/
+            }
+            else
+            {
+                s->tone_present = (s->broad_zl > s->desc->broad_threshold);
+                if (s->tone_present)
+                {
+                    if (s->sig_update)
+                        s->sig_update(s->user_data, SIG_TONE_1_CHANGE | SIG_TONE_1_PRESENT | (s->signaling_state_duration << 16));
+                    /*endif*/
+                    s->signaling_state_duration = 0;
+                }
+                /*endif*/
+            }
+            /*endif*/
+            /* Notch insertion logic */
+    
+            /* tone_present and tone_on are equivalent in flat mode */
+            if (s->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)
+                    s->notch_insertion_timeout--;
+                else
+                    s->notch_enabled = FALSE;
+                /*endif*/
+            }
+            /*endif*/
+        }
+        else
+        {
+            /* Sharp mode */
+
+            /* Modulus and leaky integrate the data */
+            s->broad_zl = ((s->broad_zl*s->desc->unfiltered_slugi) >> 15)
+                        + ((abs(amp[i])*s->desc->unfiltered_slugp) >> 15);
+     
+            /* Mow the grass to weed out the noise! */
+            s->mown_bandpass = s->broad_zl & s->desc->unfiltered_threshold;
+    
+            /* Persistence checking and notch insertion logic */
+            if (!s->tone_present)
+            {
+                if (s->mown_notch < s->mown_bandpass)
+                {
+                    /* Tone is detected this sample */
+                    if (s->tone_persistence_timeout <= 0)
+                    {
+                        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 | (s->signaling_state_duration << 16));
+                        /*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*/
+                    }
+                    /*endif*/
+                }
+                else
+                {
+                    s->tone_persistence_timeout = s->desc->tone_on_check_time;
+                    if (s->notch_insertion_timeout > 0)
+                        s->notch_insertion_timeout--;
+                    else
+                        s->notch_enabled = FALSE;
+                    /*endif*/
+                }
+                /*endif*/
+            }
+            else
+            {
+                if (s->mown_notch > s->mown_bandpass)
+                {
+                    /* Tone is not detected this sample */
+                    if (s->tone_persistence_timeout <= 0)
+                    {
+                        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 | (s->signaling_state_duration << 16));
+                        /*endif*/
+                        s->signaling_state_duration = 0;
+                    }
+                    else
+                    {
+                        s->tone_persistence_timeout--;
+                    }
+                    /*endif*/
+                }
+                else
+                {
+                    s->tone_persistence_timeout = s->desc->tone_off_check_time;
+                }
+                /*endif*/
+            }
+            /*endif*/
+        }
+        /*endif*/
+
+        if ((s->current_tx_tone & SIG_TONE_RX_PASSTHROUGH))
+        {
+            //if (s->notch_enabled)
+                amp[i] = (int16_t) notched_signal;
+            /*endif*/
+        }
+        else
+        {
+            amp[i] = 0;
+        }
+        /*endif*/
+    }
+    /*endfor*/
+    return  len;
+}
+/*- End of function --------------------------------------------------------*/
+
+int sig_tone_tx(sig_tone_state_t *s, int16_t amp[], int len)
+{
+    int i;
+    int16_t tone;
+    int update;
+    int high_low;
+
+    if (s->current_tx_tone & SIG_TONE_1_PRESENT)
+    {
+        for (i = 0;  i < len;  i++)
+        {
+            if (s->high_low_timer > 0  &&  --s->high_low_timer > 0)
+                high_low = 1;
+            else
+                high_low = 0;
+            /*endif*/
+            tone = dds_mod(&(s->phase_acc[0]), s->phase_rate[0], s->tone_scaling[high_low], 0);
+            if (s->current_tx_tone & SIG_TONE_TX_PASSTHROUGH)
+                amp[i] = saturate(amp[i] + tone);
+            else
+                amp[i] = tone;
+            /*endif*/
+            if (--s->current_tx_timeout <= 0)
+            {
+                if (s->sig_update)
+                {
+                    update = s->sig_update(s->user_data, SIG_TONE_UPDATE_REQUEST);
+                    if ((update & 0x03) == 0x03  &&  !(s->current_tx_tone & SIG_TONE_1_PRESENT))
+                        s->high_low_timer = s->desc->high_low_timeout;
+                    /*endif*/
+                    s->current_tx_tone = update & 0xFFFF;
+                    s->current_tx_timeout = (update >> 16) & 0xFFFF;
+                }
+                /*endif*/
+            }
+            /*endif*/
+        }
+        /*endfor*/
+    }
+    else
+    {
+        for (i = 0;  i < len;  )
+        {
+            if (s->current_tx_timeout < len - i)
+            {
+                if (!(s->current_tx_tone & SIG_TONE_TX_PASSTHROUGH))
+                {
+                    /* Zap any audio in the buffer */
+                    memset(amp + i, 0, sizeof(int16_t)*s->current_tx_timeout);
+                }
+                /*endif*/
+                i += s->current_tx_timeout;
+                if (s->sig_update)
+                {
+                    update = s->sig_update(s->user_data, SIG_TONE_UPDATE_REQUEST);
+                    if ((update & 0x03) == 0x03)
+                        s->high_low_timer = s->desc->high_low_timeout;
+                    /*endif*/
+                    s->current_tx_tone = update & 0xFFFF;
+                    s->current_tx_timeout = (update >> 16) & 0xFFFF;
+                }
+                /*endif*/
+            }
+            else
+            {
+                s->current_tx_timeout -= (len - i);
+                if (!(s->current_tx_tone & SIG_TONE_TX_PASSTHROUGH))
+                {
+                    /* Zap any audio in the buffer */
+                    memset(amp + i, 0, sizeof(int16_t)*(len - i));
+                    i = len;
+                }
+                /*endif*/
+            }
+            /*endif*/
+        }
+        /*endfor*/
+    }
+    /*endif*/
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+sig_tone_state_t *sig_tone_init(sig_tone_state_t *s, int tone_type, sig_tone_func_t sig_update, void *user_data)
+{
+    if (tone_type <= 0  ||  tone_type > 3)
+        return NULL;
+    /*endif*/
+
+    memset(s, 0, sizeof(*s));
+
+    s->sig_update = sig_update;
+    s->user_data = user_data;
+
+    s->desc = &sig_tones[tone_type - 1];
+
+    /* Set up the transmit side */
+    s->phase_rate[0] = dds_phase_rate((float) s->desc->tone_freq[0]);
+    if (s->desc->tone_freq[1])
+        s->phase_rate[1] = dds_phase_rate((float) s->desc->tone_freq[1]);
+    else
+        s->phase_rate[1] = 0;
+    /*endif*/
+    s->tone_scaling[0] = dds_scaling_dbm0((float) s->desc->tone_amp[0]);
+    s->tone_scaling[1] = dds_scaling_dbm0((float) s->desc->tone_amp[1]);
+
+    /* Set up the receive side */
+    s->flat_mode_timeout = 0;
+    s->notch_insertion_timeout = 0;
+    s->tone_persistence_timeout = 0;
+    s->signaling_state_duration = 0;
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.