super_tone_rx_tests.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. * SpanDSP - a series of DSP components for telephony
  3. *
  4. * super_tone_detect_tests.c
  5. *
  6. * Written by Steve Underwood <steveu@coppice.org>
  7. *
  8. * Copyright (C) 2003 Steve Underwood
  9. *
  10. * All rights reserved.
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License version 2, as
  14. * published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24. */
  25. /*! \file */
  26. /*! \page super_tone_rx_tests_page Supervisory tone detection tests
  27. \section super_tone_rx_tests_page_sec_1 What does it do?
  28. */
  29. #if defined(HAVE_CONFIG_H)
  30. #include "config.h"
  31. #endif
  32. #include <inttypes.h>
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <fcntl.h>
  36. #include <string.h>
  37. #include <strings.h>
  38. #include <ctype.h>
  39. #include <time.h>
  40. #include <sndfile.h>
  41. #if defined(HAVE_LIBXML_XMLMEMORY_H)
  42. #include <libxml/xmlmemory.h>
  43. #endif
  44. #if defined(HAVE_LIBXML_PARSER_H)
  45. #include <libxml/parser.h>
  46. #endif
  47. #if defined(HAVE_LIBXML_XINCLUDE_H)
  48. #include <libxml/xinclude.h>
  49. #endif
  50. #include "spandsp.h"
  51. #include "spandsp-sim.h"
  52. #define IN_FILE_NAME "super_tone.wav"
  53. #define MITEL_DIR "../test-data/mitel/"
  54. #define BELLCORE_DIR "../test-data/bellcore/"
  55. const char *bellcore_files[] =
  56. {
  57. MITEL_DIR "mitel-cm7291-talkoff.wav",
  58. BELLCORE_DIR "tr-tsy-00763-1.wav",
  59. BELLCORE_DIR "tr-tsy-00763-2.wav",
  60. BELLCORE_DIR "tr-tsy-00763-3.wav",
  61. BELLCORE_DIR "tr-tsy-00763-4.wav",
  62. BELLCORE_DIR "tr-tsy-00763-5.wav",
  63. BELLCORE_DIR "tr-tsy-00763-6.wav",
  64. ""
  65. };
  66. const char *tone_names[20] = {NULL};
  67. SNDFILE *inhandle;
  68. super_tone_rx_segment_t tone_segments[20][10];
  69. super_tone_tx_step_t *dialtone_tree = NULL;
  70. super_tone_tx_step_t *ringback_tree = NULL;
  71. super_tone_tx_step_t *busytone_tree = NULL;
  72. super_tone_tx_step_t *nutone_tree = NULL;
  73. super_tone_tx_step_t *congestiontone_tree = NULL;
  74. super_tone_tx_step_t *waitingtone_tree = NULL;
  75. int level;
  76. #define SAMPLES_PER_CHUNK 160
  77. #if defined(HAVE_LIBXML2)
  78. static int parse_tone(super_tone_rx_descriptor_t *desc, int tone_id, super_tone_tx_step_t **tree, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur)
  79. {
  80. xmlChar *x;
  81. float f1;
  82. float f2;
  83. float f_tol;
  84. float l1;
  85. float l2;
  86. float length;
  87. float length_tol;
  88. float recognition_length;
  89. float recognition_length_tol;
  90. int cycles;
  91. super_tone_tx_step_t *treep;
  92. int min_duration;
  93. int max_duration;
  94. cur = cur->xmlChildrenNode;
  95. while (cur)
  96. {
  97. if (xmlStrcmp(cur->name, (const xmlChar *) "step") == 0)
  98. {
  99. printf("Step - ");
  100. /* Set some defaults */
  101. f1 = 0.0;
  102. f2 = 0.0;
  103. f_tol = 1.0;
  104. l1 = -11.0;
  105. l2 = -11.0;
  106. length = 0.0;
  107. length_tol = 10.0;
  108. recognition_length = 0.0;
  109. recognition_length_tol = 10.0;
  110. cycles = 1;
  111. if ((x = xmlGetProp(cur, (const xmlChar *) "freq")))
  112. {
  113. sscanf((char *) x, "%f [%f%%]", &f1, &f_tol);
  114. sscanf((char *) x, "%f+%f [%f%%]", &f1, &f2, &f_tol);
  115. printf(" Frequency=%.2f+%.2f [%.2f%%]", f1, f2, f_tol);
  116. }
  117. if ((x = xmlGetProp(cur, (const xmlChar *) "level")))
  118. {
  119. if (sscanf((char *) x, "%f+%f", &l1, &l2) < 2)
  120. l2 = l1;
  121. printf(" Level=%.2f+%.2f", l1, l2);
  122. }
  123. if ((x = xmlGetProp(cur, (const xmlChar *) "length")))
  124. {
  125. sscanf((char *) x, "%f [%f%%]", &length, &length_tol);
  126. printf(" Length=%.2f [%.2f%%]", length, length_tol);
  127. }
  128. if ((x = xmlGetProp(cur, (const xmlChar *) "recognition-length")))
  129. {
  130. sscanf((char *) x, "%f [%f%%]", &recognition_length, &recognition_length_tol);
  131. printf(" Recognition length=%.2f [%.2f%%]", recognition_length, recognition_length_tol);
  132. }
  133. if ((x = xmlGetProp(cur, (const xmlChar *) "cycles")))
  134. {
  135. if (strcasecmp((char *) x, "endless") == 0)
  136. cycles = 0;
  137. else
  138. cycles = atoi((char *) x);
  139. printf(" Cycles='%d' ", cycles);
  140. }
  141. if ((x = xmlGetProp(cur, (const xmlChar *) "recorded-announcement")))
  142. printf(" Recorded announcement='%s'", x);
  143. printf("\n");
  144. if (f1 || f2 || length)
  145. {
  146. /* TODO: This cannot handle cycling patterns */
  147. if (length == 0.0)
  148. {
  149. if (recognition_length)
  150. min_duration = recognition_length*1000.0 + 0.5;
  151. else
  152. min_duration = 700;
  153. max_duration = 0;
  154. }
  155. else
  156. {
  157. if (recognition_length)
  158. min_duration = recognition_length*1000.0 + 0.5;
  159. else
  160. min_duration = (length*1000.0 + 0.5)*(1.0 - length_tol/100.0) - 30;
  161. max_duration = (length*1000.0 + 0.5)*(1.0 + length_tol/100.0) + 30;
  162. }
  163. printf(">>>Detector element %10d %10d %10d %10d\n", (int) (f1 + 0.5), (int) (f2 + 0.5), min_duration, max_duration);
  164. super_tone_rx_add_element(desc, tone_id, f1 + 0.5, f2 + 0.5, min_duration, max_duration);
  165. }
  166. treep = super_tone_tx_make_step(NULL,
  167. f1,
  168. l1,
  169. f2,
  170. l2,
  171. length*1000.0 + 0.5,
  172. cycles);
  173. *tree = treep;
  174. tree = &treep->next;
  175. parse_tone(desc, tone_id, &treep->nest, doc, ns, cur);
  176. }
  177. /*endif*/
  178. cur = cur->next;
  179. }
  180. /*endwhile*/
  181. return 0;
  182. }
  183. /*- End of function --------------------------------------------------------*/
  184. static void parse_tone_set(super_tone_rx_descriptor_t *desc, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur)
  185. {
  186. int tone_id;
  187. printf("Parsing tone set\n");
  188. cur = cur->xmlChildrenNode;
  189. while (cur)
  190. {
  191. if (strcmp((char *) cur->name, "dial-tone") == 0)
  192. {
  193. printf("Hit %s\n", cur->name);
  194. tone_id = super_tone_rx_add_tone(desc);
  195. dialtone_tree = NULL;
  196. parse_tone(desc, tone_id, &dialtone_tree, doc, ns, cur);
  197. tone_names[tone_id] = "Dial tone";
  198. }
  199. else if (strcmp((char *) cur->name, "ringback-tone") == 0)
  200. {
  201. printf("Hit %s\n", cur->name);
  202. tone_id = super_tone_rx_add_tone(desc);
  203. ringback_tree = NULL;
  204. parse_tone(desc, tone_id, &ringback_tree, doc, ns, cur);
  205. tone_names[tone_id] = "Ringback tone";
  206. }
  207. else if (strcmp((char *) cur->name, "busy-tone") == 0)
  208. {
  209. printf("Hit %s\n", cur->name);
  210. tone_id = super_tone_rx_add_tone(desc);
  211. busytone_tree = NULL;
  212. parse_tone(desc, tone_id, &busytone_tree, doc, ns, cur);
  213. tone_names[tone_id] = "Busy tone";
  214. }
  215. else if (strcmp((char *) cur->name, "number-unobtainable-tone") == 0)
  216. {
  217. printf("Hit %s\n", cur->name);
  218. tone_id = super_tone_rx_add_tone(desc);
  219. nutone_tree = NULL;
  220. parse_tone(desc, tone_id, &nutone_tree, doc, ns, cur);
  221. tone_names[tone_id] = "NU tone";
  222. }
  223. else if (strcmp((char *) cur->name, "congestion-tone") == 0)
  224. {
  225. printf("Hit %s\n", cur->name);
  226. tone_id = super_tone_rx_add_tone(desc);
  227. congestiontone_tree = NULL;
  228. parse_tone(desc, tone_id, &congestiontone_tree, doc, ns, cur);
  229. tone_names[tone_id] = "Congestion tone";
  230. }
  231. else if (strcmp((char *) cur->name, "waiting-tone") == 0)
  232. {
  233. printf("Hit %s\n", cur->name);
  234. tone_id = super_tone_rx_add_tone(desc);
  235. waitingtone_tree = NULL;
  236. parse_tone(desc, tone_id, &waitingtone_tree, doc, ns, cur);
  237. tone_names[tone_id] = "Waiting tone";
  238. }
  239. /*endif*/
  240. cur = cur->next;
  241. }
  242. /*endwhile*/
  243. }
  244. /*- End of function --------------------------------------------------------*/
  245. static void get_tone_set(super_tone_rx_descriptor_t *desc, const char *tone_file, const char *set_id)
  246. {
  247. xmlParserCtxtPtr ctxt;
  248. xmlDocPtr doc;
  249. xmlNsPtr ns;
  250. xmlNodePtr cur;
  251. xmlChar *x;
  252. ns = NULL;
  253. xmlKeepBlanksDefault(0);
  254. xmlCleanupParser();
  255. if ((ctxt = xmlNewParserCtxt()) == NULL)
  256. {
  257. fprintf(stderr, "Failed to allocate parser context\n");
  258. printf("Test failed\n");
  259. exit(2);
  260. }
  261. /* parse the file, activating the DTD validation option */
  262. if ((doc = xmlCtxtReadFile(ctxt, tone_file, NULL, XML_PARSE_XINCLUDE | XML_PARSE_DTDVALID)) == NULL)
  263. {
  264. fprintf(stderr, "Failed to read the XML document\n");
  265. printf("Test failed\n");
  266. exit(2);
  267. }
  268. if (ctxt->valid == 0)
  269. {
  270. fprintf(stderr, "Failed to validate the XML document\n");
  271. xmlFreeDoc(doc);
  272. xmlFreeParserCtxt(ctxt);
  273. printf("Test failed\n");
  274. exit(2);
  275. }
  276. xmlFreeParserCtxt(ctxt);
  277. /* Check the document is of the right kind */
  278. if ((cur = xmlDocGetRootElement(doc)) == NULL)
  279. {
  280. fprintf(stderr, "Empty document\n");
  281. xmlFreeDoc(doc);
  282. exit(2);
  283. }
  284. /*endif*/
  285. if (xmlStrcmp(cur->name, (const xmlChar *) "global-tones"))
  286. {
  287. fprintf(stderr, "Document of the wrong type, root node != global-tones");
  288. xmlFreeDoc(doc);
  289. exit(2);
  290. }
  291. /*endif*/
  292. cur = cur->xmlChildrenNode;
  293. while (cur && xmlIsBlankNode (cur))
  294. cur = cur->next;
  295. /*endwhile*/
  296. if (cur == NULL)
  297. exit(2);
  298. /*endif*/
  299. while (cur)
  300. {
  301. if (xmlStrcmp(cur->name, (const xmlChar *) "tone-set") == 0)
  302. {
  303. if ((x = xmlGetProp(cur, (const xmlChar *) "uncode")))
  304. {
  305. if (strcmp((char *) x, set_id) == 0)
  306. parse_tone_set(desc, doc, ns, cur);
  307. }
  308. /*endif*/
  309. }
  310. /*endif*/
  311. cur = cur->next;
  312. }
  313. /*endwhile*/
  314. xmlFreeDoc(doc);
  315. }
  316. /*- End of function --------------------------------------------------------*/
  317. #endif
  318. static void super_tone_rx_fill_descriptor(super_tone_rx_descriptor_t *desc)
  319. {
  320. int tone_id;
  321. tone_id = super_tone_rx_add_tone(desc);
  322. super_tone_rx_add_element(desc, tone_id, 400, 0, 700, 0);
  323. tone_names[tone_id] = "XXX";
  324. tone_id = super_tone_rx_add_tone(desc);
  325. super_tone_rx_add_element(desc, tone_id, 1100, 0, 400, 600);
  326. super_tone_rx_add_element(desc, tone_id, 0, 0, 2800, 3200);
  327. tone_names[tone_id] = "FAX tone";
  328. }
  329. /*- End of function --------------------------------------------------------*/
  330. static void wakeup(void *data, int code, int level, int delay)
  331. {
  332. if (code >= 0)
  333. printf("Current tone is %d '%s' '%s'\n", code, (tone_names[code]) ? tone_names[code] : "???", (char *) data);
  334. else
  335. printf("Tone off '%s'\n", (char *) data);
  336. }
  337. /*- End of function --------------------------------------------------------*/
  338. static void tone_segment(void *data, int f1, int f2, int duration)
  339. {
  340. if (f1 < 0)
  341. printf("Result %5d silence\n", duration);
  342. else if (f2 < 0)
  343. printf("Result %5d %4d\n", duration, f1);
  344. else
  345. printf("Result %5d %4d + %4d\n", duration, f1, f2);
  346. }
  347. /*- End of function --------------------------------------------------------*/
  348. static int talk_off_tests(super_tone_rx_state_t *super)
  349. {
  350. int16_t amp[8000];
  351. int sample;
  352. int frames;
  353. int j;
  354. int x;
  355. /* Test for voice immunity */
  356. printf("Talk off tests\n");
  357. for (j = 0; bellcore_files[j][0]; j++)
  358. {
  359. if ((inhandle = sf_open_telephony_read(bellcore_files[j], 1)) == NULL)
  360. {
  361. printf(" Cannot open audio file '%s'\n", bellcore_files[j]);
  362. exit(2);
  363. }
  364. while ((frames = sf_readf_short(inhandle, amp, 8000)))
  365. {
  366. for (sample = 0; sample < frames; )
  367. {
  368. x = super_tone_rx(super, amp + sample, frames - sample);
  369. sample += x;
  370. }
  371. }
  372. if (sf_close_telephony(inhandle))
  373. {
  374. printf(" Cannot close speech file '%s'\n", bellcore_files[j]);
  375. exit(2);
  376. }
  377. }
  378. return 0;
  379. }
  380. /*- End of function --------------------------------------------------------*/
  381. static int detection_range_tests(super_tone_rx_state_t *super)
  382. {
  383. int16_t amp[SAMPLES_PER_CHUNK];
  384. int i;
  385. int j;
  386. uint32_t phase;
  387. int32_t phase_inc;
  388. int scale;
  389. printf("Detection range tests\n");
  390. super_tone_rx_tone_callback(super, wakeup, (void *) "test");
  391. phase = 0;
  392. phase_inc = dds_phase_rate(440.0f);
  393. for (level = -80; level < 0; level++)
  394. {
  395. printf("Testing at %ddBm0\n", level);
  396. scale = dds_scaling_dbm0(level);
  397. for (j = 0; j < 100; j++)
  398. {
  399. for (i = 0; i < SAMPLES_PER_CHUNK; i++)
  400. amp[i] = (dds(&phase, phase_inc)*scale) >> 15;
  401. super_tone_rx(super, amp, SAMPLES_PER_CHUNK);
  402. }
  403. }
  404. return 0;
  405. }
  406. /*- End of function --------------------------------------------------------*/
  407. static int file_decode_tests(super_tone_rx_state_t *super, const char *file_name)
  408. {
  409. int16_t amp[8000];
  410. int sample;
  411. int frames;
  412. int x;
  413. awgn_state_t noise_source;
  414. printf("File decode tests\n");
  415. super_tone_rx_tone_callback(super, wakeup, (void *) "test");
  416. awgn_init_dbm0(&noise_source, 1234567, -30.0f);
  417. printf("Processing file\n");
  418. if ((inhandle = sf_open_telephony_read(file_name, 1)) == NULL)
  419. {
  420. fprintf(stderr, " Cannot open audio file '%s'\n", file_name);
  421. exit(2);
  422. }
  423. while ((frames = sf_readf_short(inhandle, amp, 8000)))
  424. {
  425. /* Add some noise to the signal for a more meaningful test. */
  426. //for (sample = 0; sample < frames; sample++)
  427. // amp[sample] += sat_add16(amp[sample], awgn (&noise_source));
  428. for (sample = 0; sample < frames; )
  429. {
  430. x = super_tone_rx(super, amp + sample, frames - sample);
  431. sample += x;
  432. }
  433. }
  434. if (sf_close_telephony(inhandle))
  435. {
  436. fprintf(stderr, " Cannot close audio file '%s'\n", file_name);
  437. exit(2);
  438. }
  439. return 0;
  440. }
  441. /*- End of function --------------------------------------------------------*/
  442. int main(int argc, char *argv[])
  443. {
  444. const char *file_name;
  445. super_tone_rx_state_t *super;
  446. super_tone_rx_descriptor_t desc;
  447. super_tone_rx_make_descriptor(&desc);
  448. #if defined(HAVE_LIBXML2)
  449. get_tone_set(&desc, "../spandsp/global-tones.xml", (argc > 1) ? argv[1] : "hk");
  450. #endif
  451. super_tone_rx_fill_descriptor(&desc);
  452. if ((super = super_tone_rx_init(NULL, &desc, wakeup, (void *) "test")) == NULL)
  453. {
  454. printf(" Failed to create detector.\n");
  455. exit(2);
  456. }
  457. super_tone_rx_segment_callback(super, tone_segment);
  458. detection_range_tests(super);
  459. file_name = IN_FILE_NAME;
  460. file_decode_tests(super, file_name);
  461. talk_off_tests(super);
  462. super_tone_rx_free(super);
  463. printf("Done\n");
  464. return 0;
  465. }
  466. /*- End of function --------------------------------------------------------*/
  467. /*- End of file ------------------------------------------------------------*/