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