comparison spandsp-0.0.6pre17/tests/r2_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 * r2_mf_tests.c - Test the R2 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: r2_mf_rx_tests.c,v 1.14 2009/05/30 15:23:14 steveu Exp $
27 */
28
29 /*! \file */
30
31 /*! \page r2_mf_tests_page R2 MF tone generation and detection tests
32 \section r2_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 R2 MF, but make a
35 fair starting point for a set of meaningful tests of R2 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 /* Enable the following definition to enable direct probing into the FAX structures */
43 //#define WITH_SPANDSP_INTERNALS
44
45 #if defined(HAVE_CONFIG_H)
46 #include "config.h"
47 #endif
48
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <fcntl.h>
52 #include <string.h>
53 #include <time.h>
54 #include <sndfile.h>
55
56 //#if defined(WITH_SPANDSP_INTERNALS)
57 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
58 //#endif
59
60 #include "spandsp.h"
61
62 /* R2 tone generation specs.
63 * Power: -11.5dBm +- 1dB
64 * Frequency: within +-4Hz
65 * Mismatch between the start time of a pair of tones: <=1ms.
66 * Mismatch between the end time of a pair of tones: <=1ms.
67 */
68 /* Basic MFC/R2 tone detection specs:
69 * Receiver response range: -5dBm to -35dBm
70 * Difference in level for a pair of frequencies
71 * Adjacent tones: <5dB
72 * Non-adjacent tones: <7dB
73 * Receiver not to detect a signal of 2 frequencies of level -5dB and
74 * duration <7ms.
75 * Receiver not to recognise a signal of 2 frequencies having a difference
76 * in level >=20dB.
77 * Max received signal frequency error: +-10Hz
78 * The sum of the operate and release times of a 2 frequency signal not to
79 * exceed 80ms (there are no individual specs for the operate and release
80 * times).
81 * Receiver not to release for signal interruptions <=7ms.
82 * System malfunction due to signal interruptions >7ms (typically 20ms) is
83 * prevented by further logic elements.
84 */
85
86 #define MF_DURATION (68*8)
87 #define MF_PAUSE (68*8)
88 #define MF_CYCLE (MF_DURATION + MF_PAUSE)
89
90 /*!
91 MF tone generator descriptor for tests.
92 */
93 typedef struct
94 {
95 float f1; /* First freq */
96 float f2; /* Second freq */
97 int8_t level1; /* Level of the first freq (dB) */
98 int8_t level2; /* Level of the second freq (dB) */
99 uint8_t on_time; /* Tone on time (ms) */
100 uint8_t off_time; /* Minimum post tone silence (ms) */
101 } mf_digit_tones_t;
102
103 static const mf_digit_tones_t r2_mf_fwd_tones[] =
104 {
105 {1380.0, 1500.0, -11, -11, 1, 0},
106 {1380.0, 1620.0, -11, -11, 1, 0},
107 {1500.0, 1620.0, -11, -11, 1, 0},
108 {1380.0, 1740.0, -11, -11, 1, 0},
109 {1500.0, 1740.0, -11, -11, 1, 0},
110 {1620.0, 1740.0, -11, -11, 1, 0},
111 {1380.0, 1860.0, -11, -11, 1, 0},
112 {1500.0, 1860.0, -11, -11, 1, 0},
113 {1620.0, 1860.0, -11, -11, 1, 0},
114 {1740.0, 1860.0, -11, -11, 1, 0},
115 {1380.0, 1980.0, -11, -11, 1, 0},
116 {1500.0, 1980.0, -11, -11, 1, 0},
117 {1620.0, 1980.0, -11, -11, 1, 0},
118 {1740.0, 1980.0, -11, -11, 1, 0},
119 {1860.0, 1980.0, -11, -11, 1, 0},
120 {0.0, 0.0, 0, 0, 0, 0}
121 };
122
123 static const mf_digit_tones_t r2_mf_back_tones[] =
124 {
125 {1140.0, 1020.0, -11, -11, 1, 0},
126 {1140.0, 900.0, -11, -11, 1, 0},
127 {1020.0, 900.0, -11, -11, 1, 0},
128 {1140.0, 780.0, -11, -11, 1, 0},
129 {1020.0, 780.0, -11, -11, 1, 0},
130 { 900.0, 780.0, -11, -11, 1, 0},
131 {1140.0, 660.0, -11, -11, 1, 0},
132 {1020.0, 660.0, -11, -11, 1, 0},
133 { 900.0, 660.0, -11, -11, 1, 0},
134 { 780.0, 660.0, -11, -11, 1, 0},
135 {1140.0, 540.0, -11, -11, 1, 0},
136 {1020.0, 540.0, -11, -11, 1, 0},
137 { 900.0, 540.0, -11, -11, 1, 0},
138 { 780.0, 540.0, -11, -11, 1, 0},
139 { 660.0, 540.0, -11, -11, 1, 0},
140 {0.0, 0.0, 0, 0, 0, 0}
141 };
142
143 static tone_gen_descriptor_t my_mf_digit_tones[16];
144
145 static char r2_mf_tone_codes[] = "1234567890BCDEF";
146
147 int callback_ok;
148 int callback_roll;
149
150 static void my_mf_gen_init(float low_fudge,
151 int low_level,
152 float high_fudge,
153 int high_level,
154 int duration,
155 int fwd)
156 {
157 const mf_digit_tones_t *tone;
158 int i;
159
160 for (i = 0; i < 15; i++)
161 {
162 if (fwd)
163 tone = &r2_mf_fwd_tones[i];
164 else
165 tone = &r2_mf_back_tones[i];
166 make_tone_gen_descriptor(&my_mf_digit_tones[i],
167 tone->f1*(1.0 + low_fudge),
168 low_level,
169 tone->f2*(1.0 + high_fudge),
170 high_level,
171 duration,
172 0,
173 0,
174 0,
175 FALSE);
176 }
177 }
178 /*- End of function --------------------------------------------------------*/
179
180 static int my_mf_generate(int16_t amp[], char digit)
181 {
182 int len;
183 char *cp;
184 tone_gen_state_t tone;
185
186 len = 0;
187 if ((cp = strchr(r2_mf_tone_codes, digit)))
188 {
189 tone_gen_init(&tone, &my_mf_digit_tones[cp - r2_mf_tone_codes]);
190 len += tone_gen(&tone, amp + len, 9999);
191 }
192 return len;
193 }
194 /*- End of function --------------------------------------------------------*/
195
196 static void codec_munge(int16_t amp[], int len)
197 {
198 int i;
199 uint8_t alaw;
200
201 for (i = 0; i < len; i++)
202 {
203 alaw = linear_to_alaw (amp[i]);
204 amp[i] = alaw_to_linear (alaw);
205 }
206 }
207 /*- End of function --------------------------------------------------------*/
208
209 static void digit_delivery(void *data, int digit, int level, int delay)
210 {
211 char ch;
212
213 if (data != (void *) 0x12345678)
214 {
215 callback_ok = FALSE;
216 return;
217 }
218 if ((callback_roll & 1))
219 ch = 0;
220 else
221 ch = r2_mf_tone_codes[callback_roll >> 1];
222 if (ch == digit)
223 callback_ok = TRUE;
224 else
225 callback_ok = FALSE;
226 if (r2_mf_tone_codes[callback_roll >> 1])
227 callback_roll++;
228 else
229 callback_ok = FALSE;
230 }
231 /*- End of function --------------------------------------------------------*/
232
233 static int test_a_tone_set(int fwd)
234 {
235 int i;
236 int j;
237 int len;
238 int sample;
239 const char *s;
240 char digit;
241 int actual;
242 int nplus;
243 int nminus;
244 float rrb;
245 float rcfo;
246 int16_t amp[100000];
247 r2_mf_rx_state_t mf_state;
248 awgn_state_t noise_source;
249 const mf_digit_tones_t *tone;
250
251 if (fwd)
252 tone = &r2_mf_fwd_tones[0];
253 else
254 tone = &r2_mf_back_tones[0];
255 r2_mf_rx_init(&mf_state, fwd, NULL, NULL);
256
257 /* Test 1: Mitel's test 1 isn't really a test. Its a calibration step,
258 which has no meaning here. */
259
260 printf ("Test 1: Calibration\n");
261 printf (" Passed\n");
262
263 /* Test 2: Decode check
264 This is a sanity check, that all digits are reliably detected
265 under ideal conditions. Each possible digit is repeated 10 times,
266 with 68ms bursts. The level of each tone is about 6dB down from clip */
267
268 printf ("Test 2: Decode check\n");
269 my_mf_gen_init(0.0, -3, 0.0, -3, 68, fwd);
270 s = r2_mf_tone_codes;
271 while (*s)
272 {
273 digit = *s++;
274 for (i = 0; i < 10; i++)
275 {
276 len = my_mf_generate(amp, digit);
277 codec_munge (amp, len);
278 r2_mf_rx(&mf_state, amp, len);
279 actual = r2_mf_rx_get(&mf_state);
280 if (actual != digit)
281 {
282 printf (" Sent '%c'\n", digit);
283 printf (" Received 0x%X\n", actual);
284 printf (" Failed\n");
285 exit (2);
286 }
287 }
288 }
289 printf (" Passed\n");
290
291 /* Test 3: Recognition bandwidth and channel centre frequency check.
292 Use all digits. Each digit types requires four tests to complete
293 the check. Each section contains 40 pulses of 68ms duration,
294 with an amplitude of -20dB from clip per frequency.
295
296 Four sections covering the tests for one tone (1 digit) are:
297 a. H frequency at 0% deviation from center, L frequency at +0.1%.
298 L frequency is then increments in +01.% steps up to +4%. The
299 number of tone bursts is noted and designated N+.
300 b. H frequency at 0% deviation, L frequency at -0.1%. L frequency
301 is then incremental in -0.1% steps, up to -4%. The number of
302 tone bursts is noted and designated N-.
303 c. The test in (a) is repeated with the L frequency at 0% and the
304 H frequency varied up to +4%.
305 d. The test in (b) is repeated with the L frequency and 0% and the
306 H frequency varied to -4%.
307
308 Receiver Recognition Bandwidth (RRB) is calculated as follows:
309 RRB% = (N+ + N-)/10
310 Receiver Center Frequency Offset (RCFO) is calculated as follows:
311 RCFO% = X + (N+ - N-)/20
312
313 Note that this test doesn't test what it says it is testing at all,
314 and the results are quite inaccurate, if not a downright lie! However,
315 it follows the Mitel procedure, so how can it be bad? :)
316
317 The spec calls for +-4 +-10Hz (ie +-14Hz) of bandwidth. */
318
319 printf ("Test 3: Recognition bandwidth and channel centre frequency check\n");
320 s = r2_mf_tone_codes;
321 j = 0;
322 while (*s)
323 {
324 digit = *s++;
325 for (nplus = 0, i = 1; i <= 60; i++)
326 {
327 my_mf_gen_init((float) i/1000.0, -17, 0.0, -17, 68, fwd);
328 len = my_mf_generate(amp, digit);
329 codec_munge(amp, len);
330 r2_mf_rx(&mf_state, amp, len);
331 if (r2_mf_rx_get(&mf_state) == digit)
332 nplus++;
333 }
334 for (nminus = 0, i = -1; i >= -60; i--)
335 {
336 my_mf_gen_init((float) i/1000.0, -17, 0.0, -17, 68, fwd);
337 len = my_mf_generate(amp, digit);
338 codec_munge(amp, len);
339 r2_mf_rx(&mf_state, amp, len);
340 if (r2_mf_rx_get(&mf_state) == digit)
341 nminus++;
342 }
343 rrb = (float) (nplus + nminus)/10.0;
344 rcfo = (float) (nplus - nminus)/10.0;
345 printf (" %c (low) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n",
346 digit,
347 rrb,
348 rcfo,
349 (float) nminus/10.0,
350 (float) nplus/10.0);
351
352 if (rrb < rcfo + (2.0*100.0*14.0/r2_mf_fwd_tones[j].f1) || rrb >= 15.0 + rcfo)
353 {
354 printf (" Failed\n");
355 exit (2);
356 }
357
358 for (nplus = 0, i = 1; i <= 60; i++)
359 {
360 my_mf_gen_init(0.0, -17, (float) i/1000.0, -17, 68, fwd);
361 len = my_mf_generate(amp, digit);
362 codec_munge(amp, len);
363 r2_mf_rx(&mf_state, amp, len);
364 if (r2_mf_rx_get(&mf_state) == digit)
365 nplus++;
366 }
367 for (nminus = 0, i = -1; i >= -60; i--)
368 {
369 my_mf_gen_init(0.0, -17, (float) i/1000.0, -17, 68, fwd);
370 len = my_mf_generate(amp, digit);
371 codec_munge(amp, len);
372 r2_mf_rx(&mf_state, amp, len);
373 if (r2_mf_rx_get(&mf_state) == digit)
374 nminus++;
375 }
376 rrb = (float) (nplus + nminus)/10.0;
377 rcfo = (float) (nplus - nminus)/10.0;
378 printf (" %c (high) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n",
379 digit,
380 rrb,
381 rcfo,
382 (float) nminus/10.0,
383 (float) nplus/10.0);
384 if (rrb < rcfo + (2.0*100.0*14.0/r2_mf_fwd_tones[j].f2) || rrb >= 15.0 + rcfo)
385 {
386 printf (" Failed\n");
387 exit (2);
388 }
389 j++;
390 }
391 printf (" Passed\n");
392
393 /* Test 4: Acceptable amplitude ratio (twist).
394 Twist all digits in both directions, and check the maximum twist
395 we can accept. The way this is done is styled after the Mitel DTMF
396 test, and has good and bad points. */
397
398 printf ("Test 4: Acceptable amplitude ratio (twist)\n");
399 s = r2_mf_tone_codes;
400 while (*s)
401 {
402 digit = *s++;
403 for (nplus = 0, i = -50; i >= -250; i--)
404 {
405 my_mf_gen_init(0.0, -5, 0.0, i/10, 68, fwd);
406
407 len = my_mf_generate(amp, digit);
408 codec_munge (amp, len);
409 r2_mf_rx(&mf_state, amp, len);
410 if (r2_mf_rx_get(&mf_state) == digit)
411 nplus++;
412 }
413 printf (" %c normal twist = %.2fdB\n", digit, (float) nplus/10.0);
414 if (nplus < 70)
415 {
416 printf (" Failed\n");
417 exit (2);
418 }
419 for (nminus = 0, i = -50; i >= -250; i--)
420 {
421 my_mf_gen_init(0.0, i/10, 0.0, -5, 68, fwd);
422
423 len = my_mf_generate(amp, digit);
424 codec_munge(amp, len);
425 r2_mf_rx(&mf_state, amp, len);
426 if (r2_mf_rx_get(&mf_state) == digit)
427 nminus++;
428 }
429 printf (" %c reverse twist = %.2fdB\n", digit, (float) nminus/10.0);
430 if (nminus < 70)
431 {
432 printf (" Failed\n");
433 exit (2);
434 }
435 }
436 printf (" Passed\n");
437
438 /* Test 5: Dynamic range
439 This test sends all possible digits, with gradually increasing
440 amplitude. We determine the span over which we achieve reliable
441 detection. */
442
443 printf ("Test 5: Dynamic range\n");
444 for (nplus = nminus = -1000, i = -50; i <= 3; i++)
445 {
446 s = r2_mf_tone_codes;
447 while (*s)
448 {
449 digit = *s++;
450 my_mf_gen_init(0.0, i, 0.0, i, 68, fwd);
451 for (j = 0; j < 100; j++)
452 {
453 len = my_mf_generate(amp, digit);
454 codec_munge(amp, len);
455 r2_mf_rx(&mf_state, amp, len);
456 if (r2_mf_rx_get(&mf_state) != digit)
457 break;
458 }
459 if (j < 100)
460 break;
461 }
462 if (j == 100)
463 {
464 if (nplus == -1000)
465 nplus = i;
466 }
467 else
468 {
469 if (nplus != -1000 && nminus == -1000)
470 nminus = i;
471 }
472 }
473 printf (" Dynamic range = %ddB to %ddB\n", nplus, nminus - 1);
474 if (nplus > -35 || nminus <= -5)
475 {
476 printf(" Failed\n");
477 exit(2);
478 }
479 printf (" Passed\n");
480
481 /* Test 6: Guard time
482 This test sends all possible digits, with a gradually reducing
483 duration. */
484
485 printf ("Test 6: Guard time\n");
486 for (i = 30; i < 62; i++)
487 {
488 s = r2_mf_tone_codes;
489 j = 0;
490 while (*s)
491 {
492 digit = *s++;
493 my_mf_gen_init(0.0, -5, 0.0, -3, i, fwd);
494 for (j = 0; j < 500; j++)
495 {
496 len = my_mf_generate(amp, digit);
497 codec_munge(amp, len);
498 r2_mf_rx(&mf_state, amp, len);
499 if (r2_mf_rx_get(&mf_state) != digit)
500 break;
501 }
502 if (j < 500)
503 break;
504 }
505 if (j == 500)
506 break;
507 }
508 printf (" Guard time = %dms\n", i);
509 if (i > 61)
510 {
511 printf(" Failed\n");
512 exit(2);
513 }
514 printf (" Passed\n");
515
516 /* Test 7: Acceptable signal to noise ratio
517 We send all possible digits at -6dBm from clip, mixed with AWGN.
518 We gradually reduce the noise until we get clean detection. */
519
520 printf ("Test 7: Acceptable signal to noise ratio\n");
521 my_mf_gen_init(0.0, -3, 0.0, -3, 68, fwd);
522 for (i = -3; i > -50; i--)
523 {
524 s = r2_mf_tone_codes;
525 while (*s)
526 {
527 digit = *s++;
528 awgn_init_dbm0(&noise_source, 1234567, (float) i);
529 for (j = 0; j < 500; j++)
530 {
531 len = my_mf_generate(amp, digit);
532 for (sample = 0; sample < len; sample++)
533 amp[sample] = saturate(amp[sample] + awgn(&noise_source));
534 codec_munge(amp, len);
535 r2_mf_rx(&mf_state, amp, len);
536 if (r2_mf_rx_get(&mf_state) != digit)
537 break;
538 }
539 if (j < 500)
540 break;
541 }
542 if (j == 500)
543 break;
544 }
545 printf(" Acceptable S/N ratio is %ddB\n", -3 - i);
546 if (-3 - i > 26)
547 {
548 printf(" Failed\n");
549 exit(2);
550 }
551 printf(" Passed\n");
552
553 printf("Test 8: Callback digit delivery mode.\n");
554 callback_ok = FALSE;
555 callback_roll = 0;
556 r2_mf_rx_init(&mf_state, fwd, digit_delivery, (void *) 0x12345678);
557 my_mf_gen_init(0.0, -3, 0.0, -3, 68, fwd);
558 s = r2_mf_tone_codes;
559 awgn_init_dbm0(&noise_source, 1234567, -40.0f);
560 while (*s)
561 {
562 digit = *s++;
563 len = my_mf_generate(amp, digit);
564 for (sample = 0; sample < len; sample++)
565 amp[sample] = saturate(amp[sample] + awgn(&noise_source));
566 codec_munge(amp, len);
567 r2_mf_rx(&mf_state, amp, len);
568 len = 160;
569 memset(amp, '\0', len*sizeof(int16_t));
570 for (sample = 0; sample < len; sample++)
571 amp[sample] = saturate(amp[sample] + awgn(&noise_source));
572 codec_munge(amp, len);
573 r2_mf_rx(&mf_state, amp, len);
574 }
575 if (!callback_ok)
576 {
577 printf(" Failed\n");
578 exit (2);
579 }
580 printf(" Passed\n");
581
582 /* The remainder of the Mitel tape is the talk-off test. This is
583 meaningless for R2 MF. However the decoder's tolerance of
584 out of band noise is significant. */
585 /* TODO: add a OOB tolerance test. */
586
587 return 0;
588 }
589 /*- End of function --------------------------------------------------------*/
590
591 int main(int argc, char *argv[])
592 {
593 time_t now;
594 time_t duration;
595
596 now = time(NULL);
597 printf("R2 forward tones\n");
598 test_a_tone_set(TRUE);
599 printf("R2 backward tones\n");
600 test_a_tone_set(FALSE);
601 duration = time(NULL) - now;
602 printf ("Tests passed in %lds\n", duration);
603 return 0;
604 }
605 /*- End of function --------------------------------------------------------*/
606 /*- End of file ------------------------------------------------------------*/

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