AC_CHECK_HEADERS([string.h])
AC_CHECK_HEADERS([strings.h])
AC_CHECK_HEADERS([malloc.h])
+AC_CHECK_HEADERS([math.h], [INSERT_MATH_HEADER="#include <math.h>"])
+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*)
;;
esac
-AC_CHECK_HEADERS([math.h], [INSERT_MATH_HEADER="#include <math.h>"])
-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])
complex_vector_float.c \
complex_vector_int.c \
crc.c \
+ data_modems.c \
dds_float.c \
dds_int.c \
dtmf.c \
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 \
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 \
#include <spandsp/t30_logging.h>
#include <spandsp/t35.h>
#include <spandsp/at_interpreter.h>
+#include <spandsp/data_modems.h>
#include <spandsp/fax_modems.h>
#include <spandsp/fax.h>
#include <spandsp/t38_core.h>
#include <spandsp/private/v42.h>
#include <spandsp/private/v42bis.h>
#include <spandsp/private/at_interpreter.h>
-//#include <spandsp/private/data_modems.h>
+#include <spandsp/private/data_modems.h>
#include <spandsp/private/fax_modems.h>
#include <spandsp/private/timezone.h>
#include <spandsp/private/image_translate.h>
#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
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,
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);
}
/*- 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 --------------------------------------------------------*/
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;
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;
val |= 0x40;
if (offered_modulations & V8_MOD_V27TER)
val |= 0x80;
- v8_put_byte(s, val);
+ buf[ptr++] = val;
}
if (++bytes < s->modulation_bytes)
{
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) ? "<CM: " : "<JM: ", &buf[1], ptr - 1);
+ v8_put_bytes(s, buf, ptr);
}
/*- End of function --------------------------------------------------------*/
len = 0;
if (s->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);
}
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 --------------------------------------------------------*/
static void send_v92(v8_state_t *s)
{
int i;
+ uint8_t buf[2];
if (s->result.v92 >= 0)
{
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, "<V.92: ", &buf[1], 1);
+ v8_put_bytes(s, buf, 2);
}
}
}
static void send_ci(v8_state_t *s)
{
int i;
+ uint8_t buf[2];
/* Send 4 CI packets in a burst (the spec says at least 3) */
for (i = 0; i < 4; i++)
{
v8_put_preamble(s);
- v8_put_byte(s, V8_CI_SYNC_OCTET);
- v8_put_byte(s, (s->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, "<CI: ", &buf[1], 1);
+ v8_put_bytes(s, buf, 2);
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE_NONSTD(int) v8_rx(v8_state_t *s, const int16_t *amp, int len)
{
- int i;
int residual_samples;
int tone;
+ uint8_t buf[3];
//span_log(&s->logging, SPAN_LOG_FLOW, "v8_rx state %d\n", s->state);
residual_samples = 0;
/* 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, "<CJ: ", &buf[1], 2);
s->state = V8_CJ_ON;
s->fsk_tx_on = true;
break;
}
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
{
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 \
modem_monitor.h \
pcap_parse.h \
pseudo_terminals.h \
+ socket_harness.h \
udptl.h
ademco_contactid_tests_SOURCES = ademco_contactid_tests.c
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
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
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * data_modems_tests.c - Tests for data_modems.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <sndfile.h>
+
+#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 ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * dummy_modems_tests.c - Tests for data_modems connected together by sockets.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <termios.h>
+#include <sndfile.h>
+
+//#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 ------------------------------------------------------------*/
\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
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)
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;
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)
{
case 'v':
t38_version = atoi(optarg);
break;
+ case 'x':
+ xml_file_name = optarg;
+ break;
case 'z':
page_header_tz = optarg;
break;
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");
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");
{
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]);
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);
}
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * socket_harness.c
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * 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 <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <signal.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#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 ------------------------------------------------------------*/
--- /dev/null
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * socket_harness.h
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2012 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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);
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
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
-:
-
+#!/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
sox \
gcc-c++ \
libtool \
- autconf \
+ autoconf \
automake \
m4 \
netpbm \