diff spandsp-0.0.3/spandsp-0.0.3/tests/dtmf_rx_tests.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/tests/dtmf_rx_tests.c	Fri Jun 25 16:00:21 2010 +0200
@@ -0,0 +1,894 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * dtmf_rx_tests.c - Test the DTMF detector against the spec., whatever the spec.
+ *                   may be :)
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001, 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 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: dtmf_rx_tests.c,v 1.23 2006/11/19 14:07:26 steveu Exp $
+ */
+
+/*
+ * These tests include conversion to and from A-law. I assume the
+ * distortion this produces is comparable to u-law, so it should be
+ * a fair test.
+ *
+ * These tests mirror those on the CM7291 test tape from Mitel.
+ * Many of these tests are highly questionable, but they are a
+ * well accepted industry standard.
+ *
+ * However standard these tests might be, Mitel appears to have stopped
+ * selling copies of their tape.
+ *
+ * For the talk-off test the Bellcore tapes may be used. However, they are
+ * copyright material, so the test data files produced from the Bellcore
+ * tapes cannot be distributed as a part of this package.
+ *
+ */
+
+/*! \page dtmf_rx_tests_page DTMF receiver tests
+\section dtmf_rx_tests_page_sec_1 What does it do?
+
+The DTMF detection test suite performs similar tests to the Mitel test tape,
+traditionally used for testing DTMF receivers. Mitel seem to have discontinued
+this product, but all it not lost. 
+
+The first side of the Mitel tape consists of a number of tone and tone+noise
+based tests. The test suite synthesizes equivalent test data. Being digitally
+generated, this data is rather more predictable than the test data on the nasty
+old stretchy cassette tapes which Mitel sold. 
+
+The second side of the Mitel tape contains fragments of real speech from real
+phone calls captured from the North American telephone network. These are
+considered troublesome for DTMF detectors. A good detector is expected to
+achieve a reasonably low number of false detections on this data. Fresh clean
+copies of this seem to be unobtainable. However, Bellcore produce a much more
+aggressive set of three cassette tapes. All six side (about 30 minutes each) are
+filled with much tougher fragments of real speech from the North American
+telephone network. If you can do well in this test, nobody cares about your
+results against the Mitel test tape. 
+
+A fresh set of tapes was purchased for these tests, and digitised, producing 6
+wave files of 16 bit signed PCM data, sampled at 8kHz. They were transcribed
+using a speed adjustable cassette player. The test tone at the start of the
+tapes is pretty accurate, and the new tapes should not have had much opportunity
+to stretch. It is believed these transcriptions are about as good as the source
+material permits. 
+
+PLEASE NOTE
+
+These transcriptions may be freely used by anyone who has a legitimate copy of
+the original tapes. However, if you don't have a legitimate copy of those tapes,
+you also have no right to use this data. The original tapes are the copyright
+material of BellCore, and they charge over US$200 for a set. I doubt they sell
+enough copies to consider this much of a business. However, it is their data,
+and it is their right to do as they wish with it. Currently I see no indication
+they wish to give it away for free. 
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include <stdio.h>
+#include <time.h>
+#include <fcntl.h>
+#include <audiofile.h>
+#include <tiffio.h>
+
+#include "spandsp.h"
+
+#include "test_utils.h"
+
+/* Basic DTMF specs:
+ *
+ * Minimum tone on = 40ms
+ * Minimum tone off = 50ms
+ * Maximum digit rate = 10 per second
+ * Normal twist <= 8dB accepted
+ * Reverse twist <= 4dB accepted
+ * S/N >= 15dB will detect OK
+ * Attenuation <= 26dB will detect OK
+ * Frequency tolerance +- 1.5% will detect, +-3.5% will reject
+ */
+
+#define DTMF_DURATION               380
+#define DTMF_PAUSE                  400
+#define DTMF_CYCLE                  (DTMF_DURATION + DTMF_PAUSE)
+
+#define ALL_POSSIBLE_DIGITS         "123A456B789C*0#D"
+
+#define MITEL_DIR                   "../itutests/mitel/"
+#define BELLCORE_DIR                "../itutests/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 tone_gen_descriptor_t my_dtmf_digit_tones[16];
+
+float dtmf_row[] =
+{
+     697.0f,  770.0f,  852.0f,  941.0f
+};
+float dtmf_col[] =
+{
+    1209.0f, 1336.0f, 1477.0f, 1633.0f
+};
+
+char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
+
+int callback_hit;
+int callback_ok;
+int callback_roll;
+int step;
+
+int use_dialtone_filter = FALSE;
+
+char *decode_test_file = NULL;
+
+static int16_t amp[1000000];
+static int16_t amp2[1000000];
+
+codec_munge_state_t *munge = NULL;
+
+static void my_dtmf_gen_init(float low_fudge,
+                             int low_level,
+                             float high_fudge,
+                             int high_level,
+                             int duration,
+                             int gap)
+{
+    int row;
+    int col;
+
+    for (row = 0;  row < 4;  row++)
+    {
+        for (col = 0;  col < 4;  col++)
+        {
+            make_tone_gen_descriptor(&my_dtmf_digit_tones[row*4 + col],
+                                     dtmf_row[row]*(1.0f + low_fudge),
+                                     low_level,
+                                     dtmf_col[col]*(1.0f + high_fudge),
+                                     high_level,
+                                     duration,
+                                     gap,
+                                     0,
+                                     0,
+                                     FALSE);
+        }
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int my_dtmf_generate(int16_t amp[], const char *digits)
+{
+    int len;
+    char *cp;
+    tone_gen_state_t tone;
+
+    len = 0;
+    while (*digits)
+    {
+        cp = strchr(dtmf_positions, *digits);
+        if (cp)
+        {
+            tone_gen_init(&tone, &my_dtmf_digit_tones[cp - dtmf_positions]);
+            len += tone_gen(&tone, amp + len, 1000);
+        }
+        digits++;
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void digit_delivery(void *data, const char *digits, int len)
+{
+    int i;
+    int seg;
+    const char *s = ALL_POSSIBLE_DIGITS;
+    const char *t;
+
+    callback_hit = TRUE;
+    if (data == (void *) 0x12345678)
+    {
+        t = s + callback_roll;
+        seg = 16 - callback_roll;
+        for (i = 0;  i < len;  i += seg, seg = 16)
+        {
+            if (i + seg > len)
+                seg = len - i;
+            if (memcmp(digits + i, t, seg))
+            {
+                callback_ok = FALSE;
+                printf("Fail at %d %d\n", i, seg);
+                break;
+            }
+            t = s;
+            callback_roll = (callback_roll + seg)%16;
+        }
+    }
+    else
+    {
+        callback_ok = FALSE;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void digit_status(void *data, int signal)
+{
+    const char *s = ALL_POSSIBLE_DIGITS;
+    int len;
+    static int last_step = 0;
+    static int first = TRUE;
+
+    callback_hit = TRUE;
+    len = step - last_step;
+    if (data == (void *) 0x12345678)
+    {
+        if (len < 320  ||  len > 480)
+        {
+            if (first)
+            {
+                /* At the beginning the apparent duration is expected to be wrong */
+                first = FALSE;
+            }
+            else
+            {
+                printf("Failed for signal %s length %d at %d\n", (callback_roll & 1)  ?  "on"  :  "off", len, step);
+                callback_ok = FALSE;
+            }
+        }
+        if (callback_roll & 1)
+        {
+            if (signal != 0)
+            {
+                printf("Failed for signal 0x%X instead of 0\n", signal);
+                callback_ok = FALSE;
+            }
+        }
+        else
+        {
+            if (signal != s[callback_roll >> 1])
+            {
+                printf("Failed for signal 0x%X instead of 0x%X\n", signal, s[callback_roll >> 1]);
+                callback_ok = FALSE;
+            }
+        }
+        if (++callback_roll >= 32)
+            callback_roll = 0;
+    }
+    else
+    {
+        callback_ok = FALSE;
+    }
+    last_step = step;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void mitel_cm7291_side_1_tests(void)
+{
+    int i;
+    int j;
+    int len;
+    int sample;
+    const char *s;
+    char digit[2];
+    char buf[128 + 1];
+    int actual;
+    int nplus;
+    int nminus;
+    float rrb;
+    float rcfo;
+    dtmf_rx_state_t dtmf_state;
+    awgn_state_t noise_source;
+
+    dtmf_rx_init(&dtmf_state, NULL, NULL);
+    if (use_dialtone_filter)
+        dtmf_rx_parms(&dtmf_state, TRUE, -1, -1);
+
+    /* Test 1: Mitel's test 1 isn't really a test. Its a calibration step,
+       which has no meaning here. */
+    printf("Test 1: Calibration\n");
+    printf("    Passed\n");
+
+    /* Test 2: Decode check
+       This is a sanity check, that all digits are reliably detected
+       under ideal conditions.  Each possible digit repeated 10 times,
+       with 50ms bursts. The level of each tone is about 6dB down from clip.
+       6dB down actually causes trouble with G.726, so we use 7dB down. */
+    printf("Test 2: Decode check\n");
+    my_dtmf_gen_init(0.0f, -4, 0.0f, -4, 50, 50);
+    s = ALL_POSSIBLE_DIGITS;
+    digit[1] = '\0';
+    while (*s)
+    {
+        digit[0] = *s++;
+        for (i = 0;  i < 10;  i++)
+        {
+            len = my_dtmf_generate(amp, digit);
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+
+            actual = dtmf_rx_get(&dtmf_state, buf, 128);
+
+            if (actual != 1  ||  buf[0] != digit[0])
+            {
+                printf("    Sent     '%s'\n", digit);
+                printf("    Received '%s'\n", buf);
+                printf("    Failed\n");
+                exit(2);
+            }
+        }
+    }
+    printf("    Passed\n");
+
+    /* Test 3: Recognition bandwidth and channel centre frequency check.
+       Use only the diagonal pairs of tones (digits 1, 5, 9 and D). Each
+       tone pair requires four test to complete the check, making 16
+       sections overall. Each section contains 40 pulses of
+       50ms duration, with an amplitude of -20dB from clip per
+       frequency.
+
+       Four sections covering the tests for one tone (1 digit) are:
+       a. H frequency at 0% deviation from center, L frequency at +0.1%.
+          L frequency is then increments in +01.% steps up to +4%. The
+          number of tone bursts is noted and designated N+.
+       b. H frequency at 0% deviation, L frequency at -0.1%. L frequency
+          is then incremental in -0.1% steps, up to -4%. The number of
+          tone bursts is noted and designated N-.
+       c. The test in (a) is repeated with the L frequency at 0% and the
+          H frequency varied up to +4%.
+       d. The test in (b) is repeated with the L frequency and 0% and the
+          H frequency varied to -4%.
+
+       Receiver Recognition Bandwidth (RRB) is calculated as follows:
+            RRB% = (N+ + N-)/10
+       Receiver Center Frequency Offset (RCFO) is calculated as follows:
+            RCFO% = X + (N+ - N-)/20
+
+       Note that this test doesn't test what it says it is testing at all,
+       and the results are quite inaccurate, if not a downright lie! However,
+       it follows the Mitel procedure, so how can it be bad? :)
+    */
+    printf("Test 3: Recognition bandwidth and channel centre frequency check\n");
+    s = "159D";
+    digit[1] = '\0';
+    while (*s)
+    {
+        digit[0] = *s++;
+        for (nplus = 0, i = 1;  i <= 60;  i++)
+        {
+            my_dtmf_gen_init((float) i/1000.0f, -17, 0.0f, -17, 50, 50);
+            len = my_dtmf_generate(amp, digit);
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+            nplus += dtmf_rx_get(&dtmf_state, buf, 128);
+        }
+        for (nminus = 0, i = -1;  i >= -60;  i--)
+        {
+            my_dtmf_gen_init((float) i/1000.0f, -17, 0.0f, -17, 50, 50);
+            len = my_dtmf_generate(amp, digit);
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+            nminus += dtmf_rx_get(&dtmf_state, buf, 128);
+        }
+        rrb = (float) (nplus + nminus)/10.0f;
+        rcfo = (float) (nplus - nminus)/10.0f;
+        printf("    %c (low)  rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n",
+               digit[0],
+               rrb,
+               rcfo,
+               (float) nminus/10.0f,
+               (float) nplus/10.0f);
+        if (rrb < 3.0f + rcfo  ||  rrb >= 15.0f + rcfo)
+        {
+            printf("    Failed\n");
+            exit(2);
+        }
+
+        for (nplus = 0, i = 1;  i <= 60;  i++)
+        {
+            my_dtmf_gen_init(0.0f, -17, (float) i/1000.0f, -17, 50, 50);
+            len = my_dtmf_generate(amp, digit);
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+            nplus += dtmf_rx_get(&dtmf_state, buf, 128);
+        }
+        for (nminus = 0, i = -1;  i >= -60;  i--)
+        {
+            my_dtmf_gen_init(0.0f, -17, (float) i/1000.0f, -17, 50, 50);
+            len = my_dtmf_generate(amp, digit);
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+            nminus += dtmf_rx_get(&dtmf_state, buf, 128);
+        }
+        rrb = (float) (nplus + nminus)/10.0f;
+        rcfo = (float) (nplus - nminus)/10.0f;
+        printf("    %c (high) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n",
+               digit[0],
+               rrb,
+               rcfo,
+               (float) nminus/10.0f,
+               (float) nplus/10.0f);
+        if (rrb < 3.0f + rcfo  ||  rrb >= 15.0f + rcfo)
+        {
+            printf("    Failed\n");
+            exit(2);
+        }
+    }
+    printf("    Passed\n");
+
+    /* Test 4: Acceptable amplitude ratio (twist).
+       Use only the diagonal pairs of tones (digits 1, 5, 9 and D). 
+       There are eight sections to the test. Each section contains 200
+       pulses with a 50ms duration for each pulse. Initially the amplitude
+       of both tones is 6dB down from clip. The two sections to test one
+       tone pair are:
+
+       a. Standard Twist: H tone amplitude is maintained at -6dB from clip,
+          L tone amplitude is attenuated gradually until the amplitude ratio
+          L/H is -20dB. Note the number of responses from the receiver.
+       b. Reverse Twist: L tone amplitude is maintained at -6dB from clip,
+          H tone amplitude is attenuated gradually until the amplitude ratio
+          is 20dB. Note the number of responses from the receiver.
+
+       All tone bursts are of 50ms duration.
+
+       The Acceptable Amplitude Ratio in dB is equal to the number of
+       responses registered in (a) or (b), divided by 10.
+       
+       TODO: This is supposed to work in 1/10dB steps, but here I used 1dB
+             steps, as the current tone generator has its amplitude set in
+             1dB steps.
+    */
+    printf("Test 4: Acceptable amplitude ratio (twist)\n");
+    s = "159D";
+    digit[1] = '\0';
+    while (*s)
+    {
+        digit[0] = *s++;
+        for (nplus = 0, i = -30;  i >= -230;  i--)
+        {
+            my_dtmf_gen_init(0.0f, -3, 0.0f, i/10, 50, 50);
+
+            len = my_dtmf_generate(amp, digit);
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+            nplus += dtmf_rx_get(&dtmf_state, buf, 128);
+        }
+        printf("    %c normal twist  = %.2fdB\n", digit[0], (float) nplus/10.0);
+        if (nplus < 80)
+        {
+            printf("    Failed\n");
+            exit(2);
+        }
+        for (nminus = 0, i = -30;  i >= -230;  i--)
+        {
+            my_dtmf_gen_init(0.0f, i/10, 0.0f, -3, 50, 50);
+
+            len = my_dtmf_generate(amp, digit);
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+            nminus += dtmf_rx_get(&dtmf_state, buf, 128);
+        }
+        printf("    %c reverse twist = %.2fdB\n", digit[0], (float) nminus/10.0);
+        if (nminus < 40)
+        {
+            printf("    Failed\n");
+            exit(2);
+        }
+    }
+    printf("    Passed\n");
+
+    /* Test 5: Dynamic range
+       This test utilizes tone pair L1 H1 (digit 1). Thirty-five tone pair
+       pulses are transmitted, with both frequencies stating at -6dB from
+       clip. The amplitude of each is gradually attenuated by -35dB at a
+       rate of 1dB per pulse. The Dynamic Range in dB is equal to the
+       number of responses from the receiver during the test.
+       
+       Well not really, but that is the Mitel test. Lets sweep a bit further,
+       and see what the real range is */
+    printf("Test 5: Dynamic range\n");
+    for (nplus = 0, i = +3;  i >= -50;  i--)
+    {
+        my_dtmf_gen_init(0.0f, i, 0.0f, i, 50, 50);
+
+        len = my_dtmf_generate(amp, "1");
+        codec_munge(munge, amp, len);
+        dtmf_rx(&dtmf_state, amp, len);
+        nplus += dtmf_rx_get(&dtmf_state, buf, 128);
+    }
+    printf("    Dynamic range = %ddB\n", nplus);
+    printf("    Passed\n");
+
+    /* Test 6: Guard time
+       This test utilizes tone pair L1 H1 (digit 1). Four hundred pulses
+       are transmitted at an amplitude of -6dB from clip per frequency.
+       Pulse duration starts at 49ms and is gradually reduced to 10ms.
+       Guard time in ms is equal to (500 - number of responses)/10.
+       
+       That is the Mitel test, and we will follow it. Its totally bogus,
+       though. Just what the heck is a pass or fail here? */
+
+    printf("Test 6: Guard time\n");
+    for (nplus = 0, i = 490;  i >= 100;  i--)
+    {
+        my_dtmf_gen_init(0.0f, -3, 0.0f, -3, i/10, 50);
+
+        len = my_dtmf_generate(amp, "1");
+        codec_munge(munge, amp, len);
+        dtmf_rx(&dtmf_state, amp, len);
+        nplus += dtmf_rx_get(&dtmf_state, buf, 128);
+    }
+    printf("    Guard time = %dms\n", (500 - nplus)/10);
+    printf("    Passed\n");
+
+    /* Test 7: Acceptable signal to noise ratio
+       This test utilizes tone pair L1 H1, transmitted on a noise background.
+       The test consists of three sections in which the tone pair is
+       transmitted 1000 times at an amplitude -6dB from clip per frequency,
+       but with a different white noise level for each section. The first
+       level is -24dBV, the second -18dBV and the third -12dBV.. The
+       acceptable signal to noise ratio is the lowest ratio of signal
+       to noise in the test where the receiver responds to all 1000 pulses.
+       
+       Well, that is the Mitel test, but it doesn't tell you what the
+       decoder can really do. Lets do a more comprehensive test */
+
+    printf("Test 7: Acceptable signal to noise ratio\n");
+    my_dtmf_gen_init(0.0f, -4, 0.0f, -4, 50, 50);
+
+    for (j = -13;  j > -50;  j--)
+    {
+        awgn_init_dbm0(&noise_source, 1234567, (float) j);
+        for (i = 0;  i < 1000;  i++)
+        {
+            len = my_dtmf_generate(amp, "1");
+
+            // TODO: Clip
+            for (sample = 0;  sample < len;  sample++)
+                amp[sample] = saturate(amp[sample] + awgn(&noise_source));
+            
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+
+            if (dtmf_rx_get(&dtmf_state, buf, 128) != 1)
+                break;
+        }
+        if (i == 1000)
+            break;
+    }
+    printf("    Acceptable S/N ratio is %ddB\n", -4 - j);
+    if (-4 - j > 26)
+    {
+        printf("    Failed\n");
+        exit(2);
+    }
+    printf("    Passed\n");
+}
+/*- End of function --------------------------------------------------------*/
+
+static void mitel_cm7291_side_2_and_bellcore_tests(void)
+{
+    int i;
+    int j;
+    int len;
+    int hits;
+    int hit_types[256];
+    char buf[128 + 1];
+    AFfilehandle inhandle;
+    int frames;
+    float x;
+    dtmf_rx_state_t dtmf_state;
+
+    dtmf_rx_init(&dtmf_state, NULL, NULL);
+    if (use_dialtone_filter)
+        dtmf_rx_parms(&dtmf_state, TRUE, -1, -1);
+
+    /* The remainder of the Mitel tape is the talk-off test */
+    /* Here we use the Bellcore test tapes (much tougher), in six
+       wave files - 1 from each side of the original 3 cassette tapes */
+    /* Bellcore say you should get no more than 470 false detections with
+       a good receiver. Dialogic claim 20. Of course, we can do better than
+       that, eh? */
+    printf("Test 8: Talk-off test\n");
+    memset(hit_types, '\0', sizeof(hit_types));
+    for (j = 0;  bellcore_files[j][0];  j++)
+    {
+        if ((inhandle = afOpenFile(bellcore_files[j], "r", 0)) == AF_NULL_FILEHANDLE)
+        {
+            printf("    Cannot open speech file '%s'\n", bellcore_files[j]);
+            exit(2);
+        }
+        if ((x = afGetFrameSize(inhandle, AF_DEFAULT_TRACK, 1)) != 2.0)
+        {
+            printf("    Unexpected frame size in speech file '%s'\n", bellcore_files[j]);
+            exit(2);
+        }
+        if ((x = afGetRate(inhandle, AF_DEFAULT_TRACK)) != (float) SAMPLE_RATE)
+        {
+            printf("    Unexpected sample rate in speech file '%s'\n", bellcore_files[j]);
+            exit(2);
+        }
+        if ((x = afGetChannels(inhandle, AF_DEFAULT_TRACK)) != 1.0)
+        {
+            printf("    Unexpected number of channels in speech file '%s'\n", bellcore_files[j]);
+            exit(2);
+        }
+        hits = 0;
+        while ((frames = afReadFrames(inhandle, AF_DEFAULT_TRACK, amp, SAMPLE_RATE)))
+        {
+            dtmf_rx(&dtmf_state, amp, frames);
+            len = dtmf_rx_get(&dtmf_state, buf, 128);
+            if (len > 0)
+            {
+                for (i = 0;  i < len;  i++)
+                    hit_types[(int) buf[i]]++;
+                hits += len;
+            }
+        }
+        if (afCloseFile(inhandle) != 0)
+        {
+            printf("    Cannot close speech file '%s'\n", bellcore_files[j]);
+            exit(2);
+        }
+        printf("    File %d gave %d false hits.\n", j + 1, hits);
+    }
+    for (i = 0, j = 0;  i < 256;  i++)
+    {
+        if (hit_types[i])
+        {
+            printf("    Digit %c had %d false hits\n", i, hit_types[i]);
+            j += hit_types[i];
+        }
+    }
+    printf("    %d hits in total\n", j);
+    if (j > 470)
+    {
+        printf("    Failed\n");
+        exit(2);
+    }
+    printf("    Passed\n");
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dial_tone_tolerance_tests(void)
+{
+    int i;
+    int j;
+    int len;
+    int sample;
+    char buf[128 + 1];
+    dtmf_rx_state_t dtmf_state;
+    tone_gen_descriptor_t dial_tone_desc;
+    tone_gen_state_t dial_tone;
+
+    dtmf_rx_init(&dtmf_state, NULL, NULL);
+    if (use_dialtone_filter)
+        dtmf_rx_parms(&dtmf_state, TRUE, -1, -1);
+
+    /* Test dial tone tolerance */
+    printf("Test: Dial tone tolerance.\n");
+    my_dtmf_gen_init(0.0f, -15, 0.0f, -15, 50, 50);
+
+    for (j = -30;  j < -3;  j++)
+    {
+        make_tone_gen_descriptor(&dial_tone_desc, 350, j, 440, j, 1, 0, 0, 0, TRUE);
+        tone_gen_init(&dial_tone, &dial_tone_desc);
+        for (i = 0;  i < 10;  i++)
+        {
+            len = my_dtmf_generate(amp, ALL_POSSIBLE_DIGITS);
+            tone_gen(&dial_tone, amp2, len);
+
+            for (sample = 0;  sample < len;  sample++)
+                amp[sample] = saturate(amp[sample] + amp2[sample]);
+            codec_munge(munge, amp, len);
+            dtmf_rx(&dtmf_state, amp, len);
+
+            if (dtmf_rx_get(&dtmf_state, buf, 128) != strlen(ALL_POSSIBLE_DIGITS))
+                break;
+        }
+        if (i != 10)
+            break;
+    }
+    printf("    Acceptable signal to dial tone ratio is %ddB\n", -15 - j);
+    if ((use_dialtone_filter  &&  (-15 - j) > -12)
+        ||
+        (!use_dialtone_filter  &&  (-15 - j) > 10)) 
+    {
+        printf("    Failed\n");
+        exit(2); 
+    }
+    printf("    Passed\n");
+}
+/*- End of function --------------------------------------------------------*/
+
+static void callback_function_tests(void)
+{
+    int i;
+    int j;
+    int len;
+    int sample;
+    dtmf_rx_state_t dtmf_state;
+
+    /* Test the callback mode for delivering detected digits */
+    printf("Test: Callback digit delivery mode.\n");
+    callback_hit = FALSE;
+    callback_ok = TRUE;
+    callback_roll = 0;
+    dtmf_rx_init(&dtmf_state, digit_delivery, (void *) 0x12345678);
+    if (use_dialtone_filter)
+        dtmf_rx_parms(&dtmf_state, TRUE, -1, -1);
+    my_dtmf_gen_init(0.0f, -10, 0.0f, -10, 50, 50);
+    for (i = 1;  i < 10;  i++)
+    {
+        len = 0;
+        for (j = 0;  j < i;  j++)
+            len += my_dtmf_generate(amp + len, ALL_POSSIBLE_DIGITS);
+        dtmf_rx(&dtmf_state, amp, len);
+        if (!callback_hit  ||  !callback_ok)
+            break;
+    }
+    if (!callback_hit  ||  !callback_ok)
+    {
+        printf("    Failed\n");
+        exit(2);
+    }
+    printf("    Passed\n");
+
+    /* Test the realtime callback mode for reporting detected digits */
+    printf("Test: Realtime callback digit delivery mode.\n");
+    callback_hit = FALSE;
+    callback_ok = TRUE;
+    callback_roll = 0;
+    dtmf_rx_init(&dtmf_state, NULL, NULL);
+    dtmf_rx_set_realtime_callback(&dtmf_state, digit_status, (void *) 0x12345678);
+    if (use_dialtone_filter)
+        dtmf_rx_parms(&dtmf_state, TRUE, -1, -1);
+    my_dtmf_gen_init(0.0f, -10, 0.0f, -10, 50, 50);
+    step = 0;
+    for (i = 1;  i < 10;  i++)
+    {
+        len = 0;
+        for (j = 0;  j < i;  j++)
+            len += my_dtmf_generate(amp + len, ALL_POSSIBLE_DIGITS);
+        for (sample = 0, j = 160;  sample < len;  sample += 160, j = ((len - sample) >= 160)  ?  160  :  (len - sample))
+        {
+            dtmf_rx(&dtmf_state, &amp[sample], j);
+            if (!callback_ok)
+                break;
+            step += j;
+        }
+        if (!callback_hit  ||  !callback_ok)
+            break;
+    }
+    if (!callback_hit  ||  !callback_ok)
+    {
+        printf("    Failed\n");
+        exit(2);
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void decode_test(const char *test_file)
+{
+    int16_t amp[160];
+    AFfilehandle inhandle;
+    dtmf_rx_state_t dtmf_state;
+    char buf[128 + 1];
+    int actual;
+    int samples;
+    int total;
+
+    dtmf_rx_init(&dtmf_state, NULL, NULL);
+    if (use_dialtone_filter)
+        dtmf_rx_parms(&dtmf_state, TRUE, -1, -1);
+
+    /* We will decode the audio from a wave file. */
+    
+    if ((inhandle = afOpenFile(decode_test_file, "r", NULL)) == AF_NULL_FILEHANDLE)
+    {
+        fprintf(stderr, "    Cannot open wave file '%s'\n", decode_test_file);
+        exit(2);
+    }
+    
+    total = 0;
+    while ((samples = afReadFrames(inhandle, AF_DEFAULT_TRACK, amp, 160)) > 0)
+    {
+        codec_munge(munge, amp, samples);
+        dtmf_rx(&dtmf_state, amp, samples);
+        if ((actual = dtmf_rx_get(&dtmf_state, buf, 128)) > 0)
+            printf("Received '%s'\n", buf);
+        total += actual;
+    }
+    printf("%d digits received\n", total);
+}
+/*- End of function --------------------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+    int duration;
+    int i;
+    time_t now;
+    int channel_codec;
+
+    use_dialtone_filter = FALSE;
+    channel_codec = MUNGE_CODEC_NONE;
+    decode_test_file = NULL;
+    for (i = 1;  i < argc;  i++)
+    {
+        if (strcmp(argv[i], "-c") == 0)
+        {
+            channel_codec = atoi(argv[++i]);
+            continue;
+        }
+        if (strcmp(argv[i], "-d") == 0)
+        {
+            decode_test_file = argv[++i];
+            continue;
+        }
+        if (strcmp(argv[i], "-f") == 0)
+        {
+            use_dialtone_filter = TRUE;
+            continue;
+        }
+    }
+    munge = codec_munge_init(channel_codec);
+
+    if (decode_test_file)
+    {
+        decode_test(decode_test_file);
+    }
+    else
+    {
+        time(&now);
+        mitel_cm7291_side_1_tests();
+        mitel_cm7291_side_2_and_bellcore_tests();
+        dial_tone_tolerance_tests();
+        callback_function_tests();
+        printf("    Passed\n");
+        duration = time(NULL) - now;
+        printf("Tests passed in %ds\n", duration);
+    }
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

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