diff --git a/libs/spandsp/configure.ac b/libs/spandsp/configure.ac index 19005f9965..ad5ecf148d 100644 --- a/libs/spandsp/configure.ac +++ b/libs/spandsp/configure.ac @@ -202,6 +202,18 @@ AC_CHECK_HEADERS([stdlib.h]) AC_CHECK_HEADERS([string.h]) AC_CHECK_HEADERS([strings.h]) AC_CHECK_HEADERS([malloc.h]) +AC_CHECK_HEADERS([math.h], [INSERT_MATH_HEADER="#include "]) +AC_CHECK_HEADERS([float.h]) +AC_CHECK_HEADERS([fcntl.h]) +AC_CHECK_HEADERS([sys/time.h]) +AC_CHECK_HEADERS([sys/select.h]) +AC_CHECK_HEADERS([sys/ioctl.h]) +AC_CHECK_HEADERS([sys/fcntl.h]) +AC_CHECK_HEADERS([sndfile.h]) +AC_CHECK_HEADERS([fenv.h]) +AC_CHECK_HEADERS([fftw3.h], , [AC_CHECK_HEADERS([fftw.h])]) +AC_CHECK_HEADERS([pcap.h]) +AC_CHECK_HEADERS([pthread.h]) case "$host" in *dragonfly*) @@ -215,18 +227,6 @@ case "$host" in ;; esac -AC_CHECK_HEADERS([math.h], [INSERT_MATH_HEADER="#include "]) -AC_CHECK_HEADERS([float.h]) -AC_CHECK_HEADERS([fcntl.h]) -AC_CHECK_HEADERS([sys/time.h]) -AC_CHECK_HEADERS([sys/select.h]) -AC_CHECK_HEADERS([sys/ioctl.h]) -AC_CHECK_HEADERS([sys/fcntl.h]) -AC_CHECK_HEADERS([sndfile.h]) -AC_CHECK_HEADERS([fenv.h]) -AC_CHECK_HEADERS([fftw3.h], , [AC_CHECK_HEADERS([fftw.h])]) -AC_CHECK_HEADERS([pcap.h]) -AC_CHECK_HEADERS([pthread.h]) if test "${build}" == "${host}" then AC_CHECK_HEADERS([X11/X.h]) diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am index 46347fe00a..6a80d13c16 100644 --- a/libs/spandsp/src/Makefile.am +++ b/libs/spandsp/src/Makefile.am @@ -110,6 +110,7 @@ libspandsp_la_SOURCES = ademco_contactid.c \ complex_vector_float.c \ complex_vector_int.c \ crc.c \ + data_modems.c \ dds_float.c \ dds_int.c \ dtmf.c \ @@ -210,6 +211,7 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \ spandsp/complex_filters.h \ spandsp/complex_vector_float.h \ spandsp/complex_vector_int.h \ + spandsp/data_modems.h \ spandsp/dc_restore.h \ spandsp/dds.h \ spandsp/dtmf.h \ @@ -292,6 +294,7 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \ spandsp/private/bell_r2_mf.h \ spandsp/private/bert.h \ spandsp/private/bitstream.h \ + spandsp/private/data_modems.h \ spandsp/private/dtmf.h \ spandsp/private/echo.h \ spandsp/private/fax.h \ diff --git a/libs/spandsp/src/spandsp.h.in b/libs/spandsp/src/spandsp.h.in index 747f4a432f..7ec041f195 100644 --- a/libs/spandsp/src/spandsp.h.in +++ b/libs/spandsp/src/spandsp.h.in @@ -124,6 +124,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/spandsp/src/spandsp/expose.h b/libs/spandsp/src/spandsp/expose.h index b65a21f1b9..d9811b4caa 100644 --- a/libs/spandsp/src/spandsp/expose.h +++ b/libs/spandsp/src/spandsp/expose.h @@ -82,7 +82,7 @@ #include #include #include -//#include +#include #include #include #include diff --git a/libs/spandsp/src/v27ter_tx.c b/libs/spandsp/src/v27ter_tx.c index 5065c004be..f6b69917bb 100644 --- a/libs/spandsp/src/v27ter_tx.c +++ b/libs/spandsp/src/v27ter_tx.c @@ -65,7 +65,7 @@ #include "spandsp/private/v27ter_tx.h" #if defined(SPANDSP_USE_FIXED_POINT) -#define FP_SCALE FP_Q6_10 +#define FP_SCALE(x) FP_Q6_10(x) #else #define FP_SCALE(x) (x) #endif diff --git a/libs/spandsp/src/v8.c b/libs/spandsp/src/v8.c index 464d90b0fe..5052e45d68 100644 --- a/libs/spandsp/src/v8.c +++ b/libs/spandsp/src/v8.c @@ -70,7 +70,7 @@ enum { - V8_WAIT_1S, /* Start point when sending CI */ + V8_WAIT_1S = 0, /* Start point when sending CI */ V8_AWAIT_ANSAM, /* Start point when sending initial silence */ V8_CI_ON, V8_CI_OFF, @@ -539,16 +539,16 @@ static void put_bit(void *user_data, int bit) switch (s->preamble_type) { case V8_SYNC_CI: - tag = "CI: "; + tag = ">CI: "; break; case V8_SYNC_CM_JM: - tag = (s->calling_party) ? "JM: " : "CM: "; + tag = (s->calling_party) ? ">JM: " : ">CM: "; break; case V8_SYNC_V92: - tag = "V92: "; + tag = ">V.92: "; break; default: - tag = "??: "; + tag = ">??: "; break; } span_log_buf(&s->logging, SPAN_LOG_FLOW, tag, s->rx_data, s->rx_data_ptr); @@ -639,20 +639,26 @@ static void v8_put_preamble(v8_state_t *s) } /*- End of function --------------------------------------------------------*/ -static void v8_put_byte(v8_state_t *s, int data) +static void v8_put_bytes(v8_state_t *s, uint8_t buf[], int len) { int i; + int j; + uint8_t byte; uint8_t bits[10]; /* Insert start & stop bits */ - bits[0] = 0; - for (i = 1; i < 9; i++) + for (i = 0; i < len; i++) { - bits[i] = (uint8_t) (data & 1); - data >>= 1; + bits[0] = 0; + byte = buf[i]; + for (j = 1; j < 9; j++) + { + bits[j] = byte & 1; + byte >>= 1; + } + bits[9] = 1; + queue_write(s->tx_queue, bits, 10); } - bits[9] = 1; - queue_write(s->tx_queue, bits, 10); } /*- End of function --------------------------------------------------------*/ @@ -661,12 +667,15 @@ static void send_cm_jm(v8_state_t *s) int val; unsigned int offered_modulations; int bytes; + uint8_t buf[10]; + int ptr; /* Send a CM, or a JM as appropriate */ v8_put_preamble(s); - v8_put_byte(s, V8_CM_JM_SYNC_OCTET); + ptr = 0; + buf[ptr++] = V8_CM_JM_SYNC_OCTET; /* Data call */ - v8_put_byte(s, (s->result.call_function << 5) | V8_CALL_FUNCTION_TAG); + buf[ptr++] = (s->result.call_function << 5) | V8_CALL_FUNCTION_TAG; /* Supported modulations */ offered_modulations = s->result.modulations; @@ -676,7 +685,9 @@ static void send_cm_jm(v8_state_t *s) val |= 0x20; if (offered_modulations & V8_MOD_V34) val |= 0x40; - v8_put_byte(s, val); + if (offered_modulations & V8_MOD_V34HDX) + val |= 0x80; + buf[ptr++] = val; if (++bytes < s->modulation_bytes) { val = 0x10; @@ -690,7 +701,7 @@ static void send_cm_jm(v8_state_t *s) val |= 0x40; if (offered_modulations & V8_MOD_V27TER) val |= 0x80; - v8_put_byte(s, val); + buf[ptr++] = val; } if (++bytes < s->modulation_bytes) { @@ -705,19 +716,21 @@ static void send_cm_jm(v8_state_t *s) val |= 0x40; if (offered_modulations & V8_MOD_V21) val |= 0x80; - v8_put_byte(s, val); + buf[ptr++] = val; } if (s->parms.protocol) - v8_put_byte(s, (s->parms.protocol << 5) | V8_PROTOCOLS_TAG); + buf[ptr++] = (s->parms.protocol << 5) | V8_PROTOCOLS_TAG; if (s->parms.pstn_access) - v8_put_byte(s, (s->parms.pstn_access << 5) | V8_PSTN_ACCESS_TAG); + buf[ptr++] = (s->parms.pstn_access << 5) | V8_PSTN_ACCESS_TAG; if (s->parms.pcm_modem_availability) - v8_put_byte(s, (s->parms.pcm_modem_availability << 5) | V8_PCM_MODEM_AVAILABILITY_TAG); + buf[ptr++] = (s->parms.pcm_modem_availability << 5) | V8_PCM_MODEM_AVAILABILITY_TAG; if (s->parms.t66 >= 0) - v8_put_byte(s, (s->parms.t66 << 5) | V8_T66_TAG); + buf[ptr++] = (s->parms.t66 << 5) | V8_T66_TAG; /* No NSF */ - //v8_put_byte(s, (0 << 5) | V8_NSF_TAG); + //buf[ptr++] = (0 << 5) | V8_NSF_TAG; + span_log_buf(&s->logging, SPAN_LOG_FLOW, (s->calling_party) ? "modem_connect_tone_tx_on) { - if (s->modem_connect_tone_tx_on > ms_to_samples(75)) + if (s->modem_connect_tone_tx_on == (ms_to_samples(75) + 2)) + { + if (s->fsk_tx_on) + { + /* The initial silence is over */ + s->modem_connect_tone_tx_on = 0; + } + } + else if (s->modem_connect_tone_tx_on == (ms_to_samples(75) + 1)) { /* Send the ANSam tone */ len = modem_connect_tones_tx(&s->ansam_tx, amp, max_len); @@ -752,14 +773,19 @@ SPAN_DECLARE_NONSTD(int) v8_tx(v8_state_t *s, int16_t *amp, int max_len) } if (s->fsk_tx_on && len < max_len) { - max_len -= len; - len = fsk_tx(&s->v21tx, amp + len, max_len); + len += fsk_tx(&s->v21tx, &[len], max_len - len); if (len < max_len) { - span_log(&s->logging, SPAN_LOG_FLOW, "FSK ends\n"); + span_log(&s->logging, SPAN_LOG_FLOW, "FSK ends (%d/%d) %d %d\n", len, max_len, s->fsk_tx_on, s->state); s->fsk_tx_on = false; + //s->state = V8_PARKED; } } + if (s->state != V8_PARKED && len < max_len) + { + vec_zeroi16(&[len], max_len - len); + len = max_len; + } return len; } /*- End of function --------------------------------------------------------*/ @@ -767,6 +793,7 @@ SPAN_DECLARE_NONSTD(int) v8_tx(v8_state_t *s, int16_t *amp, int max_len) static void send_v92(v8_state_t *s) { int i; + uint8_t buf[2]; if (s->result.v92 >= 0) { @@ -774,8 +801,10 @@ static void send_v92(v8_state_t *s) for (i = 0; i < 2; i++) { v8_put_preamble(s); - v8_put_byte(s, V8_V92_SYNC_OCTET); - v8_put_byte(s, s->result.v92); + buf[0] = V8_V92_SYNC_OCTET; + buf[1] = s->result.v92; + span_log_buf(&s->logging, SPAN_LOG_FLOW, "result.call_function << 5) | V8_CALL_FUNCTION_TAG); + buf[0] = V8_CI_SYNC_OCTET; + buf[1] = (s->result.call_function << 5) | V8_CALL_FUNCTION_TAG; + span_log_buf(&s->logging, SPAN_LOG_FLOW, "logging, SPAN_LOG_FLOW, "v8_rx state %d\n", s->state); residual_samples = 0; @@ -911,8 +943,9 @@ SPAN_DECLARE_NONSTD(int) v8_rx(v8_state_t *s, const int16_t *amp, int len) /* Now JM has been detected, we send CJ and wait for 75 ms before finishing the V.8 analysis. */ fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]); - for (i = 0; i < 3; i++) - v8_put_byte(s, 0); + memset(buf, 0, 3); + v8_put_bytes(s, buf, 3); + span_log_buf(&s->logging, SPAN_LOG_FLOW, "state = V8_CJ_ON; s->fsk_tx_on = true; break; @@ -1060,6 +1093,7 @@ SPAN_DECLARE(int) v8_restart(v8_state_t *s, bool calling_party, v8_parms_t *parm } modem_connect_tones_rx_init(&s->ansam_rx, MODEM_CONNECT_TONES_ANS_PR, NULL, NULL); fsk_tx_init(&s->v21tx, &preset_fsk_specs[FSK_V21CH1], get_bit, s); + s->modem_connect_tone_tx_on = ms_to_samples(75) + 2; } else { diff --git a/libs/spandsp/tests/Makefile.am b/libs/spandsp/tests/Makefile.am index 7149900a90..47ee06aec3 100644 --- a/libs/spandsp/tests/Makefile.am +++ b/libs/spandsp/tests/Makefile.am @@ -73,10 +73,12 @@ noinst_PROGRAMS = ademco_contactid_tests \ complex_vector_float_tests \ complex_vector_int_tests \ crc_tests \ + data_modems_tests \ dc_restore_tests \ dds_tests \ dtmf_rx_tests \ dtmf_tx_tests \ + dummy_modems_tests \ echo_tests \ fax_decode \ fax_tests \ @@ -151,6 +153,7 @@ noinst_HEADERS = echo_monitor.h \ modem_monitor.h \ pcap_parse.h \ pseudo_terminals.h \ + socket_harness.h \ udptl.h ademco_contactid_tests_SOURCES = ademco_contactid_tests.c @@ -198,6 +201,9 @@ complex_vector_int_tests_LDADD = $(LIBDIR) -lspandsp crc_tests_SOURCES = crc_tests.c crc_tests_LDADD = $(LIBDIR) -lspandsp +data_modems_tests_SOURCES = data_modems_tests.c media_monitor.cpp +data_modems_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspandsp + dc_restore_tests_SOURCES = dc_restore_tests.c dc_restore_tests_LDADD = $(LIBDIR) -lspandsp @@ -210,6 +216,9 @@ dtmf_rx_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lsp dtmf_tx_tests_SOURCES = dtmf_tx_tests.c dtmf_tx_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspandsp +dummy_modems_tests_SOURCES = dummy_modems_tests.c media_monitor.cpp socket_harness.c pseudo_terminals.c +dummy_modems_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspandsp -lutil + echo_tests_SOURCES = echo_tests.c echo_monitor.cpp echo_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspandsp diff --git a/libs/spandsp/tests/data_modems_tests.c b/libs/spandsp/tests/data_modems_tests.c new file mode 100644 index 0000000000..86bd085063 --- /dev/null +++ b/libs/spandsp/tests/data_modems_tests.c @@ -0,0 +1,312 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * data_modems_tests.c - Tests for data_modems. + * + * Written by Steve Underwood + * + * Copyright (C) 2011 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/*! \file */ + +/*! \page data_modems_tests_page Data modems tests +\section data_modems_tests_page_sec_1 What does it do? +*/ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H) +#define ENABLE_GUI +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "spandsp.h" +#include "spandsp-sim.h" + +#if defined(ENABLE_GUI) +#include "media_monitor.h" +#endif + +#define INPUT_FILE_NAME "../test-data/itu/fax/itu1.tif" +#define OUTPUT_FILE_NAME "t31.tif" +#define OUTPUT_WAVE_FILE_NAME "data_modems.wav" + +#define SAMPLES_PER_CHUNK 160 + +struct command_response_s +{ + const char *command; + int len_command; + const char *response; + int len_response; +}; + +char *decode_test_file = NULL; +int countdown = 0; +int command_response_test_step = -1; +char response_buf[1000]; +int response_buf_ptr = 0; +bool answered = false; +bool done = false; +bool sequence_terminated = false; + +data_modems_state_t *data_modems_state[2]; + +static void reporter(void *user_data, int reason, bert_results_t *results) +{ + int channel; + + channel = (int) (intptr_t) user_data; + switch (reason) + { + case BERT_REPORT_SYNCED: + fprintf(stderr, "%d: BERT report synced\n", channel); + break; + case BERT_REPORT_UNSYNCED: + fprintf(stderr, "%d: BERT report unsync'ed\n", channel); + break; + case BERT_REPORT_REGULAR: + fprintf(stderr, "%d: BERT report regular - %d bits, %d bad bits, %d resyncs\n", channel, results->total_bits, results->bad_bits, results->resyncs); + break; + case BERT_REPORT_GT_10_2: + fprintf(stderr, "%d: BERT report > 1 in 10^2\n", channel); + break; + case BERT_REPORT_LT_10_2: + fprintf(stderr, "%d: BERT report < 1 in 10^2\n", channel); + break; + case BERT_REPORT_LT_10_3: + fprintf(stderr, "%d: BERT report < 1 in 10^3\n", channel); + break; + case BERT_REPORT_LT_10_4: + fprintf(stderr, "%d: BERT report < 1 in 10^4\n", channel); + break; + case BERT_REPORT_LT_10_5: + fprintf(stderr, "%d: BERT report < 1 in 10^5\n", channel); + break; + case BERT_REPORT_LT_10_6: + fprintf(stderr, "%d: BERT report < 1 in 10^6\n", channel); + break; + case BERT_REPORT_LT_10_7: + fprintf(stderr, "%d: BERT report < 1 in 10^7\n", channel); + break; + default: + fprintf(stderr, "%d: BERT report reason %d\n", channel, reason); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static int get_msg(void *user_data, uint8_t msg[], int len) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static void put_msg(void *user_data, const uint8_t msg[], int len) +{ + if (len < 0) + printf("Status %s\n", signal_status_to_str(len)); +} +/*- End of function --------------------------------------------------------*/ + +static int modem_tests(int use_gui, int log_audio, int test_sending) +{ + int mdm_len; + int16_t mdm_amp[SAMPLES_PER_CHUNK]; + //int use_tep; + //logging_state_t *logging; + int outframes; + int16_t silence[SAMPLES_PER_CHUNK]; + int16_t out_amp[2*SAMPLES_PER_CHUNK]; + SNDFILE *wave_handle; + SNDFILE *in_handle; + int i; + int k; + int calling_party; + logging_state_t *logging; + bert_state_t *bert[2]; + + /* Test a pair of modems against each other */ + + /* Set up the test environment */ + //use_tep = false; + + wave_handle = NULL; + if (log_audio) + { + if ((wave_handle = sf_open_telephony_write(OUTPUT_WAVE_FILE_NAME, 2)) == NULL) + { + fprintf(stderr, " Cannot create audio file '%s'\n", OUTPUT_WAVE_FILE_NAME); + exit(2); + } + } + + in_handle = NULL; + if (decode_test_file) + { + if ((in_handle = sf_open_telephony_read(decode_test_file, 1)) == NULL) + { + fprintf(stderr, " Cannot create audio file '%s'\n", decode_test_file); + exit(2); + } + } + + memset(silence, 0, sizeof(silence)); + memset(mdm_amp, 0, sizeof(mdm_amp)); + mdm_len = 0; + + /* Now set up and run the modems */ + calling_party = true; + for (i = 0; i < 2; i++) + { + bert[i] = bert_init(NULL, 1000000, BERT_PATTERN_ITU_O152_11, 2400, 20); + bert_set_report(bert[i], 100000, reporter, (void *) (intptr_t) i); + if ((data_modems_state[i] = data_modems_init(NULL, + calling_party, + put_msg, + get_msg, + NULL)) == NULL) + { + fprintf(stderr, " Cannot start the data modem\n"); + exit(2); + } + logging = data_modems_get_logging_state(data_modems_state[i]); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, "Modem"); + calling_party = false; + } + +#if defined(ENABLE_GUI) + if (use_gui) + start_media_monitor(); +#endif + while (!done) + { + for (i = 0; i < 2; i++) + { + /* The receive side always expects a full block of samples, but the + transmit side may not be sending any when it doesn't need to. We + may need to pad with some silence. */ + mdm_len = data_modems_tx(data_modems_state[i], mdm_amp, SAMPLES_PER_CHUNK); + if (mdm_len < SAMPLES_PER_CHUNK) + { + vec_zeroi16(mdm_amp + mdm_len, SAMPLES_PER_CHUNK - mdm_len); + mdm_len = SAMPLES_PER_CHUNK; + } + if (log_audio) + { + for (k = 0; k < mdm_len; k++) + out_amp[2*k + i] = mdm_amp[k]; + } + if (data_modems_rx(data_modems_state[i ^ 1], mdm_amp, mdm_len)) + break; + } + + if (log_audio) + { + outframes = sf_writef_short(wave_handle, out_amp, SAMPLES_PER_CHUNK); + if (outframes != SAMPLES_PER_CHUNK) + break; + } + } + + if (decode_test_file) + { + if (sf_close_telephony(in_handle)) + { + fprintf(stderr, " Cannot close audio file '%s'\n", decode_test_file); + exit(2); + } + } + if (log_audio) + { + if (sf_close_telephony(wave_handle)) + { + fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_WAVE_FILE_NAME); + exit(2); + } + } + + if (!done || !sequence_terminated) + { + printf("Tests failed\n"); + return 2; + } + + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + int log_audio; + int test_sending; + int use_gui; + int opt; + + decode_test_file = NULL; + log_audio = false; + test_sending = false; + use_gui = false; + while ((opt = getopt(argc, argv, "d:glrs")) != -1) + { + switch (opt) + { + case 'd': + decode_test_file = optarg; + break; + case 'g': +#if defined(ENABLE_GUI) + use_gui = true; +#else + fprintf(stderr, "Graphical monitoring not available\n"); + exit(2); +#endif + break; + case 'l': + log_audio = true; + break; + case 'r': + test_sending = false; + break; + case 's': + test_sending = true; + break; + default: + //usage(); + exit(2); + break; + } + } + + modem_tests(use_gui, log_audio, test_sending); + printf("Tests passed\n"); + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/tests/dummy_modems_tests.c b/libs/spandsp/tests/dummy_modems_tests.c new file mode 100644 index 0000000000..c836f9659e --- /dev/null +++ b/libs/spandsp/tests/dummy_modems_tests.c @@ -0,0 +1,253 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * dummy_modems_tests.c - Tests for data_modems connected together by sockets. + * + * Written by Steve Underwood + * + * Copyright (C) 2011 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/*! \file */ + +/*! \page dummy_modems_tests_page Dummy data modems tests +\section dummy_modems_tests_page_sec_1 What does it do? +*/ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H) +#define ENABLE_GUI +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES + +#include "spandsp.h" +#include "spandsp-sim.h" + +#include "pseudo_terminals.h" +#include "socket_harness.h" + +#if defined(ENABLE_GUI) +#include "media_monitor.h" +#endif + +#define OUTPUT_WAVE_FILE_NAME "dummy_modems.wav" + +#define SAMPLES_PER_CHUNK 160 + +SNDFILE *wave_handle = NULL; +int16_t wave_buffer[4096]; + +data_modems_state_t *data_modem_state; + +static int get_msg(void *user_data, uint8_t msg[], int len) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static void put_msg(void *user_data, const uint8_t msg[], int len) +{ + if (len < 0) + printf("Status %s\n", signal_status_to_str(len)); + else + printf("Put %d '%s'\n", len, msg); +} +/*- End of function --------------------------------------------------------*/ + +static void terminal_callback(void *user_data, const uint8_t msg[], int len) +{ + printf("terminal callback %d\n", len); +} +/*- End of function --------------------------------------------------------*/ + +static int termios_callback(void *user_data, struct termios *termios) +{ + printf("termios callback\n"); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static void hangup_callback(void *user_data, int status) +{ +} +/*- End of function --------------------------------------------------------*/ + +static int terminal_free_space_callback(void *user_data) +{ + return 42; +} +/*- End of function --------------------------------------------------------*/ + +static int rx_callback(void *user_data, const int16_t amp[], int samples) +{ + int i; + int out_samples; + + out_samples = data_modems_rx((data_modems_state_t *) user_data, amp, samples); + if (wave_handle) + { + for (i = 0; i < samples; i++) + wave_buffer[2*i] = amp[i]; + } + return out_samples; +} +/*- End of function --------------------------------------------------------*/ + +static int rx_fillin_callback(void *user_data, int samples) +{ + return data_modems_rx_fillin((data_modems_state_t *) user_data, samples); +} +/*- End of function --------------------------------------------------------*/ + +static int tx_callback(void *user_data, int16_t amp[], int samples) +{ + int i; + int out_samples; + + out_samples = data_modems_tx((data_modems_state_t *) user_data, amp, samples); + if (wave_handle) + { + if (out_samples < samples) + memset(&[out_samples], 0, (samples - out_samples)*2); + for (i = 0; i < samples; i++) + wave_buffer[2*i + 1] = amp[i]; + sf_writef_short(wave_handle, wave_buffer, samples); + } + return samples; +} +/*- End of function --------------------------------------------------------*/ + +static int modem_tests(int use_gui, int log_audio, bool calling_party) +{ + logging_state_t *logging; + socket_harness_state_t *s; + + /* Now set up and run the modems */ + if ((data_modem_state = data_modems_init(NULL, + calling_party, + put_msg, + get_msg, + NULL)) == NULL) + { + fprintf(stderr, " Cannot start the data modem\n"); + exit(2); + } + logging = data_modems_get_logging_state(data_modem_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_DATE); + span_log_set_tag(logging, "Modem"); + + if ((s = socket_harness_init(NULL, + "/tmp/modemsocket", + "modemA", + calling_party, + terminal_callback, + termios_callback, + hangup_callback, + terminal_free_space_callback, + rx_callback, + rx_fillin_callback, + tx_callback, + data_modem_state)) == NULL) + { + fprintf(stderr, " Cannot start the socket harness\n"); + exit(2); + } + + wave_handle = NULL; + if (log_audio) + { + if ((wave_handle = sf_open_telephony_write(OUTPUT_WAVE_FILE_NAME, 2)) == NULL) + { + fprintf(stderr, " Cannot create audio file '%s'\n", OUTPUT_WAVE_FILE_NAME); + exit(2); + } + } + + socket_harness_run(s); + + if (log_audio) + { + if (sf_close_telephony(wave_handle)) + { + fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_WAVE_FILE_NAME); + exit(2); + } + } + + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + int log_audio; + int use_gui; + int opt; + bool calling_party; + + log_audio = false; + calling_party = false; + use_gui = false; + while ((opt = getopt(argc, argv, "acgl")) != -1) + { + switch (opt) + { + case 'a': + calling_party = false; + break; + case 'c': + calling_party = true; + break; + case 'g': +#if defined(ENABLE_GUI) + use_gui = true; +#else + fprintf(stderr, "Graphical monitoring not available\n"); + exit(2); +#endif + break; + case 'l': + log_audio = true; + break; + default: + //usage(); + exit(2); + break; + } + } + + if (modem_tests(use_gui, log_audio, calling_party)) + exit(2); + printf("Tests passed\n"); + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/tests/fax_tests.c b/libs/spandsp/tests/fax_tests.c index 955d4b7d25..96105edcae 100644 --- a/libs/spandsp/tests/fax_tests.c +++ b/libs/spandsp/tests/fax_tests.c @@ -29,15 +29,21 @@ \section fax_tests_page_sec_1 What does it do? These tests exercise the following FAX to FAX paths: - +--Modems-+-----------TDM/RTP-----------+-Modems--+ - | \ / | - | \ / | -T.30 <---+ T.38 gateway T.38 gateway +--->T.30 - | \ / | - | \ / | - +---T.38---+----+----UDPTL/RTP----+----+---T.38---+ - \ / - +----------TCP----------+ +TSB85 <-----------+ +-----------> TSB85 + \ / + T.31 <-----------+ \ / +-----------> T.31 + \ \ / / + +--Modems-+-+-----------TDM/RTP-----------+-+-Modems--+ + | \ / | + | \ / | + T.30 <---+ T.38 gateway T.38 gateway +---> T.30 + | \ / | + | \ / | + +---T.38---+-+----+----UDPTL/RTP----+----+ +---T.38---+ + / / \ / \ \ + T.31 <------------/ / +----------TCP----------+ \ +------------> T.31 + / \ +TSB85 <------------+ +------------> TSB85 T.30<->Modems<-------------------------TDM/RTP------------------------->Modems<->T.30 T.30<->Modems<-TDM/RTP->T.38 gateway<-UDPTL/RTP->T.38 gateway<-TDM/RTP->Modems<->T.30 @@ -45,6 +51,7 @@ T.30<->Modems<-TDM/RTP->T.38 gateway<-UDPTL/RTP-------------------------->T.38<- T.30<->T.38<--------------------------UDPTL/RTP->T.38 gateway<-TDM/RTP->Modems<->T.30 T.30<->T.38<--------------------------UDPTL/RTP-------------------------->T.38<->T.30 +The T.31 and TSB85 parts are incomplete right now. */ #if defined(HAVE_CONFIG_H) @@ -520,6 +527,7 @@ int main(int argc, char *argv[]) char *page_header_info; char *page_header_tz; const char *tag; + const char *xml_file_name; char buf[132 + 1]; #if defined(ENABLE_GUI) int use_gui; @@ -557,7 +565,8 @@ int main(int argc, char *argv[]) colour_enabled = false; t37_like_output = false; t38_transport = T38_TRANSPORT_UDPTL; - while ((opt = getopt(argc, argv, "7b:c:Cd:D:efFgH:i:Ilm:M:n:p:s:S:tT:u:v:z:")) != -1) + xml_file_name = "../spandsp/tsb85.xml"; + while ((opt = getopt(argc, argv, "7b:c:Cd:D:efFgH:i:Ilm:M:n:p:s:S:tT:u:v:x:z:")) != -1) { switch (opt) { @@ -682,6 +691,9 @@ int main(int argc, char *argv[]) case 'v': t38_version = atoi(optarg); break; + case 'x': + xml_file_name = optarg; + break; case 'z': page_header_tz = optarg; break; @@ -736,8 +748,9 @@ int main(int argc, char *argv[]) tag = (i == 0) ? "A" : "B"; memset(&expected_rx_info[i], 0, sizeof(expected_rx_info[i])); - if (mode[i] == T38_TERMINAL_FAX) + switch (mode[i]) { + case T38_TERMINAL_FAX: if ((t38_state[i] = t38_terminal_init(NULL, (i == 0), tx_packet_handler, (void *) (intptr_t) i)) == NULL) { fprintf(stderr, "Cannot start the T.38 terminal instance\n"); @@ -757,9 +770,9 @@ int main(int argc, char *argv[]) logging = t30_get_logging_state(t30_state[i]); span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); span_log_set_tag(logging, tag); - } - else - { + break; + case AUDIO_FAX: + case T38_GATEWAY_FAX: if ((fax_state[i] = fax_init(NULL, (i == 0))) == NULL) { fprintf(stderr, "Cannot start FAX instance\n"); @@ -783,7 +796,7 @@ int main(int argc, char *argv[]) { if ((t38_gateway_state[i] = t38_gateway_init(NULL, tx_packet_handler, (void *) (intptr_t) i)) == NULL) { - fprintf(stderr, "Cannot start the T.38 gateway instancel\n"); + fprintf(stderr, "Cannot start the T.38 gateway instance\n"); exit(2); } t38_core_state[i] = t38_gateway_get_t38_core_state(t38_gateway_state[i]); @@ -820,6 +833,17 @@ int main(int argc, char *argv[]) signal_scaling = powf(10.0f, signal_level/20.0f); printf("Signal scaling %f\n", signal_scaling); } + break; + case T31_AUDIO_FAX: + break; + case T31_T38_TERMINAL_FAX: + case T31_T38_GATEWAY_FAX: + break; + case TSB85_AUDIO_FAX: + break; + case TSB85_T38_TERMINAL_FAX: + case TSB85_T38_GATEWAY_FAX: + break; } set_t30_callbacks(t30_state[i], i); } diff --git a/libs/spandsp/tests/socket_harness.c b/libs/spandsp/tests/socket_harness.c new file mode 100644 index 0000000000..71b22b90b0 --- /dev/null +++ b/libs/spandsp/tests/socket_harness.c @@ -0,0 +1,346 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * socket_harness.c + * + * Written by Steve Underwood + * + * Copyright (C) 2007 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/*! \page bitstream_tests_page Bitstream tests +\section bitstream_tests_page_sec_1 What does it do? + +\section bitstream_tests_page_sec_2 How is it used? +*/ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES + +#include "spandsp.h" + +#include "pseudo_terminals.h" +#include "socket_harness.h" + +//#define SIMULATE_RING 1 + +#define CLOSE_COUNT_MAX 100 + +/* static data */ +static int16_t inbuf[4096]; +static int16_t outbuf[4096]; + +static volatile sig_atomic_t keep_running = true; + +static void log_signal(int signum) +{ + fprintf(stderr, "Signal %d: mark termination.\n", signum); + keep_running = false; + exit(2); +} +/*- End of function --------------------------------------------------------*/ + +int socket_harness_run(socket_harness_state_t *s) +{ + struct timeval tmo; + fd_set rset; + fd_set eset; + struct termios termios; + int max_fd; + int count; + int samples; + int tx_samples; + int ret; + + while (keep_running) + { + //if (s->modem->event) + // modem_event(s->modem); +#ifdef SIMULATE_RING + tmo.tv_sec = 0; + tmo.tv_usec= 1000000/RING_HZ; +#else + tmo.tv_sec = 1; + tmo.tv_usec= 0; +#endif + max_fd = 0; + FD_ZERO(&rset); + FD_ZERO(&eset); + FD_SET(s->audio_fd, &rset); + FD_SET(s->audio_fd, &eset); + FD_SET(s->pty_fd, &rset); + FD_SET(s->pty_fd, &eset); + if (s->audio_fd > max_fd) + max_fd = s->audio_fd; + if (s->pty_fd > max_fd) + max_fd = s->pty_fd; + if (s->pty_closed && s->close_count) + { + if (!s->started || s->close_count++ > CLOSE_COUNT_MAX) + s->close_count = 0; + } + else if (s->terminal_free_space_callback(s->user_data)) + { + FD_SET(s->pty_fd, &rset); + if (s->pty_fd > max_fd) + max_fd = s->pty_fd; + } + if ((ret = select(max_fd + 1, &rset, NULL, &eset, &tmo)) < 0) + { + if (errno == EINTR) + continue; + fprintf(stderr, "Error: select: %s\n", strerror(errno)); + return ret; + } + + if (ret == 0) + { + /* Timeout */ +#ifdef SIMULATE_RING + if (!modem->modem->started) + { + rcount++; + if (rcount <= RING_ON) + modem_ring(modem->modem); + else if (rcount > RING_OFF) + rcount = 0; + } +#endif + continue; + } + + if (FD_ISSET(s->audio_fd, &rset)) + { + if ((count = read(s->audio_fd, inbuf, sizeof(inbuf)/2)) < 0) + { + if (errno != EAGAIN) + { + fprintf(stderr, "Error: audio read: %s\n", strerror(errno)); + return -1; + } + count = 0; + } + if (count == 0) + { + fprintf(stderr, "Audio socket closed\n"); + return 0; + } + samples = count/2; + usleep(125*samples); + + s->rx_callback(s->user_data, inbuf, samples); + tx_samples = s->tx_callback(s->user_data, outbuf, samples); + if (tx_samples < samples) + memset(&outbuf[tx_samples], 0, (samples - tx_samples)*2); + + if ((count = write(s->audio_fd, outbuf, samples*2)) < 0) + { + if (errno != EAGAIN) + { + fprintf(stderr, "Error: audio write: %s\n", strerror(errno)); + return -1; + } + /* TODO: */ + } + if (count != samples*2) + fprintf(stderr, "audio write = %d\n", count); + } + + if (FD_ISSET(s->pty_fd, &rset)) + { + /* Check termios */ + tcgetattr(s->pty_fd, &termios); + if (memcmp(&termios, &s->termios, sizeof(termios))) + s->termios_callback(s->user_data, &termios); + /* Read data */ + if ((count = s->terminal_free_space_callback(s->user_data))) + { + if (count > sizeof(inbuf)) + count = sizeof(inbuf); + if ((count = read(s->pty_fd, inbuf, count)) < 0) + { + if (errno == EAGAIN) + { + fprintf(stderr, "pty read, errno = EAGAIN\n"); + } + else + { + if (errno == EIO) + { + if (!s->pty_closed) + { + fprintf(stderr, "pty closed.\n"); + s->pty_closed = 1; + if ((termios.c_cflag & HUPCL)) + s->hangup_callback(s->user_data, 0); + } + s->close_count = 1; + } + else + { + fprintf(stderr, "Error: pty read: %s\n", strerror(errno)); + return -1; + } + } + } + else + { + if (count == 0) + fprintf(stderr, "pty read = 0\n"); + s->pty_closed = false; + s->terminal_callback(s->user_data, (uint8_t *) inbuf, count); + } + } + } + } + + return 0; +} +/*- End of function --------------------------------------------------------*/ + +socket_harness_state_t *socket_harness_init(socket_harness_state_t *s, + const char *socket_name, + const char *tag, + int caller, + put_msg_func_t terminal_callback, + termio_update_func_t termios_callback, + modem_status_func_t hangup_callback, + put_msg_free_space_func_t terminal_free_space_callback, + span_rx_handler_t rx_callback, + span_rx_fillin_handler_t rx_fillin_callback, + span_tx_handler_t tx_callback, + void *user_data) +{ + int sockfd; + int listensockfd; + struct sockaddr_un serv_addr; + struct sockaddr_un cli_addr; + socklen_t servlen; + socklen_t clilen; + + if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + fprintf(stderr, "Socket failed - errno = %d\n", errno); + return NULL; + } + + if (s == NULL) + { + if ((s = (socket_harness_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + memset(s, 0, sizeof(*s)); + + signal(SIGINT, log_signal); + signal(SIGTERM, log_signal); + + s->terminal_callback = terminal_callback; + s->termios_callback = termios_callback; + s->hangup_callback = hangup_callback; + s->terminal_free_space_callback = terminal_free_space_callback; + + s->rx_callback = rx_callback; + s->rx_fillin_callback = rx_fillin_callback; + s->tx_callback = tx_callback; + + s->user_data = user_data; + + memset((char *) &serv_addr, 0, sizeof(serv_addr)); + serv_addr.sun_family = AF_UNIX; + /* This is a generic Unix domain socket. */ + strcpy(serv_addr.sun_path, socket_name); + printf("Creating socket '%s'\n", serv_addr.sun_path); + servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family) + 1; + if (caller) + { + fprintf(stderr, "Connecting to '%s'\n", serv_addr.sun_path); + if (connect(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0) + { + fprintf(stderr, "Connect failed - errno = %d\n", errno); + exit(2); + } + fprintf(stderr, "Connected to '%s'\n", serv_addr.sun_path); + } + else + { + fprintf(stderr, "Listening to '%s'\n", serv_addr.sun_path); + listensockfd = sockfd; + /* The file may or may not exist. Just try to delete it anyway. */ + unlink(serv_addr.sun_path); + if (bind(listensockfd, (struct sockaddr *) &serv_addr, servlen) < 0) + { + fprintf(stderr, "Bind failed - errno = %d\n", errno); + exit(2); + } + listen(listensockfd, 5); + clilen = sizeof(cli_addr); + if ((sockfd = accept(listensockfd, (struct sockaddr *) &cli_addr, &clilen)) < 0) + { + fprintf(stderr, "Accept failed - errno = %d", errno); + exit(2); + } + fprintf(stderr, "Accepted on '%s'\n", serv_addr.sun_path); + } + if (pseudo_terminal_create(&s->modem)) + { + fprintf(stderr, "Failed to create pseudo TTY\n"); + exit(2); + } + s->audio_fd = sockfd; + s->pty_fd = s->modem.master; + return s; +} +/*- End of function --------------------------------------------------------*/ + +int socket_harness_release(socket_harness_state_t *s) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int socket_harness_free(socket_harness_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/tests/socket_harness.h b/libs/spandsp/tests/socket_harness.h new file mode 100644 index 0000000000..9b05d5cfbc --- /dev/null +++ b/libs/spandsp/tests/socket_harness.h @@ -0,0 +1,73 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * socket_harness.h + * + * Written by Steve Underwood + * + * 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 General Public License version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +typedef int (*termio_update_func_t)(void *user_data, struct termios *termios); + +typedef int (*put_msg_free_space_func_t)(void *user_data); + +typedef struct socket_harness_state_s +{ + void *user_data; + + put_msg_func_t terminal_callback; + termio_update_func_t termios_callback; + modem_status_func_t hangup_callback; + put_msg_free_space_func_t terminal_free_space_callback; + + span_rx_handler_t rx_callback; + span_rx_fillin_handler_t rx_fillin_callback; + span_tx_handler_t tx_callback; + + int audio_fd; + int pty_fd; + logging_state_t logging; + struct termios termios; + + unsigned int delay; + unsigned int started; + unsigned pty_closed; + unsigned close_count; + + modem_t modem; +} socket_harness_state_t; + +int socket_harness_run(socket_harness_state_t *s); + +socket_harness_state_t *socket_harness_init(socket_harness_state_t *s, + const char *socket_name, + const char *tag, + int caller, + put_msg_func_t terminal_callback, + termio_update_func_t termios_callback, + modem_status_func_t hangup_callback, + put_msg_free_space_func_t terminal_free_space_callback, + span_rx_handler_t rx_callback, + span_rx_fillin_handler_t rx_fillin_callback, + span_tx_handler_t tx_callback, + void *user_data); + +int socket_harness_release(socket_harness_state_t *s); + +int socket_harness_free(socket_harness_state_t *s); diff --git a/libs/spandsp/unpack_gsm0610_data.sh b/libs/spandsp/unpack_gsm0610_data.sh index 482334c79e..cba7349dea 100755 --- a/libs/spandsp/unpack_gsm0610_data.sh +++ b/libs/spandsp/unpack_gsm0610_data.sh @@ -53,7 +53,7 @@ else cd gsm0610 fi -if [ $1x = --no-exe-runx ] +if [ $1x == --no-exe-runx ] then # Run the .exe files, which should be here ./FR_A.EXE @@ -77,7 +77,7 @@ rm -rf READ_FRA.TXT rm -rf ACTION rm -rf unpacked -if [ $1x = --no-exex ] +if [ $1x == --no-exex ] then # We need to prepare the .exe files to be run separately rm -rf *.INP diff --git a/libs/spandsp/yum-prepare.sh b/libs/spandsp/yum-prepare.sh index e41200482b..bcdb01e8cb 100644 --- a/libs/spandsp/yum-prepare.sh +++ b/libs/spandsp/yum-prepare.sh @@ -1,5 +1,4 @@ -: - +#!/bin/sh # # Install the things which need adding to a fresh Fedora or Centos install to make it ready to build # spandsp and its test suite @@ -19,7 +18,7 @@ yum install fftw-devel \ sox \ gcc-c++ \ libtool \ - autconf \ + autoconf \ automake \ m4 \ netpbm \