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 ------------------------------------------------------------*/

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