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

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