Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/tests/bell_mf_rx_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 * bell_mf_tests.c - Test the Bell MF detector against the spec., whatever the | |
5 * spec. may be :) | |
6 * | |
7 * Written by Steve Underwood <steveu@coppice.org> | |
8 * | |
9 * Copyright (C) 2003 Steve Underwood | |
10 * | |
11 * All rights reserved. | |
12 * | |
13 * This program is free software; you can redistribute it and/or modify | |
14 * it under the terms of the GNU General Public License version 2, as | |
15 * published by the Free Software Foundation. | |
16 * | |
17 * This program is distributed in the hope that it will be useful, | |
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 * GNU General Public License for more details. | |
21 * | |
22 * You should have received a copy of the GNU General Public License | |
23 * along with this program; if not, write to the Free Software | |
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
25 * | |
26 * $Id: bell_mf_rx_tests.c,v 1.16 2009/05/30 15:23:13 steveu Exp $ | |
27 */ | |
28 | |
29 /*! \file */ | |
30 | |
31 /*! \page bell_mf_tests_page Bell MF tone generation and detection tests | |
32 \section bell_mf_tests_page_sec_1 What does it do? | |
33 These tests are fashioned after those on the CM7291 test tape from | |
34 Mitel. Those tests are for DTMF, rather than Bell MF, but make a | |
35 fair starting point for a set of meaningful tests of Bell MF. | |
36 | |
37 These tests include conversion to and from A-law. It is assumed the | |
38 distortion this produces is comparable to u-law, so it should be | |
39 a fair test of performance in a real PSTN channel. | |
40 */ | |
41 | |
42 #if defined(HAVE_CONFIG_H) | |
43 #include "config.h" | |
44 #endif | |
45 | |
46 #include <stdlib.h> | |
47 #include <stdio.h> | |
48 #include <fcntl.h> | |
49 #include <string.h> | |
50 #include <time.h> | |
51 #include <sndfile.h> | |
52 | |
53 //#if defined(WITH_SPANDSP_INTERNALS) | |
54 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES | |
55 //#endif | |
56 | |
57 #include "spandsp.h" | |
58 | |
59 /* Basic Bell MF specs: | |
60 * | |
61 * Signal generation: | |
62 * Tone on time = KP: 100+-7ms. All other signals: 68+-7ms | |
63 * Tone off time (between digits) = 68+-7ms | |
64 * Frequency tolerance +- 1.5% | |
65 * Signal level -7+-1dBm per frequency | |
66 * | |
67 * Signal reception: | |
68 * Frequency tolerance +- 1.5% +-10Hz | |
69 * Signal level -14dBm to 0dBm | |
70 * Perform a "two and only two tones present" test. | |
71 * Twist <= 6dB accepted | |
72 * Receiver sensitive to signals above -22dBm per frequency | |
73 * Test for a minimum of 55ms if KP, or 30ms of other signals. | |
74 * Signals to be recognised if the two tones arrive within 8ms of each other. | |
75 * Invalid signals result in the return of the re-order tone. | |
76 */ | |
77 | |
78 #define MF_DURATION (68*8) | |
79 #define MF_PAUSE (68*8) | |
80 #define MF_CYCLE (MF_DURATION + MF_PAUSE) | |
81 | |
82 /*! | |
83 MF tone descriptor for tests. | |
84 */ | |
85 typedef struct | |
86 { | |
87 float f1; /* First freq */ | |
88 float f2; /* Second freq */ | |
89 int8_t level1; /* Level of the first freq (dB) */ | |
90 int8_t level2; /* Level of the second freq (dB) */ | |
91 uint8_t on_time; /* Tone on time (ms) */ | |
92 uint8_t off_time; /* Minimum post tone silence (ms) */ | |
93 } mf_digit_tones_t; | |
94 | |
95 static const mf_digit_tones_t bell_mf_tones[] = | |
96 { | |
97 { 700.0, 900.0, -7, -7, 68, 68}, | |
98 { 700.0, 1100.0, -7, -7, 68, 68}, | |
99 { 900.0, 1100.0, -7, -7, 68, 68}, | |
100 { 700.0, 1300.0, -7, -7, 68, 68}, | |
101 { 900.0, 1300.0, -7, -7, 68, 68}, | |
102 {1100.0, 1300.0, -7, -7, 68, 68}, | |
103 { 700.0, 1500.0, -7, -7, 68, 68}, | |
104 { 900.0, 1500.0, -7, -7, 68, 68}, | |
105 {1100.0, 1500.0, -7, -7, 68, 68}, | |
106 {1300.0, 1500.0, -7, -7, 68, 68}, | |
107 { 700.0, 1700.0, -7, -7, 68, 68}, /* ST''' - use 'C' */ | |
108 { 900.0, 1700.0, -7, -7, 68, 68}, /* ST' - use 'A' */ | |
109 {1100.0, 1700.0, -7, -7, 100, 68}, /* KP - use '*' */ | |
110 {1300.0, 1700.0, -7, -7, 68, 68}, /* ST'' - use 'B' */ | |
111 {1500.0, 1700.0, -7, -7, 68, 68}, /* ST - use '#' */ | |
112 {0.0, 0.0, 0, 0, 0, 0} | |
113 }; | |
114 | |
115 static tone_gen_descriptor_t my_mf_digit_tones[16]; | |
116 | |
117 static char bell_mf_tone_codes[] = "1234567890CA*B#"; | |
118 | |
119 int callback_ok; | |
120 int callback_roll; | |
121 | |
122 static void my_mf_gen_init(float low_fudge, | |
123 int low_level, | |
124 float high_fudge, | |
125 int high_level, | |
126 int duration, | |
127 int gap) | |
128 { | |
129 int i; | |
130 | |
131 /* The fiddle factor on the tone duration is to make KP consistently | |
132 50% longer than the other digits, as the digit durations are varied | |
133 for the tests. This is an approximation, as the real scaling should | |
134 be 100/68 */ | |
135 for (i = 0; i < 15; i++) | |
136 { | |
137 make_tone_gen_descriptor(&my_mf_digit_tones[i], | |
138 bell_mf_tones[i].f1*(1.0 + low_fudge), | |
139 low_level, | |
140 bell_mf_tones[i].f2*(1.0 + high_fudge), | |
141 high_level, | |
142 (i == 12) ? 3*duration/2 : duration, | |
143 gap, | |
144 0, | |
145 0, | |
146 FALSE); | |
147 } | |
148 } | |
149 /*- End of function --------------------------------------------------------*/ | |
150 | |
151 static int my_mf_generate(int16_t amp[], const char *digits) | |
152 { | |
153 int len; | |
154 char *cp; | |
155 tone_gen_state_t tone; | |
156 | |
157 len = 0; | |
158 while (*digits) | |
159 { | |
160 if ((cp = strchr(bell_mf_tone_codes, *digits))) | |
161 { | |
162 tone_gen_init(&tone, &my_mf_digit_tones[cp - bell_mf_tone_codes]); | |
163 len += tone_gen(&tone, amp + len, 9999); | |
164 } | |
165 digits++; | |
166 } | |
167 return len; | |
168 } | |
169 /*- End of function --------------------------------------------------------*/ | |
170 | |
171 static void codec_munge(int16_t amp[], int len) | |
172 { | |
173 int i; | |
174 uint8_t alaw; | |
175 | |
176 for (i = 0; i < len; i++) | |
177 { | |
178 alaw = linear_to_alaw (amp[i]); | |
179 amp[i] = alaw_to_linear (alaw); | |
180 } | |
181 } | |
182 /*- End of function --------------------------------------------------------*/ | |
183 | |
184 #define ALL_POSSIBLE_DIGITS "1234567890CA*B#" | |
185 | |
186 static void digit_delivery(void *data, const char *digits, int len) | |
187 { | |
188 int i; | |
189 int seg; | |
190 const char *s = ALL_POSSIBLE_DIGITS; | |
191 const char *t; | |
192 | |
193 if (data != (void *) 0x12345678) | |
194 { | |
195 callback_ok = FALSE; | |
196 return; | |
197 } | |
198 callback_ok = TRUE; | |
199 t = s + callback_roll; | |
200 seg = 15 - callback_roll; | |
201 for (i = 0; i < len; i += seg, seg = 15) | |
202 { | |
203 if (i + seg > len) | |
204 seg = len - i; | |
205 if (memcmp(digits + i, t, seg)) | |
206 { | |
207 callback_ok = FALSE; | |
208 printf("Fail at %d %d\n", i, seg); | |
209 break; | |
210 } | |
211 t = s; | |
212 callback_roll = (callback_roll + seg)%15; | |
213 } | |
214 } | |
215 /*- End of function --------------------------------------------------------*/ | |
216 | |
217 static int16_t amp[1000000]; | |
218 | |
219 int main(int argc, char *argv[]) | |
220 { | |
221 int duration; | |
222 int i; | |
223 int j; | |
224 int len; | |
225 int sample; | |
226 const char *s; | |
227 char digit[2]; | |
228 char buf[MAX_BELL_MF_DIGITS + 1]; | |
229 int actual; | |
230 int nplus; | |
231 int nminus; | |
232 float rrb; | |
233 float rcfo; | |
234 time_t now; | |
235 bell_mf_rx_state_t *mf_state; | |
236 awgn_state_t noise_source; | |
237 | |
238 time(&now); | |
239 mf_state = bell_mf_rx_init(NULL, NULL, NULL); | |
240 | |
241 /* Test 1: Mitel's test 1 isn't really a test. Its a calibration step, | |
242 which has no meaning here. */ | |
243 printf ("Test 1: Calibration\n"); | |
244 printf (" Passed\n"); | |
245 | |
246 /* Test 2: Decode check | |
247 This is a sanity check, that all digits are reliably detected | |
248 under ideal conditions. Each possible digit is repeated 10 times, | |
249 with 68ms bursts. The level of each tone is about 6dB down from clip */ | |
250 printf ("Test 2: Decode check\n"); | |
251 my_mf_gen_init(0.0, -3, 0.0, -3, 68, 68); | |
252 s = ALL_POSSIBLE_DIGITS; | |
253 digit[1] = '\0'; | |
254 while (*s) | |
255 { | |
256 digit[0] = *s++; | |
257 for (i = 0; i < 10; i++) | |
258 { | |
259 len = my_mf_generate(amp, digit); | |
260 codec_munge(amp, len); | |
261 bell_mf_rx(mf_state, amp, len); | |
262 actual = bell_mf_rx_get(mf_state, buf, 128); | |
263 if (actual != 1 || buf[0] != digit[0]) | |
264 { | |
265 printf (" Sent '%s'\n", digit); | |
266 printf (" Received '%s' [%d]\n", buf, actual); | |
267 printf (" Failed\n"); | |
268 exit (2); | |
269 } | |
270 } | |
271 } | |
272 printf (" Passed\n"); | |
273 | |
274 /* Test 3: Recognition bandwidth and channel centre frequency check. | |
275 Use all digits. Each digit types requires four tests to complete | |
276 the check. Each section contains 40 pulses of 68ms duration, | |
277 with an amplitude of -20dB from clip per frequency. | |
278 | |
279 Four sections covering the tests for one tone (1 digit) are: | |
280 a. H frequency at 0% deviation from center, L frequency at +0.1%. | |
281 L frequency is then increments in +01.% steps up to +4%. The | |
282 number of tone bursts is noted and designated N+. | |
283 b. H frequency at 0% deviation, L frequency at -0.1%. L frequency | |
284 is then incremental in -0.1% steps, up to -4%. The number of | |
285 tone bursts is noted and designated N-. | |
286 c. The test in (a) is repeated with the L frequency at 0% and the | |
287 H frequency varied up to +4%. | |
288 d. The test in (b) is repeated with the L frequency and 0% and the | |
289 H frequency varied to -4%. | |
290 | |
291 Receiver Recognition Bandwidth (RRB) is calculated as follows: | |
292 RRB% = (N+ + N-)/10 | |
293 Receiver Center Frequency Offset (RCFO) is calculated as follows: | |
294 RCFO% = X + (N+ - N-)/20 | |
295 | |
296 Note that this test doesn't test what it says it is testing at all, | |
297 and the results are quite inaccurate, if not a downright lie! However, | |
298 it follows the Mitel procedure, so how can it be bad? :) | |
299 | |
300 The spec calls for +-1.5% +-10Hz of bandwidth. | |
301 */ | |
302 printf ("Test 3: Recognition bandwidth and channel centre frequency check\n"); | |
303 s = ALL_POSSIBLE_DIGITS; | |
304 digit[1] = '\0'; | |
305 j = 0; | |
306 while (*s) | |
307 { | |
308 digit[0] = *s++; | |
309 for (nplus = 0, i = 1; i <= 60; i++) | |
310 { | |
311 my_mf_gen_init((float) i/1000.0, -17, 0.0, -17, 68, 68); | |
312 len = my_mf_generate(amp, digit); | |
313 codec_munge(amp, len); | |
314 bell_mf_rx(mf_state, amp, len); | |
315 nplus += bell_mf_rx_get(mf_state, buf, 128); | |
316 } | |
317 for (nminus = 0, i = -1; i >= -60; i--) | |
318 { | |
319 my_mf_gen_init((float) i/1000.0, -17, 0.0, -17, 68, 68); | |
320 len = my_mf_generate(amp, digit); | |
321 codec_munge(amp, len); | |
322 bell_mf_rx(mf_state, amp, len); | |
323 nminus += bell_mf_rx_get(mf_state, buf, 128); | |
324 } | |
325 rrb = (float) (nplus + nminus)/10.0; | |
326 rcfo = (float) (nplus - nminus)/10.0; | |
327 printf (" %c (low) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n", | |
328 digit[0], | |
329 rrb, | |
330 rcfo, | |
331 (float) nminus/10.0, | |
332 (float) nplus/10.0); | |
333 | |
334 if (rrb < 3.0 + rcfo + (2.0*100.0*10.0/bell_mf_tones[j].f1) || rrb >= 15.0 + rcfo) | |
335 { | |
336 printf (" Failed\n"); | |
337 exit (2); | |
338 } | |
339 | |
340 for (nplus = 0, i = 1; i <= 60; i++) | |
341 { | |
342 my_mf_gen_init(0.0, -17, (float) i/1000.0, -17, 68, 68); | |
343 len = my_mf_generate(amp, digit); | |
344 codec_munge(amp, len); | |
345 bell_mf_rx(mf_state, amp, len); | |
346 nplus += bell_mf_rx_get(mf_state, buf, 128); | |
347 } | |
348 for (nminus = 0, i = -1; i >= -60; i--) | |
349 { | |
350 my_mf_gen_init(0.0, -17, (float) i/1000.0, -17, 68, 68); | |
351 len = my_mf_generate(amp, digit); | |
352 codec_munge(amp, len); | |
353 bell_mf_rx(mf_state, amp, len); | |
354 nminus += bell_mf_rx_get(mf_state, buf, 128); | |
355 } | |
356 rrb = (float) (nplus + nminus)/10.0; | |
357 rcfo = (float) (nplus - nminus)/10.0; | |
358 printf (" %c (high) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n", | |
359 digit[0], | |
360 rrb, | |
361 rcfo, | |
362 (float) nminus/10.0, | |
363 (float) nplus/10.0); | |
364 if (rrb < 3.0 + rcfo + (2.0*100.0*10.0/bell_mf_tones[j].f2) || rrb >= 15.0 + rcfo) | |
365 { | |
366 printf (" Failed\n"); | |
367 exit (2); | |
368 } | |
369 j++; | |
370 } | |
371 printf (" Passed\n"); | |
372 | |
373 /* Test 4: Acceptable amplitude ratio (twist). | |
374 Twist all digits in both directions, and check the maximum twist | |
375 we can accept. The way this is done is styled after the Mitel DTMF | |
376 test, and has good and bad points. */ | |
377 | |
378 printf ("Test 4: Acceptable amplitude ratio (twist)\n"); | |
379 s = ALL_POSSIBLE_DIGITS; | |
380 digit[1] = '\0'; | |
381 while (*s) | |
382 { | |
383 digit[0] = *s++; | |
384 for (nplus = 0, i = -50; i >= -250; i--) | |
385 { | |
386 my_mf_gen_init(0.0, -5, 0.0, i/10, 68, 68); | |
387 | |
388 len = my_mf_generate(amp, digit); | |
389 codec_munge(amp, len); | |
390 bell_mf_rx(mf_state, amp, len); | |
391 nplus += bell_mf_rx_get(mf_state, buf, 128); | |
392 } | |
393 printf(" %c normal twist = %.2fdB\n", digit[0], (float) nplus/10.0); | |
394 if (nplus < 60) | |
395 { | |
396 printf(" Failed\n"); | |
397 exit(2); | |
398 } | |
399 for (nminus = 0, i = -50; i >= -250; i--) | |
400 { | |
401 my_mf_gen_init(0.0, i/10, 0.0, -5, 68, 68); | |
402 | |
403 len = my_mf_generate(amp, digit); | |
404 codec_munge(amp, len); | |
405 bell_mf_rx(mf_state, amp, len); | |
406 nminus += bell_mf_rx_get(mf_state, buf, 128); | |
407 } | |
408 printf(" %c reverse twist = %.2fdB\n", digit[0], (float) nminus/10.0); | |
409 if (nminus < 60) | |
410 { | |
411 printf(" Failed\n"); | |
412 exit(2); | |
413 } | |
414 } | |
415 printf(" Passed\n"); | |
416 | |
417 /* Test 5: Dynamic range | |
418 This test sends all possible digits, with gradually increasing | |
419 amplitude. We determine the span over which we achieve reliable | |
420 detection. The spec says we should detect between -14dBm and 0dBm, | |
421 but the tones clip above -3dBm, so this cannot really work. */ | |
422 | |
423 printf("Test 5: Dynamic range\n"); | |
424 for (nplus = nminus = -1000, i = -50; i <= 3; i++) | |
425 { | |
426 my_mf_gen_init(0.0, i, 0.0, i, 68, 68); | |
427 for (j = 0; j < 100; j++) | |
428 { | |
429 len = my_mf_generate(amp, ALL_POSSIBLE_DIGITS); | |
430 codec_munge(amp, len); | |
431 bell_mf_rx(mf_state, amp, len); | |
432 if (bell_mf_rx_get(mf_state, buf, 128) != 15) | |
433 break; | |
434 if (strcmp(buf, ALL_POSSIBLE_DIGITS) != 0) | |
435 break; | |
436 } | |
437 if (j == 100) | |
438 { | |
439 if (nplus == -1000) | |
440 nplus = i; | |
441 } | |
442 else | |
443 { | |
444 if (nplus != -1000 && nminus == -1000) | |
445 nminus = i; | |
446 } | |
447 } | |
448 printf(" Dynamic range = %ddB to %ddB\n", nplus, nminus - 1); | |
449 if (nplus > -22 || nminus <= -3) | |
450 { | |
451 printf(" Failed\n"); | |
452 exit(2); | |
453 } | |
454 printf(" Passed\n"); | |
455 | |
456 /* Test 6: Guard time | |
457 This test sends all possible digits, with a gradually reducing | |
458 duration. The spec defines a narrow range of tone duration | |
459 times we can expect, so as long as we detect reliably at the | |
460 specified minimum we should be OK. However, the spec also says | |
461 we should detect on a minimum of 55ms of KP, or 30ms of other | |
462 digits. */ | |
463 | |
464 printf("Test 6: Guard time\n"); | |
465 for (i = 30; i < 62; i++) | |
466 { | |
467 my_mf_gen_init(0.0, -5, 0.0, -3, i, 68); | |
468 for (j = 0; j < 500; j++) | |
469 { | |
470 len = my_mf_generate(amp, ALL_POSSIBLE_DIGITS); | |
471 codec_munge(amp, len); | |
472 bell_mf_rx(mf_state, amp, len); | |
473 if (bell_mf_rx_get(mf_state, buf, 128) != 15) | |
474 break; | |
475 if (strcmp(buf, ALL_POSSIBLE_DIGITS) != 0) | |
476 break; | |
477 } | |
478 if (j == 500) | |
479 break; | |
480 } | |
481 printf(" Guard time = %dms\n", i); | |
482 if (i > 61) | |
483 { | |
484 printf(" Failed\n"); | |
485 exit(2); | |
486 } | |
487 printf(" Passed\n"); | |
488 | |
489 /* Test 7: Acceptable signal to noise ratio | |
490 We send all possible digits at -6dBm from clip, mixed with AWGN. | |
491 We gradually reduce the noise until we get clean detection. */ | |
492 | |
493 printf("Test 7: Acceptable signal to noise ratio\n"); | |
494 my_mf_gen_init(0.0, -3, 0.0, -3, 68, 68); | |
495 for (i = -10; i > -50; i--) | |
496 { | |
497 awgn_init_dbm0(&noise_source, 1234567, (float) i); | |
498 for (j = 0; j < 500; j++) | |
499 { | |
500 len = my_mf_generate(amp, ALL_POSSIBLE_DIGITS); | |
501 for (sample = 0; sample < len; sample++) | |
502 amp[sample] = saturate(amp[sample] + awgn(&noise_source)); | |
503 codec_munge(amp, len); | |
504 bell_mf_rx(mf_state, amp, len); | |
505 if (bell_mf_rx_get(mf_state, buf, 128) != 15) | |
506 break; | |
507 if (strcmp(buf, ALL_POSSIBLE_DIGITS) != 0) | |
508 break; | |
509 } | |
510 if (j == 500) | |
511 break; | |
512 } | |
513 printf(" Acceptable S/N ratio is %ddB\n", -3 - i); | |
514 if (-3 - i > 26) | |
515 { | |
516 printf(" Failed\n"); | |
517 exit(2); | |
518 } | |
519 printf(" Passed\n"); | |
520 | |
521 /* The remainder of the Mitel tape is the talk-off test. This is | |
522 meaningless for Bell MF. However the decoder's tolerance of | |
523 out of band noise is significant. */ | |
524 /* TODO: add a OOB tolerance test. */ | |
525 | |
526 /* Test the callback mode for delivering detected digits */ | |
527 | |
528 printf("Test: Callback digit delivery mode.\n"); | |
529 callback_ok = FALSE; | |
530 callback_roll = 0; | |
531 mf_state = bell_mf_rx_init(NULL, digit_delivery, (void *) 0x12345678); | |
532 my_mf_gen_init(0.0, -10, 0.0, -10, 68, 68); | |
533 for (i = 1; i < 10; i++) | |
534 { | |
535 len = 0; | |
536 for (j = 0; j < i; j++) | |
537 len += my_mf_generate(amp + len, ALL_POSSIBLE_DIGITS); | |
538 bell_mf_rx(mf_state, amp, len); | |
539 if (!callback_ok) | |
540 break; | |
541 } | |
542 if (!callback_ok) | |
543 { | |
544 printf(" Failed\n"); | |
545 exit (2); | |
546 } | |
547 printf(" Passed\n"); | |
548 | |
549 duration = time (NULL) - now; | |
550 printf ("Tests passed in %ds\n", duration); | |
551 return 0; | |
552 } | |
553 /*- End of function --------------------------------------------------------*/ | |
554 /*- End of file ------------------------------------------------------------*/ |