forked from Mirrors/freeswitch
391 lines
12 KiB
C
391 lines
12 KiB
C
/*
|
|
* SpanDSP - a series of DSP components for telephony
|
|
*
|
|
* ademco_contactid.c - Ademco ContactID alarm protocol
|
|
*
|
|
* Written by Steve Underwood <steveu@coppice.org>
|
|
*
|
|
* Copyright (C) 2012 Steve Underwood
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 2.1,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/*! \page ademco_contactid_tests_page Ademco ContactID tests
|
|
\section ademco_contactid_tests_page_sec_1 What does it do?
|
|
|
|
\section ademco_contactid_tests_page_sec_2 How does it work?
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <sndfile.h>
|
|
|
|
#include "spandsp.h"
|
|
#include "spandsp-sim.h"
|
|
|
|
#define SAMPLES_PER_CHUNK 160
|
|
|
|
#define OUTPUT_FILE_NAME "ademco_contactid.wav"
|
|
|
|
#define MITEL_DIR "../test-data/mitel/"
|
|
#define BELLCORE_DIR "../test-data/bellcore/"
|
|
|
|
const char *bellcore_files[] =
|
|
{
|
|
MITEL_DIR "mitel-cm7291-talkoff.wav",
|
|
BELLCORE_DIR "tr-tsy-00763-1.wav",
|
|
BELLCORE_DIR "tr-tsy-00763-2.wav",
|
|
BELLCORE_DIR "tr-tsy-00763-3.wav",
|
|
BELLCORE_DIR "tr-tsy-00763-4.wav",
|
|
BELLCORE_DIR "tr-tsy-00763-5.wav",
|
|
BELLCORE_DIR "tr-tsy-00763-6.wav",
|
|
""
|
|
};
|
|
|
|
static const ademco_contactid_report_t reports[] =
|
|
{
|
|
{0x1234, 0x18, 0x1, 0x131, 0x1, 0x15},
|
|
{0x1234, 0x18, 0x3, 0x131, 0x1, 0x15},
|
|
{0x1234, 0x18, 0x1, 0x401, 0x2, 0x3},
|
|
{0x1234, 0x18, 0x3, 0x401, 0x3, 0x5},
|
|
{0x1234, 0x56, 0x7, 0x890, 0xBC, 0xDEF},
|
|
{0x1234, 0x56, 0x7, 0x89A, 0xBC, 0xDEF} /* This one is bad, as it contains a hex 'A' */
|
|
};
|
|
static int reports_entry = 0;
|
|
|
|
static int16_t amp[1000000];
|
|
|
|
bool tx_callback_reported = false;
|
|
bool rx_callback_reported = false;
|
|
|
|
bool sending_complete = false;
|
|
|
|
SNDFILE *outhandle;
|
|
|
|
static void talkoff_tx_callback(void *user_data, int tone, int level, int duration)
|
|
{
|
|
printf("Ademco sender report %d\n", tone);
|
|
tx_callback_reported = true;
|
|
}
|
|
|
|
static int mitel_cm7291_side_2_and_bellcore_tests(void)
|
|
{
|
|
int j;
|
|
SNDFILE *inhandle;
|
|
int frames;
|
|
ademco_contactid_sender_state_t *sender;
|
|
logging_state_t *logging;
|
|
|
|
if ((sender = ademco_contactid_sender_init(NULL, talkoff_tx_callback, NULL)) == NULL)
|
|
return -1;
|
|
logging = ademco_contactid_sender_get_logging_state(sender);
|
|
span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
|
|
span_log_set_tag(logging, "Ademco-tx");
|
|
|
|
tx_callback_reported = false;
|
|
|
|
/* The remainder of the Mitel tape is the talk-off test */
|
|
/* Here we use the Bellcore test tapes (much tougher), in six
|
|
files - 1 from each side of the original 3 cassette tapes */
|
|
/* Bellcore say you should get no more than 470 false detections with
|
|
a good receiver. Dialogic claim 20. Of course, we can do better than
|
|
that, eh? */
|
|
printf("Talk-off test\n");
|
|
for (j = 0; bellcore_files[j][0]; j++)
|
|
{
|
|
if ((inhandle = sf_open_telephony_read(bellcore_files[j], 1)) == NULL)
|
|
{
|
|
printf(" Cannot open speech file '%s'\n", bellcore_files[j]);
|
|
return -1;
|
|
}
|
|
while ((frames = sf_readf_short(inhandle, amp, SAMPLE_RATE)))
|
|
{
|
|
ademco_contactid_sender_rx(sender, amp, frames);
|
|
}
|
|
if (sf_close_telephony(inhandle))
|
|
{
|
|
printf(" Cannot close speech file '%s'\n", bellcore_files[j]);
|
|
return -1;
|
|
}
|
|
printf(" File %d gave %d false hits.\n", j + 1, 0);
|
|
}
|
|
if (tx_callback_reported)
|
|
{
|
|
printf(" Failed\n");
|
|
return -1;
|
|
}
|
|
printf(" Passed\n");
|
|
ademco_contactid_sender_free(sender);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static void rx_callback(void *user_data, const ademco_contactid_report_t *report)
|
|
{
|
|
printf("Ademco Contact ID message:\n");
|
|
printf(" Account %X\n", report->acct);
|
|
printf(" Message type %X\n", report->mt);
|
|
printf(" Qualifier %X\n", report->q);
|
|
printf(" Event %X\n", report->xyz);
|
|
printf(" Group/partition %X\n", report->gg);
|
|
printf(" User/Zone information %X\n", report->ccc);
|
|
if (memcmp(&reports[reports_entry], report, sizeof(*report)))
|
|
{
|
|
printf("Report mismatch\n");
|
|
exit(2);
|
|
}
|
|
rx_callback_reported = true;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static void tx_callback(void *user_data, int tone, int level, int duration)
|
|
{
|
|
ademco_contactid_sender_state_t *sender;
|
|
|
|
sender = (ademco_contactid_sender_state_t *) user_data;
|
|
printf("Ademco sender report %d\n", tone);
|
|
switch (tone)
|
|
{
|
|
case -1:
|
|
/* We are connected and ready to send */
|
|
ademco_contactid_sender_put(sender, &reports[reports_entry]);
|
|
break;
|
|
case 1:
|
|
/* We have succeeded in sending, and are ready to send another message. */
|
|
if (++reports_entry < 5)
|
|
ademco_contactid_sender_put(sender, &reports[reports_entry]);
|
|
else
|
|
sending_complete = true;
|
|
break;
|
|
case 0:
|
|
/* Sending failed after retries */
|
|
sending_complete = true;
|
|
break;
|
|
}
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static int end_to_end_tests(void)
|
|
{
|
|
ademco_contactid_receiver_state_t *receiver;
|
|
ademco_contactid_sender_state_t *sender;
|
|
logging_state_t *logging;
|
|
codec_munge_state_t *munge;
|
|
awgn_state_t noise_source;
|
|
int16_t amp[SAMPLES_PER_CHUNK];
|
|
int16_t sndfile_buf[2*SAMPLES_PER_CHUNK];
|
|
int samples;
|
|
int i;
|
|
int j;
|
|
|
|
printf("End to end tests\n");
|
|
|
|
if ((outhandle = sf_open_telephony_write(OUTPUT_FILE_NAME, 2)) == NULL)
|
|
{
|
|
fprintf(stderr, " Cannot open audio file '%s'\n", OUTPUT_FILE_NAME);
|
|
exit(2);
|
|
}
|
|
|
|
if ((receiver = ademco_contactid_receiver_init(NULL, rx_callback, NULL)) == NULL)
|
|
return -1;
|
|
ademco_contactid_receiver_set_realtime_callback(receiver, rx_callback, receiver);
|
|
|
|
logging = ademco_contactid_receiver_get_logging_state(receiver);
|
|
span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
|
|
span_log_set_tag(logging, "Ademco-rx");
|
|
|
|
if ((sender = ademco_contactid_sender_init(NULL, tx_callback, NULL)) == NULL)
|
|
return -1;
|
|
ademco_contactid_sender_set_realtime_callback(sender, tx_callback, sender);
|
|
logging = ademco_contactid_sender_get_logging_state(sender);
|
|
span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
|
|
span_log_set_tag(logging, "Ademco-tx");
|
|
|
|
awgn_init_dbm0(&noise_source, 1234567, -50);
|
|
munge = codec_munge_init(MUNGE_CODEC_ALAW, 0);
|
|
|
|
sending_complete = false;
|
|
rx_callback_reported = false;
|
|
|
|
for (i = 0; i < 1000; i++)
|
|
{
|
|
samples = ademco_contactid_sender_tx(sender, amp, SAMPLES_PER_CHUNK);
|
|
for (j = samples; j < SAMPLES_PER_CHUNK; j++)
|
|
amp[j] = 0;
|
|
for (j = 0; j < SAMPLES_PER_CHUNK; j++)
|
|
sndfile_buf[2*j] = amp[j];
|
|
/* There is no point in impairing this signal. It is just DTMF tones, which
|
|
will work as wel as the DTMF detector beign used. */
|
|
ademco_contactid_receiver_rx(receiver, amp, SAMPLES_PER_CHUNK);
|
|
|
|
samples = ademco_contactid_receiver_tx(receiver, amp, SAMPLES_PER_CHUNK);
|
|
for (j = samples; j < SAMPLES_PER_CHUNK; j++)
|
|
amp[j] = 0;
|
|
|
|
/* We add AWGN and codec impairments to the signal, to stress the tone detector. */
|
|
codec_munge(munge, amp, SAMPLES_PER_CHUNK);
|
|
for (j = 0; j < SAMPLES_PER_CHUNK; j++)
|
|
{
|
|
sndfile_buf[2*j + 1] = amp[j];
|
|
/* Add noise to the tones */
|
|
amp[j] += awgn(&noise_source);
|
|
}
|
|
codec_munge(munge, amp, SAMPLES_PER_CHUNK);
|
|
ademco_contactid_sender_rx(sender, amp, SAMPLES_PER_CHUNK);
|
|
|
|
sf_writef_short(outhandle, sndfile_buf, SAMPLES_PER_CHUNK);
|
|
}
|
|
codec_munge_free(munge);
|
|
if (!rx_callback_reported)
|
|
{
|
|
fprintf(stderr, " Report not received\n");
|
|
return -1;
|
|
}
|
|
|
|
if (sf_close_telephony(outhandle))
|
|
{
|
|
fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_FILE_NAME);
|
|
return -1;
|
|
}
|
|
printf(" Passed\n");
|
|
ademco_contactid_sender_free(sender);
|
|
ademco_contactid_receiver_free(receiver);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static int encode_decode_tests(void)
|
|
{
|
|
char buf[100];
|
|
ademco_contactid_receiver_state_t *receiver;
|
|
ademco_contactid_sender_state_t *sender;
|
|
logging_state_t *logging;
|
|
ademco_contactid_report_t result;
|
|
int i;
|
|
|
|
printf("Encode and decode tests\n");
|
|
|
|
if ((receiver = ademco_contactid_receiver_init(NULL, NULL, NULL)) == NULL)
|
|
return 2;
|
|
logging = ademco_contactid_receiver_get_logging_state(receiver);
|
|
span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
|
|
span_log_set_tag(logging, "Ademco-rx");
|
|
|
|
if ((sender = ademco_contactid_sender_init(NULL, NULL, NULL)) == NULL)
|
|
return 2;
|
|
logging = ademco_contactid_sender_get_logging_state(sender);
|
|
span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
|
|
span_log_set_tag(logging, "Ademco-tx");
|
|
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
if (encode_msg(buf, &reports[i]) < 0)
|
|
{
|
|
printf("Bad encode message\n");
|
|
return -1;
|
|
}
|
|
printf("'%s'\n", buf);
|
|
if (decode_msg(&result, buf))
|
|
{
|
|
printf("Bad decode message\n");
|
|
return -1;
|
|
}
|
|
ademco_contactid_receiver_log_msg(receiver, &result);
|
|
printf("\n");
|
|
if (memcmp(&reports[i], &result, sizeof(result)))
|
|
{
|
|
printf("Received message does not match the one sent\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (encode_msg(buf, &reports[5]) >= 0)
|
|
{
|
|
printf("Incorrectly good message\n");
|
|
return -1;
|
|
}
|
|
printf("'%s'\n", buf);
|
|
printf("\n");
|
|
printf(" Passed\n");
|
|
ademco_contactid_sender_free(sender);
|
|
ademco_contactid_receiver_free(receiver);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static void decode_file(const char *file)
|
|
{
|
|
//SPAN_DECLARE(int) decode_msg(ademco_contactid_report_t *report, const char buf[])
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int opt;
|
|
const char *decode_test_file;
|
|
|
|
decode_test_file = NULL;
|
|
while ((opt = getopt(argc, argv, "d:")) != -1)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'd':
|
|
decode_test_file = optarg;
|
|
break;
|
|
default:
|
|
//usage();
|
|
exit(2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (decode_test_file)
|
|
{
|
|
decode_file(decode_test_file);
|
|
return 0;
|
|
}
|
|
|
|
if (encode_decode_tests())
|
|
{
|
|
printf("Tests failed\n");
|
|
return 2;
|
|
}
|
|
|
|
if (mitel_cm7291_side_2_and_bellcore_tests())
|
|
{
|
|
printf("Tests failed\n");
|
|
return 2;
|
|
}
|
|
|
|
if (end_to_end_tests())
|
|
{
|
|
printf("Tests failed\n");
|
|
return 2;
|
|
}
|
|
|
|
printf("Tests passed\n");
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
/*- End of file ------------------------------------------------------------*/
|