diff spandsp-0.0.6pre17/tests/modem_echo_tests.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/tests/modem_echo_tests.c	Fri Jun 25 15:50:58 2010 +0200
@@ -0,0 +1,406 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * modem_echo_tests.c
+ *
+ * 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: modem_echo_tests.c,v 1.32 2009/05/30 15:23:14 steveu Exp $
+ */
+
+/*! \page modem_echo_can_tests_page Line echo cancellation for modems tests
+\section modem_echo_can_tests_page_sec_1 What does it do?
+Currently the echo cancellation tests only provide simple exercising of the
+cancellor in the way it might be used for line echo cancellation. The test code
+is in echotests.c. 
+
+The goal is to test the echo cancellor again the G.16X specs. Clearly, that also
+means the goal for the cancellor itself is to comply with those specs. Right
+now, the only aspect of these tests implemented is the line impulse response
+models in g168tests.c. 
+
+\section modem_echo_can_tests_page_sec_2 How does it work?
+The current test consists of feeding an audio file of real speech to the echo
+cancellor as the transmit signal. A very simple model of a telephone line is
+used to simulate a simple echo from the transmit signal. A second audio file of
+real speech is also used to simulate a signal received form the far end of the
+line. This is gated so it is only placed for one second every 10 seconds,
+simulating the double talk condition. The resulting echo cancelled signal can
+either be store in a file for further analysis, or played back as the data is
+processed. 
+
+A number of modified versions of this test have been performed. The signal level
+of the two speech sources has been varied. Several simple models of the
+telephone line have been used. Although the current cancellor design has known
+limitations, it seems stable for all these test conditions. No instability has
+been observed in the current version due to arithmetic overflow when the speech
+is very loud (with earlier versions, well, ....:) ). The lack of saturating
+arithmetic in general purpose CPUs is a huge disadvantage here, as software
+saturation logic would cause a major slow down. Floating point would be good,
+but is not usable in the Linux kernel. Anyway, the bottom line seems to be the
+current design is genuinely useful, if imperfect. 
+
+\section modem_echo_can_tests_page_sec_2 How do I use it?
+
+Build the tests with the command "./build". Currently there is no proper make
+setup, or way to build individual tests. "./build" will built all the tests
+which currently exist for the DSP functions. The echo cancellation test assumes
+there are two audio files containing mono, 16 bit signed PCM speech data, sampled
+at 8kHz. These should be called local_sound.wav and far_sound.wav. A third wave
+file will be produced. This very crudely starts with the first 256 bytes from
+the local_sound.wav file, followed by the results of the echo cancellation. The
+resulting audio is also played to the /dev/dsp device. A printf near the end of
+echo_tests.c is commented out with a \#if. If this is enabled, detailed
+information about the results of the echo cancellation will be written to
+stdout. By saving this into a file, Grace (recommended), GnuPlot, or some other
+plotting package may be used to graphically display the functioning of the
+cancellor.  
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#if defined(HAVE_FL_FL_H)  &&  defined(HAVE_FL_FL_CARTESIAN_H)  &&  defined(HAVE_FL_FL_AUDIO_METER_H)
+#define ENABLE_GUI
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <sndfile.h>
+#if defined(HAVE_MATH_H)
+#define GEN_CONST
+#endif
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+#include "spandsp/g168models.h"
+#include "spandsp-sim.h"
+
+#if defined(ENABLE_GUI)
+#include "echo_monitor.h"
+#endif
+
+#if !defined(NULL)
+#define NULL (void *) 0
+#endif
+
+typedef struct
+{
+    const char *name;
+    int max;
+    int cur;
+    SNDFILE *handle;
+    int16_t signal[8000];
+} signal_source_t;
+
+signal_source_t local_css;
+
+fir32_state_t line_model;
+
+SNDFILE *resulthandle;
+int16_t residue_sound[8000];
+int residue_cur = 0;
+int do_codec_munge = TRUE;
+int use_gui = FALSE;
+
+static const int16_t tone_1khz[] = {10362, 7327, 0, -7327, -10362, -7327, 0, 7327};
+
+static inline void put_residue(int16_t tx, int16_t residue)
+{
+    int outframes;
+
+    residue_sound[residue_cur++] = tx;
+    residue_sound[residue_cur++] = residue;
+    if (residue_cur >= 8000)
+    {
+        residue_cur >>= 1;
+        outframes = sf_writef_short(resulthandle, residue_sound, residue_cur);
+        if (outframes != residue_cur)
+        {
+            fprintf(stderr, "    Error writing residue sound\n");
+            exit(2);
+        }
+        residue_cur = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void signal_load(signal_source_t *sig, const char *name)
+{
+    sig->name = name;
+    if ((sig->handle = sf_open_telephony_read(sig->name, 1)) == NULL)
+    {
+        fprintf(stderr, "    Cannot open sound file '%s'\n", sig->name);
+        exit(2);
+    }
+    sig->max = sf_readf_short(sig->handle, sig->signal, 8000);
+    if (sig->max < 0)
+    {
+        fprintf(stderr, "    Error reading sound file '%s'\n", sig->name);
+        exit(2);
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void signal_free(signal_source_t *sig)
+{
+    if (sf_close(sig->handle) != 0)
+    {
+        fprintf(stderr, "    Cannot close sound file '%s'\n", sig->name);
+        exit(2);
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void signal_restart(signal_source_t *sig)
+{
+    sig->cur = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int16_t signal_amp(signal_source_t *sig)
+{
+    int16_t tx;
+
+    tx = sig->signal[sig->cur++];
+    if (sig->cur >= sig->max)
+        sig->cur = 0;
+    return tx;
+}
+/*- End of function --------------------------------------------------------*/
+
+static inline int16_t codec_munger(int16_t amp)
+{
+    if (do_codec_munge)
+        return alaw_to_linear(linear_to_alaw(amp));
+    return amp;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void channel_model_create(int model)
+{
+    static const int32_t *line_models[] =
+    {
+        line_model_d2_coeffs,
+        line_model_d3_coeffs,
+        line_model_d4_coeffs,
+        line_model_d5_coeffs,
+        line_model_d6_coeffs,
+        line_model_d7_coeffs,
+        line_model_d8_coeffs,
+        line_model_d9_coeffs
+    };
+
+    static int line_model_sizes[] =
+    {
+        sizeof(line_model_d2_coeffs)/sizeof(int32_t),
+        sizeof(line_model_d3_coeffs)/sizeof(int32_t),
+        sizeof(line_model_d4_coeffs)/sizeof(int32_t),
+        sizeof(line_model_d5_coeffs)/sizeof(int32_t),
+        sizeof(line_model_d6_coeffs)/sizeof(int32_t),
+        sizeof(line_model_d7_coeffs)/sizeof(int32_t),
+        sizeof(line_model_d8_coeffs)/sizeof(int32_t),
+        sizeof(line_model_d9_coeffs)/sizeof(int32_t)
+    };
+
+    fir32_create(&line_model, line_models[model], line_model_sizes[model]);
+}
+/*- End of function --------------------------------------------------------*/
+
+static int16_t channel_model(int16_t local, int16_t far)
+{
+    int16_t echo;
+    int16_t rx;
+
+    /* Channel modelling is merely simulating the effects of A-law distortion
+       and using one of the echo models from G.168 */
+
+    /* The local tx signal will have gone through an A-law munging before
+       it reached the line's analogue area where the echo occurs. */
+    echo = fir32(&line_model, codec_munger(local/8));
+    /* The far end signal will have been through an A-law munging, although
+       this should not affect things. */
+    rx = echo + codec_munger(far);
+    /* This mixed echo and far end signal will have been through an A-law munging when it came back into
+       the digital network. */
+    rx = codec_munger(rx);
+    return  rx;
+}
+/*- End of function --------------------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+    modem_echo_can_state_t *ctx;
+    //awgn_state_t local_noise_source;
+    awgn_state_t far_noise_source;
+    int i;
+    int clean;
+    int16_t rx;
+    int16_t tx;
+    int local_cur;
+    int far_cur;
+    int result_cur;
+    int line_model_no;
+    time_t now;
+    power_meter_t power_before;
+    power_meter_t power_after;
+    float unadapted_output_power;
+    float unadapted_echo_power;
+    float adapted_output_power;
+    float adapted_echo_power;
+#if defined(ENABLE_GUI)
+    int16_t amp[2];
+#endif
+    
+    line_model_no = 0;
+    use_gui = FALSE;
+    for (i = 1;  i < argc;  i++)
+    {
+        if (strcmp(argv[i], "-g") == 0)
+        {
+            use_gui = TRUE;
+            continue;
+        }
+        line_model_no = atoi(argv[1]);
+    }
+    time(&now);
+    ctx = modem_echo_can_create(256);
+    awgn_init_dbm0(&far_noise_source, 7162534, -50.0f);
+
+    signal_load(&local_css, "sound_c1_8k.wav");
+
+    if ((resulthandle = sf_open_telephony_write("modem_echo.wav", 2)) == NULL)
+    {
+        fprintf(stderr, "    Failed to open result file\n");
+        exit(2);
+    }
+
+#if defined(ENABLE_GUI)
+    if (use_gui)
+        start_echo_can_monitor(256);
+#endif
+
+    local_cur = 0;
+    far_cur = 0;
+    result_cur = 0;
+    channel_model_create(line_model_no);
+#if defined(ENABLE_GUI)
+    if (use_gui)
+        echo_can_monitor_line_model_update(line_model.coeffs, line_model.taps);
+#endif
+
+    modem_echo_can_flush(ctx);
+
+    power_meter_init(&power_before, 5);
+    power_meter_init(&power_after, 5);
+    
+    /* Measure the echo power before adaption */
+    modem_echo_can_adaption_mode(ctx, FALSE);
+    for (i = 0;  i < 8000*5;  i++)
+    {
+        tx = tone_1khz[i & 7];
+        rx = channel_model(tx, 0);
+        clean = modem_echo_can_update(ctx, tx, rx);
+        power_meter_update(&power_before, rx);
+        power_meter_update(&power_after, clean);
+    }
+    unadapted_output_power = power_meter_current_dbm0(&power_before);
+    unadapted_echo_power = power_meter_current_dbm0(&power_after);
+    printf("Pre-adaption: output power %10.5fdBm0, echo power %10.5fdBm0\n", unadapted_output_power, unadapted_echo_power);
+    
+    /* Converge the canceller */
+    signal_restart(&local_css);
+    modem_echo_can_adaption_mode(ctx, TRUE);
+    for (i = 0;  i < 800*2;  i++)
+    {
+        clean = modem_echo_can_update(ctx, 0, 0);
+        put_residue(0, clean);
+    }
+
+    for (i = 0;  i < 8000*50;  i++)
+    {
+        tx = signal_amp(&local_css);
+        rx = channel_model(tx, 0);
+        clean = modem_echo_can_update(ctx, tx, rx);
+        power_meter_update(&power_before, rx);
+        power_meter_update(&power_after, clean);
+#if 0
+        if (i%800 == 0)
+            printf("Powers %10.5fdBm0 %10.5fdBm0\n", power_meter_current_dbm0(&power_before), power_meter_current_dbm0(&power_after));
+#endif
+        put_residue(tx, clean);
+#if defined(ENABLE_GUI)
+        if (use_gui)
+        {
+            echo_can_monitor_can_update(ctx->fir_taps16, 256);
+            amp[0] = tx;
+            echo_can_monitor_line_spectrum_update(amp, 1);
+        }
+#endif
+    }
+
+    /* Now lets see how well adapted we are */
+    modem_echo_can_adaption_mode(ctx, FALSE);
+    for (i = 0;  i < 8000*5;  i++)
+    {
+        tx = tone_1khz[i & 7];
+        rx = channel_model(tx, 0);
+        clean = modem_echo_can_update(ctx, tx, rx);
+        power_meter_update(&power_before, rx);
+        power_meter_update(&power_after, clean);
+    }
+    adapted_output_power = power_meter_current_dbm0(&power_before);
+    adapted_echo_power = power_meter_current_dbm0(&power_after);
+    printf("Post-adaption: output power %10.5fdBm0, echo power %10.5fdBm0\n", adapted_output_power, adapted_echo_power);
+    
+    if (fabsf(adapted_output_power - unadapted_output_power) > 0.1f
+        ||
+        adapted_echo_power > unadapted_echo_power - 30.0f)
+    {
+        printf("Tests failed.\n");
+        exit(2);
+    }
+
+    modem_echo_can_free(ctx);
+    signal_free(&local_css);
+
+    if (sf_close(resulthandle) != 0)
+    {
+        fprintf(stderr, "    Cannot close speech file '%s'\n", "result_sound.wav");
+        exit(2);
+    }
+
+#if defined(ENABLE_GUI)
+    if (use_gui)
+        echo_can_monitor_wait_to_end();
+#endif
+
+    printf("Tests passed.\n");
+    return  0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

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