ademco_contactid_tests.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. * SpanDSP - a series of DSP components for telephony
  3. *
  4. * ademco_contactid.c - Ademco ContactID alarm protocol
  5. *
  6. * Written by Steve Underwood <steveu@coppice.org>
  7. *
  8. * Copyright (C) 2012 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 Lesser General Public License version 2.1,
  14. * as 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 Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24. */
  25. /*! \page ademco_contactid_tests_page Ademco ContactID tests
  26. \section ademco_contactid_tests_page_sec_1 What does it do?
  27. \section ademco_contactid_tests_page_sec_2 How does it work?
  28. */
  29. #if defined(HAVE_CONFIG_H)
  30. #include "config.h"
  31. #endif
  32. #include <unistd.h>
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #include <assert.h>
  37. #include <sndfile.h>
  38. #include "spandsp.h"
  39. #include "spandsp-sim.h"
  40. #define SAMPLES_PER_CHUNK 160
  41. #define OUTPUT_FILE_NAME "ademco_contactid.wav"
  42. #define MITEL_DIR "../test-data/mitel/"
  43. #define BELLCORE_DIR "../test-data/bellcore/"
  44. const char *bellcore_files[] =
  45. {
  46. MITEL_DIR "mitel-cm7291-talkoff.wav",
  47. BELLCORE_DIR "tr-tsy-00763-1.wav",
  48. BELLCORE_DIR "tr-tsy-00763-2.wav",
  49. BELLCORE_DIR "tr-tsy-00763-3.wav",
  50. BELLCORE_DIR "tr-tsy-00763-4.wav",
  51. BELLCORE_DIR "tr-tsy-00763-5.wav",
  52. BELLCORE_DIR "tr-tsy-00763-6.wav",
  53. ""
  54. };
  55. static const ademco_contactid_report_t reports[] =
  56. {
  57. {0x1234, 0x18, 0x1, 0x131, 0x1, 0x15},
  58. {0x1234, 0x18, 0x3, 0x131, 0x1, 0x15},
  59. {0x1234, 0x18, 0x1, 0x401, 0x2, 0x3},
  60. {0x1234, 0x18, 0x3, 0x401, 0x3, 0x5},
  61. {0x1234, 0x56, 0x7, 0x890, 0xBC, 0xDEF},
  62. {0x1234, 0x56, 0x7, 0x89A, 0xBC, 0xDEF} /* This one is bad, as it contains a hex 'A' */
  63. };
  64. static int reports_entry = 0;
  65. static int16_t amp[1000000];
  66. bool tx_callback_reported = false;
  67. bool rx_callback_reported = false;
  68. bool sending_complete = false;
  69. SNDFILE *outhandle;
  70. static void talkoff_tx_callback(void *user_data, int tone, int level, int duration)
  71. {
  72. printf("Ademco sender report %d\n", tone);
  73. tx_callback_reported = true;
  74. }
  75. static int mitel_cm7291_side_2_and_bellcore_tests(void)
  76. {
  77. int j;
  78. SNDFILE *inhandle;
  79. int frames;
  80. ademco_contactid_sender_state_t *sender;
  81. logging_state_t *logging;
  82. if ((sender = ademco_contactid_sender_init(NULL, talkoff_tx_callback, NULL)) == NULL)
  83. return -1;
  84. logging = ademco_contactid_sender_get_logging_state(sender);
  85. span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
  86. span_log_set_tag(logging, "Ademco-tx");
  87. tx_callback_reported = false;
  88. /* The remainder of the Mitel tape is the talk-off test */
  89. /* Here we use the Bellcore test tapes (much tougher), in six
  90. files - 1 from each side of the original 3 cassette tapes */
  91. /* Bellcore say you should get no more than 470 false detections with
  92. a good receiver. Dialogic claim 20. Of course, we can do better than
  93. that, eh? */
  94. printf("Talk-off test\n");
  95. for (j = 0; bellcore_files[j][0]; j++)
  96. {
  97. if ((inhandle = sf_open_telephony_read(bellcore_files[j], 1)) == NULL)
  98. {
  99. printf(" Cannot open speech file '%s'\n", bellcore_files[j]);
  100. return -1;
  101. }
  102. while ((frames = sf_readf_short(inhandle, amp, SAMPLE_RATE)))
  103. {
  104. ademco_contactid_sender_rx(sender, amp, frames);
  105. }
  106. if (sf_close_telephony(inhandle))
  107. {
  108. printf(" Cannot close speech file '%s'\n", bellcore_files[j]);
  109. return -1;
  110. }
  111. printf(" File %d gave %d false hits.\n", j + 1, 0);
  112. }
  113. if (tx_callback_reported)
  114. {
  115. printf(" Failed\n");
  116. return -1;
  117. }
  118. printf(" Passed\n");
  119. ademco_contactid_sender_free(sender);
  120. return 0;
  121. }
  122. /*- End of function --------------------------------------------------------*/
  123. static void rx_callback(void *user_data, const ademco_contactid_report_t *report)
  124. {
  125. printf("Ademco Contact ID message:\n");
  126. printf(" Account %X\n", report->acct);
  127. printf(" Message type %X\n", report->mt);
  128. printf(" Qualifier %X\n", report->q);
  129. printf(" Event %X\n", report->xyz);
  130. printf(" Group/partition %X\n", report->gg);
  131. printf(" User/Zone information %X\n", report->ccc);
  132. if (memcmp(&reports[reports_entry], report, sizeof(*report)))
  133. {
  134. printf("Report mismatch\n");
  135. exit(2);
  136. }
  137. rx_callback_reported = true;
  138. }
  139. /*- End of function --------------------------------------------------------*/
  140. static void tx_callback(void *user_data, int tone, int level, int duration)
  141. {
  142. ademco_contactid_sender_state_t *sender;
  143. sender = (ademco_contactid_sender_state_t *) user_data;
  144. printf("Ademco sender report %d\n", tone);
  145. switch (tone)
  146. {
  147. case -1:
  148. /* We are connected and ready to send */
  149. ademco_contactid_sender_put(sender, &reports[reports_entry]);
  150. break;
  151. case 1:
  152. /* We have succeeded in sending, and are ready to send another message. */
  153. if (++reports_entry < 5)
  154. ademco_contactid_sender_put(sender, &reports[reports_entry]);
  155. else
  156. sending_complete = true;
  157. break;
  158. case 0:
  159. /* Sending failed after retries */
  160. sending_complete = true;
  161. break;
  162. }
  163. }
  164. /*- End of function --------------------------------------------------------*/
  165. static int end_to_end_tests(void)
  166. {
  167. ademco_contactid_receiver_state_t *receiver;
  168. ademco_contactid_sender_state_t *sender;
  169. logging_state_t *logging;
  170. codec_munge_state_t *munge;
  171. awgn_state_t noise_source;
  172. int16_t amp[SAMPLES_PER_CHUNK];
  173. int16_t sndfile_buf[2*SAMPLES_PER_CHUNK];
  174. int samples;
  175. int i;
  176. int j;
  177. printf("End to end tests\n");
  178. if ((outhandle = sf_open_telephony_write(OUTPUT_FILE_NAME, 2)) == NULL)
  179. {
  180. fprintf(stderr, " Cannot open audio file '%s'\n", OUTPUT_FILE_NAME);
  181. exit(2);
  182. }
  183. if ((receiver = ademco_contactid_receiver_init(NULL, rx_callback, NULL)) == NULL)
  184. return -1;
  185. ademco_contactid_receiver_set_realtime_callback(receiver, rx_callback, receiver);
  186. logging = ademco_contactid_receiver_get_logging_state(receiver);
  187. span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
  188. span_log_set_tag(logging, "Ademco-rx");
  189. if ((sender = ademco_contactid_sender_init(NULL, tx_callback, NULL)) == NULL)
  190. return -1;
  191. ademco_contactid_sender_set_realtime_callback(sender, tx_callback, sender);
  192. logging = ademco_contactid_sender_get_logging_state(sender);
  193. span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
  194. span_log_set_tag(logging, "Ademco-tx");
  195. awgn_init_dbm0(&noise_source, 1234567, -50);
  196. munge = codec_munge_init(MUNGE_CODEC_ALAW, 0);
  197. sending_complete = false;
  198. rx_callback_reported = false;
  199. for (i = 0; i < 1000; i++)
  200. {
  201. samples = ademco_contactid_sender_tx(sender, amp, SAMPLES_PER_CHUNK);
  202. for (j = samples; j < SAMPLES_PER_CHUNK; j++)
  203. amp[j] = 0;
  204. for (j = 0; j < SAMPLES_PER_CHUNK; j++)
  205. sndfile_buf[2*j] = amp[j];
  206. /* There is no point in impairing this signal. It is just DTMF tones, which
  207. will work as wel as the DTMF detector beign used. */
  208. ademco_contactid_receiver_rx(receiver, amp, SAMPLES_PER_CHUNK);
  209. samples = ademco_contactid_receiver_tx(receiver, amp, SAMPLES_PER_CHUNK);
  210. for (j = samples; j < SAMPLES_PER_CHUNK; j++)
  211. amp[j] = 0;
  212. /* We add AWGN and codec impairments to the signal, to stress the tone detector. */
  213. codec_munge(munge, amp, SAMPLES_PER_CHUNK);
  214. for (j = 0; j < SAMPLES_PER_CHUNK; j++)
  215. {
  216. sndfile_buf[2*j + 1] = amp[j];
  217. /* Add noise to the tones */
  218. amp[j] += awgn(&noise_source);
  219. }
  220. codec_munge(munge, amp, SAMPLES_PER_CHUNK);
  221. ademco_contactid_sender_rx(sender, amp, SAMPLES_PER_CHUNK);
  222. sf_writef_short(outhandle, sndfile_buf, SAMPLES_PER_CHUNK);
  223. }
  224. codec_munge_free(munge);
  225. if (!rx_callback_reported)
  226. {
  227. fprintf(stderr, " Report not received\n");
  228. return -1;
  229. }
  230. if (sf_close_telephony(outhandle))
  231. {
  232. fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_FILE_NAME);
  233. return -1;
  234. }
  235. printf(" Passed\n");
  236. ademco_contactid_sender_free(sender);
  237. ademco_contactid_receiver_free(receiver);
  238. return 0;
  239. }
  240. /*- End of function --------------------------------------------------------*/
  241. static int encode_decode_tests(void)
  242. {
  243. char buf[100];
  244. ademco_contactid_receiver_state_t *receiver;
  245. ademco_contactid_sender_state_t *sender;
  246. logging_state_t *logging;
  247. ademco_contactid_report_t result;
  248. int i;
  249. printf("Encode and decode tests\n");
  250. if ((receiver = ademco_contactid_receiver_init(NULL, NULL, NULL)) == NULL)
  251. return 2;
  252. logging = ademco_contactid_receiver_get_logging_state(receiver);
  253. span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
  254. span_log_set_tag(logging, "Ademco-rx");
  255. if ((sender = ademco_contactid_sender_init(NULL, NULL, NULL)) == NULL)
  256. return 2;
  257. logging = ademco_contactid_sender_get_logging_state(sender);
  258. span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
  259. span_log_set_tag(logging, "Ademco-tx");
  260. for (i = 0; i < 5; i++)
  261. {
  262. if (encode_msg(buf, &reports[i]) < 0)
  263. {
  264. printf("Bad encode message\n");
  265. return -1;
  266. }
  267. printf("'%s'\n", buf);
  268. if (decode_msg(&result, buf))
  269. {
  270. printf("Bad decode message\n");
  271. return -1;
  272. }
  273. ademco_contactid_receiver_log_msg(receiver, &result);
  274. printf("\n");
  275. if (memcmp(&reports[i], &result, sizeof(result)))
  276. {
  277. printf("Received message does not match the one sent\n");
  278. return -1;
  279. }
  280. }
  281. if (encode_msg(buf, &reports[5]) >= 0)
  282. {
  283. printf("Incorrectly good message\n");
  284. return -1;
  285. }
  286. printf("'%s'\n", buf);
  287. printf("\n");
  288. printf(" Passed\n");
  289. ademco_contactid_sender_free(sender);
  290. ademco_contactid_receiver_free(receiver);
  291. return 0;
  292. }
  293. /*- End of function --------------------------------------------------------*/
  294. static void decode_file(const char *file)
  295. {
  296. //SPAN_DECLARE(int) decode_msg(ademco_contactid_report_t *report, const char buf[])
  297. }
  298. /*- End of function --------------------------------------------------------*/
  299. int main(int argc, char *argv[])
  300. {
  301. int opt;
  302. const char *decode_test_file;
  303. decode_test_file = NULL;
  304. while ((opt = getopt(argc, argv, "d:")) != -1)
  305. {
  306. switch (opt)
  307. {
  308. case 'd':
  309. decode_test_file = optarg;
  310. break;
  311. default:
  312. //usage();
  313. exit(2);
  314. break;
  315. }
  316. }
  317. if (decode_test_file)
  318. {
  319. decode_file(decode_test_file);
  320. return 0;
  321. }
  322. if (encode_decode_tests())
  323. {
  324. printf("Tests failed\n");
  325. return 2;
  326. }
  327. if (mitel_cm7291_side_2_and_bellcore_tests())
  328. {
  329. printf("Tests failed\n");
  330. return 2;
  331. }
  332. if (end_to_end_tests())
  333. {
  334. printf("Tests failed\n");
  335. return 2;
  336. }
  337. printf("Tests passed\n");
  338. return 0;
  339. }
  340. /*- End of function --------------------------------------------------------*/
  341. /*- End of file ------------------------------------------------------------*/