comparison 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
comparison
equal deleted inserted replaced
3:c6c5a16ce2f2 4:26cd8f1ef0b1
1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * modem_echo_tests.c
5 *
6 * Written by Steve Underwood <steveu@coppice.org>
7 *
8 * Copyright (C) 2004 Steve Underwood
9 *
10 * All rights reserved.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2, as
14 * published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 * $Id: modem_echo_tests.c,v 1.32 2009/05/30 15:23:14 steveu Exp $
26 */
27
28 /*! \page modem_echo_can_tests_page Line echo cancellation for modems tests
29 \section modem_echo_can_tests_page_sec_1 What does it do?
30 Currently the echo cancellation tests only provide simple exercising of the
31 cancellor in the way it might be used for line echo cancellation. The test code
32 is in echotests.c.
33
34 The goal is to test the echo cancellor again the G.16X specs. Clearly, that also
35 means the goal for the cancellor itself is to comply with those specs. Right
36 now, the only aspect of these tests implemented is the line impulse response
37 models in g168tests.c.
38
39 \section modem_echo_can_tests_page_sec_2 How does it work?
40 The current test consists of feeding an audio file of real speech to the echo
41 cancellor as the transmit signal. A very simple model of a telephone line is
42 used to simulate a simple echo from the transmit signal. A second audio file of
43 real speech is also used to simulate a signal received form the far end of the
44 line. This is gated so it is only placed for one second every 10 seconds,
45 simulating the double talk condition. The resulting echo cancelled signal can
46 either be store in a file for further analysis, or played back as the data is
47 processed.
48
49 A number of modified versions of this test have been performed. The signal level
50 of the two speech sources has been varied. Several simple models of the
51 telephone line have been used. Although the current cancellor design has known
52 limitations, it seems stable for all these test conditions. No instability has
53 been observed in the current version due to arithmetic overflow when the speech
54 is very loud (with earlier versions, well, ....:) ). The lack of saturating
55 arithmetic in general purpose CPUs is a huge disadvantage here, as software
56 saturation logic would cause a major slow down. Floating point would be good,
57 but is not usable in the Linux kernel. Anyway, the bottom line seems to be the
58 current design is genuinely useful, if imperfect.
59
60 \section modem_echo_can_tests_page_sec_2 How do I use it?
61
62 Build the tests with the command "./build". Currently there is no proper make
63 setup, or way to build individual tests. "./build" will built all the tests
64 which currently exist for the DSP functions. The echo cancellation test assumes
65 there are two audio files containing mono, 16 bit signed PCM speech data, sampled
66 at 8kHz. These should be called local_sound.wav and far_sound.wav. A third wave
67 file will be produced. This very crudely starts with the first 256 bytes from
68 the local_sound.wav file, followed by the results of the echo cancellation. The
69 resulting audio is also played to the /dev/dsp device. A printf near the end of
70 echo_tests.c is commented out with a \#if. If this is enabled, detailed
71 information about the results of the echo cancellation will be written to
72 stdout. By saving this into a file, Grace (recommended), GnuPlot, or some other
73 plotting package may be used to graphically display the functioning of the
74 cancellor.
75 */
76
77 #if defined(HAVE_CONFIG_H)
78 #include "config.h"
79 #endif
80
81 #if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H)
82 #define ENABLE_GUI
83 #endif
84
85 #include <stdlib.h>
86 #include <stdio.h>
87 #include <fcntl.h>
88 #include <string.h>
89 #include <time.h>
90 #include <sndfile.h>
91 #if defined(HAVE_MATH_H)
92 #define GEN_CONST
93 #endif
94
95 //#if defined(WITH_SPANDSP_INTERNALS)
96 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
97 //#endif
98
99 #include "spandsp.h"
100 #include "spandsp/g168models.h"
101 #include "spandsp-sim.h"
102
103 #if defined(ENABLE_GUI)
104 #include "echo_monitor.h"
105 #endif
106
107 #if !defined(NULL)
108 #define NULL (void *) 0
109 #endif
110
111 typedef struct
112 {
113 const char *name;
114 int max;
115 int cur;
116 SNDFILE *handle;
117 int16_t signal[8000];
118 } signal_source_t;
119
120 signal_source_t local_css;
121
122 fir32_state_t line_model;
123
124 SNDFILE *resulthandle;
125 int16_t residue_sound[8000];
126 int residue_cur = 0;
127 int do_codec_munge = TRUE;
128 int use_gui = FALSE;
129
130 static const int16_t tone_1khz[] = {10362, 7327, 0, -7327, -10362, -7327, 0, 7327};
131
132 static inline void put_residue(int16_t tx, int16_t residue)
133 {
134 int outframes;
135
136 residue_sound[residue_cur++] = tx;
137 residue_sound[residue_cur++] = residue;
138 if (residue_cur >= 8000)
139 {
140 residue_cur >>= 1;
141 outframes = sf_writef_short(resulthandle, residue_sound, residue_cur);
142 if (outframes != residue_cur)
143 {
144 fprintf(stderr, " Error writing residue sound\n");
145 exit(2);
146 }
147 residue_cur = 0;
148 }
149 }
150 /*- End of function --------------------------------------------------------*/
151
152 static void signal_load(signal_source_t *sig, const char *name)
153 {
154 sig->name = name;
155 if ((sig->handle = sf_open_telephony_read(sig->name, 1)) == NULL)
156 {
157 fprintf(stderr, " Cannot open sound file '%s'\n", sig->name);
158 exit(2);
159 }
160 sig->max = sf_readf_short(sig->handle, sig->signal, 8000);
161 if (sig->max < 0)
162 {
163 fprintf(stderr, " Error reading sound file '%s'\n", sig->name);
164 exit(2);
165 }
166 }
167 /*- End of function --------------------------------------------------------*/
168
169 static void signal_free(signal_source_t *sig)
170 {
171 if (sf_close(sig->handle) != 0)
172 {
173 fprintf(stderr, " Cannot close sound file '%s'\n", sig->name);
174 exit(2);
175 }
176 }
177 /*- End of function --------------------------------------------------------*/
178
179 static void signal_restart(signal_source_t *sig)
180 {
181 sig->cur = 0;
182 }
183 /*- End of function --------------------------------------------------------*/
184
185 static int16_t signal_amp(signal_source_t *sig)
186 {
187 int16_t tx;
188
189 tx = sig->signal[sig->cur++];
190 if (sig->cur >= sig->max)
191 sig->cur = 0;
192 return tx;
193 }
194 /*- End of function --------------------------------------------------------*/
195
196 static inline int16_t codec_munger(int16_t amp)
197 {
198 if (do_codec_munge)
199 return alaw_to_linear(linear_to_alaw(amp));
200 return amp;
201 }
202 /*- End of function --------------------------------------------------------*/
203
204 static void channel_model_create(int model)
205 {
206 static const int32_t *line_models[] =
207 {
208 line_model_d2_coeffs,
209 line_model_d3_coeffs,
210 line_model_d4_coeffs,
211 line_model_d5_coeffs,
212 line_model_d6_coeffs,
213 line_model_d7_coeffs,
214 line_model_d8_coeffs,
215 line_model_d9_coeffs
216 };
217
218 static int line_model_sizes[] =
219 {
220 sizeof(line_model_d2_coeffs)/sizeof(int32_t),
221 sizeof(line_model_d3_coeffs)/sizeof(int32_t),
222 sizeof(line_model_d4_coeffs)/sizeof(int32_t),
223 sizeof(line_model_d5_coeffs)/sizeof(int32_t),
224 sizeof(line_model_d6_coeffs)/sizeof(int32_t),
225 sizeof(line_model_d7_coeffs)/sizeof(int32_t),
226 sizeof(line_model_d8_coeffs)/sizeof(int32_t),
227 sizeof(line_model_d9_coeffs)/sizeof(int32_t)
228 };
229
230 fir32_create(&line_model, line_models[model], line_model_sizes[model]);
231 }
232 /*- End of function --------------------------------------------------------*/
233
234 static int16_t channel_model(int16_t local, int16_t far)
235 {
236 int16_t echo;
237 int16_t rx;
238
239 /* Channel modelling is merely simulating the effects of A-law distortion
240 and using one of the echo models from G.168 */
241
242 /* The local tx signal will have gone through an A-law munging before
243 it reached the line's analogue area where the echo occurs. */
244 echo = fir32(&line_model, codec_munger(local/8));
245 /* The far end signal will have been through an A-law munging, although
246 this should not affect things. */
247 rx = echo + codec_munger(far);
248 /* This mixed echo and far end signal will have been through an A-law munging when it came back into
249 the digital network. */
250 rx = codec_munger(rx);
251 return rx;
252 }
253 /*- End of function --------------------------------------------------------*/
254
255 int main(int argc, char *argv[])
256 {
257 modem_echo_can_state_t *ctx;
258 //awgn_state_t local_noise_source;
259 awgn_state_t far_noise_source;
260 int i;
261 int clean;
262 int16_t rx;
263 int16_t tx;
264 int local_cur;
265 int far_cur;
266 int result_cur;
267 int line_model_no;
268 time_t now;
269 power_meter_t power_before;
270 power_meter_t power_after;
271 float unadapted_output_power;
272 float unadapted_echo_power;
273 float adapted_output_power;
274 float adapted_echo_power;
275 #if defined(ENABLE_GUI)
276 int16_t amp[2];
277 #endif
278
279 line_model_no = 0;
280 use_gui = FALSE;
281 for (i = 1; i < argc; i++)
282 {
283 if (strcmp(argv[i], "-g") == 0)
284 {
285 use_gui = TRUE;
286 continue;
287 }
288 line_model_no = atoi(argv[1]);
289 }
290 time(&now);
291 ctx = modem_echo_can_create(256);
292 awgn_init_dbm0(&far_noise_source, 7162534, -50.0f);
293
294 signal_load(&local_css, "sound_c1_8k.wav");
295
296 if ((resulthandle = sf_open_telephony_write("modem_echo.wav", 2)) == NULL)
297 {
298 fprintf(stderr, " Failed to open result file\n");
299 exit(2);
300 }
301
302 #if defined(ENABLE_GUI)
303 if (use_gui)
304 start_echo_can_monitor(256);
305 #endif
306
307 local_cur = 0;
308 far_cur = 0;
309 result_cur = 0;
310 channel_model_create(line_model_no);
311 #if defined(ENABLE_GUI)
312 if (use_gui)
313 echo_can_monitor_line_model_update(line_model.coeffs, line_model.taps);
314 #endif
315
316 modem_echo_can_flush(ctx);
317
318 power_meter_init(&power_before, 5);
319 power_meter_init(&power_after, 5);
320
321 /* Measure the echo power before adaption */
322 modem_echo_can_adaption_mode(ctx, FALSE);
323 for (i = 0; i < 8000*5; i++)
324 {
325 tx = tone_1khz[i & 7];
326 rx = channel_model(tx, 0);
327 clean = modem_echo_can_update(ctx, tx, rx);
328 power_meter_update(&power_before, rx);
329 power_meter_update(&power_after, clean);
330 }
331 unadapted_output_power = power_meter_current_dbm0(&power_before);
332 unadapted_echo_power = power_meter_current_dbm0(&power_after);
333 printf("Pre-adaption: output power %10.5fdBm0, echo power %10.5fdBm0\n", unadapted_output_power, unadapted_echo_power);
334
335 /* Converge the canceller */
336 signal_restart(&local_css);
337 modem_echo_can_adaption_mode(ctx, TRUE);
338 for (i = 0; i < 800*2; i++)
339 {
340 clean = modem_echo_can_update(ctx, 0, 0);
341 put_residue(0, clean);
342 }
343
344 for (i = 0; i < 8000*50; i++)
345 {
346 tx = signal_amp(&local_css);
347 rx = channel_model(tx, 0);
348 clean = modem_echo_can_update(ctx, tx, rx);
349 power_meter_update(&power_before, rx);
350 power_meter_update(&power_after, clean);
351 #if 0
352 if (i%800 == 0)
353 printf("Powers %10.5fdBm0 %10.5fdBm0\n", power_meter_current_dbm0(&power_before), power_meter_current_dbm0(&power_after));
354 #endif
355 put_residue(tx, clean);
356 #if defined(ENABLE_GUI)
357 if (use_gui)
358 {
359 echo_can_monitor_can_update(ctx->fir_taps16, 256);
360 amp[0] = tx;
361 echo_can_monitor_line_spectrum_update(amp, 1);
362 }
363 #endif
364 }
365
366 /* Now lets see how well adapted we are */
367 modem_echo_can_adaption_mode(ctx, FALSE);
368 for (i = 0; i < 8000*5; i++)
369 {
370 tx = tone_1khz[i & 7];
371 rx = channel_model(tx, 0);
372 clean = modem_echo_can_update(ctx, tx, rx);
373 power_meter_update(&power_before, rx);
374 power_meter_update(&power_after, clean);
375 }
376 adapted_output_power = power_meter_current_dbm0(&power_before);
377 adapted_echo_power = power_meter_current_dbm0(&power_after);
378 printf("Post-adaption: output power %10.5fdBm0, echo power %10.5fdBm0\n", adapted_output_power, adapted_echo_power);
379
380 if (fabsf(adapted_output_power - unadapted_output_power) > 0.1f
381 ||
382 adapted_echo_power > unadapted_echo_power - 30.0f)
383 {
384 printf("Tests failed.\n");
385 exit(2);
386 }
387
388 modem_echo_can_free(ctx);
389 signal_free(&local_css);
390
391 if (sf_close(resulthandle) != 0)
392 {
393 fprintf(stderr, " Cannot close speech file '%s'\n", "result_sound.wav");
394 exit(2);
395 }
396
397 #if defined(ENABLE_GUI)
398 if (use_gui)
399 echo_can_monitor_wait_to_end();
400 #endif
401
402 printf("Tests passed.\n");
403 return 0;
404 }
405 /*- End of function --------------------------------------------------------*/
406 /*- End of file ------------------------------------------------------------*/

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