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

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