r2_mf_rx_tests.c 18 KB

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