1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * dtmf_rx_tests.c - Test the DTMF detector against the spec., whatever the spec.
5 * may be :)
6 *
7 * Written by Steve Underwood <steveu@coppice.org>
8 *
9 * Copyright (C) 2001, 2006 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
27 /*
28 * These tests include conversion to and from A-law. I assume the
29 * distortion this produces is comparable to u-law, so it should be
30 * a fair test.
31 *
32 * These tests mirror those on the CM7291 test tape from Mitel.
33 * Many of these tests are highly questionable, but they are a
34 * well accepted industry standard.
35 *
36 * However standard these tests might be, Mitel appears to have stopped
37 * selling copies of their tape.
38 *
39 * For the talk-off test the Bellcore tapes may be used. However, they are
40 * copyright material, so the test data files produced from the Bellcore
41 * tapes cannot be distributed as a part of this package.
42 *
43 */
44
45 /*! \page dtmf_rx_tests_page DTMF receiver tests
46 \section dtmf_rx_tests_page_sec_1 What does it do?
47
48 The DTMF detection test suite performs similar tests to the Mitel test tape,
49 traditionally used for testing DTMF receivers. Mitel seem to have discontinued
50 this product, but all it not lost.
51
52 The first side of the Mitel tape consists of a number of tone and tone+noise
53 based tests. The test suite synthesizes equivalent test data. Being digitally
54 generated, this data is rather more predictable than the test data on the nasty
55 old stretchy cassette tapes which Mitel sold.
56
57 The second side of the Mitel tape contains fragments of real speech from real
58 phone calls captured from the North American telephone network. These are
59 considered troublesome for DTMF detectors. A good detector is expected to
60 achieve a reasonably low number of false detections on this data. Fresh clean
61 copies of this seem to be unobtainable. However, Bellcore produce a much more
62 aggressive set of three cassette tapes. All six side (about 30 minutes each) are
63 filled with much tougher fragments of real speech from the North American
64 telephone network. If you can do well in this test, nobody cares about your
65 results against the Mitel test tape.
66
67 A fresh set of tapes was purchased for these tests, and digitised, producing 6
68 wave files of 16 bit signed PCM data, sampled at 8kHz. They were transcribed
69 using a speed adjustable cassette player. The test tone at the start of the
70 tapes is pretty accurate, and the new tapes should not have had much opportunity
71 to stretch. It is believed these transcriptions are about as good as the source
72 material permits.
73
74 PLEASE NOTE
75
76 These transcriptions may be freely used by anyone who has a legitimate copy of
77 the original tapes. However, if you don't have a legitimate copy of those tapes,
78 you also have no right to use this data. The original tapes are the copyright
79 material of BellCore, and they charge over US$200 for a set. I doubt they sell
80 enough copies to consider this much of a business. However, it is their data,
81 and it is their right to do as they wish with it. Currently I see no indication
82 they wish to give it away for free.
83 */
84
85 #if defined(HAVE_CONFIG_H)
86 #include "config.h"
87 #endif
88
89 #include <stdlib.h>
90 #include <string.h>
91 #include <stdio.h>
92 #include <fcntl.h>
93 #include <unistd.h>
94 #include <time.h>
95 #include <sndfile.h>
96
97 #include "spandsp.h"
98 #include "spandsp-sim.h"
99
100 /* Basic DTMF specs:
101 *
102 * Minimum tone on = 40ms
103 * Minimum tone off = 50ms
104 * Maximum digit rate = 10 per second
105 * Normal twist <= 8dB accepted
106 * Reverse twist <= 4dB accepted
107 * S/N >= 15dB will detect OK
108 * Attenuation <= 26dB will detect OK
109 * Frequency tolerance +- 1.5% will detect, +-3.5% will reject
110 */
111
112 #define DEFAULT_DTMF_TX_LEVEL -10
113 #define DEFAULT_DTMF_TX_ON_TIME 50
114 #define DEFAULT_DTMF_TX_OFF_TIME 50
115
116 #define SAMPLES_PER_CHUNK 160
117
118 #define ALL_POSSIBLE_DIGITS "123A456B789C*0#D"
119
120 #define MITEL_DIR "../test-data/mitel/"
121 #define BELLCORE_DIR "../test-data/bellcore/"
122
123 const char *bellcore_files[] =
124 {
125 MITEL_DIR "mitel-cm7291-talkoff.wav",
126 BELLCORE_DIR "tr-tsy-00763-1.wav",
127 BELLCORE_DIR "tr-tsy-00763-2.wav",
128 BELLCORE_DIR "tr-tsy-00763-3.wav",
129 BELLCORE_DIR "tr-tsy-00763-4.wav",
130 BELLCORE_DIR "tr-tsy-00763-5.wav",
131 BELLCORE_DIR "tr-tsy-00763-6.wav",
132 ""
133 };
134
135 static tone_gen_descriptor_t my_dtmf_digit_tones[16];
136
137 float dtmf_row[] =
138 {
139 697.0f, 770.0f, 852.0f, 941.0f
140 };
141 float dtmf_col[] =
142 {
143 1209.0f, 1336.0f, 1477.0f, 1633.0f
144 };
145
146 char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
147
148 bool callback_hit;
149 bool callback_ok;
150 int callback_roll;
151 int step;
152
153 float max_forward_twist;
154 float max_reverse_twist;
155
156 bool use_dialtone_filter = false;
157
158 char *decode_test_file = NULL;
159
160 static int16_t amp[1000000];
161 static int16_t amp2[1000000];
162
163 codec_munge_state_t *munge = NULL;
164
my_dtmf_gen_init(float low_fudge,int low_level,float high_fudge,int high_level,int duration,int gap)165 static void my_dtmf_gen_init(float low_fudge,
166 int low_level,
167 float high_fudge,
168 int high_level,
169 int duration,
170 int gap)
171 {
172 int row;
173 int col;
174
175 for (row = 0; row < 4; row++)
176 {
177 for (col = 0; col < 4; col++)
178 {
179 tone_gen_descriptor_init(&my_dtmf_digit_tones[row*4 + col],
180 dtmf_row[row]*(1.0f + low_fudge),
181 low_level,
182 dtmf_col[col]*(1.0f + high_fudge),
183 high_level,
184 duration,
185 gap,
186 0,
187 0,
188 false);
189 }
190 }
191 }
192 /*- End of function --------------------------------------------------------*/
193
my_dtmf_generate(int16_t amp[],const char * digits)194 static int my_dtmf_generate(int16_t amp[], const char *digits)
195 {
196 int len;
197 char *cp;
198 tone_gen_state_t tone;
199
200 len = 0;
201 while (*digits)
202 {
203 cp = strchr(dtmf_positions, *digits);
204 if (cp)
205 {
206 tone_gen_init(&tone, &my_dtmf_digit_tones[cp - dtmf_positions]);
207 len += tone_gen(&tone, amp + len, 1000);
208 }
209 digits++;
210 }
211 return len;
212 }
213 /*- End of function --------------------------------------------------------*/
214
digit_delivery(void * data,const char * digits,int len)215 static void digit_delivery(void *data, const char *digits, int len)
216 {
217 int i;
218 int seg;
219 const char *s = ALL_POSSIBLE_DIGITS;
220 const char *t;
221
222 callback_hit = true;
223 if (data == (void *) 0x12345678)
224 {
225 t = s + callback_roll;
226 seg = 16 - callback_roll;
227 for (i = 0; i < len; i += seg, seg = 16)
228 {
229 if (i + seg > len)
230 seg = len - i;
231 if (memcmp(digits + i, t, seg))
232 {
233 callback_ok = false;
234 printf("Fail at %d %d\n", i, seg);
235 break;
236 }
237 t = s;
238 callback_roll = (callback_roll + seg)%16;
239 }
240 }
241 else
242 {
243 callback_ok = false;
244 }
245 }
246 /*- End of function --------------------------------------------------------*/
247
digit_status(void * data,int signal,int level,int delay)248 static void digit_status(void *data, int signal, int level, int delay)
249 {
250 const char *s = ALL_POSSIBLE_DIGITS;
251 int len;
252 static int last_step = 0;
253 static int first = true;
254
255 //printf("Digit status %d %d %d\n", signal, level, delay);
256 callback_hit = true;
257 len = step - last_step;
258 if (data == (void *) 0x12345678)
259 {
260 if (len < 320 || len > 480)
261 {
262 if (first)
263 {
264 /* At the beginning the apparent duration is expected to be wrong */
265 first = false;
266 }
267 else
268 {
269 printf("Failed for signal %s length %d at %d\n", (callback_roll & 1) ? "on" : "off", len, step);
270 callback_ok = false;
271 }
272 }
273 if (callback_roll & 1)
274 {
275 if (signal != 0)
276 {
277 printf("Failed for signal 0x%X instead of 0\n", signal);
278 callback_ok = false;
279 }
280 }
281 else
282 {
283 if (signal != s[callback_roll >> 1])
284 {
285 printf("Failed for signal 0x%X instead of 0x%X\n", signal, s[callback_roll >> 1]);
286 callback_ok = false;
287 }
288 if (level < DEFAULT_DTMF_TX_LEVEL + 3 - 1 || level > DEFAULT_DTMF_TX_LEVEL + 3 + 1)
289 {
290 printf("Failed for level %d instead of %d\n", level, DEFAULT_DTMF_TX_LEVEL + 3);
291 callback_ok = false;
292 }
293 }
294 if (++callback_roll >= 32)
295 callback_roll = 0;
296 }
297 else
298 {
299 callback_ok = false;
300 }
301 last_step = step;
302 }
303 /*- End of function --------------------------------------------------------*/
304
mitel_cm7291_side_1_tests(void)305 static void mitel_cm7291_side_1_tests(void)
306 {
307 int i;
308 int j;
309 int len;
310 int sample;
311 const char *s;
312 char digit[2];
313 char buf[128 + 1];
314 int actual;
315 int nplus;
316 int nminus;
317 float rrb;
318 float rcfo;
319 dtmf_rx_state_t *dtmf_state;
320 awgn_state_t noise_source;
321 logging_state_t *logging;
322
323 dtmf_state = dtmf_rx_init(NULL, NULL, NULL);
324 if (use_dialtone_filter || max_forward_twist >= 0.0f || max_reverse_twist >= 0.0f)
325 dtmf_rx_parms(dtmf_state, use_dialtone_filter, max_forward_twist, max_reverse_twist, -99.0f);
326 logging = dtmf_rx_get_logging_state(dtmf_state);
327 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
328 span_log_set_tag(logging, "DTMF-rx");
329
330 /* Test 1: Mitel's test 1 isn't really a test. Its a calibration step,
331 which has no meaning here. */
332 printf("Test 1: Calibration\n");
333 printf(" Passed\n");
334
335 /* Test 2: Decode check
336 This is a sanity check, that all digits are reliably detected
337 under ideal conditions. Each possible digit repeated 10 times,
338 with 50ms bursts. The level of each tone is about 6dB down from clip.
339 6dB down actually causes trouble with G.726, so we use 7dB down. */
340 printf("Test 2: Decode check\n");
341 my_dtmf_gen_init(0.0f, -4, 0.0f, -4, 50, 50);
342 s = ALL_POSSIBLE_DIGITS;
343 digit[1] = '\0';
344 while (*s)
345 {
346 digit[0] = *s++;
347 for (i = 0; i < 10; i++)
348 {
349 len = my_dtmf_generate(amp, digit);
350 codec_munge(munge, amp, len);
351 dtmf_rx(dtmf_state, amp, len);
352
353 actual = dtmf_rx_get(dtmf_state, buf, 128);
354
355 if (actual != 1 || buf[0] != digit[0])
356 {
357 printf(" Sent '%s'\n", digit);
358 printf(" Received '%s'\n", buf);
359 printf(" Failed\n");
360 exit(2);
361 }
362 }
363 }
364 printf(" Passed\n");
365
366 /* Test 3: Recognition bandwidth and channel centre frequency check.
367 Use only the diagonal pairs of tones (digits 1, 5, 9 and D). Each
368 tone pair requires four test to complete the check, making 16
369 sections overall. Each section contains 40 pulses of
370 50ms duration, with an amplitude of -20dB from clip per
371 frequency.
372
373 Four sections covering the tests for one tone (1 digit) are:
374 a. H frequency at 0% deviation from center, L frequency at +0.1%.
375 L frequency is then increments in +01.% steps up to +4%. The
376 number of tone bursts is noted and designated N+.
377 b. H frequency at 0% deviation, L frequency at -0.1%. L frequency
378 is then incremental in -0.1% steps, up to -4%. The number of
379 tone bursts is noted and designated N-.
380 c. The test in (a) is repeated with the L frequency at 0% and the
381 H frequency varied up to +4%.
382 d. The test in (b) is repeated with the L frequency and 0% and the
383 H frequency varied to -4%.
384
385 Receiver Recognition Bandwidth (RRB) is calculated as follows:
386 RRB% = (N+ + N-)/10
387 Receiver Center Frequency Offset (RCFO) is calculated as follows:
388 RCFO% = X + (N+ - N-)/20
389
390 Note that this test doesn't test what it says it is testing at all,
391 and the results are quite inaccurate, if not a downright lie! However,
392 it follows the Mitel procedure, so how can it be bad? :)
393 */
394 printf("Test 3: Recognition bandwidth and channel centre frequency check\n");
395 s = "159D";
396 digit[1] = '\0';
397 while (*s)
398 {
399 digit[0] = *s++;
400 for (nplus = 0, i = 1; i <= 60; i++)
401 {
402 my_dtmf_gen_init((float) i/1000.0f, -17, 0.0f, -17, 50, 50);
403 len = my_dtmf_generate(amp, digit);
404 codec_munge(munge, amp, len);
405 dtmf_rx(dtmf_state, amp, len);
406 nplus += dtmf_rx_get(dtmf_state, buf, 128);
407 }
408 for (nminus = 0, i = -1; i >= -60; i--)
409 {
410 my_dtmf_gen_init((float) i/1000.0f, -17, 0.0f, -17, 50, 50);
411 len = my_dtmf_generate(amp, digit);
412 codec_munge(munge, amp, len);
413 dtmf_rx(dtmf_state, amp, len);
414 nminus += dtmf_rx_get(dtmf_state, buf, 128);
415 }
416 rrb = (float) (nplus + nminus)/10.0f;
417 rcfo = (float) (nplus - nminus)/10.0f;
418 printf(" %c (low) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n",
419 digit[0],
420 rrb,
421 rcfo,
422 (float) nminus/10.0f,
423 (float) nplus/10.0f);
424 if (rrb < 3.0f + rcfo || rrb >= 15.0f + rcfo)
425 {
426 printf(" Failed\n");
427 exit(2);
428 }
429
430 for (nplus = 0, i = 1; i <= 60; i++)
431 {
432 my_dtmf_gen_init(0.0f, -17, (float) i/1000.0f, -17, 50, 50);
433 len = my_dtmf_generate(amp, digit);
434 codec_munge(munge, amp, len);
435 dtmf_rx(dtmf_state, amp, len);
436 nplus += dtmf_rx_get(dtmf_state, buf, 128);
437 }
438 for (nminus = 0, i = -1; i >= -60; i--)
439 {
440 my_dtmf_gen_init(0.0f, -17, (float) i/1000.0f, -17, 50, 50);
441 len = my_dtmf_generate(amp, digit);
442 codec_munge(munge, amp, len);
443 dtmf_rx(dtmf_state, amp, len);
444 nminus += dtmf_rx_get(dtmf_state, buf, 128);
445 }
446 rrb = (float) (nplus + nminus)/10.0f;
447 rcfo = (float) (nplus - nminus)/10.0f;
448 printf(" %c (high) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n",
449 digit[0],
450 rrb,
451 rcfo,
452 (float) nminus/10.0f,
453 (float) nplus/10.0f);
454 if (rrb < 3.0f + rcfo || rrb >= 15.0f + rcfo)
455 {
456 printf(" Failed\n");
457 exit(2);
458 }
459 }
460 printf(" Passed\n");
461
462 /* Test 4: Acceptable amplitude ratio (twist).
463 Use only the diagonal pairs of tones (digits 1, 5, 9 and D).
464 There are eight sections to the test. Each section contains 200
465 pulses with a 50ms duration for each pulse. Initially the amplitude
466 of both tones is 6dB down from clip. The two sections to test one
467 tone pair are:
468
469 a. Standard Twist: H tone amplitude is maintained at -6dB from clip,
470 L tone amplitude is attenuated gradually until the amplitude ratio
471 L/H is -20dB. Note the number of responses from the receiver.
472 b. Reverse Twist: L tone amplitude is maintained at -6dB from clip,
473 H tone amplitude is attenuated gradually until the amplitude ratio
474 is 20dB. Note the number of responses from the receiver.
475
476 All tone bursts are of 50ms duration.
477
478 The Acceptable Amplitude Ratio in dB is equal to the number of
479 responses registered in (a) or (b), divided by 10.
480
481 TODO: This is supposed to work in 1/10dB steps, but here I used 1dB
482 steps, as the current tone generator has its amplitude set in
483 1dB steps.
484 */
485 printf("Test 4: Acceptable amplitude ratio (twist)\n");
486 s = "159D";
487 digit[1] = '\0';
488 while (*s)
489 {
490 digit[0] = *s++;
491 for (nplus = 0, i = -30; i >= -230; i--)
492 {
493 my_dtmf_gen_init(0.0f, -3, 0.0f, i/10, 50, 50);
494
495 len = my_dtmf_generate(amp, digit);
496 codec_munge(munge, amp, len);
497 dtmf_rx(dtmf_state, amp, len);
498 nplus += dtmf_rx_get(dtmf_state, buf, 128);
499 }
500 printf(" %c normal twist = %.2fdB\n", digit[0], (float) nplus/10.0);
501 if (nplus < 80)
502 {
503 printf(" Failed\n");
504 exit(2);
505 }
506 for (nminus = 0, i = -30; i >= -230; i--)
507 {
508 my_dtmf_gen_init(0.0f, i/10, 0.0f, -3, 50, 50);
509
510 len = my_dtmf_generate(amp, digit);
511 codec_munge(munge, amp, len);
512 dtmf_rx(dtmf_state, amp, len);
513 nminus += dtmf_rx_get(dtmf_state, buf, 128);
514 }
515 printf(" %c reverse twist = %.2fdB\n", digit[0], (float) nminus/10.0);
516 if (nminus < 40)
517 {
518 printf(" Failed\n");
519 exit(2);
520 }
521 }
522 printf(" Passed\n");
523
524 /* Test 5: Dynamic range
525 This test utilizes tone pair L1 H1 (digit 1). Thirty-five tone pair
526 pulses are transmitted, with both frequencies stating at -6dB from
527 clip. The amplitude of each is gradually attenuated by -35dB at a
528 rate of 1dB per pulse. The Dynamic Range in dB is equal to the
529 number of responses from the receiver during the test.
530
531 Well not really, but that is the Mitel test. Lets sweep a bit further,
532 and see what the real range is */
533 printf("Test 5: Dynamic range\n");
534 for (nplus = 0, i = +3; i >= -50; i--)
535 {
536 my_dtmf_gen_init(0.0f, i, 0.0f, i, 50, 50);
537
538 len = my_dtmf_generate(amp, "1");
539 codec_munge(munge, amp, len);
540 dtmf_rx(dtmf_state, amp, len);
541 nplus += dtmf_rx_get(dtmf_state, buf, 128);
542 }
543 printf(" Dynamic range = %ddB\n", nplus);
544 /* We ought to set some pass/fail condition, even if Mitel did not. If
545 we don't, regression testing is weakened. */
546 if (nplus < 35)
547 {
548 printf(" Failed\n");
549 exit(2);
550 }
551 printf(" Passed\n");
552
553 /* Test 6: Guard time
554 This test utilizes tone pair L1 H1 (digit 1). Four hundred pulses
555 are transmitted at an amplitude of -6dB from clip per frequency.
556 Pulse duration starts at 49ms and is gradually reduced to 10ms.
557 Guard time in ms is equal to (500 - number of responses)/10.
558
559 That is the Mitel test, and we will follow it. Its totally bogus,
560 though. Just what the heck is a pass or fail here? */
561
562 printf("Test 6: Guard time\n");
563 for (nplus = 0, i = 490; i >= 100; i--)
564 {
565 my_dtmf_gen_init(0.0f, -3, 0.0f, -3, i/10, 50);
566
567 len = my_dtmf_generate(amp, "1");
568 codec_munge(munge, amp, len);
569 dtmf_rx(dtmf_state, amp, len);
570 nplus += dtmf_rx_get(dtmf_state, buf, 128);
571 }
572 printf(" Guard time = %dms\n", (500 - nplus)/10);
573 printf(" Passed\n");
574
575 /* Test 7: Acceptable signal to noise ratio
576 This test utilizes tone pair L1 H1, transmitted on a noise background.
577 The test consists of three sections in which the tone pair is
578 transmitted 1000 times at an amplitude -6dB from clip per frequency,
579 but with a different white noise level for each section. The first
580 level is -24dBV, the second -18dBV and the third -12dBV.. The
581 acceptable signal to noise ratio is the lowest ratio of signal
582 to noise in the test where the receiver responds to all 1000 pulses.
583
584 Well, that is the Mitel test, but it doesn't tell you what the
585 decoder can really do. Lets do a more comprehensive test */
586
587 printf("Test 7: Acceptable signal to noise ratio\n");
588 my_dtmf_gen_init(0.0f, -4, 0.0f, -4, 50, 50);
589
590 for (j = -13; j > -50; j--)
591 {
592 awgn_init_dbm0(&noise_source, 1234567, (float) j);
593 for (i = 0; i < 1000; i++)
594 {
595 len = my_dtmf_generate(amp, "1");
596
597 // TODO: Clip
598 for (sample = 0; sample < len; sample++)
599 amp[sample] = sat_add16(amp[sample], awgn(&noise_source));
600
601 codec_munge(munge, amp, len);
602 dtmf_rx(dtmf_state, amp, len);
603
604 if (dtmf_rx_get(dtmf_state, buf, 128) != 1)
605 break;
606 }
607 if (i == 1000)
608 break;
609 }
610 printf(" Acceptable S/N ratio is %ddB\n", -4 - j);
611 if (-4 - j > 26)
612 {
613 printf(" Failed\n");
614 exit(2);
615 }
616 dtmf_rx_free(dtmf_state);
617 printf(" Passed\n");
618 }
619 /*- End of function --------------------------------------------------------*/
620
mitel_cm7291_side_2_and_bellcore_tests(void)621 static void mitel_cm7291_side_2_and_bellcore_tests(void)
622 {
623 int i;
624 int j;
625 int len;
626 int hits;
627 int hit_types[256];
628 char buf[128 + 1];
629 SNDFILE *inhandle;
630 int frames;
631 dtmf_rx_state_t *dtmf_state;
632 logging_state_t *logging;
633
634 dtmf_state = dtmf_rx_init(NULL, NULL, NULL);
635 if (use_dialtone_filter || max_forward_twist >= 0.0f || max_reverse_twist >= 0.0f)
636 dtmf_rx_parms(dtmf_state, use_dialtone_filter, max_forward_twist, max_reverse_twist, -99.0f);
637 logging = dtmf_rx_get_logging_state(dtmf_state);
638 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
639 span_log_set_tag(logging, "DTMF-rx");
640
641 /* The remainder of the Mitel tape is the talk-off test */
642 /* Here we use the Bellcore test tapes (much tougher), in six
643 files - 1 from each side of the original 3 cassette tapes */
644 /* Bellcore say you should get no more than 470 false detections with
645 a good receiver. Dialogic claim 20. Of course, we can do better than
646 that, eh? */
647 printf("Test 8: Talk-off test\n");
648 memset(hit_types, '\0', sizeof(hit_types));
649 for (j = 0; bellcore_files[j][0]; j++)
650 {
651 if ((inhandle = sf_open_telephony_read(bellcore_files[j], 1)) == NULL)
652 {
653 printf(" Cannot open speech file '%s'\n", bellcore_files[j]);
654 exit(2);
655 }
656 hits = 0;
657 while ((frames = sf_readf_short(inhandle, amp, SAMPLE_RATE)))
658 {
659 dtmf_rx(dtmf_state, amp, frames);
660 len = dtmf_rx_get(dtmf_state, buf, 128);
661 if (len > 0)
662 {
663 for (i = 0; i < len; i++)
664 hit_types[(int) buf[i]]++;
665 hits += len;
666 }
667 }
668 if (sf_close_telephony(inhandle))
669 {
670 printf(" Cannot close speech file '%s'\n", bellcore_files[j]);
671 exit(2);
672 }
673 printf(" File %d gave %d false hits.\n", j + 1, hits);
674 }
675 for (i = 0, j = 0; i < 256; i++)
676 {
677 if (hit_types[i])
678 {
679 printf(" Digit %c had %d false hits.\n", i, hit_types[i]);
680 j += hit_types[i];
681 }
682 }
683 printf(" %d false hits in total.\n", j);
684 if (j > 470)
685 {
686 printf(" Failed\n");
687 exit(2);
688 }
689 printf(" Passed\n");
690 dtmf_rx_free(dtmf_state);
691 }
692 /*- End of function --------------------------------------------------------*/
693
dial_tone_tolerance_tests(void)694 static void dial_tone_tolerance_tests(void)
695 {
696 int i;
697 int j;
698 int len;
699 int sample;
700 char buf[128 + 1];
701 dtmf_rx_state_t *dtmf_state;
702 tone_gen_descriptor_t dial_tone_desc;
703 tone_gen_state_t dial_tone;
704 logging_state_t *logging;
705
706 dtmf_state = dtmf_rx_init(NULL, NULL, NULL);
707 if (use_dialtone_filter || max_forward_twist >= 0.0f || max_reverse_twist >= 0.0f)
708 dtmf_rx_parms(dtmf_state, use_dialtone_filter, max_forward_twist, max_reverse_twist, -99.0f);
709 logging = dtmf_rx_get_logging_state(dtmf_state);
710 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
711 span_log_set_tag(logging, "DTMF-rx");
712
713 /* Test dial tone tolerance */
714 printf("Test: Dial tone tolerance.\n");
715 my_dtmf_gen_init(0.0f, -15, 0.0f, -15, DEFAULT_DTMF_TX_ON_TIME, DEFAULT_DTMF_TX_OFF_TIME);
716
717 for (j = -30; j < -3; j++)
718 {
719 tone_gen_descriptor_init(&dial_tone_desc, 350, j, 440, j, 1, 0, 0, 0, true);
720 tone_gen_init(&dial_tone, &dial_tone_desc);
721 for (i = 0; i < 10; i++)
722 {
723 len = my_dtmf_generate(amp, ALL_POSSIBLE_DIGITS);
724 tone_gen(&dial_tone, amp2, len);
725
726 for (sample = 0; sample < len; sample++)
727 amp[sample] = sat_add16(amp[sample], amp2[sample]);
728 codec_munge(munge, amp, len);
729 dtmf_rx(dtmf_state, amp, len);
730
731 if (dtmf_rx_get(dtmf_state, buf, 128) != strlen(ALL_POSSIBLE_DIGITS))
732 break;
733 }
734 if (i != 10)
735 break;
736 }
737 printf(" Acceptable signal to dial tone ratio is %ddB\n", -15 - j);
738 if ((use_dialtone_filter && (-15 - j) > -12)
739 ||
740 (!use_dialtone_filter && (-15 - j) > 10))
741 {
742 printf(" Failed\n");
743 exit(2);
744 }
745 printf(" Passed\n");
746 dtmf_rx_free(dtmf_state);
747 }
748 /*- End of function --------------------------------------------------------*/
749
callback_function_tests(void)750 static void callback_function_tests(void)
751 {
752 int i;
753 int j;
754 int len;
755 int sample;
756 dtmf_rx_state_t *dtmf_state;
757 logging_state_t *logging;
758
759 /* Test the callback mode for delivering detected digits */
760 printf("Test: Callback digit delivery mode.\n");
761 callback_hit = false;
762 callback_ok = true;
763 callback_roll = 0;
764 dtmf_state = dtmf_rx_init(NULL, digit_delivery, (void *) 0x12345678);
765 if (use_dialtone_filter || max_forward_twist >= 0.0f || max_reverse_twist >= 0.0f)
766 dtmf_rx_parms(dtmf_state, use_dialtone_filter, max_forward_twist, max_reverse_twist, -99.0f);
767 logging = dtmf_rx_get_logging_state(dtmf_state);
768 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
769 span_log_set_tag(logging, "DTMF-rx");
770
771 my_dtmf_gen_init(0.0f, DEFAULT_DTMF_TX_LEVEL, 0.0f, DEFAULT_DTMF_TX_LEVEL, DEFAULT_DTMF_TX_ON_TIME, DEFAULT_DTMF_TX_OFF_TIME);
772 for (i = 1; i < 10; i++)
773 {
774 len = 0;
775 for (j = 0; j < i; j++)
776 len += my_dtmf_generate(amp + len, ALL_POSSIBLE_DIGITS);
777 dtmf_rx(dtmf_state, amp, len);
778 if (!callback_hit || !callback_ok)
779 break;
780 }
781 if (!callback_hit || !callback_ok)
782 {
783 printf(" Failed\n");
784 exit(2);
785 }
786 printf(" Passed\n");
787
788 /* Test the realtime callback mode for reporting detected digits */
789 printf("Test: Realtime callback digit delivery mode.\n");
790 callback_hit = false;
791 callback_ok = true;
792 callback_roll = 0;
793 dtmf_rx_init(dtmf_state, NULL, NULL);
794 dtmf_rx_set_realtime_callback(dtmf_state, digit_status, (void *) 0x12345678);
795 if (use_dialtone_filter || max_forward_twist >= 0.0f || max_reverse_twist >= 0.0f)
796 dtmf_rx_parms(dtmf_state, use_dialtone_filter, max_forward_twist, max_reverse_twist, -99.0f);
797 logging = dtmf_rx_get_logging_state(dtmf_state);
798 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
799 span_log_set_tag(logging, "DTMF-rx");
800
801 my_dtmf_gen_init(0.0f, DEFAULT_DTMF_TX_LEVEL, 0.0f, DEFAULT_DTMF_TX_LEVEL, DEFAULT_DTMF_TX_ON_TIME, DEFAULT_DTMF_TX_OFF_TIME);
802 step = 0;
803 for (i = 1; i < 10; i++)
804 {
805 len = 0;
806 for (j = 0; j < i; j++)
807 len += my_dtmf_generate(amp + len, ALL_POSSIBLE_DIGITS);
808 for (sample = 0, j = SAMPLES_PER_CHUNK; sample < len; sample += SAMPLES_PER_CHUNK, j = ((len - sample) >= SAMPLES_PER_CHUNK) ? SAMPLES_PER_CHUNK : (len - sample))
809 {
810 dtmf_rx(dtmf_state, &[sample], j);
811 if (!callback_ok)
812 break;
813 step += j;
814 }
815 if (!callback_hit || !callback_ok)
816 break;
817 }
818 if (!callback_hit || !callback_ok)
819 {
820 printf(" Failed\n");
821 exit(2);
822 }
823 dtmf_rx_free(dtmf_state);
824 }
825 /*- End of function --------------------------------------------------------*/
826
decode_test(const char * test_file)827 static void decode_test(const char *test_file)
828 {
829 int16_t amp[SAMPLES_PER_CHUNK];
830 SNDFILE *inhandle;
831 dtmf_rx_state_t *dtmf_state;
832 char buf[128 + 1];
833 int actual;
834 int samples;
835 int total;
836 logging_state_t *logging;
837
838 dtmf_state = dtmf_rx_init(NULL, NULL, NULL);
839 if (use_dialtone_filter || max_forward_twist >= 0.0f || max_reverse_twist >= 0.0f)
840 dtmf_rx_parms(dtmf_state, use_dialtone_filter, max_forward_twist, max_reverse_twist, -99.0f);
841 logging = dtmf_rx_get_logging_state(dtmf_state);
842 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
843 span_log_set_tag(logging, "DTMF-rx");
844
845 /* We will decode the audio from a file. */
846
847 if ((inhandle = sf_open_telephony_read(decode_test_file, 1)) == NULL)
848 {
849 fprintf(stderr, " Cannot open audio file '%s'\n", decode_test_file);
850 exit(2);
851 }
852
853 total = 0;
854 while ((samples = sf_readf_short(inhandle, amp, SAMPLES_PER_CHUNK)) > 0)
855 {
856 codec_munge(munge, amp, samples);
857 dtmf_rx(dtmf_state, amp, samples);
858 //printf("Status 0x%X\n", dtmf_rx_status(dtmf_state));
859 if ((actual = dtmf_rx_get(dtmf_state, buf, 128)) > 0)
860 printf("Received '%s'\n", buf);
861 total += actual;
862 }
863 printf("%d digits received\n", total);
864 }
865 /*- End of function --------------------------------------------------------*/
866
main(int argc,char * argv[])867 int main(int argc, char *argv[])
868 {
869 int duration;
870 time_t now;
871 int channel_codec;
872 int opt;
873
874 use_dialtone_filter = false;
875 channel_codec = MUNGE_CODEC_NONE;
876 decode_test_file = NULL;
877 max_forward_twist = -1.0f;
878 max_reverse_twist = -1.0f;
879 while ((opt = getopt(argc, argv, "c:d:F:fR:")) != -1)
880 {
881 switch (opt)
882 {
883 case 'c':
884 channel_codec = atoi(optarg);
885 break;
886 case 'd':
887 decode_test_file = optarg;
888 break;
889 case 'F':
890 max_forward_twist = atof(optarg);
891 break;
892 case 'f':
893 use_dialtone_filter = true;
894 break;
895 case 'R':
896 max_reverse_twist = atof(optarg);
897 break;
898 default:
899 //usage();
900 exit(2);
901 break;
902 }
903 }
904 munge = codec_munge_init(channel_codec, 0);
905
906 if (decode_test_file)
907 {
908 decode_test(decode_test_file);
909 }
910 else
911 {
912 time(&now);
913 mitel_cm7291_side_1_tests();
914 mitel_cm7291_side_2_and_bellcore_tests();
915 dial_tone_tolerance_tests();
916 callback_function_tests();
917 printf(" Passed\n");
918 duration = time(NULL) - now;
919 printf("Tests passed in %ds\n", duration);
920 }
921
922 codec_munge_free(munge);
923 return 0;
924 }
925 /*- End of function --------------------------------------------------------*/
926 /*- End of file ------------------------------------------------------------*/
927