comparison spandsp-0.0.3/spandsp-0.0.3/tests/modem_echo_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
comparison
equal deleted inserted replaced
4:26cd8f1ef0b1 5:f762bf195c4b
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.22 2006/11/19 14:07:27 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 a wave 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 wave 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 wave 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 #ifdef 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 <inttypes.h>
87 #include <string.h>
88 #include <time.h>
89 #include <stdio.h>
90 #include <fcntl.h>
91 #include <audiofile.h>
92 #include <tiffio.h>
93 #if defined(HAVE_TGMATH_H)
94 #include <tgmath.h>
95 #endif
96 #if defined(HAVE_MATH_H)
97 #define GEN_CONST
98 #include <math.h>
99 #endif
100
101 #include "spandsp.h"
102 #include "spandsp/g168models.h"
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 AFfilehandle 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 AFfilehandle 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 = afWriteFrames(resulthandle,
142 AF_DEFAULT_TRACK,
143 residue_sound,
144 residue_cur);
145 if (outframes != residue_cur)
146 {
147 fprintf(stderr, " Error writing residue sound\n");
148 exit(2);
149 }
150 residue_cur = 0;
151 }
152 }
153 /*- End of function --------------------------------------------------------*/
154
155 static void signal_load(signal_source_t *sig, const char *name)
156 {
157 float x;
158
159 sig->name = name;
160 if ((sig->handle = afOpenFile(sig->name, "r", 0)) == AF_NULL_FILEHANDLE)
161 {
162 fprintf(stderr, " Cannot open sound file '%s'\n", sig->name);
163 exit(2);
164 }
165 if ((x = afGetFrameSize(sig->handle, AF_DEFAULT_TRACK, 1)) != 2.0)
166 {
167 fprintf(stderr, " Unexpected frame size in wave file '%s'\n", sig->name);
168 exit(2);
169 }
170 if ((x = afGetRate(sig->handle, AF_DEFAULT_TRACK)) != (float) SAMPLE_RATE)
171 {
172 printf(" Unexpected sample rate in wave file '%s'\n", sig->name);
173 exit(2);
174 }
175 if ((x = afGetChannels(sig->handle, AF_DEFAULT_TRACK)) != 1.0)
176 {
177 printf(" Unexpected number of channels in wave file '%s'\n", sig->name);
178 exit(2);
179 }
180 sig->max = afReadFrames(sig->handle, AF_DEFAULT_TRACK, sig->signal, 8000);
181 if (sig->max < 0)
182 {
183 fprintf(stderr, " Error reading sound file '%s'\n", sig->name);
184 exit(2);
185 }
186 }
187 /*- End of function --------------------------------------------------------*/
188
189 static void signal_free(signal_source_t *sig)
190 {
191 if (afCloseFile(sig->handle) != 0)
192 {
193 fprintf(stderr, " Cannot close sound file '%s'\n", sig->name);
194 exit(2);
195 }
196 }
197 /*- End of function --------------------------------------------------------*/
198
199 static void signal_restart(signal_source_t *sig)
200 {
201 sig->cur = 0;
202 }
203 /*- End of function --------------------------------------------------------*/
204
205 static int16_t signal_amp(signal_source_t *sig)
206 {
207 int16_t tx;
208
209 tx = sig->signal[sig->cur++];
210 if (sig->cur >= sig->max)
211 sig->cur = 0;
212 return tx;
213 }
214 /*- End of function --------------------------------------------------------*/
215
216 static inline int16_t codec_munge(int16_t amp)
217 {
218 if (do_codec_munge)
219 return alaw_to_linear(linear_to_alaw(amp));
220 return amp;
221 }
222 /*- End of function --------------------------------------------------------*/
223
224 static void channel_model_create(int model)
225 {
226 static const int32_t *line_models[] =
227 {
228 line_model_d2_coeffs,
229 line_model_d3_coeffs,
230 line_model_d4_coeffs,
231 line_model_d5_coeffs,
232 line_model_d6_coeffs,
233 line_model_d7_coeffs,
234 line_model_d8_coeffs,
235 line_model_d9_coeffs
236 };
237
238 static int line_model_sizes[] =
239 {
240 sizeof(line_model_d2_coeffs)/sizeof(int32_t),
241 sizeof(line_model_d3_coeffs)/sizeof(int32_t),
242 sizeof(line_model_d4_coeffs)/sizeof(int32_t),
243 sizeof(line_model_d5_coeffs)/sizeof(int32_t),
244 sizeof(line_model_d6_coeffs)/sizeof(int32_t),
245 sizeof(line_model_d7_coeffs)/sizeof(int32_t),
246 sizeof(line_model_d8_coeffs)/sizeof(int32_t),
247 sizeof(line_model_d9_coeffs)/sizeof(int32_t)
248 };
249
250 fir32_create(&line_model, line_models[model], line_model_sizes[model]);
251 }
252 /*- End of function --------------------------------------------------------*/
253
254 static int16_t channel_model(int16_t local, int16_t far)
255 {
256 int16_t echo;
257 int16_t rx;
258
259 /* Channel modelling is merely simulating the effects of A-law distortion
260 and using one of the echo models from G.168 */
261
262 /* The local tx signal will have gone through an A-law munging before
263 it reached the line's analogue area where the echo occurs. */
264 echo = fir32(&line_model, codec_munge(local/8));
265 /* The far end signal will have been through an A-law munging, although
266 this should not affect things. */
267 rx = echo + codec_munge(far);
268 /* This mixed echo and far end signal will have been through an A-law munging when it came back into
269 the digital network. */
270 rx = codec_munge(rx);
271 return rx;
272 }
273 /*- End of function --------------------------------------------------------*/
274
275 int main(int argc, char *argv[])
276 {
277 modem_echo_can_state_t *ctx;
278 //awgn_state_t local_noise_source;
279 awgn_state_t far_noise_source;
280 int i;
281 int clean;
282 int16_t rx;
283 int16_t tx;
284 int local_cur;
285 int far_cur;
286 int result_cur;
287 AFfilesetup filesetup;
288 int line_model_no;
289 time_t now;
290 power_meter_t power_before;
291 power_meter_t power_after;
292 float unadapted_output_power;
293 float unadapted_echo_power;
294 float adapted_output_power;
295 float adapted_echo_power;
296 #if defined(ENABLE_GUI)
297 int16_t amp[2];
298 #endif
299
300 line_model_no = 0;
301 use_gui = FALSE;
302 for (i = 1; i < argc; i++)
303 {
304 if (strcmp(argv[i], "-g") == 0)
305 {
306 use_gui = TRUE;
307 continue;
308 }
309 line_model_no = atoi(argv[1]);
310 }
311 time(&now);
312 ctx = modem_echo_can_create(256);
313 awgn_init_dbm0(&far_noise_source, 7162534, -50.0f);
314
315 signal_load(&local_css, "sound_c1_8k.wav");
316
317 filesetup = afNewFileSetup();
318 if (filesetup == AF_NULL_FILESETUP)
319 {
320 fprintf(stderr, " Failed to create file setup\n");
321 exit(2);
322 }
323 afInitSampleFormat(filesetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16);
324 afInitRate(filesetup, AF_DEFAULT_TRACK, (float) SAMPLE_RATE);
325 afInitFileFormat(filesetup, AF_FILE_WAVE);
326 afInitChannels(filesetup, AF_DEFAULT_TRACK, 2);
327
328 resulthandle = afOpenFile("modem_echo.wav", "w", filesetup);
329 if (resulthandle == AF_NULL_FILEHANDLE)
330 {
331 fprintf(stderr, " Failed to open result file\n");
332 exit(2);
333 }
334
335 #if defined(ENABLE_GUI)
336 if (use_gui)
337 start_echo_can_monitor(256);
338 #endif
339
340 local_cur = 0;
341 far_cur = 0;
342 result_cur = 0;
343 channel_model_create(line_model_no);
344 #if defined(ENABLE_GUI)
345 if (use_gui)
346 echo_can_monitor_line_model_update(line_model.coeffs, line_model.taps);
347 #endif
348
349 modem_echo_can_flush(ctx);
350
351 power_meter_init(&power_before, 5);
352 power_meter_init(&power_after, 5);
353
354 /* Measure the echo power before adaption */
355 modem_echo_can_adaption_mode(ctx, FALSE);
356 for (i = 0; i < 8000*5; i++)
357 {
358 tx = tone_1khz[i & 7];
359 rx = channel_model(tx, 0);
360 clean = modem_echo_can_update(ctx, tx, rx);
361 power_meter_update(&power_before, rx);
362 power_meter_update(&power_after, clean);
363 }
364 unadapted_output_power = power_meter_dbm0(&power_before);
365 unadapted_echo_power = power_meter_dbm0(&power_after);
366 printf("Pre-adaption: output power %10.5fdBm0, echo power %10.5fdBm0\n", unadapted_output_power, unadapted_echo_power);
367
368 /* Converge the canceller */
369 signal_restart(&local_css);
370 modem_echo_can_adaption_mode(ctx, TRUE);
371 for (i = 0; i < 800*2; i++)
372 {
373 clean = modem_echo_can_update(ctx, 0, 0);
374 put_residue(0, clean);
375 }
376
377 for (i = 0; i < 8000*50; i++)
378 {
379 tx = signal_amp(&local_css);
380 rx = channel_model(tx, 0);
381 clean = modem_echo_can_update(ctx, tx, rx);
382 power_meter_update(&power_before, rx);
383 power_meter_update(&power_after, clean);
384 #if 0
385 if (i%800 == 0)
386 printf("Powers %10.5fdBm0 %10.5fdBm0\n", power_meter_dbm0(&power_before), power_meter_dbm0(&power_after));
387 #endif
388 put_residue(tx, clean);
389 #if defined(ENABLE_GUI)
390 if (use_gui)
391 {
392 echo_can_monitor_can_update(ctx->fir_taps16, 256);
393 amp[0] = tx;
394 echo_can_monitor_line_spectrum_update(amp, 1);
395 }
396 #endif
397 }
398
399 /* Now lets see how well adapted we are */
400 modem_echo_can_adaption_mode(ctx, FALSE);
401 for (i = 0; i < 8000*5; i++)
402 {
403 tx = tone_1khz[i & 7];
404 rx = channel_model(tx, 0);
405 clean = modem_echo_can_update(ctx, tx, rx);
406 power_meter_update(&power_before, rx);
407 power_meter_update(&power_after, clean);
408 }
409 adapted_output_power = power_meter_dbm0(&power_before);
410 adapted_echo_power = power_meter_dbm0(&power_after);
411 printf("Post-adaption: output power %10.5fdBm0, echo power %10.5fdBm0\n", adapted_output_power, adapted_echo_power);
412
413 if (fabsf(adapted_output_power - unadapted_output_power) > 0.1f
414 ||
415 adapted_echo_power > unadapted_echo_power - 30.0f)
416 {
417 printf("Tests failed.\n");
418 exit(2);
419 }
420
421 modem_echo_can_free(ctx);
422 signal_free(&local_css);
423
424 if (afCloseFile(resulthandle) != 0)
425 {
426 fprintf(stderr, " Cannot close speech file '%s'\n", "result_sound.wav");
427 exit(2);
428 }
429 afFreeFileSetup(filesetup);
430
431 #if defined(ENABLE_GUI)
432 if (use_gui)
433 echo_can_monitor_wait_to_end();
434 #endif
435
436 printf("Tests passed.\n");
437 return 0;
438 }
439 /*- End of function --------------------------------------------------------*/
440 /*- End of file ------------------------------------------------------------*/

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