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