5
|
1 /*
|
|
2 * SpanDSP - a series of DSP components for telephony
|
|
3 *
|
|
4 * echo_tests.c
|
|
5 *
|
|
6 * Written by Steve Underwood <steveu@coppice.org>
|
|
7 *
|
|
8 * Copyright (C) 2001 Steve Underwood
|
|
9 *
|
|
10 * Based on a bit from here, a bit from there, eye of toad,
|
|
11 * ear of bat, etc - plus, of course, my own 2 cents.
|
|
12 *
|
|
13 * All rights reserved.
|
|
14 *
|
|
15 * This program is free software; you can redistribute it and/or modify
|
|
16 * it under the terms of the GNU General Public License version 2, as
|
|
17 * published by the Free Software Foundation.
|
|
18 *
|
|
19 * This program is distributed in the hope that it will be useful,
|
|
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
22 * GNU General Public License for more details.
|
|
23 *
|
|
24 * You should have received a copy of the GNU General Public License
|
|
25 * along with this program; if not, write to the Free Software
|
|
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
27 *
|
|
28 * $Id: echo_tests.c,v 1.27 2006/11/19 14:07:27 steveu Exp $
|
|
29 */
|
|
30
|
|
31 /*! \page echo_can_tests_page Line echo cancellation for voice tests
|
|
32
|
|
33 \section echo_can_tests_page_sec_1 What does it do?
|
|
34 The echo cancellation tests test the echo cancellor against the G.168 spec. Not
|
|
35 all the tests in G.168 are fully implemented at this time.
|
|
36
|
|
37 \section echo_can_tests_page_sec_2 How does it work?
|
|
38
|
|
39 \section echo_can_tests_page_sec_2 How do I use it?
|
|
40
|
|
41 */
|
|
42
|
|
43 #ifdef HAVE_CONFIG_H
|
|
44 #include "config.h"
|
|
45 #endif
|
|
46
|
|
47 #if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H)
|
|
48 #define ENABLE_GUI
|
|
49 #endif
|
|
50
|
|
51 #define _GNU_SOURCE
|
|
52
|
|
53 #include <unistd.h>
|
|
54 #include <inttypes.h>
|
|
55 #include <stdlib.h>
|
|
56 #include <stdio.h>
|
|
57 #include <string.h>
|
|
58 #include <strings.h>
|
|
59 #if defined(HAVE_TGMATH_H)
|
|
60 #include <tgmath.h>
|
|
61 #endif
|
|
62 #if defined(HAVE_MATH_H)
|
|
63 #include <math.h>
|
|
64 #endif
|
|
65 #include <assert.h>
|
|
66 #include <audiofile.h>
|
|
67 #include <tiffio.h>
|
|
68
|
|
69 #define GEN_CONST
|
|
70 #include <math.h>
|
|
71
|
|
72 #include "spandsp.h"
|
|
73 #include "spandsp/g168models.h"
|
|
74 #if defined(ENABLE_GUI)
|
|
75 #include "echo_monitor.h"
|
|
76 #endif
|
|
77
|
|
78 #define TEST_EC_TAPS 256
|
|
79
|
|
80 #if !defined(NULL)
|
|
81 #define NULL (void *) 0
|
|
82 #endif
|
|
83
|
|
84 typedef struct
|
|
85 {
|
|
86 const char *name;
|
|
87 int max;
|
|
88 int cur;
|
|
89 AFfilehandle handle;
|
|
90 int16_t signal[SAMPLE_RATE];
|
|
91 } signal_source_t;
|
|
92
|
|
93 typedef struct
|
|
94 {
|
|
95 int type;
|
|
96 fir_float_state_t *fir;
|
|
97 float history[35*8];
|
|
98 int pos;
|
|
99 float factor;
|
|
100 float power;
|
|
101 } level_measurement_device_t;
|
|
102
|
|
103 signal_source_t local_css;
|
|
104 signal_source_t far_css;
|
|
105
|
|
106 fir32_state_t line_model;
|
|
107 float model_ki, erl;
|
|
108
|
|
109 AFfilehandle residuehandle;
|
|
110 int16_t residue_sound[SAMPLE_RATE];
|
|
111 int residue_cur = 0;
|
|
112 int munge;
|
|
113
|
|
114 FILE *fdump;
|
|
115
|
|
116 float clip(float x);
|
|
117 float clip(float x) {
|
|
118 if (x > 32767.0) x = 32767.0;
|
|
119 if (x < -32767.0) x = -32767.0;
|
|
120
|
|
121 return x;
|
|
122 }
|
|
123 /*- End of function --------------------------------------------------------*/
|
|
124
|
|
125 static inline void put_residue(int16_t amp)
|
|
126 {
|
|
127 int outframes;
|
|
128
|
|
129 residue_sound[residue_cur++] = amp;
|
|
130 if (residue_cur >= SAMPLE_RATE) {
|
|
131 outframes = afWriteFrames(residuehandle,
|
|
132 AF_DEFAULT_TRACK,
|
|
133 residue_sound,
|
|
134 residue_cur);
|
|
135 if (outframes != residue_cur)
|
|
136 {
|
|
137 fprintf(stderr, " Error writing residue sound\n");
|
|
138 exit(2);
|
|
139 }
|
|
140 residue_cur = 0;
|
|
141 }
|
|
142 }
|
|
143 /*- End of function --------------------------------------------------------*/
|
|
144
|
|
145 static void signal_load(signal_source_t *sig, const char *name)
|
|
146 {
|
|
147 float x;
|
|
148
|
|
149 sig->name = name;
|
|
150 if ((sig->handle = afOpenFile(sig->name, "r", 0)) == AF_NULL_FILEHANDLE)
|
|
151 {
|
|
152 fprintf(stderr, " Cannot open wave file '%s'\n", sig->name);
|
|
153 exit(2);
|
|
154 }
|
|
155 if ((x = afGetFrameSize(sig->handle, AF_DEFAULT_TRACK, 1)) != 2.0)
|
|
156 {
|
|
157 fprintf(stderr, " Unexpected frame size in wave file '%s'\n", sig->name);
|
|
158 exit(2);
|
|
159 }
|
|
160 if ((x = afGetRate(sig->handle, AF_DEFAULT_TRACK)) != (float) SAMPLE_RATE)
|
|
161 {
|
|
162 printf(" Unexpected sample rate in wave file '%s'\n", sig->name);
|
|
163 exit(2);
|
|
164 }
|
|
165 if ((x = afGetChannels(sig->handle, AF_DEFAULT_TRACK)) != 1.0)
|
|
166 {
|
|
167 printf(" Unexpected number of channels in wave file '%s'\n", sig->name);
|
|
168 exit(2);
|
|
169 }
|
|
170 sig->max = afReadFrames(sig->handle, AF_DEFAULT_TRACK, sig->signal, SAMPLE_RATE);
|
|
171 if (sig->max < 0)
|
|
172 {
|
|
173 fprintf(stderr, " Error reading sound file '%s'\n", sig->name);
|
|
174 exit(2);
|
|
175 }
|
|
176 }
|
|
177 /*- End of function --------------------------------------------------------*/
|
|
178
|
|
179 static AFfilehandle af_file_open_for_read(const char *name)
|
|
180 {
|
|
181 float x;
|
|
182 AFfilehandle handle;
|
|
183
|
|
184 if ((handle = afOpenFile(name, "r", 0)) == AF_NULL_FILEHANDLE)
|
|
185 {
|
|
186 fprintf(stderr, " Cannot open wave file '%s'\n", name);
|
|
187 exit(2);
|
|
188 }
|
|
189 if ((x = afGetFrameSize(handle, AF_DEFAULT_TRACK, 1)) != 2.0)
|
|
190 {
|
|
191 fprintf(stderr, " Unexpected frame size in wave file '%s'\n", name);
|
|
192 exit(2);
|
|
193 }
|
|
194 if ((x = afGetRate(handle, AF_DEFAULT_TRACK)) != (float) SAMPLE_RATE)
|
|
195 {
|
|
196 printf(" Unexpected sample rate in wave file '%s'\n", name);
|
|
197 exit(2);
|
|
198 }
|
|
199 if ((x = afGetChannels(handle, AF_DEFAULT_TRACK)) != 1.0)
|
|
200 {
|
|
201 printf(" Unexpected number of channels in wave file '%s'\n", name);
|
|
202 exit(2);
|
|
203 }
|
|
204
|
|
205 return handle;
|
|
206 }
|
|
207 /*- End of function --------------------------------------------------------*/
|
|
208
|
|
209 static AFfilehandle af_file_open_for_write(const char *name)
|
|
210 {
|
|
211 AFfilesetup setup;
|
|
212 AFfilehandle handle;
|
|
213
|
|
214 setup = afNewFileSetup();
|
|
215 if (setup == AF_NULL_FILESETUP)
|
|
216 {
|
|
217 fprintf(stderr, " %s: Failed to create file setup\n", name);
|
|
218 exit(2);
|
|
219 }
|
|
220 afInitSampleFormat(setup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16);
|
|
221 afInitRate(setup, AF_DEFAULT_TRACK, (float) SAMPLE_RATE);
|
|
222 afInitFileFormat(setup, AF_FILE_WAVE);
|
|
223 afInitChannels(setup, AF_DEFAULT_TRACK, 1);
|
|
224 handle = afOpenFile(name, "w", setup);
|
|
225
|
|
226 if (handle == AF_NULL_FILEHANDLE)
|
|
227 {
|
|
228 fprintf(stderr, " Failed to open result file\n");
|
|
229 exit(2);
|
|
230 }
|
|
231 afFreeFileSetup(setup);
|
|
232
|
|
233 return handle;
|
|
234 }
|
|
235 /*- End of function --------------------------------------------------------*/
|
|
236
|
|
237 static void signal_restart(signal_source_t *sig)
|
|
238 {
|
|
239 sig->cur = 0;
|
|
240 }
|
|
241 /*- End of function --------------------------------------------------------*/
|
|
242
|
|
243 static int16_t signal_amp(signal_source_t *sig)
|
|
244 {
|
|
245 int16_t tx;
|
|
246
|
|
247 tx = sig->signal[sig->cur++];
|
|
248 if (sig->cur >= sig->max)
|
|
249 sig->cur = 0;
|
|
250 return tx;
|
|
251 }
|
|
252 /*- End of function --------------------------------------------------------*/
|
|
253
|
|
254 /* note mu-law used, alaw has big DC Offsets that causes probs with G168
|
|
255 tests, due to alaw idle values being passed thru when NLP opens for
|
|
256 very low level signals. Probably need a DC blocking filter in e/c
|
|
257 */
|
|
258 static inline int16_t codec_munge(int16_t amp)
|
|
259 {
|
|
260 return ulaw_to_linear(linear_to_ulaw(amp));
|
|
261 }
|
|
262 /*- End of function --------------------------------------------------------*/
|
|
263
|
|
264 static int channel_model_create(int model)
|
|
265 {
|
|
266 static const int32_t *line_models[] =
|
|
267 {
|
|
268 line_model_d2_coeffs,
|
|
269 line_model_d3_coeffs,
|
|
270 line_model_d4_coeffs,
|
|
271 line_model_d5_coeffs,
|
|
272 line_model_d6_coeffs,
|
|
273 line_model_d7_coeffs,
|
|
274 line_model_d8_coeffs,
|
|
275 line_model_d9_coeffs
|
|
276 };
|
|
277
|
|
278 static int line_model_sizes[] =
|
|
279 {
|
|
280 sizeof(line_model_d2_coeffs)/sizeof(int32_t),
|
|
281 sizeof(line_model_d3_coeffs)/sizeof(int32_t),
|
|
282 sizeof(line_model_d4_coeffs)/sizeof(int32_t),
|
|
283 sizeof(line_model_d5_coeffs)/sizeof(int32_t),
|
|
284 sizeof(line_model_d6_coeffs)/sizeof(int32_t),
|
|
285 sizeof(line_model_d7_coeffs)/sizeof(int32_t),
|
|
286 sizeof(line_model_d8_coeffs)/sizeof(int32_t),
|
|
287 sizeof(line_model_d9_coeffs)/sizeof(int32_t)
|
|
288 };
|
|
289
|
|
290 static float ki[] =
|
|
291 {
|
|
292 1.39E-5, 1.44E-5, 1.52E-5, 1.77E-5, 9.33E-6, 1.51E-5, 2.33E-5, 1.33E-5
|
|
293 };
|
|
294
|
|
295 if (model < 1 || model > (int) (sizeof(line_model_sizes)/sizeof(line_model_sizes[0])))
|
|
296 return -1;
|
|
297 fir32_create(&line_model, line_models[model-1], line_model_sizes[model-1]);
|
|
298
|
|
299 model_ki = ki[model-1];
|
|
300
|
|
301 return 0;
|
|
302 }
|
|
303 /*- End of function --------------------------------------------------------*/
|
|
304
|
|
305 static int16_t channel_model(int16_t *new_local, int16_t *new_far, int16_t local, int16_t far)
|
|
306 {
|
|
307 int16_t echo;
|
|
308 int16_t rx;
|
|
309
|
|
310 /* Channel modelling is merely simulating the effects of A-law or u-law distortion
|
|
311 and using one of the echo models from G.168. Simulating the codec is very important,
|
|
312 as this is usually the limiting factor in how much echo reduction is achieved. */
|
|
313
|
|
314 /* The local tx signal will usually have gone through an A-law munging before
|
|
315 it reached the line's analogue area, where the echo occurs. */
|
|
316 if (munge == TRUE)
|
|
317 local = codec_munge(local);
|
|
318 /* Now we need to model the echo. We only model a single analogue segment, as per
|
|
319 the G.168 spec. However, there will generally be near end and far end analogue/echoey
|
|
320 segments in the real world, unless an end is purely digital. */
|
|
321 echo = fir32(&line_model, local*erl*(32768.0*model_ki));
|
|
322 /* The far end signal will have been through an A-law munging, although
|
|
323 this should not affect things. */
|
|
324 if (munge == TRUE)
|
|
325 rx = clip(echo + codec_munge(far));
|
|
326 else
|
|
327 rx = clip(echo + far);
|
|
328
|
|
329 /* This mixed echo and far end signal will have been through an A-law munging
|
|
330 when it came back into the digital network. */
|
|
331 if (munge == TRUE)
|
|
332 rx = codec_munge(rx);
|
|
333 if (new_far)
|
|
334 *new_far = rx;
|
|
335 if (new_local)
|
|
336 *new_local = local;
|
|
337 return rx;
|
|
338 }
|
|
339 /*- End of function --------------------------------------------------------*/
|
|
340
|
|
341 /*
|
|
342 250Hz HP filter, designed using this excellent site:
|
|
343
|
|
344 http://www-users.cs.york.ac.uk/~fisher/mkfilter/
|
|
345
|
|
346 Included as preliminary test to see if this sort of filter will help
|
|
347 hum removal from low cost X100P type cards. Unfortunately I couldn't
|
|
348 get thios to work well in fixed point, so had to leave it out of
|
|
349 treh core echo canceller.
|
|
350 */
|
|
351
|
|
352 #define NZEROS 4
|
|
353 #define NPOLES 4
|
|
354
|
|
355 #define FIXED
|
|
356 #ifdef FIXED
|
|
357 #define GAIN 1.293080949e+00
|
|
358 #define QCONST32(x,bits) ((int)((x)*(1<<(bits))))
|
|
359
|
|
360 static int hp_filter(int xv[], int yv[], int x)
|
|
361 {
|
|
362 xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4];
|
|
363 xv[4] = x * (int)((1<<5)/GAIN);
|
|
364 yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4];
|
|
365 yv[4] = (xv[0] + xv[4]) - 4 * (xv[1] + xv[3]) + 6 * xv[2];
|
|
366 yv[4] += (QCONST32(-0.5980652616f, 10) * yv[0]) >> 10;
|
|
367 yv[4] += (QCONST32( 2.6988843913f, 10) * yv[1]) >> 10;
|
|
368 yv[4] += (QCONST32(-4.5892912321f, 10) * yv[2]) >> 10;
|
|
369 yv[4] += (QCONST32( 3.4873077415f, 10) * yv[3]) >> 10;
|
|
370
|
|
371 return yv[4] >> 5;
|
|
372 }
|
|
373
|
|
374 #else
|
|
375 #define GAIN 1.293080949e+00
|
|
376
|
|
377 static float hp_filter(float xv[], float yv[], float x)
|
|
378 {
|
|
379 xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4];
|
|
380 xv[4] = x / GAIN;
|
|
381 yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4];
|
|
382 yv[4] = (xv[0] + xv[4]) - 4 * (xv[1] + xv[3]) + 6 * xv[2]
|
|
383 + ( -0.5980652616 * yv[0]) + ( 2.6988843913 * yv[1])
|
|
384 + ( -4.5892912321 * yv[2]) + ( 3.4873077415 * yv[3]);
|
|
385 return yv[4];
|
|
386 }
|
|
387 #endif
|
|
388
|
|
389 static level_measurement_device_t *level_measurement_device_create(int type)
|
|
390 {
|
|
391 level_measurement_device_t *dev;
|
|
392 int i;
|
|
393
|
|
394 dev = (level_measurement_device_t *) malloc(sizeof(level_measurement_device_t));
|
|
395 dev->fir = (fir_float_state_t *) malloc(sizeof(fir_float_state_t));
|
|
396 fir_float_create(dev->fir,
|
|
397 level_measurement_bp_coeffs,
|
|
398 sizeof(level_measurement_bp_coeffs)/sizeof(float));
|
|
399 for (i = 0; i < 35*8; i++)
|
|
400 dev->history[i] = 0.0;
|
|
401 dev->pos = 0;
|
|
402 dev->factor = exp(-1.0/((float) SAMPLE_RATE*0.035));
|
|
403 dev->power = 0;
|
|
404 dev->type = type;
|
|
405 return dev;
|
|
406 }
|
|
407 /*- End of function --------------------------------------------------------*/
|
|
408
|
|
409 static void level_measurement_device_reset(level_measurement_device_t *dev)
|
|
410 {
|
|
411 int i;
|
|
412
|
|
413 for (i = 0; i < 35*8; i++)
|
|
414 dev->history[i] = 0.0;
|
|
415 dev->pos = 0;
|
|
416 dev->power = 0;
|
|
417 }
|
|
418 /*- End of function --------------------------------------------------------*/
|
|
419
|
|
420 static int level_measurement_device_release(level_measurement_device_t *s)
|
|
421 {
|
|
422 fir_float_free(s->fir);
|
|
423 free(s->fir);
|
|
424 free(s);
|
|
425 return 0;
|
|
426 }
|
|
427 /*- End of function --------------------------------------------------------*/
|
|
428
|
|
429 static float level_measurement_device(level_measurement_device_t *dev, int16_t amp)
|
|
430 {
|
|
431 float signal;
|
|
432
|
|
433 signal = fir_float(dev->fir, amp);
|
|
434 signal *= signal;
|
|
435 if (dev->type == 0)
|
|
436 {
|
|
437 dev->power = dev->power*dev->factor + signal*(1.0 - dev->factor);
|
|
438 signal = sqrt(dev->power);
|
|
439 }
|
|
440 else
|
|
441 {
|
|
442 dev->power -= dev->history[dev->pos];
|
|
443 dev->power += signal;
|
|
444 dev->history[dev->pos++] = signal;
|
|
445 signal = sqrt(dev->power/(35.8*8.0));
|
|
446 }
|
|
447 if (signal > 0.0)
|
|
448 return DBM0_MAX_POWER + 20.0*log10(signal/32767.0);
|
|
449 else
|
|
450 return -1000.0;
|
|
451 }
|
|
452 /*- End of function --------------------------------------------------------*/
|
|
453
|
|
454 /* Globals used for performing tests */
|
|
455
|
|
456 echo_can_state_t *ctx;
|
|
457 awgn_state_t sgen_noise_source;
|
|
458 awgn_state_t rin_noise_source;
|
|
459
|
|
460 level_measurement_device_t *Rin_power_meter;
|
|
461 level_measurement_device_t *Sgen_power_meter;
|
|
462 level_measurement_device_t *Sin_power_meter;
|
|
463 level_measurement_device_t *Sout_power_meter;
|
|
464
|
|
465 float LRin, maxLRin;
|
|
466 float LSgen, maxLSgen;
|
|
467 float LSin, maxLSin;
|
|
468 float LSout, maxLSout;
|
|
469 float Lres, maxLres;
|
|
470 float test_clock;
|
|
471 float maxHoth;
|
|
472
|
|
473 float Rin_level, Sgen_level;
|
|
474 FILE *flevel;
|
|
475 int Rin_type, Sgen_type;
|
|
476 int failed;
|
|
477 int verbose, quiet;
|
|
478 float threshold;
|
|
479 int model_number;
|
|
480 char test_name[80];
|
|
481
|
|
482 tone_gen_state_t rin_tone_state;
|
|
483 tone_gen_state_t sgen_tone_state;
|
|
484
|
|
485 /*
|
|
486 Test callback functions are called one for every processed sample
|
|
487 during run_test(). They are user supplied, and return TRUE if the
|
|
488 test is passing or FALSE if a combination of variables mean that
|
|
489 the test has failed (for example Lres exceeding some threshold).
|
|
490
|
|
491 Different test callback functions are required for each G168 test.
|
|
492 */
|
|
493 int (*test_callback)(void);
|
|
494
|
|
495 /* macros to convert units for run_test */
|
|
496
|
|
497 #define MSEC (SAMPLE_RATE/1000)
|
|
498 #define SEC SAMPLE_RATE
|
|
499
|
|
500 /* Sgen signal generator types */
|
|
501
|
|
502 #define NONE 0
|
|
503 #define CSS 1
|
|
504 #define HOTH 2
|
|
505 #define TONE 3
|
|
506
|
|
507 /* Experimentally generated constants to normalise levels to dBm0 */
|
|
508
|
|
509 #define HOTH_SCALE 2.40
|
|
510 #define CSS_SCALE 5.60
|
|
511
|
|
512 static void reset_all(void) {
|
|
513 echo_can_flush(ctx);
|
|
514 maxLRin = maxLSgen = maxLSin = maxLSout = maxLres = -100.0;
|
|
515 signal_restart(&local_css);
|
|
516 signal_restart(&far_css);
|
|
517 test_callback = NULL;
|
|
518 Rin_type = CSS;
|
|
519 Sgen_type = NONE;
|
|
520 failed = FALSE;
|
|
521 test_clock = 0.0;
|
|
522 }
|
|
523
|
|
524 static void reset_meter_peaks(void) {
|
|
525 maxLRin = maxLSgen = maxLSin = maxLSout = maxLres = -100.0;
|
|
526 }
|
|
527
|
|
528 static void install_test_callback(int (*f)(void)) {
|
|
529 test_callback = f;
|
|
530 }
|
|
531
|
|
532 /* note: maybe we should use absolute levels rather than gain? Need to
|
|
533 normalise levels from various signal types to do this */
|
|
534
|
|
535 static void set_Sgen(int source_type, float gain) {
|
|
536 Sgen_type = source_type;
|
|
537 Sgen_level = pow(10.0, gain/20.0);
|
|
538 }
|
|
539
|
|
540 static void set_Rin(int source_type, float gain) {
|
|
541 Rin_type = source_type;
|
|
542 Rin_level = pow(10.0, gain/20.0);
|
|
543 }
|
|
544
|
|
545 static void mute_Rin(void) {
|
|
546 Rin_type = NONE;
|
|
547 }
|
|
548
|
|
549 static void unmute_Rin(void) {
|
|
550 Rin_type = CSS;
|
|
551 }
|
|
552
|
|
553 static void update_levels(int16_t rin, int16_t sin, int16_t sout, int16_t sgen)
|
|
554 {
|
|
555 LRin = level_measurement_device(Rin_power_meter, rin);
|
|
556 LSin = level_measurement_device(Sin_power_meter, sin);
|
|
557 LSout = level_measurement_device(Sout_power_meter, sout);
|
|
558 LSgen = level_measurement_device(Sgen_power_meter, sgen);
|
|
559 if (LRin > maxLRin) maxLRin = LRin;
|
|
560 if (LSin > maxLSin) maxLSin = LSin;
|
|
561 if (LSout > maxLSout) maxLSout = LSout;
|
|
562 if (LSgen > maxLSgen) maxLSgen = LSgen;
|
|
563 }
|
|
564
|
|
565 static void write_log_files(int16_t rout, int16_t sin)
|
|
566 {
|
|
567 fprintf(flevel, "%f\t%f\t%f\t%f\n",LRin, LSin, LSout, LSgen);
|
|
568 fprintf(fdump, "%d %d %d", ctx->tx, ctx->rx, ctx->clean);
|
|
569 fprintf(fdump, " %d %d %d %d %d %d %d %d %d %d\n", ctx->clean_nlp, ctx->Ltx,
|
|
570 ctx->Lrx, ctx->Lclean,
|
|
571 (ctx->nonupdate_dwell > 0), ctx->adapt, ctx->Lclean_bg, ctx->Pstates,
|
|
572 ctx->Lbgn_upper, ctx->Lbgn);
|
|
573 }
|
|
574
|
|
575 static void run_test(float time, float units) {
|
|
576 int i;
|
|
577 int samples;
|
|
578 int16_t rout, rin=0, sin;
|
|
579 int16_t sgen=0, sout;
|
|
580 float rin_hoth_noise = 0;
|
|
581 float sgen_hoth_noise = 0;
|
|
582
|
|
583 samples = time * units;
|
|
584
|
|
585 for (i = 0; i < samples; i++) {
|
|
586
|
|
587 switch(Rin_type) {
|
|
588 case NONE:
|
|
589 rin = 0;
|
|
590 break;
|
|
591 case CSS:
|
|
592 rin = clip(Rin_level*signal_amp(&local_css)*CSS_SCALE);
|
|
593 break;
|
|
594 case HOTH:
|
|
595 rin_hoth_noise = rin_hoth_noise*0.625 + awgn(&rin_noise_source)*0.375;
|
|
596 rin = clip(Rin_level*rin_hoth_noise*HOTH_SCALE);
|
|
597 break;
|
|
598 case TONE:
|
|
599 tone_gen(&rin_tone_state, &rin, 1);
|
|
600 break;
|
|
601 }
|
|
602
|
|
603 switch(Sgen_type) {
|
|
604 case NONE:
|
|
605 sgen = 0;
|
|
606 break;
|
|
607 case CSS:
|
|
608 sgen = clip(Sgen_level*signal_amp(&far_css)*CSS_SCALE);
|
|
609 break;
|
|
610 case HOTH:
|
|
611 sgen_hoth_noise = sgen_hoth_noise*0.625 + awgn(&sgen_noise_source)*0.375;
|
|
612 sgen = clip(Sgen_level*sgen_hoth_noise*HOTH_SCALE);
|
|
613 break;
|
|
614 case TONE:
|
|
615 tone_gen(&sgen_tone_state, &sgen, 1);
|
|
616 break;
|
|
617 }
|
|
618
|
|
619 rout = echo_can_hpf_tx(ctx, rin);
|
|
620 channel_model(&rout, &sin, rin, sgen);
|
|
621 sout = echo_can_update(ctx, rout, sin);
|
|
622 update_levels(rin, sin, sout, sgen);
|
|
623 write_log_files(rout, sin);
|
|
624
|
|
625 /* now test for fail condition */
|
|
626 if (test_callback != NULL) {
|
|
627 if ( (failed == FALSE) && (test_callback() == FALSE)) {
|
|
628 /* test has failed */
|
|
629 failed = TRUE;
|
|
630 }
|
|
631 }
|
|
632
|
|
633 /* stop clock on fail - points to time of failure in test */
|
|
634
|
|
635 if (failed == FALSE)
|
|
636 test_clock += (float)1/SAMPLE_RATE;
|
|
637 }
|
|
638 }
|
|
639
|
|
640 static void print_title(const char *title) {
|
|
641 if (quiet == FALSE)
|
|
642 printf(title);
|
|
643 }
|
|
644
|
|
645 static void print_results(void) {
|
|
646
|
|
647 if (quiet == FALSE)
|
|
648 printf("test model ERL time Max Rin Max Sin Max Sgen Max Sout Result\n");
|
|
649 printf("%-4s %-1d %-5.1f%6.2fs%9.2f%9.2f%10.2f%10.2f ",
|
|
650 test_name, model_number, 20.0*log10(erl),
|
|
651 test_clock, maxLRin, maxLSin, maxLSgen, maxLSout);
|
|
652 if (failed == TRUE)
|
|
653 printf("FAIL\n");
|
|
654 else
|
|
655 printf("PASS\n");
|
|
656 }
|
|
657
|
|
658 static int test_2a(void) {
|
|
659 if (LSout > -65.0)
|
|
660 return FALSE;
|
|
661 else
|
|
662 return TRUE;
|
|
663 }
|
|
664
|
|
665 static int test_2c(void) {
|
|
666 if (LSout > maxHoth)
|
|
667 return FALSE;
|
|
668 else
|
|
669 return TRUE;
|
|
670 }
|
|
671
|
|
672 static int test_3a(void) {
|
|
673 if (LSout > maxLSgen)
|
|
674 return FALSE;
|
|
675 else
|
|
676 return TRUE;
|
|
677 }
|
|
678
|
|
679 static int test_3b(void) {
|
|
680 if (LSout > threshold)
|
|
681 return FALSE;
|
|
682 else
|
|
683 return TRUE;
|
|
684 }
|
|
685
|
|
686 static int test_3c_t2(void) {
|
|
687 if (LSout > maxLSgen)
|
|
688 return FALSE;
|
|
689 else
|
|
690 return TRUE;
|
|
691 }
|
|
692
|
|
693 static int test_3c_t4t5(void) {
|
|
694 if (LSout > (maxLSgen+6.0))
|
|
695 return FALSE;
|
|
696 else
|
|
697 return TRUE;
|
|
698 }
|
|
699
|
|
700 static int test_9(void) {
|
|
701 if (fabs(LSout - LSgen) > 2.0)
|
|
702 return FALSE;
|
|
703 else
|
|
704 return TRUE;
|
|
705 }
|
|
706
|
|
707 #define N_TESTS 9
|
|
708 static const char *supported_tests[] = {"ut1", "2aa", "2ca", "3a", "3ba",
|
|
709 "3bb", "3c", "6", "9"};
|
|
710
|
|
711 static int is_test_supported(char *test) {
|
|
712 int i;
|
|
713 for(i=0; i<N_TESTS; i++) {
|
|
714 if (!strcasecmp(test, supported_tests[i]))
|
|
715 return TRUE;
|
|
716 }
|
|
717
|
|
718 return FALSE;
|
|
719 }
|
|
720
|
|
721 /* dump estimate echo response */
|
|
722 static void dump_h(void) {
|
|
723 int i;
|
|
724 FILE *f = fopen("h.txt","wt");
|
|
725 for(i=0; i<TEST_EC_TAPS; i++) {
|
|
726 fprintf(f, "%f\n", (float)ctx->fir_taps16[0][i]/(1<<15));
|
|
727 }
|
|
728 fclose(f);
|
|
729 }
|
|
730
|
|
731 int main(int argc, char *argv[])
|
|
732 {
|
|
733 //awgn_state_t local_noise_source;
|
|
734 int i;
|
|
735 //int j;
|
|
736 //int k;
|
|
737 //tone_gen_descriptor_t tone_desc;
|
|
738 //tone_gen_state_t tone_state;
|
|
739 //int16_t local_sound[40000];
|
|
740 //int local_max;
|
|
741 //int local_cur;
|
|
742 int far_cur;
|
|
743 int result_cur;
|
|
744 AFfilehandle txfile, rxfile, ecfile;
|
|
745 time_t now;
|
|
746 int tone_burst_step;
|
|
747 float X_level, Sgen_leveldB;
|
|
748 #ifdef FIXED
|
|
749 int xvrx[NZEROS+1], yvrx[NPOLES+1];
|
|
750 int xvtx[NZEROS+1], yvtx[NPOLES+1];
|
|
751 #else
|
|
752 float xvrx[NZEROS+1], yvrx[NPOLES+1];
|
|
753 float xvtx[NZEROS+1], yvtx[NPOLES+1];
|
|
754 #endif
|
|
755
|
|
756 int file_mode;
|
|
757 float tmp;
|
|
758 int cng;
|
|
759 int hpf;
|
|
760
|
|
761 /* default config ------------------------------------------------*/
|
|
762
|
|
763 model_number = 1;
|
|
764 erl = pow(10.0, -10.0/20.0);
|
|
765 verbose = quiet = FALSE;
|
|
766 file_mode = FALSE;
|
|
767 Rin_level = pow(10.0, -15.0/20.0);
|
|
768 Sgen_leveldB = -15.0;
|
|
769 Sgen_level = pow(10.0, Sgen_leveldB/20.0);
|
|
770 X_level = pow(10.0, 6.0/20.0);
|
|
771 tone_burst_step = 0;
|
|
772 txfile = rxfile = ecfile = NULL;
|
|
773 munge = TRUE;
|
|
774 cng = FALSE;
|
|
775 hpf = TRUE;
|
|
776 for(i=0; i<NPOLES+1; i++) {
|
|
777 xvtx[i] = yvtx[i] = xvrx[i] = yvrx[i] = 0.0;
|
|
778 }
|
|
779
|
|
780 /* Check which tests we should run ----------------------------------------*/
|
|
781
|
|
782 if (argc < 2) {
|
|
783 fprintf(stderr, "Usage: echo [2aa] [2ca] [3a] [3ba] [3bb] [3c] [6] [9]\n"
|
|
784 "[-m ChannelModelNumber]\n"
|
|
785 "[-erl ERL_in_dB\n"
|
|
786 "[-file RinInputFile.wav SinInputFile.wav SoutOutputFile.wav\n"
|
|
787 "[-r RinLeveldBm0] [-s SgenLeveldBm0] [-x XLeveldB]\n"
|
|
788 "[-nomunge]\n"
|
|
789 "[-cng]\n"
|
|
790 "[-nohpf] Disable DC block HPF (-file mode)\n");
|
|
791
|
|
792 exit(1);
|
|
793 }
|
|
794
|
|
795 for (i = 1; i < argc; i++)
|
|
796 {
|
|
797 if (is_test_supported(argv[i])) {
|
|
798 }
|
|
799 else if (strcmp(argv[i], "-m") == 0)
|
|
800 {
|
|
801 if (++i < argc)
|
|
802 model_number = atoi(argv[i]);
|
|
803 }
|
|
804 else if (strcmp(argv[i], "-r") == 0)
|
|
805 {
|
|
806 if (++i < argc)
|
|
807 Rin_level = pow(10.0, atof(argv[i])/20.0);
|
|
808 }
|
|
809 else if (strcmp(argv[i], "-s") == 0)
|
|
810 {
|
|
811 if (++i < argc) {
|
|
812 Sgen_leveldB = atof(argv[i]);
|
|
813 Sgen_level = pow(10.0, Sgen_leveldB/20.0);
|
|
814 }
|
|
815 }
|
|
816 else if (strcmp(argv[i], "-x") == 0)
|
|
817 {
|
|
818 if (++i < argc)
|
|
819 X_level = pow(10.0, atof(argv[i])/20.0);
|
|
820 }
|
|
821 else if (strcmp(argv[i], "-erl") == 0)
|
|
822 {
|
|
823 if (++i < argc) {
|
|
824 erl = atof(argv[i]);
|
|
825 if (erl < 0.0) {
|
|
826 printf("ERL must be >= 0.0 dB\n");
|
|
827 exit(1);
|
|
828 }
|
|
829 erl = pow(10.0, -erl/20.0);
|
|
830 }
|
|
831 }
|
|
832 else if (strcmp(argv[i], "-v") == 0)
|
|
833 {
|
|
834 verbose = TRUE;
|
|
835 }
|
|
836 else if (strcmp(argv[i], "-q") == 0)
|
|
837 {
|
|
838 quiet = TRUE;
|
|
839 }
|
|
840 else if (strcmp(argv[i], "-file") == 0)
|
|
841 {
|
|
842 file_mode = TRUE;
|
|
843 if (argc < (i+3)) {
|
|
844 printf("not enough arguments for --file\n");
|
|
845 exit(2);
|
|
846 }
|
|
847 txfile = af_file_open_for_read(argv[i+1]);
|
|
848 rxfile = af_file_open_for_read(argv[i+2]);
|
|
849 ecfile = af_file_open_for_write(argv[i+3]);
|
|
850 i += 3;
|
|
851 }
|
|
852 else if (strcmp(argv[i], "-nomunge") == 0)
|
|
853 {
|
|
854 munge = FALSE;
|
|
855 }
|
|
856 else if (strcmp(argv[i], "-cng") == 0)
|
|
857 {
|
|
858 cng = TRUE;
|
|
859 }
|
|
860 else if (strcmp(argv[i], "-nohpf") == 0)
|
|
861 {
|
|
862 hpf = FALSE;
|
|
863 }
|
|
864 else
|
|
865 {
|
|
866 fprintf(stderr, "Unknown test/option '%s' specified\n", argv[i]);
|
|
867 exit(2);
|
|
868 }
|
|
869 }
|
|
870
|
|
871 /* initialise a bunch of modules we need ------------------------------*/
|
|
872
|
|
873 time(&now);
|
|
874
|
|
875 ctx = echo_can_create(TEST_EC_TAPS, 0);
|
|
876 awgn_init_dbm0(&rin_noise_source, 7162534, 0.0f);
|
|
877 awgn_init_dbm0(&sgen_noise_source, 7162534, 0.0f);
|
|
878 Rin_power_meter = level_measurement_device_create(0);
|
|
879 Sgen_power_meter = level_measurement_device_create(0);
|
|
880 Sin_power_meter = level_measurement_device_create(0);
|
|
881 Sout_power_meter = level_measurement_device_create(0);
|
|
882 if (channel_model_create(model_number))
|
|
883 {
|
|
884 fprintf(stderr, " Failed to create line model\n");
|
|
885 exit(2);
|
|
886 }
|
|
887
|
|
888 far_cur = 0;
|
|
889 result_cur = 0;
|
|
890
|
|
891 if (verbose == TRUE) {
|
|
892 printf("ERL (linear)......: %6.2f (%5.2f)\n"
|
|
893 "Rin level (linear).: %6.2f (%5.2f)\n"
|
|
894 "Sgen level (linear): %6.2f (%5.2f)\n",
|
|
895 20.0*log10(erl), erl,
|
|
896 20.0*log10(Rin_level), Rin_level,
|
|
897 20.0*log10(Sgen_level), Sgen_level);
|
|
898 }
|
|
899
|
|
900 fdump = fopen("dump.txt","wt");
|
|
901 assert(fdump != NULL);
|
|
902 flevel = fopen("level.txt","wt");
|
|
903 assert(flevel != NULL);
|
|
904
|
|
905 if (file_mode == TRUE) {
|
|
906 /* process wave files instead of running tests, useful for
|
|
907 testing real world signals */
|
|
908 int ntx, nrx, nec;
|
|
909 int16_t rin, rout, sin, sout;
|
|
910 int mode;
|
|
911
|
|
912 mode = ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP;
|
|
913 if (cng)
|
|
914 mode |= ECHO_CAN_USE_CNG;
|
|
915 else
|
|
916 mode |= ECHO_CAN_USE_CLIP;
|
|
917 if (hpf) {
|
|
918 mode |= ECHO_CAN_USE_TX_HPF;
|
|
919 mode |= ECHO_CAN_USE_RX_HPF;
|
|
920 }
|
|
921 echo_can_adaption_mode(ctx, mode);
|
|
922 do {
|
|
923 ntx = afReadFrames(txfile, AF_DEFAULT_TRACK, &rin, 1);
|
|
924 if (ntx < 0) {
|
|
925 fprintf(stderr, " Error reading tx sound file\n");
|
|
926 exit(2);
|
|
927 }
|
|
928 nrx = afReadFrames(rxfile, AF_DEFAULT_TRACK, &sin, 1);
|
|
929 if (nrx < 0) {
|
|
930 fprintf(stderr, " Error reading rx sound file\n");
|
|
931 exit(2);
|
|
932 }
|
|
933
|
|
934 rout = echo_can_hpf_tx(ctx, rin);
|
|
935 sout = echo_can_update(ctx, rout, sin);
|
|
936
|
|
937 nec = afWriteFrames(ecfile, AF_DEFAULT_TRACK, &sout, 1);
|
|
938 if (nec != 1) {
|
|
939 fprintf(stderr, " Error writing ec sound file\n");
|
|
940 exit(2);
|
|
941 }
|
|
942
|
|
943 update_levels(rin, sin, sout, 0);
|
|
944 write_log_files(rin, sin);
|
|
945
|
|
946 } while (ntx && nrx);
|
|
947
|
|
948 dump_h();
|
|
949
|
|
950 afCloseFile(txfile);
|
|
951 afCloseFile(rxfile);
|
|
952 afCloseFile(ecfile);
|
|
953 exit(0);
|
|
954 }
|
|
955
|
|
956 signal_load(&local_css, "sound_c1_8k.wav");
|
|
957 signal_load(&far_css, "sound_c3_8k.wav");
|
|
958
|
|
959 strcpy(test_name, argv[1]);
|
|
960
|
|
961 /* basic unit test used in e/c dvelopment */
|
|
962
|
|
963 if (!strcasecmp(argv[1], "ut1")) {
|
|
964 int16_t rin, sin, rout, sout, sgen;
|
|
965
|
|
966 print_title("Performing Unit Test 1 - DC inputs\n");
|
|
967 reset_all();
|
|
968 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
969
|
|
970 rout = rin = 2000;
|
|
971 sin = 1000;
|
|
972 sgen = 0;
|
|
973 for(i=0; i<10; i++) {
|
|
974 rout = 2000+2*i;
|
|
975 sout = echo_can_update(ctx, rout, sin);
|
|
976 update_levels(rin, sin, sout, sgen);
|
|
977 write_log_files(rout, sin);
|
|
978 }
|
|
979 dump_h();
|
|
980 }
|
|
981
|
|
982 /* Test 1 - Steady state residual and returned echo level test */
|
|
983 /* This functionality has been merged with test 2 in newer versions of G.168,
|
|
984 so test 1 no longer exists. */
|
|
985
|
|
986 /* Test 2 - Convergence and steady state residual and returned echo level test */
|
|
987
|
|
988 /*
|
|
989 NOTE: This test is only partially implemented, only the conidtion after
|
|
990 1s is tested and I am still not sure if LSout == Lres in the part
|
|
991 of the test after 1s.
|
|
992 */
|
|
993
|
|
994 if (!strcasecmp(argv[1], "2aa")) {
|
|
995
|
|
996 print_title("Performing test 2A(a) - Convergence with NLP enabled\n");
|
|
997 reset_all();
|
|
998 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP);
|
|
999
|
|
1000 /* initial zero input as reqd by G168 */
|
|
1001
|
|
1002 mute_Rin();
|
|
1003 run_test(200, MSEC);
|
|
1004 unmute_Rin();
|
|
1005
|
|
1006 /* Now test convergence */
|
|
1007
|
|
1008 run_test(1, SEC);
|
|
1009 reset_meter_peaks();
|
|
1010 install_test_callback(test_2a);
|
|
1011 run_test(10, SEC);
|
|
1012
|
|
1013 print_results();
|
|
1014 }
|
|
1015
|
|
1016 #ifdef OTHER_TESTS
|
|
1017 if ((test_list & PERFORM_TEST_2B))
|
|
1018 {
|
|
1019 printf("Performing test 2B - Re-convergence with NLP disabled\n");
|
|
1020
|
|
1021 /* Test 2B - Re-convergence with NLP disabled */
|
|
1022
|
|
1023 echo_can_flush(ctx);
|
|
1024 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1025
|
|
1026 /* Converge a canceller */
|
|
1027
|
|
1028 signal_restart(&local_css);
|
|
1029 for (i = 0; i < 800*2; i++)
|
|
1030 {
|
|
1031 clean = echo_can_update(ctx, 0, 0);
|
|
1032 put_residue(clean);
|
|
1033 }
|
|
1034
|
|
1035 for (i = 0; i < SAMPLE_RATE*5; i++)
|
|
1036 {
|
|
1037 tx = signal_amp(&local_css);
|
|
1038 channel_model(&tx, &rx, tx, 0);
|
|
1039 clean = echo_can_update(ctx, tx, rx);
|
|
1040 put_residue(clean);
|
|
1041 #if defined(ENABLE_GUI)
|
|
1042 if (use_gui)
|
|
1043 echo_can_monitor_can_update(ctx->fir_taps16[ctx->tap_set], TEST_EC_TAPS);
|
|
1044 #endif
|
|
1045 }
|
|
1046 #if defined(ENABLE_GUI)
|
|
1047 if (use_gui)
|
|
1048 echo_can_monitor_can_update(ctx->fir_taps16[ctx->tap_set], TEST_EC_TAPS);
|
|
1049 #endif
|
|
1050 }
|
|
1051 #endif
|
|
1052
|
|
1053 if (!strcasecmp(argv[1], "2ca")) {
|
|
1054 float SgenLeveldB;
|
|
1055
|
|
1056 print_title("Performing test 2C(a) - Convergence with background noise present\n");
|
|
1057 reset_all();
|
|
1058 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP);
|
|
1059
|
|
1060 /* Converge canceller with background noise */
|
|
1061
|
|
1062 mute_Rin();
|
|
1063 run_test(200, MSEC);
|
|
1064 unmute_Rin();
|
|
1065
|
|
1066 SgenLeveldB = 20.0*log10(Rin_level) - 15.0;
|
|
1067 if (SgenLeveldB > -30.0) SgenLeveldB = -30.0;
|
|
1068 set_Sgen(HOTH, SgenLeveldB);
|
|
1069 run_test(1, SEC);
|
|
1070 maxHoth = maxLSgen;
|
|
1071
|
|
1072 /* After 1 second freeze adaption, switch off noise. */
|
|
1073
|
|
1074 mute_Rin();
|
|
1075 run_test(150, MSEC);
|
|
1076
|
|
1077 echo_can_adaption_mode(ctx, ECHO_CAN_USE_NLP);
|
|
1078 run_test(1, SEC);
|
|
1079
|
|
1080 unmute_Rin();
|
|
1081 set_Sgen(NONE, 0.0);
|
|
1082 run_test(500, MSEC);
|
|
1083
|
|
1084 /* now measure the echo */
|
|
1085
|
|
1086 reset_meter_peaks();
|
|
1087 maxLSgen = maxHoth; /* keep this peak for print out but reset the rest */
|
|
1088 install_test_callback(test_2c);
|
|
1089 set_Sgen(NONE, 0.0);
|
|
1090 run_test(5, SEC);
|
|
1091
|
|
1092 print_results();
|
|
1093 }
|
|
1094
|
|
1095 /* Test 3 - Performance under double talk conditions */
|
|
1096
|
|
1097 if (!strcasecmp(argv[1], "3a")) {
|
|
1098 print_title("Performing test 3A - Double talk test with low cancelled-end levels\n");
|
|
1099 reset_all();
|
|
1100
|
|
1101 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1102 set_Sgen(CSS, -15.0 + 20.0*log10(Rin_level));
|
|
1103 run_test(5, SEC);
|
|
1104 tmp = maxLSgen;
|
|
1105
|
|
1106 /* now freeze adaption */
|
|
1107
|
|
1108 echo_can_adaption_mode(ctx, 0);
|
|
1109 set_Sgen(NONE, 0.0);
|
|
1110 run_test(500, MSEC);
|
|
1111
|
|
1112 /* Now measure the echo */
|
|
1113
|
|
1114 reset_meter_peaks();
|
|
1115 maxLSgen = tmp;
|
|
1116 install_test_callback(test_3a);
|
|
1117 run_test(5, SEC);
|
|
1118
|
|
1119 print_results();
|
|
1120 }
|
|
1121
|
|
1122 if (!strcasecmp(argv[1], "3ba")) {
|
|
1123 float fig11;
|
|
1124
|
|
1125 print_title("Performing test 3B(a) - Double talk stability test with high cancelled-end levels\n");
|
|
1126 reset_all();
|
|
1127
|
|
1128 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1129 run_test(5, SEC);
|
|
1130
|
|
1131 /* Apply double talk */
|
|
1132
|
|
1133 set_Sgen(CSS, 20.0*log10(Sgen_level));
|
|
1134 run_test(5, SEC);
|
|
1135 tmp = maxLSgen;
|
|
1136
|
|
1137 /* freeze adaption and measure echo */
|
|
1138
|
|
1139 mute_Rin();
|
|
1140 run_test(150, MSEC);
|
|
1141
|
|
1142 echo_can_adaption_mode(ctx, 0);
|
|
1143 run_test(1, SEC);
|
|
1144
|
|
1145 unmute_Rin();
|
|
1146 set_Sgen(NONE, 0.0);
|
|
1147 run_test(500, MSEC);
|
|
1148
|
|
1149 /* Now measure the echo */
|
|
1150
|
|
1151 fig11 = (25.0/30.0)*maxLRin - 30.0; /* pass/fail based on clean level @ tx peak */
|
|
1152 threshold = fig11 + 10.0;
|
|
1153 reset_meter_peaks();
|
|
1154 maxLSgen = tmp;
|
|
1155 install_test_callback(test_3b);
|
|
1156 run_test(5, SEC);
|
|
1157
|
|
1158 print_results();
|
|
1159 }
|
|
1160
|
|
1161 if (!strcasecmp(argv[1], "3bb")) {
|
|
1162 float fig11;
|
|
1163
|
|
1164 print_title("Performing test 3B(b) - Double talk stability test with low cancelled-end levels\n");
|
|
1165 reset_all();
|
|
1166
|
|
1167 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1168 run_test(5, SEC);
|
|
1169
|
|
1170 /* Apply double talk */
|
|
1171
|
|
1172 set_Sgen(CSS, 20.0*log10(Rin_level) - 20.0*log10(X_level));
|
|
1173 run_test(5, SEC);
|
|
1174 tmp = maxLSgen;
|
|
1175
|
|
1176 /* freeze adaption and measure echo */
|
|
1177
|
|
1178 mute_Rin();
|
|
1179 run_test(150, MSEC);
|
|
1180
|
|
1181 echo_can_adaption_mode(ctx, 0);
|
|
1182 run_test(1, SEC);
|
|
1183
|
|
1184 unmute_Rin();
|
|
1185 set_Sgen(NONE, 0.0);
|
|
1186 run_test(500, MSEC);
|
|
1187
|
|
1188 /* Now measure the echo */
|
|
1189
|
|
1190 fig11 = (25.0/30.0)*maxLRin - 30.0; /* pass/fail based on clean level @ tx peak */
|
|
1191 threshold = fig11 + 3.0;
|
|
1192 reset_meter_peaks();
|
|
1193 maxLSgen = tmp;
|
|
1194 install_test_callback(test_3b);
|
|
1195 run_test(5, SEC);
|
|
1196
|
|
1197 print_results();
|
|
1198 }
|
|
1199
|
|
1200 if (!strcasecmp(argv[1], "3c")) {
|
|
1201 print_title("Performing test 3C - Double talk test under simulated conversation\n");
|
|
1202 reset_all();
|
|
1203
|
|
1204 /* t1 (5.6s) - double talk */
|
|
1205
|
|
1206 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP);
|
|
1207 set_Sgen(CSS, Sgen_leveldB);
|
|
1208 run_test(5600, MSEC);
|
|
1209
|
|
1210 /* t2 (1.4s) - to pass Sout <= Sgen */
|
|
1211
|
|
1212 set_Sgen(NONE, 0.0);
|
|
1213 install_test_callback(test_3c_t2);
|
|
1214 run_test(1400, MSEC);
|
|
1215
|
|
1216 /* t3 - (5s) - single talk to converge e/c */
|
|
1217
|
|
1218 run_test(5000, MSEC);
|
|
1219
|
|
1220 /* t4 - (5.6s) - double talk again */
|
|
1221
|
|
1222 install_test_callback(test_3c_t4t5);
|
|
1223 set_Sgen(CSS, Sgen_leveldB);
|
|
1224 run_test(5600, MSEC);
|
|
1225
|
|
1226 /* t5 - (5.6s) - near end single talk */
|
|
1227
|
|
1228 mute_Rin();
|
|
1229 run_test(5600, MSEC);
|
|
1230
|
|
1231 print_results();
|
|
1232 }
|
|
1233
|
|
1234 #ifdef OTHER_TESTS
|
|
1235 if ((test_list & PERFORM_TEST_4))
|
|
1236 {
|
|
1237 printf("Performing test 4 - Leak rate test\n");
|
|
1238 /* Test 4 - Leak rate test */
|
|
1239 echo_can_flush(ctx);
|
|
1240 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1241 /* Converge a canceller */
|
|
1242 signal_restart(&local_css);
|
|
1243 for (i = 0; i < SAMPLE_RATE*5; i++)
|
|
1244 {
|
|
1245 tx = signal_amp(&local_css);
|
|
1246 channel_model(&tx, &rx, tx, 0);
|
|
1247 clean = echo_can_update(ctx, tx, rx);
|
|
1248 put_residue(clean);
|
|
1249 }
|
|
1250 /* Put 2 minutes of silence through it */
|
|
1251 for (i = 0; i < SAMPLE_RATE*120; i++)
|
|
1252 {
|
|
1253 clean = echo_can_update(ctx, 0, 0);
|
|
1254 put_residue(clean);
|
|
1255 }
|
|
1256 /* Now freeze it, and check if it is still well adapted. */
|
|
1257 echo_can_adaption_mode(ctx, 0);
|
|
1258 for (i = 0; i < SAMPLE_RATE*5; i++)
|
|
1259 {
|
|
1260 tx = signal_amp(&local_css);
|
|
1261 channel_model(&tx, &rx, tx, 0);
|
|
1262 clean = echo_can_update(ctx, tx, rx);
|
|
1263 put_residue(clean);
|
|
1264 }
|
|
1265 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1266 #if defined(ENABLE_GUI)
|
|
1267 if (use_gui)
|
|
1268 echo_can_monitor_can_update(ctx->fir_taps16[ctx->tap_set], TEST_EC_TAPS);
|
|
1269 #endif
|
|
1270 }
|
|
1271
|
|
1272 if ((test_list & PERFORM_TEST_5))
|
|
1273 {
|
|
1274 printf("Performing test 5 - Infinite return loss convergence test\n");
|
|
1275 /* Test 5 - Infinite return loss convergence test */
|
|
1276 echo_can_flush(ctx);
|
|
1277 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1278 /* Converge the canceller */
|
|
1279 signal_restart(&local_css);
|
|
1280 for (i = 0; i < SAMPLE_RATE*5; i++)
|
|
1281 {
|
|
1282 tx = signal_amp(&local_css);
|
|
1283 channel_model(&tx, &rx, tx, 0);
|
|
1284 clean = echo_can_update(ctx, tx, rx);
|
|
1285 put_residue(clean);
|
|
1286 }
|
|
1287 /* Now stop echoing, and see we don't do anything unpleasant as the
|
|
1288 echo path is open looped. */
|
|
1289 for (i = 0; i < SAMPLE_RATE*5; i++)
|
|
1290 {
|
|
1291 tx = signal_amp(&local_css);
|
|
1292 rx = 0;
|
|
1293 tx = codec_munge(tx);
|
|
1294 clean = echo_can_update(ctx, tx, rx);
|
|
1295 put_residue(clean);
|
|
1296 }
|
|
1297 #if defined(ENABLE_GUI)
|
|
1298 if (use_gui)
|
|
1299 echo_can_monitor_can_update(ctx->fir_taps16[ctx->tap_set], TEST_EC_TAPS);
|
|
1300 #endif
|
|
1301 }
|
|
1302
|
|
1303 #endif
|
|
1304
|
|
1305 if (!strcasecmp(argv[1], "6"))
|
|
1306 {
|
|
1307 int k;
|
|
1308 float fig11;
|
|
1309
|
|
1310 printf("Performing test 6 - Non-divergence on narrow-band signals\n");
|
|
1311
|
|
1312 reset_all();
|
|
1313 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1314 run_test(5, SEC);
|
|
1315
|
|
1316 /* Now put 5s bursts of a list of tones through the converged canceller, and check
|
|
1317 that nothing unpleasant happens. */
|
|
1318
|
|
1319 for (k = 0; tones_6_4_2_7[k][0]; k++)
|
|
1320 {
|
|
1321 tone_gen_descriptor_t tone_desc;
|
|
1322
|
|
1323 /* 5 secs of each tone */
|
|
1324
|
|
1325 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1326 set_Rin(TONE, 20.0*log10(Rin_level)); /* level actually set by next func */
|
|
1327 make_tone_gen_descriptor(&tone_desc,
|
|
1328 tones_6_4_2_7[k][0],
|
|
1329 -11,
|
|
1330 tones_6_4_2_7[k][1],
|
|
1331 -9,
|
|
1332 1,
|
|
1333 0,
|
|
1334 0,
|
|
1335 0,
|
|
1336 1);
|
|
1337 tone_gen_init(&rin_tone_state, &tone_desc);
|
|
1338 run_test(5, SEC);
|
|
1339 }
|
|
1340
|
|
1341 /* disable adaption, back to speech */
|
|
1342
|
|
1343 echo_can_adaption_mode(ctx, 0);
|
|
1344 set_Rin(CSS, 20.0*log10(Rin_level));
|
|
1345 run_test(1, SEC);
|
|
1346
|
|
1347 /* now test convergence as per test 2 fig 11 */
|
|
1348
|
|
1349 fig11 = (25.0/30.0)*maxLRin - 30.0; /* pass/fail based on clean level @ tx peak */
|
|
1350 threshold = fig11 + 10.0;
|
|
1351 reset_meter_peaks();
|
|
1352 install_test_callback(test_3b);
|
|
1353 run_test(5, SEC);
|
|
1354
|
|
1355 print_results();
|
|
1356 }
|
|
1357
|
|
1358 #ifdef OTHER_TESTS
|
|
1359
|
|
1360 if ((test_list & PERFORM_TEST_7))
|
|
1361 {
|
|
1362 printf("Performing test 7 - Stability\n");
|
|
1363 /* Test 7 - Stability */
|
|
1364 /* Put tones through an unconverged canceller, and check nothing unpleasant
|
|
1365 happens. */
|
|
1366 echo_can_flush(ctx);
|
|
1367 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION);
|
|
1368 make_tone_gen_descriptor(&tone_desc,
|
|
1369 tones_6_4_2_7[0][0],
|
|
1370 -11,
|
|
1371 tones_6_4_2_7[0][1],
|
|
1372 -9,
|
|
1373 1,
|
|
1374 0,
|
|
1375 0,
|
|
1376 0,
|
|
1377 1);
|
|
1378 tone_gen_init(&tone_state, &tone_desc);
|
|
1379 j = 0;
|
|
1380 for (i = 0; i < 120; i++)
|
|
1381 {
|
|
1382 local_max = tone_gen(&tone_state, local_sound, SAMPLE_RATE);
|
|
1383 for (j = 0; j < SAMPLE_RATE; j++)
|
|
1384 {
|
|
1385 tx = local_sound[j];
|
|
1386 channel_model(&tx, &rx, tx, 0);
|
|
1387 clean = echo_can_update(ctx, tx, rx);
|
|
1388 put_residue(clean);
|
|
1389 }
|
|
1390 #if defined(ENABLE_GUI)
|
|
1391 if (use_gui)
|
|
1392 {
|
|
1393 echo_can_monitor_can_update(ctx->fir_taps16[ctx->tap_set], TEST_EC_TAPS);
|
|
1394 echo_can_monitor_update_display();
|
|
1395 usleep(100000);
|
|
1396 }
|
|
1397 #endif
|
|
1398 }
|
|
1399 #if defined(ENABLE_GUI)
|
|
1400 if (use_gui)
|
|
1401 echo_can_monitor_can_update(ctx->fir_taps16[ctx->tap_set], TEST_EC_TAPS);
|
|
1402 #endif
|
|
1403 }
|
|
1404
|
|
1405 if ((test_list & PERFORM_TEST_8))
|
|
1406 {
|
|
1407 printf("Performing test 8 - Non-convergence on No 5, 6, and 7 in-band signalling\n");
|
|
1408 /* Test 8 - Non-convergence on No 5, 6, and 7 in-band signalling */
|
|
1409 fprintf(stderr, "Test 8 not yet implemented\n");
|
|
1410 }
|
|
1411 #endif
|
|
1412
|
|
1413 if (!strcasecmp(argv[1], "9"))
|
|
1414 {
|
|
1415 printf("Performing test 9 - Comfort noise test\n");
|
|
1416
|
|
1417 echo_can_flush(ctx);
|
|
1418 echo_can_adaption_mode(ctx, ECHO_CAN_USE_ADAPTION
|
|
1419 | ECHO_CAN_USE_NLP
|
|
1420 | ECHO_CAN_USE_CNG);
|
|
1421
|
|
1422 /* Test 9 Part 1 - matching */
|
|
1423
|
|
1424 set_Sgen(HOTH, -45.0);
|
|
1425 mute_Rin();
|
|
1426 run_test(5, SEC); /* should be 30s but I wanted to speed up sim */
|
|
1427 set_Rin(HOTH, -10.0);
|
|
1428 run_test(2, SEC);
|
|
1429
|
|
1430 reset_meter_peaks();
|
|
1431 install_test_callback(test_9);
|
|
1432 run_test(700, MSEC);
|
|
1433
|
|
1434 /* Test 9 Part 2 - adjustment down */
|
|
1435
|
|
1436 install_test_callback(NULL);
|
|
1437 set_Sgen(HOTH, -55.0);
|
|
1438 mute_Rin();
|
|
1439 run_test(5, SEC); /* should be 10s but I wanted to speed up sim */
|
|
1440 set_Rin(HOTH, -10.0);
|
|
1441 run_test(2, SEC);
|
|
1442
|
|
1443 reset_meter_peaks();
|
|
1444 install_test_callback(test_9);
|
|
1445 run_test(700, MSEC);
|
|
1446
|
|
1447 /* Test 9 Part 3 - adjustment up */
|
|
1448
|
|
1449 install_test_callback(NULL);
|
|
1450 set_Sgen(HOTH, -45.0);
|
|
1451 mute_Rin();
|
|
1452 run_test(5, SEC); /* should be 10s but I wanted to speed up sim */
|
|
1453 set_Rin(HOTH, -10.0);
|
|
1454 run_test(2, SEC);
|
|
1455
|
|
1456 reset_meter_peaks();
|
|
1457 install_test_callback(test_9);
|
|
1458 run_test(700, MSEC);
|
|
1459
|
|
1460 print_results();
|
|
1461 }
|
|
1462
|
|
1463 #ifdef OTHER_TESTS
|
|
1464 /* Test 10 - FAX test during call establishment phase */
|
|
1465 if ((test_list & PERFORM_TEST_10A))
|
|
1466 {
|
|
1467 printf("Performing test 10A - Canceller operation on the calling station side\n");
|
|
1468 /* Test 10A - Canceller operation on the calling station side */
|
|
1469 fprintf(stderr, "Test 10A not yet implemented\n");
|
|
1470 }
|
|
1471
|
|
1472 if ((test_list & PERFORM_TEST_10B))
|
|
1473 {
|
|
1474 printf("Performing test 10B - Canceller operation on the called station side\n");
|
|
1475 /* Test 10B - Canceller operation on the called station side */
|
|
1476 fprintf(stderr, "Test 10B not yet implemented\n");
|
|
1477 }
|
|
1478
|
|
1479 if ((test_list & PERFORM_TEST_10C))
|
|
1480 {
|
|
1481 printf("Performing test 10C - Canceller operation on the calling station side during page\n"
|
|
1482 "transmission and page breaks (for further study)\n");
|
|
1483 /* Test 10C - Canceller operation on the calling station side during page
|
|
1484 transmission and page breaks (for further study) */
|
|
1485 fprintf(stderr, "Test 10C not yet implemented\n");
|
|
1486 }
|
|
1487
|
|
1488 if ((test_list & PERFORM_TEST_11))
|
|
1489 {
|
|
1490 printf("Performing test 11 - Tandem echo canceller test (for further study)\n");
|
|
1491 /* Test 11 - Tandem echo canceller test (for further study) */
|
|
1492 fprintf(stderr, "Test 11 not yet implemented\n");
|
|
1493 }
|
|
1494
|
|
1495 if ((test_list & PERFORM_TEST_12))
|
|
1496 {
|
|
1497 printf("Performing test 12 - Residual acoustic echo test (for further study)\n");
|
|
1498 /* Test 12 - Residual acoustic echo test (for further study) */
|
|
1499 fprintf(stderr, "Test 12 not yet implemented\n");
|
|
1500 }
|
|
1501
|
|
1502 if ((test_list & PERFORM_TEST_13))
|
|
1503 {
|
|
1504 printf("Performing test 13 - Performance with ITU-T low-bit rate coders in echo path (Optional, under study)\n");
|
|
1505 /* Test 13 - Performance with ITU-T low-bit rate coders in echo path
|
|
1506 (Optional, under study) */
|
|
1507 fprintf(stderr, "Test 13 not yet implemented\n");
|
|
1508 }
|
|
1509
|
|
1510 if ((test_list & PERFORM_TEST_14))
|
|
1511 {
|
|
1512 printf("Performing test 14 - Performance with V-series low-speed data modems\n");
|
|
1513 /* Test 14 - Performance with V-series low-speed data modems */
|
|
1514 fprintf(stderr, "Test 14 not yet implemented\n");
|
|
1515 }
|
|
1516
|
|
1517 if ((test_list & PERFORM_TEST_15))
|
|
1518 {
|
|
1519 printf("Performing test 15 - PCM offset test (Optional)\n");
|
|
1520 /* Test 15 - PCM offset test (Optional) */
|
|
1521 fprintf(stderr, "Test 15 not yet implemented\n");
|
|
1522 }
|
|
1523
|
|
1524 echo_can_free(ctx);
|
|
1525
|
|
1526 signal_free(&local_css);
|
|
1527 signal_free(&far_css);
|
|
1528
|
|
1529 if (afCloseFile(resulthandle) != 0)
|
|
1530 {
|
|
1531 fprintf(stderr, " Cannot close speech file '%s'\n", "result_sound.wav");
|
|
1532 exit(2);
|
|
1533 }
|
|
1534 if (afCloseFile(residuehandle) != 0)
|
|
1535 {
|
|
1536 fprintf(stderr, " Cannot close speech file '%s'\n", "residue_sound.wav");
|
|
1537 exit(2);
|
|
1538 }
|
|
1539 afFreeFileSetup(filesetup);
|
|
1540 afFreeFileSetup(filesetup2);
|
|
1541
|
|
1542 #if defined(XYZZY)
|
|
1543 for (j = 0; j < ctx->taps; j++)
|
|
1544 {
|
|
1545 for (i = 0; i < coeff_index; i++)
|
|
1546 fprintf(stderr, "%d ", coeffs[i][j]);
|
|
1547 fprintf(stderr, "\n");
|
|
1548 }
|
|
1549 #endif
|
|
1550 #endif
|
|
1551 if (verbose == TRUE)
|
|
1552 printf("Run time %lds\n", time(NULL) - now);
|
|
1553
|
|
1554 #if defined(ENABLE_GUI)
|
|
1555 if (use_gui)
|
|
1556 echo_can_monitor_wait_to_end();
|
|
1557 #endif
|
|
1558
|
|
1559
|
|
1560 fclose(fdump);
|
|
1561 fclose(flevel);
|
|
1562
|
|
1563 return 0;
|
|
1564 }
|
|
1565 /*- End of function --------------------------------------------------------*/
|
|
1566 /*- End of file ------------------------------------------------------------*/
|