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