bell_mf_rx_tests.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. /*
  2. * SpanDSP - a series of DSP components for telephony
  3. *
  4. * bell_mf_tests.c - Test the Bell 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. /*! \file */
  27. /*! \page bell_mf_tests_page Bell MF tone generation and detection tests
  28. \section bell_mf_tests_page_sec_1 What does it do?
  29. These tests are fashioned after those on the CM7291 test tape from
  30. Mitel. Those tests are for DTMF, rather than Bell MF, but make a
  31. fair starting point for a set of meaningful tests of Bell MF.
  32. These tests include conversion to and from A-law. It is assumed the
  33. distortion this produces is comparable to u-law, so it should be
  34. a fair test of performance in a real PSTN channel.
  35. */
  36. #if defined(HAVE_CONFIG_H)
  37. #include "config.h"
  38. #endif
  39. #include <stdlib.h>
  40. #include <stdio.h>
  41. #include <fcntl.h>
  42. #include <string.h>
  43. #include <time.h>
  44. #include <sndfile.h>
  45. #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
  46. #include "spandsp.h"
  47. /* Basic Bell MF specs:
  48. *
  49. * Signal generation:
  50. * Tone on time = KP: 100+-7ms. All other signals: 68+-7ms
  51. * Tone off time (between digits) = 68+-7ms
  52. * Frequency tolerance +- 1.5%
  53. * Signal level -7+-1dBm per frequency
  54. *
  55. * Signal reception:
  56. * Frequency tolerance +- 1.5% +-10Hz
  57. * Signal level -14dBm to 0dBm
  58. * Perform a "two and only two tones present" test.
  59. * Twist <= 6dB accepted
  60. * Receiver sensitive to signals above -22dBm per frequency
  61. * Test for a minimum of 55ms if KP, or 30ms of other signals.
  62. * Signals to be recognised if the two tones arrive within 8ms of each other.
  63. * Invalid signals result in the return of the re-order tone.
  64. */
  65. #define MF_DURATION (68*8)
  66. #define MF_PAUSE (68*8)
  67. #define MF_CYCLE (MF_DURATION + MF_PAUSE)
  68. /*!
  69. MF tone descriptor for tests.
  70. */
  71. typedef struct
  72. {
  73. float f1; /* First freq */
  74. float f2; /* Second freq */
  75. int8_t level1; /* Level of the first freq (dB) */
  76. int8_t level2; /* Level of the second freq (dB) */
  77. uint8_t on_time; /* Tone on time (ms) */
  78. uint8_t off_time; /* Minimum post tone silence (ms) */
  79. } mf_digit_tones_t;
  80. static const mf_digit_tones_t bell_mf_tones[] =
  81. {
  82. { 700.0, 900.0, -7, -7, 68, 68},
  83. { 700.0, 1100.0, -7, -7, 68, 68},
  84. { 900.0, 1100.0, -7, -7, 68, 68},
  85. { 700.0, 1300.0, -7, -7, 68, 68},
  86. { 900.0, 1300.0, -7, -7, 68, 68},
  87. {1100.0, 1300.0, -7, -7, 68, 68},
  88. { 700.0, 1500.0, -7, -7, 68, 68},
  89. { 900.0, 1500.0, -7, -7, 68, 68},
  90. {1100.0, 1500.0, -7, -7, 68, 68},
  91. {1300.0, 1500.0, -7, -7, 68, 68},
  92. { 700.0, 1700.0, -7, -7, 68, 68}, /* ST''' - use 'C' */
  93. { 900.0, 1700.0, -7, -7, 68, 68}, /* ST' - use 'A' */
  94. {1100.0, 1700.0, -7, -7, 100, 68}, /* KP - use '*' */
  95. {1300.0, 1700.0, -7, -7, 68, 68}, /* ST'' - use 'B' */
  96. {1500.0, 1700.0, -7, -7, 68, 68}, /* ST - use '#' */
  97. {0.0, 0.0, 0, 0, 0, 0}
  98. };
  99. static tone_gen_descriptor_t my_mf_digit_tones[16];
  100. static char bell_mf_tone_codes[] = "1234567890CA*B#";
  101. bool callback_ok;
  102. int callback_roll;
  103. static void my_mf_gen_init(float low_fudge,
  104. int low_level,
  105. float high_fudge,
  106. int high_level,
  107. int duration,
  108. int gap)
  109. {
  110. int i;
  111. /* The fiddle factor on the tone duration is to make KP consistently
  112. 50% longer than the other digits, as the digit durations are varied
  113. for the tests. This is an approximation, as the real scaling should
  114. be 100/68 */
  115. for (i = 0; i < 15; i++)
  116. {
  117. tone_gen_descriptor_init(&my_mf_digit_tones[i],
  118. bell_mf_tones[i].f1*(1.0 + low_fudge),
  119. low_level,
  120. bell_mf_tones[i].f2*(1.0 + high_fudge),
  121. high_level,
  122. (i == 12) ? 3*duration/2 : duration,
  123. gap,
  124. 0,
  125. 0,
  126. false);
  127. }
  128. }
  129. /*- End of function --------------------------------------------------------*/
  130. static int my_mf_generate(int16_t amp[], const char *digits)
  131. {
  132. int len;
  133. char *cp;
  134. tone_gen_state_t tone;
  135. len = 0;
  136. while (*digits)
  137. {
  138. if ((cp = strchr(bell_mf_tone_codes, *digits)))
  139. {
  140. tone_gen_init(&tone, &my_mf_digit_tones[cp - bell_mf_tone_codes]);
  141. len += tone_gen(&tone, amp + len, 9999);
  142. }
  143. digits++;
  144. }
  145. return len;
  146. }
  147. /*- End of function --------------------------------------------------------*/
  148. static void codec_munge(int16_t amp[], int len)
  149. {
  150. int i;
  151. uint8_t alaw;
  152. for (i = 0; i < len; i++)
  153. {
  154. alaw = linear_to_alaw (amp[i]);
  155. amp[i] = alaw_to_linear (alaw);
  156. }
  157. }
  158. /*- End of function --------------------------------------------------------*/
  159. #define ALL_POSSIBLE_DIGITS "1234567890CA*B#"
  160. static void digit_delivery(void *data, const char *digits, int len)
  161. {
  162. int i;
  163. int seg;
  164. const char *s = ALL_POSSIBLE_DIGITS;
  165. const char *t;
  166. if (data != (void *) 0x12345678)
  167. {
  168. callback_ok = false;
  169. return;
  170. }
  171. callback_ok = true;
  172. t = s + callback_roll;
  173. seg = 15 - callback_roll;
  174. for (i = 0; i < len; i += seg, seg = 15)
  175. {
  176. if (i + seg > len)
  177. seg = len - i;
  178. if (memcmp(digits + i, t, seg))
  179. {
  180. callback_ok = false;
  181. printf("Fail at %d %d\n", i, seg);
  182. break;
  183. }
  184. t = s;
  185. callback_roll = (callback_roll + seg)%15;
  186. }
  187. }
  188. /*- End of function --------------------------------------------------------*/
  189. static int16_t amp[1000000];
  190. int main(int argc, char *argv[])
  191. {
  192. int duration;
  193. int i;
  194. int j;
  195. int len;
  196. int sample;
  197. const char *s;
  198. char digit[2];
  199. char buf[MAX_BELL_MF_DIGITS + 1];
  200. int actual;
  201. int nplus;
  202. int nminus;
  203. float rrb;
  204. float rcfo;
  205. time_t now;
  206. bell_mf_rx_state_t *mf_state;
  207. awgn_state_t noise_source;
  208. time(&now);
  209. mf_state = bell_mf_rx_init(NULL, NULL, NULL);
  210. /* Test 1: Mitel's test 1 isn't really a test. Its a calibration step,
  211. which has no meaning here. */
  212. printf("Test 1: Calibration\n");
  213. printf(" Passed\n");
  214. /* Test 2: Decode check
  215. This is a sanity check, that all digits are reliably detected
  216. under ideal conditions. Each possible digit is repeated 10 times,
  217. with 68ms bursts. The level of each tone is about 6dB down from clip */
  218. printf("Test 2: Decode check\n");
  219. my_mf_gen_init(0.0, -3, 0.0, -3, 68, 68);
  220. s = ALL_POSSIBLE_DIGITS;
  221. digit[1] = '\0';
  222. while (*s)
  223. {
  224. digit[0] = *s++;
  225. for (i = 0; i < 10; i++)
  226. {
  227. len = my_mf_generate(amp, digit);
  228. codec_munge(amp, len);
  229. bell_mf_rx(mf_state, amp, len);
  230. actual = bell_mf_rx_get(mf_state, buf, 128);
  231. if (actual != 1 || buf[0] != digit[0])
  232. {
  233. printf(" Sent '%s'\n", digit);
  234. printf(" Received '%s' [%d]\n", buf, actual);
  235. printf(" Failed\n");
  236. exit(2);
  237. }
  238. }
  239. }
  240. printf(" Passed\n");
  241. /* Test 3: Recognition bandwidth and channel centre frequency check.
  242. Use all digits. Each digit types requires four tests to complete
  243. the check. Each section contains 40 pulses of 68ms duration,
  244. with an amplitude of -20dB from clip per frequency.
  245. Four sections covering the tests for one tone (1 digit) are:
  246. a. H frequency at 0% deviation from center, L frequency at +0.1%.
  247. L frequency is then increments in +01.% steps up to +4%. The
  248. number of tone bursts is noted and designated N+.
  249. b. H frequency at 0% deviation, L frequency at -0.1%. L frequency
  250. is then incremental in -0.1% steps, up to -4%. The number of
  251. tone bursts is noted and designated N-.
  252. c. The test in (a) is repeated with the L frequency at 0% and the
  253. H frequency varied up to +4%.
  254. d. The test in (b) is repeated with the L frequency and 0% and the
  255. H frequency varied to -4%.
  256. Receiver Recognition Bandwidth (RRB) is calculated as follows:
  257. RRB% = (N+ + N-)/10
  258. Receiver Center Frequency Offset (RCFO) is calculated as follows:
  259. RCFO% = X + (N+ - N-)/20
  260. Note that this test doesn't test what it says it is testing at all,
  261. and the results are quite inaccurate, if not a downright lie! However,
  262. it follows the Mitel procedure, so how can it be bad? :)
  263. The spec calls for +-1.5% +-10Hz of bandwidth.
  264. */
  265. printf("Test 3: Recognition bandwidth and channel centre frequency check\n");
  266. s = ALL_POSSIBLE_DIGITS;
  267. digit[1] = '\0';
  268. j = 0;
  269. while (*s)
  270. {
  271. digit[0] = *s++;
  272. for (nplus = 0, i = 1; i <= 60; i++)
  273. {
  274. my_mf_gen_init((float) i/1000.0, -17, 0.0, -17, 68, 68);
  275. len = my_mf_generate(amp, digit);
  276. codec_munge(amp, len);
  277. bell_mf_rx(mf_state, amp, len);
  278. nplus += bell_mf_rx_get(mf_state, buf, 128);
  279. }
  280. for (nminus = 0, i = -1; i >= -60; i--)
  281. {
  282. my_mf_gen_init((float) i/1000.0, -17, 0.0, -17, 68, 68);
  283. len = my_mf_generate(amp, digit);
  284. codec_munge(amp, len);
  285. bell_mf_rx(mf_state, amp, len);
  286. nminus += bell_mf_rx_get(mf_state, buf, 128);
  287. }
  288. rrb = (float) (nplus + nminus)/10.0;
  289. rcfo = (float) (nplus - nminus)/10.0;
  290. printf(" %c (low) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n",
  291. digit[0],
  292. rrb,
  293. rcfo,
  294. (float) nminus/10.0,
  295. (float) nplus/10.0);
  296. if (rrb < 3.0 + rcfo + (2.0*100.0*10.0/bell_mf_tones[j].f1) || rrb >= 15.0 + rcfo)
  297. {
  298. printf(" Failed\n");
  299. exit(2);
  300. }
  301. for (nplus = 0, i = 1; i <= 60; i++)
  302. {
  303. my_mf_gen_init(0.0, -17, (float) i/1000.0, -17, 68, 68);
  304. len = my_mf_generate(amp, digit);
  305. codec_munge(amp, len);
  306. bell_mf_rx(mf_state, amp, len);
  307. nplus += bell_mf_rx_get(mf_state, buf, 128);
  308. }
  309. for (nminus = 0, i = -1; i >= -60; i--)
  310. {
  311. my_mf_gen_init(0.0, -17, (float) i/1000.0, -17, 68, 68);
  312. len = my_mf_generate(amp, digit);
  313. codec_munge(amp, len);
  314. bell_mf_rx(mf_state, amp, len);
  315. nminus += bell_mf_rx_get(mf_state, buf, 128);
  316. }
  317. rrb = (float) (nplus + nminus)/10.0;
  318. rcfo = (float) (nplus - nminus)/10.0;
  319. printf(" %c (high) rrb = %5.2f%%, rcfo = %5.2f%%, max -ve = %5.2f, max +ve = %5.2f\n",
  320. digit[0],
  321. rrb,
  322. rcfo,
  323. (float) nminus/10.0,
  324. (float) nplus/10.0);
  325. if (rrb < 3.0 + rcfo + (2.0*100.0*10.0/bell_mf_tones[j].f2) || rrb >= 15.0 + rcfo)
  326. {
  327. printf(" Failed\n");
  328. exit(2);
  329. }
  330. j++;
  331. }
  332. printf(" Passed\n");
  333. /* Test 4: Acceptable amplitude ratio (twist).
  334. Twist all digits in both directions, and check the maximum twist
  335. we can accept. The way this is done is styled after the Mitel DTMF
  336. test, and has good and bad points. */
  337. printf("Test 4: Acceptable amplitude ratio (twist)\n");
  338. s = ALL_POSSIBLE_DIGITS;
  339. digit[1] = '\0';
  340. while (*s)
  341. {
  342. digit[0] = *s++;
  343. for (nplus = 0, i = -50; i >= -250; i--)
  344. {
  345. my_mf_gen_init(0.0, -5, 0.0, i/10, 68, 68);
  346. len = my_mf_generate(amp, digit);
  347. codec_munge(amp, len);
  348. bell_mf_rx(mf_state, amp, len);
  349. nplus += bell_mf_rx_get(mf_state, buf, 128);
  350. }
  351. printf(" %c normal twist = %.2fdB\n", digit[0], (float) nplus/10.0);
  352. if (nplus < 60)
  353. {
  354. printf(" Failed\n");
  355. exit(2);
  356. }
  357. for (nminus = 0, i = -50; i >= -250; i--)
  358. {
  359. my_mf_gen_init(0.0, i/10, 0.0, -5, 68, 68);
  360. len = my_mf_generate(amp, digit);
  361. codec_munge(amp, len);
  362. bell_mf_rx(mf_state, amp, len);
  363. nminus += bell_mf_rx_get(mf_state, buf, 128);
  364. }
  365. printf(" %c reverse twist = %.2fdB\n", digit[0], (float) nminus/10.0);
  366. if (nminus < 60)
  367. {
  368. printf(" Failed\n");
  369. exit(2);
  370. }
  371. }
  372. printf(" Passed\n");
  373. /* Test 5: Dynamic range
  374. This test sends all possible digits, with gradually increasing
  375. amplitude. We determine the span over which we achieve reliable
  376. detection. The spec says we should detect between -14dBm and 0dBm,
  377. but the tones clip above -3dBm, so this cannot really work. */
  378. printf("Test 5: Dynamic range\n");
  379. for (nplus = nminus = -1000, i = -50; i <= 3; i++)
  380. {
  381. my_mf_gen_init(0.0, i, 0.0, i, 68, 68);
  382. for (j = 0; j < 100; j++)
  383. {
  384. len = my_mf_generate(amp, ALL_POSSIBLE_DIGITS);
  385. codec_munge(amp, len);
  386. bell_mf_rx(mf_state, amp, len);
  387. if (bell_mf_rx_get(mf_state, buf, 128) != 15)
  388. break;
  389. if (strcmp(buf, ALL_POSSIBLE_DIGITS) != 0)
  390. break;
  391. }
  392. if (j == 100)
  393. {
  394. if (nplus == -1000)
  395. nplus = i;
  396. }
  397. else
  398. {
  399. if (nplus != -1000 && nminus == -1000)
  400. nminus = i;
  401. }
  402. }
  403. printf(" Dynamic range = %ddB to %ddB\n", nplus, nminus - 1);
  404. if (nplus > -22 || nminus <= -3)
  405. {
  406. printf(" Failed\n");
  407. exit(2);
  408. }
  409. printf(" Passed\n");
  410. /* Test 6: Guard time
  411. This test sends all possible digits, with a gradually reducing
  412. duration. The spec defines a narrow range of tone duration
  413. times we can expect, so as long as we detect reliably at the
  414. specified minimum we should be OK. However, the spec also says
  415. we should detect on a minimum of 55ms of KP, or 30ms of other
  416. digits. */
  417. printf("Test 6: Guard time\n");
  418. for (i = 30; i < 62; i++)
  419. {
  420. my_mf_gen_init(0.0, -5, 0.0, -3, i, 68);
  421. for (j = 0; j < 500; j++)
  422. {
  423. len = my_mf_generate(amp, ALL_POSSIBLE_DIGITS);
  424. codec_munge(amp, len);
  425. bell_mf_rx(mf_state, amp, len);
  426. if (bell_mf_rx_get(mf_state, buf, 128) != 15)
  427. break;
  428. if (strcmp(buf, ALL_POSSIBLE_DIGITS) != 0)
  429. break;
  430. }
  431. if (j == 500)
  432. break;
  433. }
  434. printf(" Guard time = %dms\n", i);
  435. if (i > 61)
  436. {
  437. printf(" Failed\n");
  438. exit(2);
  439. }
  440. printf(" Passed\n");
  441. /* Test 7: Acceptable signal to noise ratio
  442. We send all possible digits at -6dBm from clip, mixed with AWGN.
  443. We gradually reduce the noise until we get clean detection. */
  444. printf("Test 7: Acceptable signal to noise ratio\n");
  445. my_mf_gen_init(0.0, -3, 0.0, -3, 68, 68);
  446. for (i = -10; i > -50; i--)
  447. {
  448. awgn_init_dbm0(&noise_source, 1234567, (float) i);
  449. for (j = 0; j < 500; j++)
  450. {
  451. len = my_mf_generate(amp, ALL_POSSIBLE_DIGITS);
  452. for (sample = 0; sample < len; sample++)
  453. amp[sample] = sat_add16(amp[sample], awgn(&noise_source));
  454. codec_munge(amp, len);
  455. bell_mf_rx(mf_state, amp, len);
  456. if (bell_mf_rx_get(mf_state, buf, 128) != 15)
  457. break;
  458. if (strcmp(buf, ALL_POSSIBLE_DIGITS) != 0)
  459. break;
  460. }
  461. if (j == 500)
  462. break;
  463. }
  464. printf(" Acceptable S/N ratio is %ddB\n", -3 - i);
  465. if (-3 - i > 26)
  466. {
  467. printf(" Failed\n");
  468. exit(2);
  469. }
  470. bell_mf_rx_free(mf_state);
  471. printf(" Passed\n");
  472. /* The remainder of the Mitel tape is the talk-off test. This is
  473. meaningless for Bell MF. However the decoder's tolerance of
  474. out of band noise is significant. */
  475. /* TODO: add a OOB tolerance test. */
  476. /* Test the callback mode for delivering detected digits */
  477. printf("Test: Callback digit delivery mode.\n");
  478. callback_ok = false;
  479. callback_roll = 0;
  480. mf_state = bell_mf_rx_init(NULL, digit_delivery, (void *) 0x12345678);
  481. my_mf_gen_init(0.0, -10, 0.0, -10, 68, 68);
  482. for (i = 1; i < 10; i++)
  483. {
  484. len = 0;
  485. for (j = 0; j < i; j++)
  486. len += my_mf_generate(amp + len, ALL_POSSIBLE_DIGITS);
  487. bell_mf_rx(mf_state, amp, len);
  488. if (!callback_ok)
  489. break;
  490. }
  491. if (!callback_ok)
  492. {
  493. printf(" Failed\n");
  494. exit(2);
  495. }
  496. bell_mf_rx_free(mf_state);
  497. printf(" Passed\n");
  498. duration = time (NULL) - now;
  499. printf("Tests passed in %ds\n", duration);
  500. return 0;
  501. }
  502. /*- End of function --------------------------------------------------------*/
  503. /*- End of file ------------------------------------------------------------*/