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