diff spandsp-0.0.6pre17/src/modem_connect_tones.c @ 4:26cd8f1ef0b1

import spandsp-0.0.6pre17
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 15:50:58 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spandsp-0.0.6pre17/src/modem_connect_tones.c	Fri Jun 25 15:50:58 2010 +0200
@@ -0,0 +1,623 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * modem_connect_tones.c - Generation and detection of tones
+ * associated with modems calling and answering calls.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2006 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: modem_connect_tones.c,v 1.41 2009/11/06 19:21:33 steveu Exp $
+ */
+ 
+/*! \file */
+
+/* CNG is 0.5s+-15% of 1100+-38Hz, 3s+-15% off, repeating.
+
+   CED is 0.2s silence, 3.3+-0.7s of 2100+-15Hz, and 75+-20ms of silence.
+   
+   ANS is 3.3+-0.7s of 2100+-15Hz.
+   
+   ANS/ is 3.3+-0.7s of 2100+-15Hz, with phase reversals (180+-10 degrees, hopping in <1ms) every 450+-25ms.
+
+   ANSam/ is 2100+-1Hz, with phase reversals (180+-10 degrees, hopping in <1ms) every 450+-25ms, and AM with a sinewave of 15+-0.1Hz. 
+   The modulated envelope ranges in amplitude between (0.8+-0.01) and (1.2+-0.01) times its average
+   amplitude. It lasts up to 5s, but will be stopped early if the V.8 protocol proceeds. */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <memory.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include "floating_fudge.h"
+#include <stdio.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/fast_convert.h"
+#include "spandsp/logging.h"
+#include "spandsp/complex.h"
+#include "spandsp/dds.h"
+#include "spandsp/tone_detect.h"
+#include "spandsp/tone_generate.h"
+#include "spandsp/super_tone_rx.h"
+#include "spandsp/power_meter.h"
+#include "spandsp/async.h"
+#include "spandsp/fsk.h"
+#include "spandsp/modem_connect_tones.h"
+
+#include "spandsp/private/fsk.h"
+#include "spandsp/private/modem_connect_tones.h"
+
+#define HDLC_FRAMING_OK_THRESHOLD       5
+
+SPAN_DECLARE(const char *) modem_connect_tone_to_str(int tone)
+{
+    switch (tone)
+    {
+    case MODEM_CONNECT_TONES_NONE:
+        return "No tone";
+    case MODEM_CONNECT_TONES_FAX_CNG:
+        return "FAX CNG";
+    case MODEM_CONNECT_TONES_ANS:
+        return "ANS or FAX CED";
+    case MODEM_CONNECT_TONES_ANS_PR:
+        return "ANS/";
+    case MODEM_CONNECT_TONES_ANSAM:
+        return "ANSam";
+    case MODEM_CONNECT_TONES_ANSAM_PR:
+        return "ANSam/";
+    case MODEM_CONNECT_TONES_FAX_PREAMBLE:
+        return "FAX preamble";
+    case MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE:
+        return "FAX CED or preamble";
+    }
+    return "???";
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE_NONSTD(int) modem_connect_tones_tx(modem_connect_tones_tx_state_t *s,
+                                                int16_t amp[],
+                                                int len)
+{
+    int16_t mod;
+    int i;
+    int xlen;
+
+    i = 0;
+    switch (s->tone_type)
+    {
+    case MODEM_CONNECT_TONES_FAX_CNG:
+        for (  ;  i < len;  i++)
+        {
+            if (s->duration_timer > ms_to_samples(3000))
+            {
+                if ((xlen = i + s->duration_timer - ms_to_samples(3000)) > len)
+                    xlen = len;
+                s->duration_timer -= (xlen - i);
+                for (  ;  i < xlen;  i++)
+                    amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, s->level, 0);
+            }
+            if (s->duration_timer > 0)
+            {
+                if ((xlen = i + s->duration_timer) > len)
+                    xlen = len;
+                s->duration_timer -= (xlen - i);
+                memset(amp + i, 0, sizeof(int16_t)*(xlen - i));
+                i = xlen;
+            }
+            if (s->duration_timer == 0)
+                s->duration_timer = ms_to_samples(500 + 3000);
+        }
+        break;
+    case MODEM_CONNECT_TONES_ANS:
+        if (s->duration_timer < len)
+            len = s->duration_timer;
+        if (s->duration_timer > ms_to_samples(2600))
+        {
+            /* There is some initial silence to be generated. */
+            if ((i = s->duration_timer - ms_to_samples(2600)) > len)
+                i = len;
+            memset(amp, 0, sizeof(int16_t)*i);
+        }
+        for (  ;  i < len;  i++)
+            amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, s->level, 0);
+        s->duration_timer -= len;
+        break;
+    case MODEM_CONNECT_TONES_ANS_PR:
+        if (s->duration_timer < len)
+            len = s->duration_timer;
+        if (s->duration_timer > ms_to_samples(3300))
+        {
+            if ((i = s->duration_timer - ms_to_samples(3300)) > len)
+                i = len;
+            memset(amp, 0, sizeof(int16_t)*i);
+        }
+        for (  ;  i < len;  i++)
+        {
+            if (--s->hop_timer <= 0)
+            {
+                s->hop_timer = ms_to_samples(450);
+                s->tone_phase += 0x80000000;
+            }
+            amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, s->level, 0);
+        }
+        s->duration_timer -= len;
+        break;
+    case MODEM_CONNECT_TONES_ANSAM:
+        if (s->duration_timer < len)
+            len = s->duration_timer;
+        if (s->duration_timer > ms_to_samples(5000))
+        {
+            if ((i = s->duration_timer - ms_to_samples(5000)) > len)
+                i = len;
+            memset(amp, 0, sizeof(int16_t)*i);
+        }
+        for (  ;  i < len;  i++)
+        {
+            mod = (int16_t) (s->level + dds_mod(&s->mod_phase, s->mod_phase_rate, s->mod_level, 0));
+            amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, mod, 0);
+        }
+        s->duration_timer -= len;
+        break;
+    case MODEM_CONNECT_TONES_ANSAM_PR:
+        if (s->duration_timer < len)
+            len = s->duration_timer;
+        if (s->duration_timer > ms_to_samples(5000))
+        {
+            if ((i = s->duration_timer - ms_to_samples(5000)) > len)
+                i = len;
+            memset(amp, 0, sizeof(int16_t)*i);
+        }
+        for (  ;  i < len;  i++)
+        {
+            if (--s->hop_timer <= 0)
+            {
+                s->hop_timer = ms_to_samples(450);
+                s->tone_phase += 0x80000000;
+            }
+            mod = (int16_t) (s->level + dds_mod(&s->mod_phase, s->mod_phase_rate, s->mod_level, 0));
+            amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, mod, 0);
+        }
+        s->duration_timer -= len;
+        break;
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(modem_connect_tones_tx_state_t *) modem_connect_tones_tx_init(modem_connect_tones_tx_state_t *s,
+                                                                           int tone_type)
+{
+    int alloced;
+
+    alloced = FALSE;
+    if (s == NULL)
+    {
+        if ((s = (modem_connect_tones_tx_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+        alloced = TRUE;
+    }
+    s->tone_type = tone_type;
+    switch (s->tone_type)
+    {
+    case MODEM_CONNECT_TONES_FAX_CNG:
+        /* 0.5s of 1100Hz+-38Hz + 3.0s of silence repeating. Timing +-15% */
+        s->tone_phase_rate = dds_phase_rate(1100.0);
+        s->level = dds_scaling_dbm0(-11);
+        s->duration_timer = ms_to_samples(500 + 3000);
+        s->mod_phase_rate = 0;
+        s->tone_phase = 0;
+        s->mod_phase = 0;
+        s->mod_level = 0;
+        s->hop_timer = 0;
+        break;
+    case MODEM_CONNECT_TONES_ANS:
+    case MODEM_CONNECT_TONES_ANSAM:
+        /* 0.2s of silence, then 2.6s to 4s of 2100Hz+-15Hz tone, then 75ms of silence. */
+        s->tone_phase_rate = dds_phase_rate(2100.0);
+        s->level = dds_scaling_dbm0(-11);
+        if (s->tone_type == MODEM_CONNECT_TONES_ANSAM)
+        {
+            s->mod_phase_rate = dds_phase_rate(15.0);
+            s->mod_level = s->level/5;
+            s->duration_timer = ms_to_samples(200 + 5000);
+        }
+        else
+        {
+            s->mod_phase_rate = 0;
+            s->mod_level = 0;
+            s->duration_timer = ms_to_samples(200 + 2600);
+        }
+        s->tone_phase = 0;
+        s->mod_phase = 0;
+        s->hop_timer = 0;
+        break;
+    case MODEM_CONNECT_TONES_ANS_PR:
+    case MODEM_CONNECT_TONES_ANSAM_PR:
+        s->tone_phase_rate = dds_phase_rate(2100.0);
+        s->level = dds_scaling_dbm0(-12);
+        if (s->tone_type == MODEM_CONNECT_TONES_ANSAM_PR)
+        {
+            s->mod_phase_rate = dds_phase_rate(15.0);
+            s->mod_level = s->level/5;
+            s->duration_timer = ms_to_samples(200 + 5000);
+        }
+        else
+        {
+            s->mod_phase_rate = 0;
+            s->mod_level = 0;
+            s->duration_timer = ms_to_samples(200 + 3300);
+        }
+        s->tone_phase = 0;
+        s->mod_phase = 0;
+        s->hop_timer = ms_to_samples(450);
+        break;
+    default:
+        if (alloced)
+            free(s);
+        return NULL;
+    }
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) modem_connect_tones_tx_release(modem_connect_tones_tx_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) modem_connect_tones_tx_free(modem_connect_tones_tx_state_t *s)
+{
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void report_tone_state(modem_connect_tones_rx_state_t *s, int tone, int level)
+{
+    if (tone != s->tone_present)
+    {
+        if (s->tone_callback)
+        {
+            s->tone_callback(s->callback_data, tone, level, 0);
+        }
+        else
+        {
+            if (tone != MODEM_CONNECT_TONES_NONE)
+                s->hit = tone;
+        }
+        s->tone_present = tone;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void v21_put_bit(void *user_data, int bit)
+{
+    modem_connect_tones_rx_state_t *s;
+
+    s = (modem_connect_tones_rx_state_t *) user_data;
+    if (bit < 0)
+    {
+        /* Special conditions. */
+        switch (bit)
+        {
+        case SIG_STATUS_CARRIER_DOWN:
+            /* Only declare tone off, if we were the one to declare tone on. */
+            if (s->tone_present == MODEM_CONNECT_TONES_FAX_PREAMBLE)
+                report_tone_state(s, MODEM_CONNECT_TONES_NONE, -99);
+            /* Fall through */
+        case SIG_STATUS_CARRIER_UP:
+            s->raw_bit_stream = 0;
+            s->num_bits = 0;
+            s->flags_seen = 0;
+            s->framing_ok_announced = FALSE;
+            break;
+        }
+        return;
+    }
+    /* Look for enough FAX V.21 message preamble (back to back HDLC flag octets) to be sure
+       we are really seeing preamble, and declare the signal to be present. Any change from
+       preamble declares the signal to not be present, though it will probably be the body
+       of the messages following the preamble. */
+    s->raw_bit_stream = (s->raw_bit_stream << 1) | ((bit << 8) & 0x100);
+    s->num_bits++;
+    if ((s->raw_bit_stream & 0x7F00) == 0x7E00)
+    {
+        if ((s->raw_bit_stream & 0x8000))
+        {
+            /* Hit HDLC abort */
+            s->flags_seen = 0;
+        }
+        else
+        {
+            /* Hit HDLC flag */
+            if (s->flags_seen < HDLC_FRAMING_OK_THRESHOLD)
+            {
+                /* Check the flags are back-to-back when testing for valid preamble. This
+                   greatly reduces the chances of false preamble detection, and anything
+                   which doesn't send them back-to-back is badly broken. */
+                if (s->num_bits != 8)
+                    s->flags_seen = 0;
+                if (++s->flags_seen >= HDLC_FRAMING_OK_THRESHOLD  &&  !s->framing_ok_announced)
+                {
+                    report_tone_state(s, MODEM_CONNECT_TONES_FAX_PREAMBLE, lfastrintf(fsk_rx_signal_power(&(s->v21rx))));
+                    s->framing_ok_announced = TRUE;
+                }
+            }
+        }
+        s->num_bits = 0;
+    }
+    else
+    {
+        if (s->flags_seen >= HDLC_FRAMING_OK_THRESHOLD)
+        {
+            if (s->num_bits == 8)
+            {
+                s->framing_ok_announced = FALSE;
+                s->flags_seen = 0;
+            }
+        }
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE_NONSTD(int) modem_connect_tones_rx(modem_connect_tones_rx_state_t *s,
+                                                const int16_t amp[],
+                                                int len)
+{
+    int i;
+    int16_t notched;
+    float v1;
+    float famp;
+    float filtered;
+
+    switch (s->tone_type)
+    {
+    case MODEM_CONNECT_TONES_FAX_CNG:
+        for (i = 0;  i < len;  i++)
+        {
+            famp = amp[i];
+            /* A Cauer notch at 1100Hz, spread just wide enough to meet our detection bandwidth
+               criteria. */
+            v1 = 0.792928f*famp + 1.0018744927985f*s->znotch_1 - 0.54196833412465f*s->znotch_2;
+            famp = v1 - 1.2994747954630f*s->znotch_1 + s->znotch_2;
+            s->znotch_2 = s->znotch_1;
+            s->znotch_1 = v1;
+            notched = (int16_t) lfastrintf(famp);
+
+            /* Estimate the overall energy in the channel, and the energy in
+               the notch (i.e. overall channel energy - tone energy => noise).
+               Use abs instead of multiply for speed (is it really faster?). */
+            s->channel_level += ((abs(amp[i]) - s->channel_level) >> 5);
+            s->notch_level += ((abs(notched) - s->notch_level) >> 5);
+            if (s->channel_level > 70  &&  s->notch_level*6 < s->channel_level)
+            {
+                /* There is adequate energy in the channel, and it is mostly at 1100Hz. */
+                if (s->tone_present != MODEM_CONNECT_TONES_FAX_CNG)
+                {
+                    if (++s->tone_cycle_duration >= ms_to_samples(415))
+                        report_tone_state(s, MODEM_CONNECT_TONES_FAX_CNG, lfastrintf(log10f(s->channel_level/32768.0f)*20.0f + DBM0_MAX_POWER + 0.8f));
+                }
+            }
+            else
+            {
+                /* If the signal looks wrong, even for a moment, we consider this the
+                   end of the tone. */
+                if (s->tone_present == MODEM_CONNECT_TONES_FAX_CNG)
+                    report_tone_state(s, MODEM_CONNECT_TONES_NONE, -99);
+                s->tone_cycle_duration = 0;
+            }
+        }
+        break;
+    case MODEM_CONNECT_TONES_FAX_PREAMBLE:
+        /* Ignore any CED tone, and just look for V.21 preamble. */
+        fsk_rx(&(s->v21rx), amp, len);
+        break;
+    case MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE:
+        /* Also look for V.21 preamble. A lot of machines don't send the 2100Hz burst. It
+           might also not be seen all the way through the channel, due to switching delays. */
+        fsk_rx(&(s->v21rx), amp, len);
+        /* Now fall through and look for a 2100Hz tone */
+    case MODEM_CONNECT_TONES_ANS:
+        for (i = 0;  i < len;  i++)
+        {
+            famp = amp[i];
+            /* A Cauer bandpass at 15Hz, with which we demodulate the AM signal. */
+            v1 = fabs(famp) + 1.996667f*s->z15hz_1 - 0.9968004f*s->z15hz_2;
+            filtered = 0.001599787f*(v1 - s->z15hz_2);
+            s->z15hz_2 = s->z15hz_1;
+            s->z15hz_1 = v1;
+            s->am_level += abs(lfastrintf(filtered)) - (s->am_level >> 8);
+            //printf("%9.1f %10.4f %9d %9d\n", famp, filtered, s->am_level, s->channel_level);
+            /* A Cauer notch at 2100Hz, spread just wide enough to meet our detection bandwidth
+               criteria. */
+            /* This is actually centred at 2095Hz, but gets the balance we want, due
+               to the asymmetric walls of the notch */
+            v1 = 0.76000f*famp - 0.1183852f*s->znotch_1 - 0.5104039f*s->znotch_2;
+            famp = v1 + 0.1567596f*s->znotch_1 + s->znotch_2;
+            s->znotch_2 = s->znotch_1;
+            s->znotch_1 = v1;
+            notched = (int16_t) lfastrintf(famp);
+            /* Estimate the overall energy in the channel, and the energy in
+               the notch (i.e. overall channel energy - tone energy => noise).
+               Use abs instead of multiply for speed (is it really faster?).
+               Damp the overall energy a little more for a stable result.
+               Damp the notch energy a little less, so we don't damp out the
+               blip every time the phase reverses. */
+            s->channel_level += ((abs(amp[i]) - s->channel_level) >> 5);
+            s->notch_level += ((abs(notched) - s->notch_level) >> 4);
+            /* This should cut off at about -43dBm0 */
+            if (s->channel_level <= 70)
+            {
+                /* If the energy level is low, even for a moment, we consider this the
+                   end of the tone. */
+                if (s->tone_present != MODEM_CONNECT_TONES_NONE)
+                    report_tone_state(s, MODEM_CONNECT_TONES_NONE, -99);
+                s->tone_cycle_duration = 0;
+                s->good_cycles = 0;
+                s->tone_on = FALSE;
+                continue;
+            }
+            /* There is adequate energy in the channel. Is it mostly at 2100Hz? */
+            s->tone_cycle_duration++;
+            if (s->notch_level*6 < s->channel_level)
+            {
+                /* The notch test says yes, so we have the tone. */
+                /* We should get a kick from the notch filter every 450+-25ms, as the phase reverses, for an
+                   EC disable tone. For a simple answer tone, the tone should persist unbroken for longer. */
+                if (!s->tone_on)
+                {
+                    if (s->tone_cycle_duration >= ms_to_samples(450 - 25))
+                    {
+                        if (++s->good_cycles == 3)
+                        {
+                            report_tone_state(s,
+                                              (s->am_level*15/256 > s->channel_level)  ?  MODEM_CONNECT_TONES_ANSAM_PR  :  MODEM_CONNECT_TONES_ANS_PR,
+                                              lfastrintf(log10f(s->channel_level/32768.0f)*20.0f + DBM0_MAX_POWER + 0.8f));
+                        }
+                    }
+                    else
+                    {
+                        s->good_cycles = 0;
+                    }
+                    /* Cycles are timed from rising edge to rising edge */
+                    s->tone_cycle_duration = 0;
+                }
+                else
+                {
+                    if (s->tone_cycle_duration >= ms_to_samples(450 + 100))
+                    {
+                        if (s->tone_present == MODEM_CONNECT_TONES_NONE)
+                        {
+                            report_tone_state(s,
+                                              (s->am_level*15/256 > s->channel_level)  ?  MODEM_CONNECT_TONES_ANSAM  :  MODEM_CONNECT_TONES_ANS,
+                                              lfastrintf(log10f(s->channel_level/32768.0f)*20.0f + DBM0_MAX_POWER + 0.8f));
+                        }
+                        s->good_cycles = 0;
+                        s->tone_cycle_duration = ms_to_samples(450 + 100);
+                    }
+                }
+                s->tone_on = TRUE;
+            }
+            else if (s->notch_level*5 > s->channel_level)
+            {
+                if (s->tone_present == MODEM_CONNECT_TONES_ANS)
+                {
+                    report_tone_state(s, MODEM_CONNECT_TONES_NONE, -99);
+                    s->good_cycles = 0;
+                }
+                else
+                {
+                    if (s->tone_cycle_duration >= ms_to_samples(450 + 25))
+                    {
+                        /* The change came too late for a cycle of ANS_PR tone */
+                        if (s->tone_present == MODEM_CONNECT_TONES_ANS_PR  ||  s->tone_present == MODEM_CONNECT_TONES_ANSAM_PR)
+                            report_tone_state(s, MODEM_CONNECT_TONES_NONE, -99);
+                        s->good_cycles = 0;
+                    }
+                }
+                s->tone_on = FALSE;
+            }
+        }
+        break;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) modem_connect_tones_rx_get(modem_connect_tones_rx_state_t *s)
+{
+    int x;
+    
+    x = s->hit;
+    s->hit = MODEM_CONNECT_TONES_NONE;
+    return x;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(modem_connect_tones_rx_state_t *) modem_connect_tones_rx_init(modem_connect_tones_rx_state_t *s,
+                                                                           int tone_type,
+                                                                           tone_report_func_t tone_callback,
+                                                                           void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (modem_connect_tones_rx_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+
+    s->tone_type = tone_type;
+    switch (s->tone_type)
+    {
+    case MODEM_CONNECT_TONES_FAX_PREAMBLE:
+    case MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE:
+        fsk_rx_init(&(s->v21rx), &preset_fsk_specs[FSK_V21CH2], FSK_FRAME_MODE_SYNC, v21_put_bit, s);
+        fsk_rx_signal_cutoff(&(s->v21rx), -45.5f);
+        break;
+    case MODEM_CONNECT_TONES_ANS_PR:
+    case MODEM_CONNECT_TONES_ANSAM:
+    case MODEM_CONNECT_TONES_ANSAM_PR:
+        /* Treat these all the same for receive purposes */
+        s->tone_type = MODEM_CONNECT_TONES_ANS;
+        break;
+    }
+    s->channel_level = 0;
+    s->notch_level = 0;
+    s->am_level = 0;
+    s->tone_present = MODEM_CONNECT_TONES_NONE;
+    s->tone_cycle_duration = 0;
+    s->good_cycles = 0;
+    s->hit = MODEM_CONNECT_TONES_NONE;
+    s->tone_on = FALSE;
+    s->tone_callback = tone_callback;
+    s->callback_data = user_data;
+    s->znotch_1 = 0.0f;
+    s->znotch_2 = 0.0f;
+    s->z15hz_1 = 0.0f;
+    s->z15hz_2 = 0.0f;
+    s->num_bits = 0;
+    s->flags_seen = 0;
+    s->framing_ok_announced = FALSE;
+    s->raw_bit_stream = 0;
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) modem_connect_tones_rx_release(modem_connect_tones_rx_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) modem_connect_tones_rx_free(modem_connect_tones_rx_state_t *s)
+{
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

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