Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/tests/t31_tests.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 15:50:58 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.6pre17/tests/t31_tests.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,950 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * t31_tests.c - Tests for the T.31 modem. + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2004 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. + * + * $Id: t31_tests.c,v 1.72 2009/05/30 15:23:14 steveu Exp $ + */ + +/*! \file */ + +/*! \page t31_tests_page T.31 tests +\section t31_tests_page_sec_1 What does it do? +*/ + +/* Enable the following definition to enable direct probing into the FAX structures */ +//#define WITH_SPANDSP_INTERNALS + +#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 <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <sndfile.h> + +//#if defined(WITH_SPANDSP_INTERNALS) +#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES +//#endif + +#include "spandsp.h" +#include "spandsp/t30_fcf.h" +#include "spandsp-sim.h" + +#if defined(ENABLE_GUI) +#include "media_monitor.h" +#endif +#include "fax_utils.h" + +#define INPUT_FILE_NAME "../test-data/itu/fax/itu1.tif" +#define OUTPUT_FILE_NAME "t31.tif" +#define OUTPUT_WAVE_FILE_NAME "t31_tests.wav" + +enum +{ + ETX = 0x03, + DLE = 0x10, + SUB = 0x1A +}; + +#define MANUFACTURER "www.soft-switch.org" + +#define SAMPLES_PER_CHUNK 160 + +struct command_response_s +{ + const char *command; + int len_command; + const char *response; + int len_response; +}; + +g1050_state_t *path_a_to_b; +g1050_state_t *path_b_to_a; + +double when = 0.0; + +#define EXCHANGE(a,b) {a, sizeof(a) - 1, b, sizeof(b) - 1} +#define RESPONSE(b) {"", 0, b, sizeof(b) - 1} +#define FAST_RESPONSE(b) {NULL, -1, b, sizeof(b) - 1} +#define FAST_SEND(b) {(const char *) 1, -2, b, sizeof(b) - 1} +#define FAST_SEND_TCF(b) {(const char *) 2, -2, b, sizeof(b) - 1} + +static const struct command_response_s fax_send_test_seq[] = +{ + EXCHANGE("ATE0\r", "ATE0\r\r\nOK\r\n"), + EXCHANGE("AT+FCLASS=1\r", "\r\nOK\r\n"), + EXCHANGE("ATD123456789\r", "\r\nCONNECT\r\n"), + //<NSF frame> AT+FRH=3 is implied when dialing in the AT+FCLASS=1 state + //RESPONSE("\xFF\x03\x10\x03"), + //RESPONSE("\r\nOK\r\n"), + //EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), + //<CSI frame data> + RESPONSE("\xFF\x03\x40\x31\x31\x31\x31\x31\x31\x31\x31\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x1e\x46\x10\x03"), + RESPONSE("\r\nOK\r\n"), + EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), + //<DIS frame data> + RESPONSE("\xFF\x13\x80\x00\xEE\xF8\x80\x80\x91\x80\x80\x80\x18\x78\x57\x10\x03"), // For audio FAXing + //RESPONSE("\xFF\x13\x80\x04\xEE\xF8\x80\x80\x91\x80\x80\x80\x18\xE4\xE7\x10\x03"), // For T.38 FAXing + RESPONSE("\r\nOK\r\n"), + //EXCHANGE("AT+FRH=3\r", "\r\nNO CARRIER\r\n"), + EXCHANGE("AT+FTH=3\r", "\r\nCONNECT\r\n"), + //<TSI frame data> + EXCHANGE("\xFF\x03\x43\x32\x32\x32\x32\x32\x32\x32\x32\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x10\x03", "\r\nCONNECT\r\n"), + //<DCS frame data> + EXCHANGE("\xFF\x13\x83\x01\xC6\x80\x80\x80\x80\x01\xFD\x13\x10\x03", "\r\nOK\r\n"), + //Do a wait for timed silence at this point, or there won't be one in the tests + EXCHANGE("AT+FRS=7\r", "\r\nOK\r\n"), + //EXCHANGE("AT+FTS=8;+FTM=96\r", "\r\nCONNECT\r\n"), + EXCHANGE("AT+FTS=8\r", "\r\nOK\r\n"), + EXCHANGE("AT+FTM=96\r", "\r\nCONNECT\r\n"), + //<TCF data pattern> + FAST_SEND_TCF("\r\nOK\r\n"), + EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), + //<CFR frame data> + RESPONSE("\xFF\x13\x84\xEA\x7D\x10\x03"), + RESPONSE("\r\nOK\r\n"), + EXCHANGE("AT+FTM=96\r", "\r\nCONNECT\r\n"), + //<page image data> + FAST_SEND("\r\nOK\r\n"), + //EXCHANGE("AT+FTS=8;+FTH=3\r", "\r\nCONNECT\r\n"), + EXCHANGE("AT+FTS=8\r", "\r\nOK\r\n"), + EXCHANGE("AT+FTH=3\r", "\r\nCONNECT\r\n"), + //<EOP frame data> + EXCHANGE("\xFF\x13\x2E\x10\x03", "\r\nOK\r\n"), + EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), + //<MCF frame data> + RESPONSE("\xFF\x13\x8C\xA2\xF1\x10\x03"), + RESPONSE("\r\nOK\r\n"), + EXCHANGE("AT+FTH=3\r", "\r\nCONNECT\r\n"), + // <DCN frame data> + EXCHANGE("\xFF\x13\xFB\x10\x03", "\r\nOK\r\n"), + EXCHANGE("ATH0\r", "\r\nOK\r\n") +}; + +static const struct command_response_s fax_receive_test_seq[] = +{ + EXCHANGE("ATE0\r", "ATE0\r\r\nOK\r\n"), + EXCHANGE("AT+FCLASS=1\r", "\r\nOK\r\n"), + RESPONSE("\r\nRING\r\n"), + EXCHANGE("ATA\r", "\r\nCONNECT\r\n"), + //<CSI frame data> + EXCHANGE("\xFF\x03\x40\x32\x32\x32\x32\x32\x32\x32\x32\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x10\x03", "\r\nCONNECT\r\n"), + //<DIS frame data> + EXCHANGE("\xFF\x13\x80\x01\xCE\xF4\x80\x80\x81\x80\x80\x80\x18\x10\x03", "\r\nOK\r\n"), + EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), + //<TSI frame data> + RESPONSE("\xFF\x03\x43\x31\x31\x31\x31\x31\x31\x31\x31\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\xAA\x1F\x10\x03"), + RESPONSE("\r\nOK\r\n"), + EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), + //<DCS frame data> + RESPONSE("\xFF\x13\x83\x00\xC6\x74\x53\x00\x10\x03"), + RESPONSE("\r\nOK\r\n"), + EXCHANGE("AT+FRM=96\r", "\r\nCONNECT\r\n"), + //<TCF data> + FAST_RESPONSE(NULL), + RESPONSE("\r\nNO CARRIER\r\n"), + EXCHANGE("AT+FTH=3\r", "\r\nCONNECT\r\n"), + //<CFR frame data> + EXCHANGE("\xFF\x13\x84\x10\x03", "\r\nOK\r\n"), + EXCHANGE("AT+FRM=96\r", "\r\nCONNECT\r\n"), + //<page image data> + FAST_RESPONSE(NULL), + RESPONSE("\r\nNO CARRIER\r\n"), + EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), + //<EOP frame data> + RESPONSE("\xFF\x13\x2F\x33\x66\x10\x03"), + RESPONSE("\r\nOK\r\n"), + EXCHANGE("AT+FTH=3\r", "\r\nCONNECT\r\n"), + //<MCF frame data> + EXCHANGE("\xFF\x13\x8C\x10\x03", "\r\nOK\r\n"), + EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), + //<DCN frame data> + RESPONSE("\xFF\x13\xfb\x9a\xf6\x10\x03"), + RESPONSE("\r\nOK\r\n"), + EXCHANGE("ATH0\r", "\r\nOK\r\n") +}; + +char *decode_test_file = NULL; +int countdown = 0; +int command_response_test_step = -1; +char response_buf[1000]; +int response_buf_ptr = 0; +int answered = FALSE; +int kick = FALSE; +int dled = FALSE; +int done = FALSE; + +static const struct command_response_s *fax_test_seq; + +int test_seq_ptr = 0; + +t31_state_t *t31_state; + +static int phase_b_handler(t30_state_t *s, void *user_data, int result) +{ + int i; + char tag[20]; + + i = (int) (intptr_t) user_data; + snprintf(tag, sizeof(tag), "%c: Phase B:", i); + printf("%c: Phase B handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result)); + log_rx_parameters(s, tag); + return T30_ERR_OK; +} +/*- End of function --------------------------------------------------------*/ + +static int phase_d_handler(t30_state_t *s, void *user_data, int result) +{ + int i; + char tag[20]; + + i = (int) (intptr_t) user_data; + snprintf(tag, sizeof(tag), "%c: Phase D:", i); + printf("%c: Phase D handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result)); + log_transfer_statistics(s, tag); + log_tx_parameters(s, tag); + log_rx_parameters(s, tag); + return T30_ERR_OK; +} +/*- End of function --------------------------------------------------------*/ + +static void phase_e_handler(t30_state_t *s, void *user_data, int result) +{ + int i; + char tag[20]; + + i = (intptr_t) user_data; + snprintf(tag, sizeof(tag), "%c: Phase E:", i); + printf("Phase E handler on channel %c\n", i); + log_transfer_statistics(s, tag); + log_tx_parameters(s, tag); + log_rx_parameters(s, tag); + //exit(0); +} +/*- End of function --------------------------------------------------------*/ + +static int modem_call_control(t31_state_t *s, void *user_data, int op, const char *num) +{ + switch (op) + { + case AT_MODEM_CONTROL_ANSWER: + printf("\nModem control - Answering\n"); + answered = TRUE; + break; + case AT_MODEM_CONTROL_CALL: + printf("\nModem control - Dialing '%s'\n", num); + t31_call_event(t31_state, AT_CALL_EVENT_CONNECTED); + break; + case AT_MODEM_CONTROL_HANGUP: + printf("\nModem control - Hanging up\n"); + done = TRUE; + break; + case AT_MODEM_CONTROL_OFFHOOK: + printf("\nModem control - Going off hook\n"); + break; + case AT_MODEM_CONTROL_DTR: + printf("\nModem control - DTR %d\n", (int) (intptr_t) num); + break; + case AT_MODEM_CONTROL_RTS: + printf("\nModem control - RTS %d\n", (int) (intptr_t) num); + break; + case AT_MODEM_CONTROL_CTS: + printf("\nModem control - CTS %d\n", (int) (intptr_t) num); + break; + case AT_MODEM_CONTROL_CAR: + printf("\nModem control - CAR %d\n", (int) (intptr_t) num); + break; + case AT_MODEM_CONTROL_RNG: + printf("\nModem control - RNG %d\n", (int) (intptr_t) num); + break; + case AT_MODEM_CONTROL_DSR: + printf("\nModem control - DSR %d\n", (int) (intptr_t) num); + break; + default: + printf("\nModem control - operation %d\n", op); + break; + } + /*endswitch*/ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int at_tx_handler(at_state_t *s, void *user_data, const uint8_t *buf, size_t len) +{ + size_t i; + + i = 0; + if (fax_test_seq[test_seq_ptr].command == NULL) + { + /* TCF or non-ECM image data expected */ + for ( ; i < len; i++) + { + if (dled) + { + if (buf[i] == ETX) + { + printf("\nFast data ended\n"); + response_buf_ptr = 0; + response_buf[response_buf_ptr] = '\0'; + test_seq_ptr++; + if (fax_test_seq[test_seq_ptr].command) + kick = TRUE; + break; + } + dled = FALSE; + } + else + { + if (buf[i] == DLE) + dled = TRUE; + } + } + i++; + if (i >= len) + return 0; + } + for ( ; i < len; i++) + { + response_buf[response_buf_ptr++] = buf[i]; + putchar(buf[i]); + } + response_buf[response_buf_ptr] = '\0'; + printf("Expected "); + for (i = 0; i < response_buf_ptr; i++) + printf("%02x ", fax_test_seq[test_seq_ptr].response[i] & 0xFF); + printf("\n"); + printf("Response "); + for (i = 0; i < response_buf_ptr; i++) + printf("%02x ", response_buf[i] & 0xFF); + printf("\n"); +printf("Match %d against %d\n", response_buf_ptr, fax_test_seq[test_seq_ptr].len_response); + if (response_buf_ptr >= fax_test_seq[test_seq_ptr].len_response + && + memcmp(fax_test_seq[test_seq_ptr].response, response_buf, fax_test_seq[test_seq_ptr].len_response) == 0) + { + printf("\nMatched\n"); + test_seq_ptr++; + response_buf_ptr = 0; + response_buf[response_buf_ptr] = '\0'; + if (fax_test_seq[test_seq_ptr].command) + kick = TRUE; + else + dled = FALSE; + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count) +{ + t31_state_t *t; + int i; + + /* This routine queues messages between two instances of T.38 processing */ + t = (t31_state_t *) user_data; + span_log(&s->logging, SPAN_LOG_FLOW, "Send seq %d, len %d, count %d\n", s->tx_seq_no, len, count); + + for (i = 0; i < count; i++) + { + if (g1050_put(path_a_to_b, buf, len, s->tx_seq_no, when) < 0) + printf("Lost packet %d\n", s->tx_seq_no); + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int t31_tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count) +{ + //t38_terminal_state_t *t; + int i; + + /* This routine queues messages between two instances of T.38 processing */ + //t = (t38_terminal_state_t *) user_data; + span_log(&s->logging, SPAN_LOG_FLOW, "Send seq %d, len %d, count %d\n", s->tx_seq_no, len, count); + + for (i = 0; i < count; i++) + { + if (g1050_put(path_b_to_a, buf, len, s->tx_seq_no, when) < 0) + printf("Lost packet %d\n", s->tx_seq_no); + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int t38_tests(int use_gui, int test_sending, int model_no, int speed_pattern_no) +{ + t38_terminal_state_t *t38_state; + int fast_send; + int fast_send_tcf; + int fast_blocks; + uint8_t fast_buf[1000]; + int msg_len; + uint8_t msg[1024]; + int t38_version; + int without_pacing; + int use_tep; + int seq_no; + double tx_when; + double rx_when; + t30_state_t *t30; + t38_core_state_t *t38_core; + logging_state_t *logging; + int i; + + t38_version = 1; + without_pacing = FALSE; + use_tep = FALSE; + + srand48(0x1234567); + if ((path_a_to_b = g1050_init(model_no, speed_pattern_no, 100, 33)) == NULL) + { + fprintf(stderr, "Failed to start IP network path model\n"); + exit(2); + } + if ((path_b_to_a = g1050_init(model_no, speed_pattern_no, 100, 33)) == NULL) + { + fprintf(stderr, "Failed to start IP network path model\n"); + exit(2); + } + if ((t31_state = t31_init(NULL, at_tx_handler, NULL, modem_call_control, NULL, t31_tx_packet_handler, NULL)) == NULL) + { + fprintf(stderr, " Cannot start the T.31 T.38 modem\n"); + exit(2); + } + logging = t31_get_logging_state(t31_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, "T.31"); + + t38_core = t31_get_t38_core_state(t31_state); + span_log_set_level(&t38_core->logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(&t38_core->logging, "T.31"); + + span_log_set_level(&t31_state->at_state.logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(&t31_state->at_state.logging, "T.31"); + + t31_set_mode(t31_state, TRUE); + t38_set_t38_version(t38_core, t38_version); + + if (test_sending) + { + if ((t38_state = t38_terminal_init(NULL, FALSE, t38_tx_packet_handler, t31_state)) == NULL) + { + fprintf(stderr, "Cannot start the T.38 channel\n"); + exit(2); + } + t30 = t38_terminal_get_t30_state(t38_state); + t30_set_rx_file(t30, OUTPUT_FILE_NAME, -1); + fax_test_seq = fax_send_test_seq; + countdown = 0; + } + else + { + if ((t38_state = t38_terminal_init(NULL, TRUE, t38_tx_packet_handler, t31_state)) == NULL) + { + fprintf(stderr, "Cannot start the T.38 channel\n"); + exit(2); + } + t30 = t38_terminal_get_t30_state(t38_state); + t30_set_tx_file(t30, INPUT_FILE_NAME, -1, -1); + fax_test_seq = fax_receive_test_seq; + countdown = 250; + } + + t30 = t38_terminal_get_t30_state(t38_state); + t38_core = t38_terminal_get_t38_core_state(t38_state); + t38_set_t38_version(t38_core, t38_version); + t38_terminal_set_config(t38_state, without_pacing); + t38_terminal_set_tep_mode(t38_state, use_tep); + + t30_set_tx_ident(t30, "11111111"); + t30_set_supported_modems(t30, T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17); + //t30_set_tx_nsf(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp\x00", 12); + t30_set_phase_b_handler(t30, phase_b_handler, (void *) 'A'); + t30_set_phase_d_handler(t30, phase_d_handler, (void *) 'A'); + t30_set_phase_e_handler(t30, phase_e_handler, (void *) 'A'); + + logging = t38_terminal_get_logging_state(t38_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, "T.38"); + + t38_core = t38_terminal_get_t38_core_state(t38_state); + span_log_set_level(&t38_core->logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(&t38_core->logging, "T.38"); + + logging = t30_get_logging_state(t30); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, "T.38"); + + fast_send = FALSE; + fast_send_tcf = TRUE; + fast_blocks = 0; + kick = TRUE; +#if defined(ENABLE_GUI) + if (use_gui) + start_media_monitor(); +#endif + while (!done) + { + logging = t38_terminal_get_logging_state(t38_state); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + t38_core = t38_terminal_get_t38_core_state(t38_state); + logging = t38_core_get_logging_state(t38_core); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + t30 = t38_terminal_get_t30_state(t38_state); + logging = t30_get_logging_state(t30); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + + logging = t31_get_logging_state(t31_state); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + t38_core = t31_get_t38_core_state(t31_state); + logging = t38_core_get_logging_state(t38_core); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + span_log_bump_samples(&t31_state->at_state.logging, SAMPLES_PER_CHUNK); + + t38_terminal_send_timeout(t38_state, SAMPLES_PER_CHUNK); + t31_t38_send_timeout(t31_state, SAMPLES_PER_CHUNK); + + when += (float) SAMPLES_PER_CHUNK/(float) SAMPLE_RATE; + + if (kick) + { + kick = FALSE; + if (fax_test_seq[test_seq_ptr].command > (const char *) 2) + { + if (fax_test_seq[test_seq_ptr].command[0]) + { + printf("%s\n", fax_test_seq[test_seq_ptr].command); + t31_at_rx(t31_state, fax_test_seq[test_seq_ptr].command, fax_test_seq[test_seq_ptr].len_command); + } + } + else + { + if (fax_test_seq[test_seq_ptr].command == (const char *) 2) + { + printf("Fast send TCF\n"); + fast_send = TRUE; + fast_send_tcf = TRUE; + fast_blocks = 100; + } + else + { + printf("Fast send image\n"); + fast_send = TRUE; + fast_send_tcf = FALSE; + fast_blocks = 100; + } + } + } + if (fast_send) + { + /* Send fast modem data */ + if (fast_send_tcf) + { + memset(fast_buf, 0, 36); + } + else + { + if (fast_blocks == 1) + { + /* Create the end of page condition */ + for (i = 0; i < 36; i += 2) + { + fast_buf[i] = 0x00; + fast_buf[i + 1] = 0x80; + } + } + else + { + /* Create a chunk of white page */ + for (i = 0; i < 36; i += 4) + { + fast_buf[i] = 0x00; + fast_buf[i + 1] = 0x80; + fast_buf[i + 2] = 0xB2; + fast_buf[i + 3] = 0x01; + } + } + } + if (fast_blocks == 1) + { + /* Insert EOLs */ + fast_buf[35] = ETX; + fast_buf[34] = DLE; + fast_buf[31] = + fast_buf[28] = + fast_buf[25] = + fast_buf[22] = + fast_buf[19] = + fast_buf[16] = 1; + } + t31_at_rx(t31_state, (char *) fast_buf, 36); + if (--fast_blocks == 0) + fast_send = FALSE; + } + if (countdown) + { + if (answered) + { + countdown = 0; + t31_call_event(t31_state, AT_CALL_EVENT_ANSWERED); + } + else if (--countdown == 0) + { + t31_call_event(t31_state, AT_CALL_EVENT_ALERTING); + countdown = 250; + } + } + while ((msg_len = g1050_get(path_a_to_b, msg, 1024, when, &seq_no, &tx_when, &rx_when)) >= 0) + { +#if defined(ENABLE_GUI) + if (use_gui) + media_monitor_rx(seq_no, tx_when, rx_when); +#endif + t38_core = t31_get_t38_core_state(t31_state); + t38_core_rx_ifp_packet(t38_core, msg, msg_len, seq_no); + } + while ((msg_len = g1050_get(path_b_to_a, msg, 1024, when, &seq_no, &tx_when, &rx_when)) >= 0) + { +#if defined(ENABLE_GUI) + if (use_gui) + media_monitor_rx(seq_no, tx_when, rx_when); +#endif + t38_core = t38_terminal_get_t38_core_state(t38_state); + t38_core_rx_ifp_packet(t38_core, msg, msg_len, seq_no); + } +#if defined(ENABLE_GUI) + if (use_gui) + media_monitor_update_display(); +#endif + } + t38_terminal_release(t38_state); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int t30_tests(int log_audio, int test_sending) +{ + int k; + int outframes; + fax_state_t *fax_state; + int16_t t30_amp[SAMPLES_PER_CHUNK]; + int16_t t31_amp[SAMPLES_PER_CHUNK]; + int16_t silence[SAMPLES_PER_CHUNK]; + int16_t out_amp[2*SAMPLES_PER_CHUNK]; + int t30_len; + int t31_len; + SNDFILE *wave_handle; + SNDFILE *in_handle; + int fast_send; + int fast_send_tcf; + int fast_blocks; + uint8_t fast_buf[1000]; + t30_state_t *t30; + logging_state_t *logging; + int i; + + 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); + } + } + + memset(silence, 0, sizeof(silence)); + + 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); + } + } + + if ((t31_state = t31_init(NULL, at_tx_handler, NULL, modem_call_control, NULL, NULL, NULL)) == NULL) + { + fprintf(stderr, " Cannot start the T.31 FAX modem\n"); + exit(2); + } + logging = t31_get_logging_state(t31_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, "T.31"); + + if (test_sending) + { + fax_state = fax_init(NULL, FALSE); + t30 = fax_get_t30_state(fax_state); + t30_set_rx_file(t30, OUTPUT_FILE_NAME, -1); + fax_test_seq = fax_send_test_seq; + countdown = 0; + } + else + { + fax_state = fax_init(NULL, TRUE); + t30 = fax_get_t30_state(fax_state); + t30_set_tx_file(t30, INPUT_FILE_NAME, -1, -1); + fax_test_seq = fax_receive_test_seq; + countdown = 250; + } + + t30_set_tx_ident(t30, "11111111"); + t30_set_supported_modems(t30, T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17); + t30_set_phase_b_handler(t30, phase_b_handler, (void *) 'A'); + t30_set_phase_d_handler(t30, phase_d_handler, (void *) 'A'); + t30_set_phase_e_handler(t30, phase_e_handler, (void *) 'A'); + memset(t30_amp, 0, sizeof(t30_amp)); + + logging = t30_get_logging_state(t30); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, "FAX"); + + logging = fax_get_logging_state(fax_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, "FAX"); + + fast_send = FALSE; + fast_send_tcf = TRUE; + fast_blocks = 0; + kick = TRUE; + while (!done) + { + if (kick) + { + kick = FALSE; + if (fax_test_seq[test_seq_ptr].command > (const char *) 2) + { + if (fax_test_seq[test_seq_ptr].command[0]) + { + printf("%s\n", fax_test_seq[test_seq_ptr].command); + t31_at_rx(t31_state, fax_test_seq[test_seq_ptr].command, fax_test_seq[test_seq_ptr].len_command); + } + } + else + { + if (fax_test_seq[test_seq_ptr].command == (const char *) 2) + { + printf("Fast send TCF\n"); + fast_send = TRUE; + fast_send_tcf = TRUE; + fast_blocks = 100; + } + else + { + printf("Fast send image\n"); + fast_send = TRUE; + fast_send_tcf = FALSE; + fast_blocks = 100; + } + } + } + if (fast_send) + { + /* Send fast modem data */ + if (fast_send_tcf) + { + memset(fast_buf, 0, 36); + } + else + { + if (fast_blocks == 1) + { + /* Create the end of page condition */ + for (i = 0; i < 36; i += 2) + { + fast_buf[i] = 0x00; + fast_buf[i + 1] = 0x80; + } + } + else + { + /* Create a chunk of white page */ + for (i = 0; i < 36; i += 4) + { + fast_buf[i] = 0x00; + fast_buf[i + 1] = 0x80; + fast_buf[i + 2] = 0xB2; + fast_buf[i + 3] = 0x01; + } + } + } + if (fast_blocks == 1) + { + /* Insert EOLs */ + fast_buf[35] = ETX; + fast_buf[34] = DLE; + fast_buf[31] = + fast_buf[28] = + fast_buf[25] = + fast_buf[22] = + fast_buf[19] = + fast_buf[16] = 1; + } + t31_at_rx(t31_state, (char *) fast_buf, 36); + if (--fast_blocks == 0) + fast_send = FALSE; + } + t30_len = fax_tx(fax_state, t30_amp, SAMPLES_PER_CHUNK); + /* 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. */ + if (t30_len < SAMPLES_PER_CHUNK) + { + memset(t30_amp + t30_len, 0, sizeof(int16_t)*(SAMPLES_PER_CHUNK - t30_len)); + t30_len = SAMPLES_PER_CHUNK; + } + if (log_audio) + { + for (k = 0; k < t30_len; k++) + out_amp[2*k] = t30_amp[k]; + } + if (t31_rx(t31_state, t30_amp, t30_len)) + break; + if (countdown) + { + if (answered) + { + countdown = 0; + t31_call_event(t31_state, AT_CALL_EVENT_ANSWERED); + } + else if (--countdown == 0) + { + t31_call_event(t31_state, AT_CALL_EVENT_ALERTING); + countdown = 250; + } + } + + t31_len = t31_tx(t31_state, t31_amp, SAMPLES_PER_CHUNK); + if (t31_len < SAMPLES_PER_CHUNK) + { + memset(t31_amp + t31_len, 0, sizeof(int16_t)*(SAMPLES_PER_CHUNK - t31_len)); + t31_len = SAMPLES_PER_CHUNK; + } + if (log_audio) + { + for (k = 0; k < t31_len; k++) + out_amp[2*k + 1] = t31_amp[k]; + } + if (fax_rx(fax_state, t31_amp, SAMPLES_PER_CHUNK)) + break; + + span_log_bump_samples(&fax_state->logging, SAMPLES_PER_CHUNK); + span_log_bump_samples(&t30->logging, SAMPLES_PER_CHUNK); + + 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(in_handle) != 0) + { + fprintf(stderr, " Cannot close audio file '%s'\n", decode_test_file); + exit(2); + } + } + if (log_audio) + { + if (sf_close(wave_handle) != 0) + { + 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 t38_mode; + int test_sending; + int use_gui; + int opt; + + decode_test_file = NULL; + log_audio = FALSE; + test_sending = FALSE; + t38_mode = FALSE; + use_gui = FALSE; + while ((opt = getopt(argc, argv, "d:glrst")) != -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; + case 't': + t38_mode = TRUE; + break; + default: + //usage(); + exit(2); + break; + } + } + + if (t38_mode) + t38_tests(use_gui, test_sending, 0, 1); + else + t30_tests(log_audio, test_sending); + if (done) + { + printf("Tests passed\n"); + } + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/