/* * SpanDSP - a series of DSP components for telephony * * tsb85_tests.c * * Written by Steve Underwood * * Copyright (C) 2008 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. * * $Id: tsb85_tests.c,v 1.30 2009/02/20 12:34:20 steveu Exp $ */ /*! \file */ #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #if defined(HAVE_TGMATH_H) #include #endif #if defined(HAVE_MATH_H) #include #endif #include "floating_fudge.h" #include #include #include #include #include #if defined(HAVE_LIBXML_XMLMEMORY_H) #include #endif #if defined(HAVE_LIBXML_PARSER_H) #include #endif #if defined(HAVE_LIBXML_XINCLUDE_H) #include #endif //#if defined(WITH_SPANDSP_INTERNALS) #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES //#endif #include "spandsp.h" #include "spandsp-sim.h" #include "fax_tester.h" #include "fax_utils.h" #define OUTPUT_TIFF_FILE_NAME "tsb85.tif" #define OUTPUT_FILE_NAME_WAVE "tsb85.wav" #define SAMPLES_PER_CHUNK 160 AFfilehandle out_handle; int use_receiver_not_ready = FALSE; int test_local_interrupt = FALSE; const char *output_tiff_file_name; fax_state_t *fax; faxtester_state_t state; uint8_t image[1000000]; uint8_t awaited[1000]; int awaited_len = 0; char image_path[1024]; t30_exchanged_info_t expected_rx_info; char next_tx_file[1000]; static int next_step(faxtester_state_t *s); static int phase_b_handler(t30_state_t *s, void *user_data, int result) { int i; int status; const char *u; i = (intptr_t) user_data; status = T30_ERR_OK; if ((u = t30_get_rx_ident(s))) { printf("%c: Phase B: remote ident '%s'\n", i, u); if (expected_rx_info.ident[0] && strcmp(expected_rx_info.ident, u)) { printf("%c: Phase B: remote ident incorrect! - expected '%s'\n", i, expected_rx_info.ident); status = T30_ERR_IDENT_UNACCEPTABLE; } } else { if (expected_rx_info.ident[0]) { printf("%c: Phase B: remote ident missing!\n", i); status = T30_ERR_IDENT_UNACCEPTABLE; } } if ((u = t30_get_rx_sub_address(s))) { printf("%c: Phase B: remote sub-address '%s'\n", i, u); if (expected_rx_info.sub_address[0] && strcmp(expected_rx_info.sub_address, u)) { printf("%c: Phase B: remote sub-address incorrect! - expected '%s'\n", i, expected_rx_info.sub_address); status = T30_ERR_SUB_UNACCEPTABLE; } } else { if (expected_rx_info.sub_address[0]) { printf("%c: Phase B: remote sub-address missing!\n", i); status = T30_ERR_SUB_UNACCEPTABLE; } } if ((u = t30_get_rx_polled_sub_address(s))) { printf("%c: Phase B: remote polled sub-address '%s'\n", i, u); if (expected_rx_info.polled_sub_address[0] && strcmp(expected_rx_info.polled_sub_address, u)) { printf("%c: Phase B: remote polled sub-address incorrect! - expected '%s'\n", i, expected_rx_info.polled_sub_address); status = T30_ERR_PSA_UNACCEPTABLE; } } else { if (expected_rx_info.polled_sub_address[0]) { printf("%c: Phase B: remote polled sub-address missing!\n", i); status = T30_ERR_PSA_UNACCEPTABLE; } } if ((u = t30_get_rx_selective_polling_address(s))) { printf("%c: Phase B: remote selective polling address '%s'\n", i, u); if (expected_rx_info.selective_polling_address[0] && strcmp(expected_rx_info.selective_polling_address, u)) { printf("%c: Phase B: remote selective polling address incorrect! - expected '%s'\n", i, expected_rx_info.selective_polling_address); status = T30_ERR_SEP_UNACCEPTABLE; } } else { if (expected_rx_info.selective_polling_address[0]) { printf("%c: Phase B: remote selective polling address missing!\n", i); status = T30_ERR_SEP_UNACCEPTABLE; } } if ((u = t30_get_rx_sender_ident(s))) { printf("%c: Phase B: remote sender ident '%s'\n", i, u); if (expected_rx_info.sender_ident[0] && strcmp(expected_rx_info.sender_ident, u)) { printf("%c: Phase B: remote sender ident incorrect! - expected '%s'\n", i, expected_rx_info.sender_ident); status = T30_ERR_SID_UNACCEPTABLE; } } else { if (expected_rx_info.sender_ident[0]) { printf("%c: Phase B: remote sender ident missing!\n", i); status = T30_ERR_SID_UNACCEPTABLE; } } if ((u = t30_get_rx_password(s))) { printf("%c: Phase B: remote password '%s'\n", i, u); if (expected_rx_info.password[0] && strcmp(expected_rx_info.password, u)) { printf("%c: Phase B: remote password incorrect! - expected '%s'\n", i, expected_rx_info.password); status = T30_ERR_PWD_UNACCEPTABLE; } } else { if (expected_rx_info.password[0]) { printf("%c: Phase B: remote password missing!\n", i); status = T30_ERR_PWD_UNACCEPTABLE; } } printf("%c: Phase B handler on channel %d - (0x%X) %s\n", i, i, result, t30_frametype(result)); return status; } /*- End of function --------------------------------------------------------*/ static int phase_d_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 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); if (use_receiver_not_ready) t30_set_receiver_not_ready(s, 3); if (test_local_interrupt) { if (i == 'A') { printf("%c: Initiating interrupt request\n", i); t30_local_interrupt_request(s, TRUE); } else { switch (result) { case T30_PIP: case T30_PRI_MPS: case T30_PRI_EOM: case T30_PRI_EOP: printf("%c: Accepting interrupt request\n", i); t30_local_interrupt_request(s, TRUE); break; case T30_PIN: break; } } } 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("%c: Phase E handler on channel %c - (%d) %s\n", i, i, result, t30_completion_code_to_str(result)); log_transfer_statistics(s, tag); log_tx_parameters(s, tag); log_rx_parameters(s, tag); } /*- End of function --------------------------------------------------------*/ static void t30_real_time_frame_handler(t30_state_t *s, void *user_data, int direction, const uint8_t *msg, int len) { if (msg == NULL) { } else { fprintf(stderr, "T.30: Real time frame handler - %s, %s, length = %d\n", (direction) ? "line->T.30" : "T.30->line", t30_frametype(msg[2]), len); } } /*- End of function --------------------------------------------------------*/ static int document_handler(t30_state_t *s, void *user_data, int event) { int i; i = (intptr_t) user_data; fprintf(stderr, "%d: Document handler on channel %d - event %d\n", i, i, event); if (next_tx_file[0]) { t30_set_tx_file(s, next_tx_file, -1, -1); next_tx_file[0] = '\0'; return TRUE; } return FALSE; } /*- End of function --------------------------------------------------------*/ static void faxtester_real_time_frame_handler(faxtester_state_t *s, void *user_data, int direction, const uint8_t *msg, int len) { if (msg == NULL) { while (next_step(s) == 0) ; /*endwhile*/ } else { fprintf(stderr, "TST: Real time frame handler - %s, %s, length = %d\n", (direction) ? "line->tester" : "tester->line", t30_frametype(msg[2]), len); if (direction && msg[1] == awaited[1]) { if ((awaited_len >= 0 && len != abs(awaited_len)) || (awaited_len < 0 && len < abs(awaited_len)) || memcmp(msg, awaited, abs(awaited_len)) != 0) { span_log_buf(&s->logging, SPAN_LOG_FLOW, "Expected", awaited, abs(awaited_len)); span_log_buf(&s->logging, SPAN_LOG_FLOW, "Received", msg, len); exit(2); } } if (msg[1] == awaited[1]) { while (next_step(s) == 0) ; /*endwhile*/ } } } /*- End of function --------------------------------------------------------*/ static void faxtester_front_end_step_complete_handler(faxtester_state_t *s, void *user_data) { while (next_step(s) == 0) ; /*endwhile*/ } /*- End of function --------------------------------------------------------*/ static void faxtester_front_end_step_timeout_handler(faxtester_state_t *s, void *user_data) { span_log(&s->logging, SPAN_LOG_FLOW, "FAX tester step timed out\n"); exit(2); } /*- End of function --------------------------------------------------------*/ static void fax_prepare(void) { t30_state_t *t30; logging_state_t *logging; t30 = fax_get_t30_state(fax); fax_set_transmit_on_idle(fax, TRUE); fax_set_tep_mode(fax, TRUE); #if 0 t30_set_tx_ident(t30, "1234567890"); t30_set_tx_sub_address(t30, "Sub-address"); t30_set_tx_sender_ident(t30, "Sender ID"); t30_set_tx_password(t30, "Password"); t30_set_tx_polled_sub_address(t30, "Polled sub-address"); t30_set_tx_selective_polling_address(t30, "Sel polling address"); #endif t30_set_tx_nsf(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSF\x00", 16); //t30_set_tx_nss(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSS\x00", 16); t30_set_tx_nsc(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSC\x00", 16); t30_set_ecm_capability(t30, TRUE); t30_set_supported_t30_features(t30, T30_SUPPORT_IDENTIFICATION | T30_SUPPORT_SELECTIVE_POLLING | T30_SUPPORT_SUB_ADDRESSING); t30_set_supported_image_sizes(t30, T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH | T30_SUPPORT_215MM_WIDTH | T30_SUPPORT_255MM_WIDTH | T30_SUPPORT_303MM_WIDTH); t30_set_supported_resolutions(t30, T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION | T30_SUPPORT_R8_RESOLUTION | T30_SUPPORT_R16_RESOLUTION | T30_SUPPORT_300_300_RESOLUTION | T30_SUPPORT_400_400_RESOLUTION | T30_SUPPORT_600_600_RESOLUTION | T30_SUPPORT_1200_1200_RESOLUTION | T30_SUPPORT_300_600_RESOLUTION | T30_SUPPORT_400_800_RESOLUTION | T30_SUPPORT_600_1200_RESOLUTION); t30_set_supported_modems(t30, T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17); t30_set_supported_compressions(t30, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); t30_set_phase_b_handler(t30, phase_b_handler, (void *) (intptr_t) 'A'); t30_set_phase_d_handler(t30, phase_d_handler, (void *) (intptr_t) 'A'); t30_set_phase_e_handler(t30, phase_e_handler, (void *) (intptr_t) 'A'); t30_set_real_time_frame_handler(t30, t30_real_time_frame_handler, (void *) (intptr_t) 'A'); t30_set_document_handler(t30, document_handler, (void *) (intptr_t) 'A'); logging = fax_get_logging_state(fax); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); span_log_set_tag(logging, "A"); logging = t30_get_logging_state(t30); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); span_log_set_tag(logging, "A"); #if 0 span_log_set_level(&fax.modems.v27ter_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); span_log_set_tag(&fax.modems.v27ter_rx.logging, "A"); span_log_set_level(&fax.modems.v29_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); span_log_set_tag(&fax.modems.v29_rx.logging, "A"); span_log_set_level(&fax.modems.v17_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); span_log_set_tag(&fax.modems.v17_rx.logging, "A"); #endif } /*- End of function --------------------------------------------------------*/ static int string_to_msg(uint8_t msg[], uint8_t mask[], const char buf[]) { int i; int x; const char *t; msg[0] = 0; mask[0] = 0xFF; i = 0; t = (char *) buf; while (*t) { /* Skip white space */ while (isspace(*t)) t++; /* If we find ... we allow arbitrary addition info beyond this point in the message */ if (t[0] == '.' && t[1] == '.' && t[2] == '.') { return -i; } else if (isxdigit(*t)) { for ( ; isxdigit(*t); t++) { x = *t; if (x >= 'a') x -= 0x20; if (x >= 'A') x -= ('A' - 10); else x -= '0'; msg[i] = (msg[i] << 4) | x; } mask[i] = 0xFF; if (*t == '/') { /* There is a mask following the byte */ mask[i] = 0; for (t++; isxdigit(*t); t++) { x = *t; if (x >= 'a') x -= 0x20; if (x >= 'A') x -= ('A' - 10); else x -= '0'; mask[i] = (mask[i] << 4) | x; } } if (*t && !isspace(*t)) { /* Bad string */ return 0; } i++; } } return i; } /*- End of function --------------------------------------------------------*/ #if 0 static void string_test2(const uint8_t msg[], int len) { int i; if (len > 0) { for (i = 0; i < len - 1; i++) printf("%02X ", msg[i]); printf("%02X", msg[i]); } } /*- End of function --------------------------------------------------------*/ static void string_test3(const char buf[]) { uint8_t msg[1000]; uint8_t mask[1000]; int len; int i; len = string_to_msg(msg, mask, buf); printf("Len = %d: ", len); string_test2(msg, abs(len)); printf("/"); string_test2(mask, abs(len)); printf("\n"); } /*- End of function --------------------------------------------------------*/ static int string_test(void) { string_test3("FF C8 12 34 56 78"); string_test3("FF C8 12/55 34 56/aA 78 "); string_test3("FF C8 12/55 34 56/aA 78 ..."); string_test3("FF C8 12/55 34 56/aA 78..."); string_test3("FF C8 12/55 34 56/aA 78 ... 99 88 77"); exit(0); } /*- End of function --------------------------------------------------------*/ #endif static void corrupt_image(faxtester_state_t *s, uint8_t image[], int len, const char *bad_rows) { int i; int j; int k; uint32_t bits; uint32_t bitsx; int list[1000]; int x; int row; const char *t; /* Form the list of rows to be hit */ x = 0; t = bad_rows; while (*t) { while (isspace(*t)) t++; if (sscanf(t, "%d", &list[x]) < 1) break; x++; while (isdigit(*t)) t++; if (*t == ',') t++; } /* Go through the image, and corrupt the first bit of every listed row */ bits = 0x7FF; bitsx = 0x7FF; row = 0; for (i = 0; i < len; i++) { bits ^= (image[i] << 11); bitsx ^= (image[i] << 11); for (j = 0; j < 8; j++) { if ((bits & 0xFFF) == 0x800) { /* We are at an EOL. Is this row in the list of rows to be corrupted? */ row++; for (k = 0; k < x; k++) { if (list[k] == row) { /* Corrupt this row. TSB85 says to hit the first bit after the EOL */ bitsx ^= 0x1000; } } } bits >>= 1; bitsx >>= 1; } image[i] = (bitsx >> 3) & 0xFF; } span_log(&s->logging, SPAN_LOG_FLOW, "%d rows found. %d corrupted\n", row, x); } /*- End of function --------------------------------------------------------*/ static int next_step(faxtester_state_t *s) { int delay; int flags; xmlChar *dir; xmlChar *type; xmlChar *modem; xmlChar *value; xmlChar *tag; xmlChar *bad_rows; xmlChar *crc_error; xmlChar *pattern; xmlChar *timeout; xmlChar *min_bits; xmlChar *frame_size; xmlChar *block; xmlChar *compression; uint8_t buf[1000]; uint8_t mask[1000]; char path[1024]; int i; int j; int hdlc; int short_train; int min_row_bits; int ecm_frame_size; int ecm_block; int compression_type; int timer; int len; t4_state_t t4_state; t30_state_t *t30; if (s->cur == NULL) { if (!s->final_delayed) { /* Add a bit of waiting at the end, to ensure everything gets flushed through, any timers can expire, etc. */ faxtester_set_timeout(s, -1); faxtester_set_rx_type(s, T30_MODEM_NONE, 0, FALSE, FALSE); faxtester_set_tx_type(s, T30_MODEM_PAUSE, 0, 120000, FALSE); s->final_delayed = TRUE; return 1; } /* Finished */ exit(0); } while (s->cur && xmlStrcmp(s->cur->name, (const xmlChar *) "step") != 0) s->cur = s->cur->next; if (s->cur == NULL) { /* Finished */ exit(0); } dir = xmlGetProp(s->cur, (const xmlChar *) "dir"); type = xmlGetProp(s->cur, (const xmlChar *) "type"); modem = xmlGetProp(s->cur, (const xmlChar *) "modem"); value = xmlGetProp(s->cur, (const xmlChar *) "value"); tag = xmlGetProp(s->cur, (const xmlChar *) "tag"); bad_rows = xmlGetProp(s->cur, (const xmlChar *) "bad_rows"); crc_error = xmlGetProp(s->cur, (const xmlChar *) "crc_error"); pattern = xmlGetProp(s->cur, (const xmlChar *) "pattern"); timeout = xmlGetProp(s->cur, (const xmlChar *) "timeout"); min_bits = xmlGetProp(s->cur, (const xmlChar *) "min_bits"); frame_size = xmlGetProp(s->cur, (const xmlChar *) "frame_size"); block = xmlGetProp(s->cur, (const xmlChar *) "block"); compression = xmlGetProp(s->cur, (const xmlChar *) "compression"); s->cur = s->cur->next; span_log(&s->logging, SPAN_LOG_FLOW, "Dir - %s, type - %s, modem - %s, value - %s, timeout - %s, tag - %s\n", (dir) ? (const char *) dir : "", (type) ? (const char *) type : "", (modem) ? (const char *) modem : "", (value) ? (const char *) value : "", (timeout) ? (const char *) timeout : "", (tag) ? (const char *) tag : ""); if (type == NULL) return 1; if (timeout) timer = atoi((const char *) timeout); else timer = -1; if (dir && strcasecmp((const char *) dir, "R") == 0) { /* Receive always has a timeout applied. */ if (timer < 0) timer = 7000; faxtester_set_timeout(s, timer); if (modem) { hdlc = (strcasecmp((const char *) type, "PREAMBLE") == 0); short_train = (strcasecmp((const char *) type, "TCF") != 0); faxtester_set_tx_type(s, T30_MODEM_NONE, 0, FALSE, FALSE); if (strcasecmp((const char *) modem, "V.21") == 0) { faxtester_set_rx_type(s, T30_MODEM_V21, 300, FALSE, TRUE); } else if (strcasecmp((const char *) modem, "V.17/14400") == 0) { faxtester_set_rx_type(s, T30_MODEM_V17, 14400, short_train, hdlc); } else if (strcasecmp((const char *) modem, "V.17/12000") == 0) { faxtester_set_rx_type(s, T30_MODEM_V17, 12000, short_train, hdlc); } else if (strcasecmp((const char *) modem, "V.17/9600") == 0) { faxtester_set_rx_type(s, T30_MODEM_V17, 9600, short_train, hdlc); } else if (strcasecmp((const char *) modem, "V.17/7200") == 0) { faxtester_set_rx_type(s, T30_MODEM_V17, 7200, short_train, hdlc); } else if (strcasecmp((const char *) modem, "V.29/9600") == 0) { faxtester_set_rx_type(s, T30_MODEM_V29, 9600, FALSE, hdlc); } else if (strcasecmp((const char *) modem, "V.29/7200") == 0) { faxtester_set_rx_type(s, T30_MODEM_V29, 7200, FALSE, hdlc); } else if (strcasecmp((const char *) modem, "V.27ter/4800") == 0) { faxtester_set_rx_type(s, T30_MODEM_V27TER, 4800, FALSE, hdlc); } else if (strcasecmp((const char *) modem, "V.27ter/2400") == 0) { faxtester_set_rx_type(s, T30_MODEM_V27TER, 2400, FALSE, hdlc); } else { span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised modem\n"); } } if (strcasecmp((const char *) type, "SET") == 0) { if (strcasecmp((const char *) tag, "IDENT") == 0) strcpy(expected_rx_info.ident, (const char *) value); else if (strcasecmp((const char *) tag, "SUB") == 0) strcpy(expected_rx_info.sub_address, (const char *) value); else if (strcasecmp((const char *) tag, "SEP") == 0) strcpy(expected_rx_info.selective_polling_address, (const char *) value); else if (strcasecmp((const char *) tag, "PSA") == 0) strcpy(expected_rx_info.polled_sub_address, (const char *) value); else if (strcasecmp((const char *) tag, "SID") == 0) strcpy(expected_rx_info.sender_ident, (const char *) value); else if (strcasecmp((const char *) tag, "PWD") == 0) strcpy(expected_rx_info.password, (const char *) value); return 0; } else if (strcasecmp((const char *) type, "CNG") == 0) { /* Look for CNG */ faxtester_set_rx_type(s, T30_MODEM_CNG, 0, FALSE, FALSE); faxtester_set_tx_type(s, T30_MODEM_NONE, 0, FALSE, FALSE); } else if (strcasecmp((const char *) type, "CED") == 0) { /* Look for CED */ faxtester_set_rx_type(s, T30_MODEM_CED, 0, FALSE, FALSE); faxtester_set_tx_type(s, T30_MODEM_NONE, 0, FALSE, FALSE); } else if (strcasecmp((const char *) type, "HDLC") == 0) { i = string_to_msg(buf, mask, (const char *) value); bit_reverse(awaited, buf, abs(i)); awaited_len = i; } else if (strcasecmp((const char *) type, "TCF") == 0) { } else if (strcasecmp((const char *) type, "MSG") == 0) { } else if (strcasecmp((const char *) type, "PP") == 0) { } else if (strcasecmp((const char *) type, "SILENCE") == 0) { faxtest_set_rx_silence(s); } else { span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised type '%s'\n", (const char *) type); return 0; } } else { faxtester_set_timeout(s, timer); if (modem) { hdlc = (strcasecmp((const char *) type, "PREAMBLE") == 0); short_train = (strcasecmp((const char *) type, "TCF") != 0); faxtester_set_rx_type(s, T30_MODEM_NONE, 0, FALSE, FALSE); if (strcasecmp((const char *) modem, "V.21") == 0) { faxtester_set_tx_type(s, T30_MODEM_V21, 300, FALSE, TRUE); } else if (strcasecmp((const char *) modem, "V.17/14400") == 0) { faxtester_set_tx_type(s, T30_MODEM_V17, 14400, short_train, hdlc); } else if (strcasecmp((const char *) modem, "V.17/12000") == 0) { faxtester_set_tx_type(s, T30_MODEM_V17, 12000, short_train, hdlc); } else if (strcasecmp((const char *) modem, "V.17/9600") == 0) { faxtester_set_tx_type(s, T30_MODEM_V17, 9600, short_train, hdlc); } else if (strcasecmp((const char *) modem, "V.17/7200") == 0) { faxtester_set_tx_type(s, T30_MODEM_V17, 7200, short_train, hdlc); } else if (strcasecmp((const char *) modem, "V.29/9600") == 0) { faxtester_set_tx_type(s, T30_MODEM_V29, 9600, FALSE, hdlc); } else if (strcasecmp((const char *) modem, "V.29/7200") == 0) { faxtester_set_tx_type(s, T30_MODEM_V29, 7200, FALSE, hdlc); } else if (strcasecmp((const char *) modem, "V.27ter/4800") == 0) { faxtester_set_tx_type(s, T30_MODEM_V27TER, 4800, FALSE, hdlc); } else if (strcasecmp((const char *) modem, "V.27ter/2400") == 0) { faxtester_set_tx_type(s, T30_MODEM_V27TER, 2400, FALSE, hdlc); } else { span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised modem\n"); } } if (strcasecmp((const char *) type, "SET") == 0) { t30 = fax_get_t30_state(fax); if (strcasecmp((const char *) tag, "IDENT") == 0) t30_set_tx_ident(t30, (const char *) value); else if (strcasecmp((const char *) tag, "SUB") == 0) t30_set_tx_sub_address(t30, (const char *) value); else if (strcasecmp((const char *) tag, "SEP") == 0) t30_set_tx_selective_polling_address(t30, (const char *) value); else if (strcasecmp((const char *) tag, "PSA") == 0) t30_set_tx_polled_sub_address(t30, (const char *) value); else if (strcasecmp((const char *) tag, "SID") == 0) t30_set_tx_sender_ident(t30, (const char *) value); else if (strcasecmp((const char *) tag, "PWD") == 0) t30_set_tx_password(t30, (const char *) value); else if (strcasecmp((const char *) tag, "RXFILE") == 0) { if (value) t30_set_rx_file(t30, (const char *) value, -1); else t30_set_rx_file(t30, output_tiff_file_name, -1); } else if (strcasecmp((const char *) tag, "TXFILE") == 0) { sprintf(next_tx_file, "%s/%s", image_path, (const char *) value); printf("Push '%s'\n", next_tx_file); } return 0; } else if (strcasecmp((const char *) type, "CALL") == 0) { fax = fax_init(NULL, FALSE); fax_prepare(); next_tx_file[0] = '\0'; t30 = fax_get_t30_state(fax); t30_set_rx_file(t30, output_tiff_file_name, -1); /* Avoid libtiff 3.8.2 and earlier bug on complex 2D lines. */ t30_set_rx_encoding(t30, T4_COMPRESSION_ITU_T4_1D); if (value) { sprintf(path, "%s/%s", image_path, (const char *) value); t30_set_tx_file(t30, path, -1, -1); } return 0; } else if (strcasecmp((const char *) type, "ANSWER") == 0) { fax = fax_init(NULL, TRUE); fax_prepare(); next_tx_file[0] = '\0'; t30 = fax_get_t30_state(fax); /* Avoid libtiff 3.8.2 and earlier bug on complex 2D lines. */ t30_set_rx_encoding(t30, T4_COMPRESSION_ITU_T4_1D); if (value) { sprintf(path, "%s/%s", image_path, (const char *) value); t30_set_tx_file(t30, path, -1, -1); } return 0; } else if (strcasecmp((const char *) type, "CNG") == 0) { faxtester_set_rx_type(s, T30_MODEM_NONE, 0, FALSE, FALSE); faxtester_set_tx_type(s, T30_MODEM_CNG, 0, FALSE, FALSE); } else if (strcasecmp((const char *) type, "CED") == 0) { faxtester_set_rx_type(s, T30_MODEM_NONE, 0, FALSE, FALSE); faxtester_set_tx_type(s, T30_MODEM_CED, 0, FALSE, FALSE); } else if (strcasecmp((const char *) type, "WAIT") == 0) { delay = (value) ? atoi((const char *) value) : 1; faxtester_set_rx_type(s, T30_MODEM_NONE, 0, FALSE, FALSE); faxtester_set_tx_type(s, T30_MODEM_PAUSE, 0, delay, FALSE); } else if (strcasecmp((const char *) type, "PREAMBLE") == 0) { flags = (value) ? atoi((const char *) value) : 37; faxtester_send_hdlc_flags(s, flags); } else if (strcasecmp((const char *) type, "POSTAMBLE") == 0) { flags = (value) ? atoi((const char *) value) : 5; faxtester_send_hdlc_flags(s, flags); } else if (strcasecmp((const char *) type, "HDLC") == 0) { i = string_to_msg(buf, mask, (const char *) value); bit_reverse(buf, buf, abs(i)); if (crc_error && strcasecmp((const char *) crc_error, "0") == 0) faxtester_send_hdlc_msg(s, buf, abs(i), FALSE); else faxtester_send_hdlc_msg(s, buf, abs(i), TRUE); } else if (strcasecmp((const char *) type, "TCF") == 0) { if (value) i = atoi((const char *) value); else i = 450; if (pattern) { /* TODO: implement proper patterns */ j = atoi((const char *) pattern); memset(image, 0x55, j); if (i > j) memset(image + j, 0, i - j); } else { memset(image, 0, i); } faxtester_set_non_ecm_image_buffer(s, image, i); } else if (strcasecmp((const char *) type, "MSG") == 0) { /* A non-ECM page */ min_row_bits = (min_bits) ? atoi((const char *) min_bits) : 0; sprintf(path, "%s/%s", image_path, (const char *) value); if (t4_tx_init(&t4_state, path, -1, -1) == NULL) { span_log(&s->logging, SPAN_LOG_FLOW, "Failed to init T.4 send\n"); exit(2); } t4_tx_set_min_row_bits(&t4_state, min_row_bits); t4_tx_set_header_info(&t4_state, NULL); compression_type = T4_COMPRESSION_ITU_T4_1D; if (compression) { if (strcasecmp((const char *) compression, "T.4 2D") == 0) compression_type = T4_COMPRESSION_ITU_T4_2D; else if (strcasecmp((const char *) compression, "T.6") == 0) compression_type = T4_COMPRESSION_ITU_T6; } t4_tx_set_tx_encoding(&t4_state, compression_type); if (t4_tx_start_page(&t4_state)) { span_log(&s->logging, SPAN_LOG_FLOW, "Failed to start T.4 send\n"); exit(2); } len = t4_tx_get_chunk(&t4_state, image, sizeof(image)); if (bad_rows) { span_log(&s->logging, SPAN_LOG_FLOW, "We need to corrupt the image\n"); corrupt_image(s, image, len, (const char *) bad_rows); } t4_tx_release(&t4_state); span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM image is %d bytes\n", len); faxtester_set_non_ecm_image_buffer(s, image, len); } else if (strcasecmp((const char *) type, "PP") == 0) { min_row_bits = (min_bits) ? atoi((const char *) min_bits) : 0; ecm_block = (block) ? atoi((const char *) block) : 0; ecm_frame_size = (frame_size) ? atoi((const char *) frame_size) : 64; i = (crc_error) ? atoi((const char *) crc_error) : -1; sprintf(path, "%s/%s", image_path, (const char *) value); if (t4_tx_init(&t4_state, path, -1, -1) == NULL) { span_log(&s->logging, SPAN_LOG_FLOW, "Failed to init T.4 send\n"); exit(2); } t4_tx_set_min_row_bits(&t4_state, min_row_bits); t4_tx_set_header_info(&t4_state, NULL); compression_type = T4_COMPRESSION_ITU_T4_1D; if (compression) { if (strcasecmp((const char *) compression, "T.4 2D") == 0) compression_type = T4_COMPRESSION_ITU_T4_2D; else if (strcasecmp((const char *) compression, "T.6") == 0) compression_type = T4_COMPRESSION_ITU_T6; } t4_tx_set_tx_encoding(&t4_state, compression_type); if (t4_tx_start_page(&t4_state)) { span_log(&s->logging, SPAN_LOG_FLOW, "Failed to start T.4 send\n"); exit(2); } /*endif*/ len = t4_tx_get_chunk(&t4_state, image, sizeof(image)); if (bad_rows) { span_log(&s->logging, SPAN_LOG_FLOW, "We need to corrupt the image\n"); corrupt_image(s, image, len, (const char *) bad_rows); } /*endif*/ t4_tx_release(&t4_state); span_log(&s->logging, SPAN_LOG_FLOW, "ECM image is %d bytes\n", len); faxtester_set_ecm_image_buffer(s, image, len, ecm_block, ecm_frame_size, i); } else { span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised type '%s'\n", (const char *) type); return 0; } /*endif*/ } /*endif*/ return 1; } /*- End of function --------------------------------------------------------*/ static void exchange(faxtester_state_t *s) { int16_t amp[SAMPLES_PER_CHUNK]; int16_t out_amp[2*SAMPLES_PER_CHUNK]; int len; int i; int total_audio_time; int log_audio; logging_state_t *logging; log_audio = TRUE; output_tiff_file_name = OUTPUT_TIFF_FILE_NAME; if (log_audio) { if ((out_handle = afOpenFile_telephony_write(OUTPUT_FILE_NAME_WAVE, 2)) == AF_NULL_FILEHANDLE) { fprintf(stderr, " Cannot create wave file '%s'\n", OUTPUT_FILE_NAME_WAVE); exit(2); } /*endif*/ } /*endif*/ total_audio_time = 0; faxtester_set_transmit_on_idle(&state, TRUE); faxtester_set_real_time_frame_handler(&state, faxtester_real_time_frame_handler, NULL); faxtester_set_front_end_step_complete_handler(&state, faxtester_front_end_step_complete_handler, NULL); faxtester_set_front_end_step_timeout_handler(&state, faxtester_front_end_step_timeout_handler, NULL); fax = fax_init(NULL, FALSE); fax_prepare(); next_tx_file[0] = '\0'; while (next_step(s) == 0) ; /*endwhile*/ for (;;) { len = fax_tx(fax, amp, SAMPLES_PER_CHUNK); faxtester_rx(s, amp, len); if (log_audio) { for (i = 0; i < len; i++) out_amp[2*i + 0] = amp[i]; /*endfor*/ } /*endif*/ total_audio_time += SAMPLES_PER_CHUNK; logging = t30_get_logging_state(fax_get_t30_state(fax)); span_log_bump_samples(logging, len); #if 0 span_log_bump_samples(&fax.modems.v27ter_rx.logging, len); span_log_bump_samples(&fax.modems.v29_rx.logging, len); span_log_bump_samples(&fax.modems.v17_rx.logging, len); #endif logging = fax_get_logging_state(fax); span_log_bump_samples(logging, len); span_log_bump_samples(&s->logging, len); len = faxtester_tx(s, amp, 160); if (fax_rx(fax, amp, len)) break; /*endif*/ if (log_audio) { for (i = 0; i < len; i++) out_amp[2*i + 1] = amp[i]; /*endfor*/ if (afWriteFrames(out_handle, AF_DEFAULT_TRACK, out_amp, SAMPLES_PER_CHUNK) != SAMPLES_PER_CHUNK) break; /*endif*/ } /*endif*/ } /*endfor*/ if (log_audio) { if (afCloseFile(out_handle)) { fprintf(stderr, " Cannot close wave file '%s'\n", OUTPUT_FILE_NAME_WAVE); exit(2); } /*endif*/ } /*endif*/ } /*- End of function --------------------------------------------------------*/ static int parse_config(faxtester_state_t *s, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur) { xmlChar *x; xmlChar *y; while (cur) { if (xmlStrcmp(cur->name, (const xmlChar *) "path") == 0) { if ((x = xmlGetProp(cur, (const xmlChar *) "type")) && (y = xmlGetProp(cur, (const xmlChar *) "value"))) { if (strcasecmp((const char *) x, "IMAGE") == 0) { span_log(&s->logging, SPAN_LOG_FLOW, "Found '%s' '%s'\n", (char *) x, (char *) y); strcpy(image_path, (const char *) y); } /*endif*/ } /*endif*/ } /*endif*/ cur = cur->next; } /*endwhile*/ return -1; } /*- End of function --------------------------------------------------------*/ static int parse_test_group(faxtester_state_t *s, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, const char *test) { xmlChar *x; while (cur) { if (xmlStrcmp(cur->name, (const xmlChar *) "test") == 0) { if ((x = xmlGetProp(cur, (const xmlChar *) "name"))) { if (xmlStrcmp(x, (const xmlChar *) test) == 0) { span_log(&s->logging, SPAN_LOG_FLOW, "Found '%s'\n", (char *) x); s->cur = cur->xmlChildrenNode; return 0; } /*endif*/ } /*endif*/ } /*endif*/ cur = cur->next; } /*endwhile*/ return -1; } /*- End of function --------------------------------------------------------*/ static int get_test_set(faxtester_state_t *s, const char *test_file, const char *test) { xmlDocPtr doc; xmlNsPtr ns; xmlNodePtr cur; xmlValidCtxt valid; ns = NULL; xmlKeepBlanksDefault(0); xmlCleanupParser(); if ((doc = xmlParseFile(test_file)) == NULL) { fprintf(stderr, "No document\n"); exit(2); } /*endif*/ xmlXIncludeProcess(doc); if (!xmlValidateDocument(&valid, doc)) { fprintf(stderr, "Invalid document\n"); exit(2); } /*endif*/ /* Check the document is of the right kind */ if ((cur = xmlDocGetRootElement(doc)) == NULL) { fprintf(stderr, "Empty document\n"); xmlFreeDoc(doc); exit(2); } /*endif*/ if (xmlStrcmp(cur->name, (const xmlChar *) "fax-tests")) { fprintf(stderr, "Document of the wrong type, root node != fax-tests"); xmlFreeDoc(doc); exit(2); } /*endif*/ cur = cur->xmlChildrenNode; while (cur && xmlIsBlankNode(cur)) cur = cur->next; /*endwhile*/ if (cur == NULL) exit(2); /*endif*/ while (cur) { if (xmlStrcmp(cur->name, (const xmlChar *) "config") == 0) { parse_config(s, doc, ns, cur->xmlChildrenNode); } /*endif*/ if (xmlStrcmp(cur->name, (const xmlChar *) "test-group") == 0) { if (parse_test_group(s, doc, ns, cur->xmlChildrenNode, test) == 0) { /* We found the test we want, so run it. */ exchange(s); break; } /*endif*/ } /*endif*/ cur = cur->next; } /*endwhile*/ xmlFreeDoc(doc); return 0; } /*- End of function --------------------------------------------------------*/ int main(int argc, char *argv[]) { const char *test_name; //string_test(); test_name = "MRGN01"; if (argc > 1) test_name = argv[1]; strcpy(image_path, "."); faxtester_init(&state, TRUE); memset(&expected_rx_info, 0, sizeof(expected_rx_info)); span_log_set_level(&state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); span_log_set_tag(&state.logging, "B"); get_test_set(&state, "../spandsp/tsb85.xml", test_name); printf("Done\n"); return 0; } /*- End of function --------------------------------------------------------*/ /*- End of file ------------------------------------------------------------*/