m4_include(m4/ax_check_export_capability.m4)
m4_include(m4/ax_check_arm_neon.m4)
m4_include(m4/ax_func_aligned_alloc.m4)
-m4_include(m4/memmove.m4)
+m4_include(m4/ac_func_memmove.m4)
AC_CONFIG_SRCDIR([src/tone_generate.c])
AC_CONFIG_AUX_DIR([config])
}
/*- End of function --------------------------------------------------------*/
+SPAN_DECLARE(void) at_set_at_tx_handler(at_state_t *s,
+ at_tx_handler_t at_tx_handler,
+ void *at_tx_user_data)
+{
+ s->at_tx_handler = at_tx_handler;
+ s->at_tx_user_data = at_tx_user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
SPAN_DECLARE(logging_state_t *) at_get_logging_state(at_state_t *s)
{
return &s->logging;
#if defined(HAVE_MATH_H)
#include <math.h>
#endif
+#if defined(HAVE_STDBOOL_H)
+#include <stdbool.h>
+#else
+#include "spandsp/stdbool.h"
+#endif
#include "floating_fudge.h"
#include "spandsp/telephony.h"
#include "spandsp/private/awgn.h"
-/* Gaussian noise generator constants */
+/* Random number generator constants */
#define M1 259200
#define IA1 7141
#define IC1 54773
-#define RM1 (1.0/M1)
+#define RM1 (1.0/(double) M1)
#define M2 134456
#define IA2 8121
#define IC2 28411
-#define RM2 (1.0/M2)
+#define RM2 (1.0/(double) M2)
#define M3 243000
#define IA3 4561
#define IC3 51349
-static double ran1(awgn_state_t *s)
+static void ran_init(awgn_state_t *s, int idum)
+{
+ int j;
+
+ if (idum < 0)
+ idum = -idum;
+ s->ix1 = (IC1 + (int32_t) idum)%M1;
+ s->ix1 = (IA1*s->ix1 + IC1)%M1;
+ s->ix2 = s->ix1%M2;
+ s->ix1 = (IA1*s->ix1 + IC1)%M1;
+ s->ix3 = s->ix1%M3;
+ for (j = 0; j < 97; j++)
+ {
+ s->ix1 = (IA1*s->ix1 + IC1)%M1;
+ s->ix2 = (IA2*s->ix2 + IC2)%M2;
+ s->r[j] = (s->ix1 + s->ix2*RM2)*RM1;
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static double ran(awgn_state_t *s)
{
double temp;
int j;
+ /* This produces evenly spread random numbers between 0.0 and 1.0 */
s->ix1 = (IA1*s->ix1 + IC1)%M1;
s->ix2 = (IA2*s->ix2 + IC2)%M2;
s->ix3 = (IA3*s->ix3 + IC3)%M3;
- j = 1 + ((97*s->ix3)/M3);
- if (j > 97 || j < 1)
+ j = (97*s->ix3)/M3;
+ if (j > 96 || j < 0)
{
/* Error */
- return -1;
+ temp = -1.0;
+ }
+ else
+ {
+ temp = s->r[j];
+ s->r[j] = (s->ix1 + s->ix2*RM2)*RM1;
}
- temp = s->r[j];
- s->r[j] = (s->ix1 + s->ix2*RM2)*RM1;
return temp;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(awgn_state_t *) awgn_init_dbov(awgn_state_t *s, int idum, float level)
{
- int j;
-
if (s == NULL)
{
if ((s = (awgn_state_t *) span_alloc(sizeof(*s))) == NULL)
return NULL;
}
- if (idum < 0)
- idum = -idum;
- s->rms = pow(10.0, level/20.0)*32768.0;
+ ran_init(s, idum);
- s->ix1 = (IC1 + idum)%M1;
- s->ix1 = (IA1*s->ix1 + IC1)%M1;
- s->ix2 = s->ix1%M2;
- s->ix1 = (IA1*s->ix1 + IC1)%M1;
- s->ix3 = s->ix1%M3;
- s->r[0] = 0.0;
- for (j = 1; j <= 97; j++)
- {
- s->ix1 = (IA1*s->ix1 + IC1)%M1;
- s->ix2 = (IA2*s->ix2 + IC2)%M2;
- s->r[j] = (s->ix1 + s->ix2*RM2)*RM1;
- }
- s->gset = 0.0;
- s->iset = 0;
+ s->rms = pow(10.0, level/20.0)*32768.0;
+ s->amp2 = 0.0;
+ s->odd = true;
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int16_t) awgn(awgn_state_t *s)
{
- double fac;
double r;
double v1;
double v2;
double amp;
- if (s->iset == 0)
+ /* The polar method of generating a Gaussian distribution */
+ if ((s->odd = !s->odd))
+ {
+ amp = s->amp2;
+ }
+ else
{
do
{
- v1 = 2.0*ran1(s) - 1.0;
- v2 = 2.0*ran1(s) - 1.0;
+ v1 = 2.0*ran(s) - 1.0;
+ v2 = 2.0*ran(s) - 1.0;
r = v1*v1 + v2*v2;
}
while (r >= 1.0);
- fac = sqrt(-2.0*log(r)/r);
- s->gset = v1*fac;
- s->iset = 1;
- amp = v2*fac*s->rms;
- }
- else
- {
- s->iset = 0;
- amp = s->gset*s->rms;
+ r = sqrt(-2.0*log(r)/r);
+ s->amp2 = v1*r;
+ amp = v2*r;
}
+ amp *= s->rms;
return fsaturate(amp);
}
/*- End of function --------------------------------------------------------*/
#include "spandsp/tone_detect.h"
#include "spandsp/tone_generate.h"
#include "spandsp/async.h"
+#include "spandsp/at_interpreter.h"
#include "spandsp/silence_gen.h"
#include "spandsp/fsk.h"
#include "spandsp/v29rx.h"
#include "spandsp/data_modems.h"
#include "spandsp/private/logging.h"
+#include "spandsp/private/at_interpreter.h"
#include "spandsp/private/silence_gen.h"
#include "spandsp/private/power_meter.h"
#include "spandsp/private/fsk.h"
case DATA_MODEM_V34:
return "V.34 duplex";
}
+ /*endswitch*/
return "???";
}
/*- End of function --------------------------------------------------------*/
}
/*- End of function --------------------------------------------------------*/
+SPAN_DECLARE(void) data_modems_call_event(data_modems_state_t *s, int event)
+{
+ span_log(&s->logging, SPAN_LOG_FLOW, "Call event %s (%d) received\n", at_call_state_to_str(event), event);
+ at_call_event(&s->at_state, event);
+}
+/*- End of function --------------------------------------------------------*/
+
static int async_get_byte(void *user_data)
{
data_modems_state_t *s;
msg[0] = byte;
if (byte < 0)
s->put_msg(s->user_data, msg, byte);
+ /*endif*/
s->put_msg(s->user_data, msg, 1);
}
/*- End of function --------------------------------------------------------*/
span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s%s", comma, v8_modulation_to_str(modulation_schemes & (1 << i)));
comma = ", ";
}
+ /*endif*/
}
+ /*endfor*/
span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, " supported\n");
}
/*- End of function --------------------------------------------------------*/
v8_parms.modem_connect_tone = MODEM_CONNECT_TONES_NONE;
else
v8_parms.modem_connect_tone = MODEM_CONNECT_TONES_ANSAM_PR;
+ /*endif*/
v8_parms.send_ci = false;
v8_parms.v92 = -1;
v8_parms.call_function = V8_CALL_V_SERIES;
| V8_MOD_V23
| V8_MOD_V27TER
| V8_MOD_V29
+#if defined(SPANDSP_SUPPORT_V34)
+ | V8_MOD_V34HDX
+#endif
| 0;
v8_parms.protocol = V8_PROTOCOL_LAPM_V42;
#elif 1
v8_parms.nsf = -1;
v8_parms.t66 = -1;
v8_init(&s->modems.v8, s->calling_party, &v8_parms, v8_handler, (void *) s);
- logging = v8_get_logging_state(&s->modems.v8);
- level = span_log_get_level(&s->logging);
- span_log_set_level(logging, level);
- span_log_set_tag(logging, "V.8");
+ logging = v8_get_logging_state(&s->modems.v8);
+ level = span_log_get_level(&s->logging);
+ span_log_set_level(logging, level);
+ span_log_set_tag(logging, "V.8");
break;
case DATA_MODEM_BELL103:
s->rx_handler = (span_rx_handler_t) &fsk_rx;
fsk_rx_spec = &preset_fsk_specs[FSK_BELL103CH1];
fsk_tx_spec = &preset_fsk_specs[FSK_BELL103CH2];
}
+ /*endif*/
fsk_rx_init(&s->modems.fsk.rx, fsk_rx_spec, FSK_FRAME_MODE_SYNC, s->put_bit, s->put_user_data);
fsk_tx_init(&s->modems.fsk.tx, fsk_tx_spec, s->get_bit, s->get_user_data);
break;
fsk_rx_spec = &preset_fsk_specs[FSK_V21CH1];
fsk_tx_spec = &preset_fsk_specs[FSK_V21CH2];
}
+ /*endif*/
fsk_rx_init(&s->modems.fsk.rx, fsk_rx_spec, FSK_FRAME_MODE_SYNC, s->put_bit, s->put_user_data);
fsk_tx_init(&s->modems.fsk.tx, fsk_tx_spec, s->get_bit, s->get_user_data);
break;
fsk_rx_spec = &preset_fsk_specs[FSK_V23CH1];
fsk_tx_spec = &preset_fsk_specs[FSK_V23CH2];
}
+ /*endif*/
fsk_rx_init(&s->modems.fsk.rx, fsk_rx_spec, FSK_FRAME_MODE_SYNC, s->put_bit, s->put_user_data);
fsk_tx_init(&s->modems.fsk.tx, fsk_tx_spec, s->get_bit, s->get_user_data);
break;
s->tx_handler = (span_tx_handler_t) &v22bis_tx;
s->tx_user_data = &s->modems.v22bis;
v22bis_init(&s->modems.v22bis, bit_rate, 0, s->calling_party, s->get_bit, s->get_user_data, s->put_bit, s->put_user_data);
- logging = v22bis_get_logging_state(&s->modems.v22bis);
- level = span_log_get_level(&s->logging);
- span_log_set_level(logging, level);
- span_log_set_tag(logging, "V.22bis");
+ logging = v22bis_get_logging_state(&s->modems.v22bis);
+ level = span_log_get_level(&s->logging);
+ span_log_set_level(logging, level);
+ span_log_set_tag(logging, "V.22bis");
break;
#if defined(SPANDSP_SUPPORT_V32BIS)
case DATA_MODEM_V32BIS:
s->tx_handler = (span_tx_handler_t) &v32bis_tx;
s->tx_user_data = &s->modems.v32bis;
v32bis_init(&s->modems.v32bis, bit_rate, s->calling_party, s->get_bit, s->get_user_data, s->put_bit, s->put_user_data);
- logging = v32bis_get_logging_state(&s->modems.v32bis);
- level = span_log_get_level(&s->logging);
- span_log_set_level(logging, level);
- span_log_set_tag(logging, "V.32bis");
+ logging = v32bis_get_logging_state(&s->modems.v32bis);
+ level = span_log_get_level(&s->logging);
+ span_log_set_level(logging, level);
+ span_log_set_tag(logging, "V.32bis");
break;
#endif
#if defined(SPANDSP_SUPPORT_V34)
s->tx_handler = (span_tx_handler_t) &v34_tx;
s->tx_user_data = &s->modems.v34;
v34_init(&s->modems.v34, baud_rate, bit_rate, s->calling_party, true, s->get_bit, s->get_user_data, s->put_bit, s->put_user_data);
- logging = v34_get_logging_state(&s->modems.v34);
- level = span_log_get_level(&s->logging);
- span_log_set_level(logging, level);
- span_log_set_tag(logging, "V.34");
+ logging = v34_get_logging_state(&s->modems.v34);
+ level = span_log_get_level(&s->logging);
+ span_log_set_level(logging, level);
+ span_log_set_tag(logging, "V.34");
break;
#endif
}
+ /*endswitch*/
s->current_modem = which;
}
/*- End of function --------------------------------------------------------*/
if (s->rx_handler == NULL)
return len;
+ /*endif*/
res = s->rx_handler(s->rx_user_data, amp, len);
if (s->current_modem != s->queued_modem)
data_modems_set_modem_type(s, s->queued_modem, s->queued_baud_rate, s->queued_bit_rate);
+ /*endif*/
return res;
}
/*- End of function --------------------------------------------------------*/
{
if (s->rx_fillin_handler == NULL)
return len;
+ /*endif*/
return s->rx_fillin_handler(s->rx_user_data, len);
}
/*- End of function --------------------------------------------------------*/
{
if (s->tx_handler == NULL)
break;
+ /*endif*/
len += s->tx_handler(s->tx_user_data, &[len], max_len - len);
}
+ /*endfor*/
return len;
}
/*- End of function --------------------------------------------------------*/
+static int data_modems_control_handler(void *user_data, int op, const char *num)
+{
+ data_modems_state_t *s;
+
+ s = (data_modems_state_t *) user_data;
+ switch (op)
+ {
+ case AT_MODEM_CONTROL_CALL:
+ s->call_samples = 0;
+ break;
+ case AT_MODEM_CONTROL_ANSWER:
+ s->call_samples = 0;
+ break;
+ case AT_MODEM_CONTROL_ONHOOK:
+ if (s->at_state.rx_signal_present)
+ {
+ s->at_state.rx_data_bytes = 0;
+ }
+ /*endif*/
+ break;
+ case AT_MODEM_CONTROL_RESTART:
+ return 0;
+ case AT_MODEM_CONTROL_DTE_TIMEOUT:
+ return 0;
+ }
+ /*endswitch*/
+ return s->modem_control_handler(s, s->modem_control_user_data, op, num);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) data_modems_set_at_tx_handler(data_modems_state_t *s,
+ at_tx_handler_t at_tx_handler,
+ void *at_tx_user_data)
+{
+ at_set_at_tx_handler(&s->at_state, at_tx_handler, at_tx_user_data);
+}
+/*- End of function --------------------------------------------------------*/
+
SPAN_DECLARE(int) data_modems_restart(data_modems_state_t *s)
{
return 0;
SPAN_DECLARE(data_modems_state_t *) data_modems_init(data_modems_state_t *s,
bool calling_party,
+ at_tx_handler_t at_tx_handler,
+ void *at_tx_user_data,
+ data_modems_control_handler_t modem_control_handler,
+ void *modem_control_user_data,
put_msg_func_t put_msg,
get_msg_func_t get_msg,
void *user_data)
{
+ if (at_tx_handler == NULL || modem_control_handler == NULL)
+ return NULL;
+ /*endif*/
+
if (s == NULL)
{
if ((s = (data_modems_state_t *) span_alloc(sizeof(*s))) == NULL)
return NULL;
+ /*endif*/
}
+ /*endif*/
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "Modem");
dc_restore_init(&s->dc_restore);
+ s->modem_control_handler = modem_control_handler;
+ s->modem_control_user_data = modem_control_user_data;
+
s->put_msg = put_msg;
s->get_msg = get_msg;
s->user_data = user_data;
data_modems_set_async_mode(s, 8, 1, 1);
+ at_init(&s->at_state, at_tx_handler, at_tx_user_data, data_modems_control_handler, s);
+
s->get_bit = async_tx_get_bit;
s->get_user_data = &s->async_tx;
s->put_bit = async_rx_put_bit;
{
if (s)
span_free(s);
+ /*endif*/
return 0;
}
/*- End of function --------------------------------------------------------*/
{
if (s->octet_count_report_interval == 0)
return;
+ /*endif*/
/* If we are not in octet counting mode, we start it.
If we are in octet counting mode, we update it. */
/* An input byte will generate between 8 and 10 output bits */
return (s->octets_in_progress >> s->num_bits) & 0xFF;
}
+ /*endif*/
/* Untimed idling on flags */
if (s->tx_end)
{
{
if ((s->byte = hdlc_tx_get_byte(s)) < 0)
return s->byte;
+ /*endif*/
s->bits = 8;
}
+ /*endif*/
s->bits--;
txbit = (s->byte >> s->bits) & 0x01;
return txbit;
{
if ((x = hdlc_tx_get_byte(s)) == SIG_STATUS_END_OF_DATA)
return i;
+ /*endif*/
buf[i] = (uint8_t) x;
}
+ /*endfor*/
return (int) i;
}
/*- End of function --------------------------------------------------------*/
s->output_row = -1;
}
}
- /* Apply Floyd-Steinberg dithering to the 8 bit pixels, using a bustrophodontic
+ /* Apply Floyd-Steinberg dithering to the 8 bit pixels, using a bustrophedontic
scan, to reduce the grayscale image to pure black and white */
/* The first and last pixels in each row need special treatment, so we do not
step outside the row. */
static const float precoef = 0.9375f;
float amdf[60];
- float abuf[156];
+ float abuf[LPC10_MIN_PITCH];
float ivrc[2];
float temp;
float phi[100] /* was [10][10] */;
s->zpre = preemp(&s->inbuf[i - 181], &s->pebuf[i - 181], LPC10_SAMPLES_PER_FRAME, precoef, s->zpre);
onset(s, s->pebuf, s->osbuf, &s->osptr, 10, 181, 720, LPC10_SAMPLES_PER_FRAME);
- lpc10_placev(s->osbuf, &s->osptr, 10, &s->obound[2], s->vwin, 3, LPC10_SAMPLES_PER_FRAME, 90, 156, 307, 462);
+ lpc10_placev(s->osbuf, &s->osptr, 10, &s->obound[2], s->vwin, 3, LPC10_SAMPLES_PER_FRAME, 90, LPC10_MIN_PITCH, 307, 462);
/* The Pitch Extraction algorithm estimates the pitch for a frame
of speech by locating the minimum of the average magnitude difference
function (AMDF). The AMDF operates on low-pass, inverse filtered
/* eval_highres_amdf reads indices PWINL = 229 through
(PWINL-1)+MAXWIN+(TAU(LTAU)-TAU(1))/2 = 452 of IVBUF, and writes
indices 1 through LTAU = 60 of AMDF. */
- eval_highres_amdf(s->ivbuf, 156, tau, 60, amdf, &minptr, &maxptr, &mintau);
+ eval_highres_amdf(s->ivbuf, LPC10_MIN_PITCH, tau, 60, amdf, &minptr, &maxptr, &mintau);
/* Voicing decisions are made for each half frame of input speech.
An initial voicing classification is made for each half of the
analysis frame, and the voicing decisions for the present frame
dynamic_pitch_tracking(s, amdf, 60, &minptr, s->voibuf[3][1], pitch, &midx);
ipitch = tau[midx - 1];
/* Place spectrum analysis and energy windows */
- lpc10_placea(&ipitch, s->voibuf, &s->obound[2], 3, s->vwin, s->awin, ewin, LPC10_SAMPLES_PER_FRAME, 156);
+ lpc10_placea(&ipitch, s->voibuf, &s->obound[2], 3, s->vwin, s->awin, ewin, LPC10_SAMPLES_PER_FRAME, LPC10_MIN_PITCH);
/* Remove short term DC bias over the analysis window. */
lanal = s->awin[2][1] + 1 - s->awin[2][0];
remove_dc_bias(&s->pebuf[s->awin[2][0] - 181], lanal, abuf);
#include "spandsp/lpc10.h"
#include "spandsp/private/lpc10.h"
-#define LPC10_ORDER 10
+#include "lpc10_encdecs.h"
#if !defined(min)
#define min(a,b) ((a) <= (b) ? (a) : (b))
}
/*- End of function --------------------------------------------------------*/
-static __inline__ int32_t pow_ii(int32_t x, int32_t n)
-{
- int32_t pow;
- uint32_t u;
-
- if (n <= 0)
- {
- if (n == 0 || x == 1)
- return 1;
- if (x != -1)
- return (x != 0) ? 1/x : 0;
- n = -n;
- }
- u = n;
- for (pow = 1; ; )
- {
- if ((u & 1))
- pow *= x;
- if ((u >>= 1) == 0)
- break;
- x *= x;
- }
- return pow;
-}
-/*- End of function --------------------------------------------------------*/
-
/* Synthesize one pitch epoch */
static void bsynz(lpc10_decode_state_t *s,
float coef[],
int32_t j;
int32_t k;
int32_t px;
- float noise[166];
+ float noise[LPC10_MIN_PITCH];
float pulse;
float r1;
float gain;
}
for (i = 0; i < ip; i++)
{
- noise[LPC10_ORDER + i] = lpc10_random(s)/64.0f;
- hpi0 = noise[LPC10_ORDER + i];
- noise[LPC10_ORDER + i] = noise[LPC10_ORDER + i]*-0.125f + s->hpi[0]*0.25f + s->hpi[1]*-0.125f;
+ hpi0 = lpc10_random(s)/64.0f;
+ noise[i] = hpi0*-0.125f + s->hpi[0]*0.25f + s->hpi[1]*-0.125f;
s->hpi[1] = s->hpi[0];
s->hpi[0] = hpi0;
}
for (i = 0; i < ip; i++)
- s->exc[LPC10_ORDER + i] += noise[LPC10_ORDER + i];
+ s->exc[LPC10_ORDER + i] += noise[i];
}
/* Synthesis filters: */
/* Modify the excitation with all-zero filter 1 + G*SUM */
/* Synthesize a single pitch epoch */
static int pitsyn(lpc10_decode_state_t *s,
- int voice[],
+ int voice[2],
int32_t *pitch,
- float *rms,
- float *rc,
- int32_t ivuv[],
- int32_t ipiti[],
- float *rmsi,
- float *rci,
+ float rms,
+ float rc[LPC10_ORDER],
+ int32_t ivuv[16],
+ int32_t ipiti[16],
+ float rmsi[16],
+ float rci[16*LPC10_ORDER],
int32_t *nout,
float *ratio)
{
- int32_t rci_dim1;
- int32_t rci_offset;
- int32_t i1;
int32_t i;
int32_t j;
int32_t vflag;
float xxy;
float msix;
- rci_dim1 = LPC10_ORDER;
- rci_offset = rci_dim1 + 1;
- rci -= rci_offset;
-
- if (*rms < 1.0f)
- *rms = 1.0f;
+ if (rms < 1.0f)
+ rms = 1.0f;
if (s->rmso < 1.0f)
s->rmso = 1.0f;
uvpit = 0.0f;
- *ratio = *rms/(s->rmso + 8.0f);
+ *ratio = rms/(s->rmso + 8.0f);
if (s->first_pitsyn)
{
ivoice = voice[1];
*nout = LPC10_SAMPLES_PER_FRAME / *pitch;
s->jsamp = LPC10_SAMPLES_PER_FRAME - *nout * *pitch;
- i1 = *nout;
- for (i = 0; i < i1; i++)
+ for (i = 0; i < *nout; i++)
{
for (j = 0; j < LPC10_ORDER; j++)
- rci[j + (i + 1)*rci_dim1 + 1] = rc[j];
+ rci[j + i*LPC10_ORDER] = rc[j];
ivuv[i] = ivoice;
ipiti[i] = *pitch;
- rmsi[i] = *rms;
+ rmsi[i] = rms;
}
s->first_pitsyn = false;
}
*pitch = LPC10_SAMPLES_PER_FRAME/4;
s->ipito = *pitch;
if (*ratio > 8.0f)
- s->rmso = *rms;
+ s->rmso = rms;
}
/* SSVC - - 1 , 1 , 1 */
slope = (*pitch - s->ipito)/(float) lsamp;
rmsi[1] = s->rmso;
for (i = 0; i < LPC10_ORDER; i++)
{
- rci[i + rci_dim1 + 1] = s->rco[i];
- rci[i + (rci_dim1 << 1) + 1] = s->rco[i];
+ rci[i] = s->rco[i];
+ rci[i + LPC10_ORDER] = s->rco[i];
s->rco[i] = rc[i];
}
*nout = 2;
ip = (int32_t) uvpit;
if (ip <= i - jused)
{
- ++(*nout);
- ipiti[*nout - 1] = ip;
+ ipiti[*nout] = ip;
*pitch = ip;
- ivuv[*nout - 1] = ivoice;
+ ivuv[*nout] = ivoice;
jused += ip;
prop = (jused - ip/2)/(float) lsamp;
for (j = 0; j < LPC10_ORDER; j++)
alrn = logf((rc[j] + 1)/(1 - rc[j]));
xxy = alro + prop*(alrn - alro);
xxy = expf(xxy);
- rci[j + *nout*rci_dim1 + 1] = (xxy - 1.0f)/(xxy + 1.0f);
+ rci[j + *nout*LPC10_ORDER] = (xxy - 1.0f)/(xxy + 1.0f);
}
- msix = logf(*rms) - logf(s->rmso);
+ msix = logf(rms) - logf(s->rmso);
msix = prop*msix;
msix = logf(s->rmso) + msix;
- rmsi[*nout - 1] = expf(msix);
+ rmsi[*nout] = expf(msix);
+ (*nout)++;
}
}
if (vflag != 1)
uvpit = (float) ((lsamp - istart)/2);
if (uvpit > 90.0f)
uvpit /= 2;
- s->rmso = *rms;
+ s->rmso = rms;
for (i = 0; i < LPC10_ORDER; i++)
{
rc[i] = yarc[i];
{
s->ivoico = voice[1];
s->ipito = *pitch;
- s->rmso = *rms;
+ s->rmso = rms;
for (i = 0; i < LPC10_ORDER; i++)
s->rco[i] = rc[i];
}
/*- End of function --------------------------------------------------------*/
static int synths(lpc10_decode_state_t *s,
- int voice[],
+ int voice[2],
int32_t *pitch,
- float *rms,
- float *rc,
+ float rms,
+ float rc[LPC10_ORDER],
float speech[])
{
- int32_t i1;
int32_t ivuv[16];
int32_t ipiti[16];
int32_t nout;
float rmsi[16];
float ratio;
float g2pass;
- float pc[10];
- float rci[160];
+ float pc[LPC10_ORDER];
+ float rci[16*LPC10_ORDER];
- i1 = min(*pitch, 156);
- *pitch = max(i1, 20);
+ *pitch = max(min(*pitch, LPC10_MIN_PITCH), LPC10_MAX_PITCH);
for (i = 0; i < LPC10_ORDER; i++)
rc[i] = max(min(rc[i], 0.99f), -0.99f);
pitsyn(s, voice, pitch, rms, rc, ivuv, ipiti, rmsi, rci, &nout, &ratio);
for (j = 0; j < nout; j++)
{
/* Add synthesized speech for pitch period J to the end of s->buf. */
- g2pass = reflection_coeffs_to_predictor_coeffs(&rci[j*10], pc, 0.7f);
+ g2pass = reflection_coeffs_to_predictor_coeffs(&rci[j*LPC10_ORDER], pc, 0.7f);
bsynz(s, pc, ipiti[j], &ivuv[j], &s->buf[s->buflen], rmsi[j], ratio, g2pass);
deemp(s, &s->buf[s->buflen], ipiti[j]);
s->buflen += ipiti[j];
}
/* Copy first MAXFRM samples from BUF to output array speech (scaling them),
and then remove them from the beginning of s->buf. */
-
for (i = 0; i < LPC10_SAMPLES_PER_FRAME; i++)
speech[i] = s->buf[i]/4096.0f;
s->buflen -= LPC10_SAMPLES_PER_FRAME;
static void decode(lpc10_decode_state_t *s,
lpc10_frame_t *t,
- int voice[],
+ int voice[2],
int32_t *pitch,
float *rms,
float rc[])
{
lpc10_unpack(&frame, &code[i*7]);
decode(s, &frame, voice, &pitch, &rms, rc);
- synths(s, voice, &pitch, &rms, rc, speech);
+ synths(s, voice, &pitch, rms, rc, speech);
base = i*LPC10_SAMPLES_PER_FRAME;
for (j = 0; j < LPC10_SAMPLES_PER_FRAME; j++)
amp[base + j] = (int16_t) lfastrintf(32768.0f*speech[j]);
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define LPC10_ORDER 10
+#define LPC10_ORDER 10
+#define LPC10_MAX_PITCH 20
+#define LPC10_MIN_PITCH 156
#if !defined(min)
#define min(a,b) ((a) <= (b) ? (a) : (b))
/* Voicing decision for current half-frame: 1 = Voiced; 0 = Unvoiced */
s->voibuf[3][half] = (s->voice[2][half] > 0.0f) ? 1 : 0;
/* Skip voicing decision smoothing in first half-frame: */
- /* Give a value to VSTATE, so that trace statements below will print */
- /* a consistent value from one call to the next when HALF .EQ. 1. */
- /* The value of VSTATE is not used for any other purpose when this is */
- /* true. */
if (half != 0)
{
/* Voicing decision smoothing rules (override of linear combination): */
exit(2);
/*endif*/
- printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS");
+ printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS\n");
printf(" FILE MAY BE OVERWRITTEN DURING FUTURE BUILDS OF THE SOFTWARE */\n");
printf("\n");
trie_recursive_build_packed_trie(s->root);
dump_trie();
-
+
trie_free(s);
return 0;
uint8_t srgb;
int i;
- printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS");
+ printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS\n");
printf(" FILE MAY BE OVERWRITTEN DURING FUTURE BUILDS OF THE SOFTWARE */\n");
printf("\n");
double val;
int ival;
- printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS");
+ printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS\n");
printf(" FILE MAY BE OVERWRITTEN DURING FUTURE BUILDS OF THE SOFTWARE */\n");
printf("\n");
#include <time.h>
#include <fcntl.h>
#include <math.h>
-#if defined(HAVE_STDBOOL_H)
#include <stdbool.h>
-#else
-#include "spandsp/stdbool.h"
-#endif
#if defined(__sunos) || defined(__solaris) || defined(__sun)
#include <getopt.h>
#endif
/* Churn out the data as a C source code header file, which can be directly included by the
modem code. */
- printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS");
+ printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS\n");
printf(" FILE MAY BE OVERWRITTEN DURING FUTURE BUILDS OF THE SOFTWARE */\n");
printf("\n");
printf("#if defined(SPANDSP_USE_FIXED_POINT)\n");
int new_gray;
int restore;
- printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS");
+ printf("/* THIS FILE WAS AUTOMATICALLY GENERATED - ANY MODIFICATIONS MADE TO THIS\n");
printf(" FILE MAY BE OVERWRITTEN DURING FUTURE BUILDS OF THE SOFTWARE */\n");
printf("\n");
at_modem_control_handler_t modem_control_handler,
void *modem_control_user_data);
+SPAN_DECLARE(void) at_set_at_tx_handler(at_state_t *s,
+ at_tx_handler_t at_tx_handler,
+ void *at_tx_user_data);
+
/*! Initialise an AT interpreter context.
\brief Initialise an AT interpreter context.
\param s The AT context.
*/
typedef struct data_modems_state_s data_modems_state_t;
+typedef int (*data_modems_control_handler_t)(data_modems_state_t *s, void *user_data, int op, const char *num);
+
#if defined(__cplusplus)
extern "C"
{
SPAN_DECLARE(logging_state_t *) data_modems_get_logging_state(data_modems_state_t *s);
+SPAN_DECLARE(void) data_modems_call_event(data_modems_state_t *s, int event);
+
SPAN_DECLARE(int) data_modems_restart(data_modems_state_t *s);
SPAN_DECLARE(void) data_modems_set_async_mode(data_modems_state_t *s,
SPAN_DECLARE(int) data_modems_tx(data_modems_state_t *s, int16_t amp[], int max_len);
+SPAN_DECLARE(void) data_modems_set_at_tx_handler(data_modems_state_t *s,
+ at_tx_handler_t at_tx_handler,
+ void *at_tx_user_data);
+
SPAN_DECLARE(data_modems_state_t *) data_modems_init(data_modems_state_t *s,
bool calling_party,
+ at_tx_handler_t at_tx_handler,
+ void *at_tx_user_data,
+ data_modems_control_handler_t modem_control_handler,
+ void *modem_control_user_data,
put_msg_func_t put_msg,
get_msg_func_t get_msg,
void *user_data);
/* Enable the trap as per the MIL-STD */
//#define G711_ULAW_ZEROTRAP
/*! Bias for u-law encoding from linear. */
-#define G711_ULAW_BIAS 0x84
+#define G711_ULAW_BIAS 0x84
/*! \brief Encode a linear sample to u-law
\param linear The sample to encode.
*/
struct awgn_state_s
{
+ /* Scaling factor */
double rms;
- long int ix1;
- long int ix2;
- long int ix3;
- double r[98];
- double gset;
- int iset;
+ /* Working data for the Gaussian generator */
+ bool odd;
+ double amp2;
+ /* Working data for the random number generator */
+ int32_t ix1;
+ int32_t ix2;
+ int32_t ix3;
+ double r[97];
};
#endif
silent audio. */
bool transmit_on_idle;
+ at_state_t at_state;
+ data_modems_control_handler_t modem_control_handler;
+ void *modem_control_user_data;
get_bit_func_t get_bit;
void *get_user_data;
put_bit_func_t put_bit;
async_tx_state_t async_tx;
async_rx_state_t async_rx;
+ /*! \brief Samples elapsed in the current call */
+ int64_t call_samples;
+
union
{
v8_state_t v8;
/*! \brief True if we are the calling modem */
bool calling_party;
int mode;
+ int initial_mode;
+ int current_mode;
int nation;
put_msg_func_t put_msg;
void *user_data;
- int repeat_shifts;
+ bool repeat_shifts;
+ bool autobauding;
union
{
async_tx_state_t async_tx;
int baudot_tx_shift;
int tx_signal_on;
+ bool tx_draining;
uint8_t next_byte;
fsk_rx_state_t fsk_rx;
dtmf_rx_state_t dtmf_rx;
+
+#if defined(SPANDSP_USE_FIXED_POINTx)
+ /*! Minimum acceptable tone level for detection. */
+ int32_t threshold;
+ /*! The accumlating total energy on the same period over which the Goertzels work. */
+ int32_t energy;
+#else
+ /*! Minimum acceptable tone level for detection. */
+ float threshold;
+ /*! The accumlating total energy on the same period over which the Goertzels work. */
+ float energy;
+#endif
+ goertzel_state_t tone_390;
+ goertzel_state_t tone_980;
+ goertzel_state_t tone_1180;
+ goertzel_state_t tone_1270;
+ goertzel_state_t tone_1300;
+ goertzel_state_t tone_1400;
+ goertzel_state_t tone_1650;
+ goertzel_state_t tone_1800;
+ /*! The current sample number within a processing block. */
+ int current_sample;
+ /*! Tone state duration */
+ int duration;
+ int target_duration;
+ int in_tone;
+
int baudot_rx_shift;
int consecutive_ones;
uint8_t rx_msg[256 + 1];
int bit_pos;
int in_progress;
int rx_suppression;
+ int tx_suppression;
/*! \brief Error and flow logging control */
logging_state_t logging;
#if !defined(__cplusplus)
-#define _Bool int
-#define bool int
+typedef int _Bool;
+typedef int bool;
#define false 0
#define true (!false)
#else
-#define _Bool bool
-#define bool bool
+typedef bool _Bool;
#define false false
#define true true
/*! \brief A handler for transmit, where the buffer will be filled. */
typedef int (*span_tx_handler_t)(void *s, int16_t amp[], int max_len);
+#define seconds_to_samples(t) ((t)*SAMPLE_RATE)
+#define milliseconds_to_samples(t) ((t)*(SAMPLE_RATE/1000))
+#define microseconds_to_samples(t) ((t)/(1000000/SAMPLE_RATE))
+
#define ms_to_samples(t) ((t)*(SAMPLE_RATE/1000))
#define us_to_samples(t) ((t)/(1000000/SAMPLE_RATE))
/* Fixed point constant macros for 16 bit values */
-#define FP_Q16_0(x) ((int16_t) (1.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q15_1(x) ((int16_t) (2.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q14_2(x) ((int16_t) (4.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q13_3(x) ((int16_t) (8.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q12_4(x) ((int16_t) (16.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q11_5(x) ((int16_t) (32.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q10_6(x) ((int16_t) (64.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q9_7(x) ((int16_t) (128.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q8_8(x) ((int16_t) (256.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q7_9(x) ((int16_t) (512.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q6_10(x) ((int16_t) (1024.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q5_11(x) ((int16_t) (2048.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q4_12(x) ((int16_t) (4096.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q3_13(x) ((int16_t) (8192.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q2_14(x) ((int16_t) (16384.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q1_15(x) ((int16_t) (32768.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q16_0(x) ((int16_t) (1.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q15_1(x) ((int16_t) (2.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q14_2(x) ((int16_t) (4.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q13_3(x) ((int16_t) (8.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q12_4(x) ((int16_t) (16.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q11_5(x) ((int16_t) (32.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q10_6(x) ((int16_t) (64.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q9_7(x) ((int16_t) (128.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q8_8(x) ((int16_t) (256.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q7_9(x) ((int16_t) (512.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q6_10(x) ((int16_t) (1024.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q5_11(x) ((int16_t) (2048.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q4_12(x) ((int16_t) (4096.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q3_13(x) ((int16_t) (8192.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q2_14(x) ((int16_t) (16384.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q1_15(x) ((int16_t) (32768.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
/* Fixed point constant macros for 32 bit values */
-#define FP_Q32_0(x) ((int32_t) (1.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q31_1(x) ((int32_t) (2.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q30_2(x) ((int32_t) (4.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q29_3(x) ((int32_t) (8.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q28_4(x) ((int32_t) (16.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q27_5(x) ((int32_t) (32.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q26_6(x) ((int32_t) (64.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q25_7(x) ((int32_t) (128.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q24_8(x) ((int32_t) (256.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q23_9(x) ((int32_t) (512.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q22_10(x) ((int32_t) (1024.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q21_11(x) ((int32_t) (2048.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q20_12(x) ((int32_t) (4096.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q19_13(x) ((int32_t) (8192.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q18_14(x) ((int32_t) (16384.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q17_15(x) ((int32_t) (32768.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q16_16(x) ((int32_t) (65536.0*1.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q15_17(x) ((int32_t) (65536.0*2.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q14_18(x) ((int32_t) (65536.0*4.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q13_19(x) ((int32_t) (65536.0*8.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q12_20(x) ((int32_t) (65536.0*16.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q11_21(x) ((int32_t) (65536.0*32.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q10_22(x) ((int32_t) (65536.0*64.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q9_23(x) ((int32_t) (65536.0*128.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q8_24(x) ((int32_t) (65536.0*256.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q7_25(x) ((int32_t) (65536.0*512.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q6_26(x) ((int32_t) (65536.0*1024.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q5_27(x) ((int32_t) (65536.0*2048.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q4_28(x) ((int32_t) (65536.0*4096.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q3_29(x) ((int32_t) (65536.0*8192.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q2_30(x) ((int32_t) (65536.0*16384.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
-#define FP_Q1_31(x) ((int32_t) (65536.0*32768.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q32_0(x) ((int32_t) (1.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q31_1(x) ((int32_t) (2.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q30_2(x) ((int32_t) (4.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q29_3(x) ((int32_t) (8.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q28_4(x) ((int32_t) (16.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q27_5(x) ((int32_t) (32.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q26_6(x) ((int32_t) (64.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q25_7(x) ((int32_t) (128.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q24_8(x) ((int32_t) (256.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q23_9(x) ((int32_t) (512.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q22_10(x) ((int32_t) (1024.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q21_11(x) ((int32_t) (2048.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q20_12(x) ((int32_t) (4096.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q19_13(x) ((int32_t) (8192.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q18_14(x) ((int32_t) (16384.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q17_15(x) ((int32_t) (32768.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q16_16(x) ((int32_t) (65536.0*1.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q15_17(x) ((int32_t) (65536.0*2.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q14_18(x) ((int32_t) (65536.0*4.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q13_19(x) ((int32_t) (65536.0*8.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q12_20(x) ((int32_t) (65536.0*16.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q11_21(x) ((int32_t) (65536.0*32.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q10_22(x) ((int32_t) (65536.0*64.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q9_23(x) ((int32_t) (65536.0*128.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q8_24(x) ((int32_t) (65536.0*256.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q7_25(x) ((int32_t) (65536.0*512.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q6_26(x) ((int32_t) (65536.0*1024.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q5_27(x) ((int32_t) (65536.0*2048.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q4_28(x) ((int32_t) (65536.0*4096.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q3_29(x) ((int32_t) (65536.0*8192.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q2_30(x) ((int32_t) (65536.0*16384.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q1_31(x) ((int32_t) (65536.0*32768.0*(x) + (((x) >= 0.0) ? 0.5 : -0.5)))
#if defined(__cplusplus)
/* C++ doesn't seem to have sane rounding functions/macros yet */
{
V18_AUTOMODING_GLOBAL = 0,
+ V18_AUTOMODING_NONE,
+
/* 5-bit, V.21, V.23, EDT, DTMF, Bell 103 */
V18_AUTOMODING_AUSTRALIA,
V18_AUTOMODING_IRELAND,
invalid, this function will return -1. */
SPAN_DECLARE(int) v18_put(v18_state_t *s, const char msg[], int len);
+/*! \brief Get the current mode of a V.18 connection.
+ \param s The V.18 context.
+ \return The mode. */
+SPAN_DECLARE(int) v18_get_current_mode(v18_state_t *s);
+
/*! \brief Return a short name for an V.18 mode
\param mode The code for the V.18 mode.
\return A pointer to the name.
octet_bit_field(log, pkt, 96, "Extension indicator", NULL, NULL);
if (!(pkt[14] & DISBIT8))
return;
+ /*endif*/
if (len <= 15)
{
span_log(log, SPAN_LOG_FLOW, " Frame is short\n");
s->status_handler(s->status_user_data, status);
else if (s->put_bit)
s->put_bit(s->put_bit_user_data, status);
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
s->scramble_reg |= out_bit;
else
s->scramble_reg |= (in_bit & 1);
+ /*endif*/
return out_bit;
}
/*- End of function --------------------------------------------------------*/
testing for ones, but just rely on a constellation mismatch measurement. */
//span_log(&s->logging, SPAN_LOG_FLOW, "A 1 is really %d\n", out_bit);
}
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
int re;
int im;
int raw;
+ int min_index;
+ int set;
int constellation_state;
#if defined(SPANDSP_USE_FIXED_POINTx)
#define DIST_FACTOR 1024 /* Something less than sqrt(0xFFFFFFFF/10)/10 */
re = 35;
else if (re < 0)
re = 0;
+ /*endif*/
if (im > 35)
im = 35;
else if (im < 0)
im = 0;
+ /*endif*/
if (s->bits_per_symbol == 2)
{
/* 4800bps V.32bis mode, without trellis coding */
- nearest = constel_map_4800[re][im];
- raw = v32bis_4800_differential_decoder[s->diff][nearest];
- s->diff = nearest;
+ constellation_state = constel_map_4800[re][im];
+ raw = v32bis_4800_differential_decoder[s->diff][constellation_state];
+ s->diff = constellation_state;
put_bit(s, raw);
put_bit(s, raw >> 1);
- return nearest;
+ return constellation_state;
}
+ /*endif*/
/* Find a set of 8 candidate constellation positions, that are the closest
to the target, with different patterns in the last 3 bits. */
#else
min = 9999999.0f;
#endif
- j = 0;
+ min_index = 0;
for (i = 0; i < 8; i++)
{
nearest = constel_maps[s->space_map][re][im][i];
if (min > distances[i])
{
min = distances[i];
- j = i;
+ min_index = i;
}
+ /*endif*/
}
- /* Use the nearest of these soft-decisions as the basis for DFE */
- constellation_state = constel_maps[s->space_map][re][im][j];
- /* Control the equalizer, carrier tracking, etc. based on the non-trellis
- corrected information. The trellis correct stuff comes out a bit late. */
+ /*endfor*/
+ /* Use the nearest of these soft-decisions as the basis for DFE and carrier
+ tracking. This is a compromise. It means we will use the correct error
+ less often, but using the output of the traceback would put more lag
+ into the feedback path. */
+ constellation_state = constel_maps[s->space_map][re][im][min_index];
track_carrier(s, z, &s->constellation[constellation_state]);
//tune_equalizer(s, z, &s->constellation[constellation_state]);
/* Update the minimum accumulated distance to each of the 8 states */
if (++s->trellis_ptr >= V17_TRELLIS_STORAGE_DEPTH)
s->trellis_ptr = 0;
- for (i = 0; i < 4; i++)
+ /*endif*/
+ for (i = 0; i < 8; i++)
{
- min = distances[tcm_paths[i][0]] + s->distances[0];
- k = 0;
+ set = i >> 2;
+ min = distances[tcm_paths[i][0]] + s->distances[set];
+ min_index = 0;
for (j = 1; j < 4; j++)
{
- if (min > distances[tcm_paths[i][j]] + s->distances[j << 1])
+ k = (j << 1) + set;
+ if (min > distances[tcm_paths[i][j]] + s->distances[k])
{
- min = distances[tcm_paths[i][j]] + s->distances[j << 1];
- k = j;
+ min = distances[tcm_paths[i][j]] + s->distances[k];
+ min_index = j;
}
+ /*endif*/
}
+ /*endfor*/
+ k = (min_index << 1) + set;
/* Use an elementary IIR filter to track the distance to date. */
#if defined(SPANDSP_USE_FIXED_POINTx)
- new_distances[i] = s->distances[k << 1]*9/10 + distances[tcm_paths[i][k]]*1/10;
-#else
- new_distances[i] = s->distances[k << 1]*0.9f + distances[tcm_paths[i][k]]*0.1f;
-#endif
- s->full_path_to_past_state_locations[s->trellis_ptr][i] = constel_maps[s->space_map][re][im][tcm_paths[i][k]];
- s->past_state_locations[s->trellis_ptr][i] = k << 1;
- }
- for (i = 4; i < 8; i++)
- {
- min = distances[tcm_paths[i][0]] + s->distances[1];
- k = 0;
- for (j = 1; j < 4; j++)
- {
- if (min > distances[tcm_paths[i][j]] + s->distances[(j << 1) + 1])
- {
- min = distances[tcm_paths[i][j]] + s->distances[(j << 1) + 1];
- k = j;
- }
- }
-#if defined(SPANDSP_USE_FIXED_POINTx)
- new_distances[i] = s->distances[(k << 1) + 1]*9/10 + distances[tcm_paths[i][k]]*1/10;
+ new_distances[i] = s->distances[k]*9/10 + distances[tcm_paths[i][min_index]]*1/10;
#else
- new_distances[i] = s->distances[(k << 1) + 1]*0.9f + distances[tcm_paths[i][k]]*0.1f;
+ new_distances[i] = s->distances[k]*0.9f + distances[tcm_paths[i][min_index]]*0.1f;
#endif
- s->full_path_to_past_state_locations[s->trellis_ptr][i] = constel_maps[s->space_map][re][im][tcm_paths[i][k]];
- s->past_state_locations[s->trellis_ptr][i] = (k << 1) + 1;
+ s->full_path_to_past_state_locations[s->trellis_ptr][i] = constel_maps[s->space_map][re][im][tcm_paths[i][min_index]];
+ s->past_state_locations[s->trellis_ptr][i] = k;
}
+ /*endfor*/
memcpy(s->distances, new_distances, sizeof(s->distances));
/* Find the minimum distance to date. This is the start of the path back to the result. */
min = s->distances[0];
- k = 0;
+ min_index = 0;
for (i = 1; i < 8; i++)
{
if (min > s->distances[i])
{
min = s->distances[i];
- k = i;
+ min_index = i;
}
+ /*endif*/
}
+ /*endfor*/
/* Trace back through every time step, starting with the current one, and find the
state from which the path came one step before. At the end of this search, the
last state found also points to the constellation point at that state. This is the
output of the trellis. */
+ k = min_index;
for (i = 0, j = s->trellis_ptr; i < V17_TRELLIS_LOOKBACK_DEPTH - 1; i++)
{
k = s->past_state_locations[j][k];
if (--j < 0)
j = V17_TRELLIS_STORAGE_DEPTH - 1;
+ /*endif*/
}
+ /*endfor*/
nearest = s->full_path_to_past_state_locations[j][k] >> 1;
/* Differentially decode */
put_bit(s, raw);
raw >>= 1;
}
+ /*endfor*/
return constellation_state;
}
/*- End of function --------------------------------------------------------*/
i = (v > FP_SYNC_SCALE_32(1000.0f)) ? 15 : 1;
if (s->baud_phase < FP_SYNC_SCALE_32(0.0f))
i = -i;
+ /*endif*/
//printf("v = %10.5f %5d - %f %f %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction);
s->eq_put_step += i;
s->total_baud_timing_correction += i;
}
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
s->eq_buf[s->eq_step] = *sample;
if (++s->eq_step >= V17_EQUALIZER_LEN)
s->eq_step = 0;
+ /*endif*/
/* On alternate insertions we have a whole baud and must process it. */
if ((s->baud_half ^= 1))
return;
+ /*endif*/
/* Symbol timing synchronisation */
symbol_sync(s);
s->last_angles[0] = arctan2(z.im, z.re);
if (s->agc_scaling_save == FP_SCALE(0.0f))
s->agc_scaling_save = s->agc_scaling;
+ /*endif*/
}
+ /*endif*/
break;
case TRAINING_STAGE_LOG_PHASE:
/* Record the current alternate phase angle */
s->last_angles[0] = DDS_PHASE(180.0f + 18.433f);
s->last_angles[1] = DDS_PHASE(270.0f + 18.433f);
}
+ /*endif*/
/* Make a step shift in the phase, to pull it into line. We need to rotate the equalizer
buffer, as well as the carrier phase, for this to play out nicely. */
/* angle is now the difference between where A is, and where it should be */
z16 = complex_seti16(fixed_cos(ip), -fixed_sin(ip));
for (i = 0; i < V17_EQUALIZER_LEN; i++)
s->eq_buf[i] = complex_mul_q1_15(&s->eq_buf[i], &z16);
+ /*endfor*/
s->carrier_track_p = 500000;
#else
p = dds_phase_to_radians(phase_step);
zz = complex_setf(cosf(p), -sinf(p));
for (i = 0; i < V17_EQUALIZER_LEN; i++)
s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz);
+ /*endfor*/
s->carrier_track_p = 500000.0f;
#endif
s->carrier_phase += phase_step;
s->last_angles[1] = angle;
s->training_stage = TRAINING_STAGE_WAIT_FOR_CDBA;
}
+ /*endif*/
break;
case TRAINING_STAGE_WAIT_FOR_CDBA:
target = &zero;
s->carrier_phase_rate += 3*16*(ang/20);
span_log(&s->logging, SPAN_LOG_FLOW, "Angles %x, %x, dist %d\n", s->last_angles[0], s->last_angles[1], i);
}
+ /*endif*/
span_log(&s->logging, SPAN_LOG_FLOW, "Coarse carrier frequency %7.2f (%d)\n", dds_frequencyf(s->carrier_phase_rate), s->training_count);
/* Check if the carrier frequency is plausible */
if (s->carrier_phase_rate < DDS_PHASE_RATE(CARRIER_NOMINAL_FREQ - 20.0f)
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
break;
}
+ /*endif*/
/* Make a step shift in the phase, to pull it into line. We need to rotate the equalizer buffer,
as well as the carrier phase, for this to play out nicely. */
z16 = complex_seti16(fixed_cos(ip), -fixed_sin(ip));
for (i = 0; i < V17_EQUALIZER_LEN; i++)
s->eq_buf[i] = complex_mul_q1_15(&s->eq_buf[i], &z16);
+ /*endfor*/
#else
p = dds_phase_to_radians(phase_step);
span_log(&s->logging, SPAN_LOG_FLOW, "Spin (long) by %.5f rads\n", p);
zz = complex_setf(cosf(p), -sinf(p));
for (i = 0; i < V17_EQUALIZER_LEN; i++)
s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz);
+ /*endfor*/
#endif
s->carrier_phase += phase_step;
report_status_change(s, SIG_STATUS_TRAINING_IN_PROGRESS);
break;
}
+ /*endif*/
if (++s->training_count > V17_TRAINING_SEG_1_LEN)
{
/* This is bogus. There are not this many bits in this section
s->training_stage = TRAINING_STAGE_PARKED;
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
}
+ /*endif*/
break;
case TRAINING_STAGE_COARSE_TRAIN_ON_CDBA:
/* Train on the scrambled CDBA section. */
#endif
s->training_stage = TRAINING_STAGE_FINE_TRAIN_ON_CDBA;
}
+ /*endif*/
break;
case TRAINING_STAGE_FINE_TRAIN_ON_CDBA:
/* Train on the scrambled CDBA section. */
#endif
s->training_stage = TRAINING_STAGE_TRAIN_ON_CDBA_AND_TEST;
}
+ /*endif*/
break;
case TRAINING_STAGE_TRAIN_ON_CDBA_AND_TEST:
/* Continue training on the scrambled CDBA section, but measure the quality of training too. */
s->training_stage = TRAINING_STAGE_PARKED;
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
}
+ /*endif*/
}
+ /*endif*/
break;
case TRAINING_STAGE_BRIDGE:
descramble(s, V17_BRIDGE_WORD >> ((s->training_count & 0x7) << 1));
/* Wait for the trellis to wind up */
s->training_stage = TRAINING_STAGE_TCM_WINDUP;
}
+ /*endif*/
}
+ /*endif*/
break;
case TRAINING_STAGE_SHORT_WAIT_FOR_CDBA:
/* Look for the initial ABAB sequence to display a phase reversal, which will
s->training_error = FP_SCALE(0.0f);
s->training_count = 1;
s->training_stage = TRAINING_STAGE_SHORT_TRAIN_ON_CDBA_AND_TEST;
- break;
}
- target = &cdba[(s->training_count & 1) + 2];
- track_carrier(s, &z, target);
- if (++s->training_count > V17_TRAINING_SEG_1_LEN)
+ else
{
- /* This is bogus. There are not this many bits in this section
- of a real training sequence. Note that this might be TEP. */
- span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n");
- /* Park this modem */
- s->training_stage = TRAINING_STAGE_PARKED;
- report_status_change(s, SIG_STATUS_TRAINING_FAILED);
+ target = &cdba[(s->training_count & 1) + 2];
+ track_carrier(s, &z, target);
+ if (++s->training_count > V17_TRAINING_SEG_1_LEN)
+ {
+ /* This is bogus. There are not this many bits in this section
+ of a real training sequence. Note that this might be TEP. */
+ span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n");
+ /* Park this modem */
+ s->training_stage = TRAINING_STAGE_PARKED;
+ report_status_change(s, SIG_STATUS_TRAINING_FAILED);
+ }
+ /*endif*/
}
+ /*endif*/
break;
case TRAINING_STAGE_SHORT_TRAIN_ON_CDBA_AND_TEST:
/* Short retrain on the scrambled CDBA section, but measure the quality of training too. */
s->training_error += powerf(&zz);
#endif
}
+ /*endif*/
if (++s->training_count >= V17_TRAINING_SHORT_SEG_2_LEN)
{
#if defined(SPANDSP_USE_FIXED_POINTx)
/* Wait for the trellis to wind up */
s->training_stage = TRAINING_STAGE_TCM_WINDUP;
}
+ /*endif*/
report_status_change(s, SIG_STATUS_TRAINING_IN_PROGRESS);
}
else
s->training_stage = TRAINING_STAGE_PARKED;
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
}
+ /*endif*/
}
+ /*endif*/
break;
case TRAINING_STAGE_TCM_WINDUP:
/* We need to wait 15 bauds while the trellis fills up. */
s->diff = (s->short_train) ? 0 : 1;
s->training_stage = TRAINING_STAGE_TEST_ONES;
}
+ /*endif*/
break;
case TRAINING_STAGE_TEST_ONES:
/* We are in the test phase, where we check that we can receive reliably.
#endif
if (!s->short_train)
s->agc_scaling_save = FP_SCALE(0.0f);
+ /*endif*/
s->training_stage = TRAINING_STAGE_PARKED;
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
}
+ /*endif*/
}
+ /*endif*/
break;
case TRAINING_STAGE_PARKED:
default:
target = &zero;
break;
}
+ /*endswitch*/
if (s->qam_report)
{
#if defined(SPANDSP_USE_FIXED_POINT)
s->qam_report(s->qam_user_data, &z, target, constellation_state);
#endif
}
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
s->high_sample = 0;
s->low_samples = 0;
}
+ /*endif*/
}
else
{
s->low_samples = 0;
if (diff > s->high_sample)
s->high_sample = diff;
+ /*endif*/
}
+ /*endif*/
#endif
if (s->signal_present > 0)
{
report_status_change(s, SIG_STATUS_CARRIER_DOWN);
return 0;
}
+ /*endif*/
#if defined(IAXMODEM_STUFF)
/* Carrier has dropped, but the put_bit is pending the signal_present delay. */
s->carrier_drop_pending = true;
#endif
}
+ /*endif*/
}
else
{
/* Look for power exceeding turn-on threshold to turn the carrier on */
if (power < s->carrier_on_power)
return 0;
+ /*endif*/
s->signal_present = 1;
#if defined(IAXMODEM_STUFF)
s->carrier_drop_pending = false;
#endif
report_status_change(s, SIG_STATUS_CARRIER_UP);
}
+ /*endif*/
return power;
}
/*- End of function --------------------------------------------------------*/
s->rrc_filter[s->rrc_filter_step] = amp[i];
if (++s->rrc_filter_step >= V17_RX_FILTER_STEPS)
s->rrc_filter_step = 0;
+ /*endif*/
if ((power = signal_detect(s, amp[i])) == 0)
continue;
+ /*endif*/
if (s->training_stage == TRAINING_STAGE_PARKED)
continue;
+ /*endif*/
/* Only spend effort processing this data if the modem is not
parked, after training failure. */
s->eq_put_step -= RX_PULSESHAPER_COEFF_SETS;
step = -s->eq_put_step;
if (step < 0)
step += RX_PULSESHAPER_COEFF_SETS;
+ /*endif*/
if (step < 0)
step = 0;
else if (step > RX_PULSESHAPER_COEFF_SETS - 1)
step = RX_PULSESHAPER_COEFF_SETS - 1;
+ /*endif*/
#if defined(SPANDSP_USE_FIXED_POINTx)
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_re[step], V17_RX_FILTER_STEPS, s->rrc_filter_step) >> 15;
sample.re = (v*s->agc_scaling) >> 10;
{
if ((root_power = fixed_sqrt32(power)) == 0)
root_power = 1;
+ /*endif*/
#if defined(SPANDSP_USE_FIXED_POINTx)
s->agc_scaling = saturate16(((int32_t) (FP_SCALE(2.17f)*1024.0f))/root_power);
#else
s->agc_scaling = (FP_SCALE(2.17f)/RX_PULSESHAPER_GAIN)/root_power;
#endif
}
+ /*endif*/
/* Pulse shape while still at the carrier frequency, using a quadrature
pair of filters. This results in a properly bandpass filtered complex
signal, which can be brought directly to baseband by complex mixing.
s->eq_put_step += RX_PULSESHAPER_COEFF_SETS*10/(3*2);
process_half_baud(s, &zz);
}
+ /*endif*/
#if defined(SPANDSP_USE_FIXED_POINT)
dds_advance(&s->carrier_phase, s->carrier_phase_rate);
#else
dds_advancef(&s->carrier_phase, s->carrier_phase_rate);
#endif
}
+ /*endfor*/
return 0;
}
/*- End of function --------------------------------------------------------*/
span_log(&s->logging, SPAN_LOG_FLOW, "Fill-in %d samples\n", len);
if (s->signal_present <= 0)
return 0;
+ /*endif*/
if (s->training_stage == TRAINING_STAGE_PARKED)
return 0;
+ /*endif*/
for (i = 0; i < len; i++)
{
#if defined(SPANDSP_USE_FIXED_POINT)
s->eq_put_step -= RX_PULSESHAPER_COEFF_SETS;
if (s->eq_put_step <= 0)
s->eq_put_step += RX_PULSESHAPER_COEFF_SETS*10/(3*2);
+ /*endif*/
/* TODO: Should we rotate any buffers */
}
+ /*endfor*/
return 0;
}
/*- End of function --------------------------------------------------------*/
default:
return -1;
}
+ /*endswitch*/
s->bit_rate = bit_rate;
#if defined(SPANDSP_USE_FIXED_POINTx)
vec_zeroi16(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
#endif
if (short_train != 2)
s->short_train = short_train;
+ /*endif*/
vec_zeroi32(s->last_angles, 2);
vec_zeroi32(s->diff_angles, 16);
initial paths to merge at the zero states. */
for (i = 0; i < 8; i++)
s->distances[i] = FP_CONSTELLATION_SCALE(99.0f)*FP_CONSTELLATION_SCALE(1.0f);
+ /*endfor*/
memset(s->full_path_to_past_state_locations, 0, sizeof(s->full_path_to_past_state_locations));
memset(s->past_state_locations, 0, sizeof(s->past_state_locations));
s->distances[0] = 0;
s->carrier_track_p = 40000.0f;
#endif
}
+ /*endif*/
s->last_sample = 0;
span_log(&s->logging, SPAN_LOG_FLOW, "Gains %f %f\n", (float) s->agc_scaling_save, (float) s->agc_scaling);
span_log(&s->logging, SPAN_LOG_FLOW, "Phase rates %f %f\n", dds_frequencyf(s->carrier_phase_rate), dds_frequencyf(s->carrier_phase_rate_save));
s->symbol_sync_high[i] = FP_SCALE(0.0f);
s->symbol_sync_dc_filter[i] = FP_SCALE(0.0f);
}
+ /*endfor*/
s->baud_phase = FP_SCALE(0.0f);
s->baud_half = 0;
default:
return NULL;
}
+ /*endswitch*/
if (s == NULL)
{
if ((s = (v17_rx_state_t *) span_alloc(sizeof(*s))) == NULL)
return NULL;
+ /*endif*/
}
+ /*endif*/
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "V.17 RX");
*
* Written by Steve Underwood <steveu@coppice.org>
*
- * Copyright (C) 2004-2009 Steve Underwood
+ * Copyright (C) 2004-2015 Steve Underwood
*
* All rights reserved.
*
enum
{
- V8_WAIT_1S = 0, /* Start point when sending CI */
- V8_AWAIT_ANSAM, /* Start point when sending initial silence */
+ /* Start point when sending CI */
+ V8_WAIT_1S = 0,
+ /* Start point when sending initial silence */
+ V8_AWAIT_ANSAM,
V8_CI_ON,
V8_CI_OFF,
V8_HEARD_ANSAM,
V8_V92_SYNC_OCTET = 0x55
};
+#define Te_TIMEOUT 500
+
SPAN_DECLARE(const char *) v8_call_function_to_str(int call_function)
{
switch (call_function)
case V8_CALL_FUNCTION_EXTENSION:
return "Call function is in extension octet";
}
+ /*endswitch*/
return "Unknown call function";
}
/*- End of function --------------------------------------------------------*/
case V8_MOD_V92:
return "V.92 duplex";
}
+ /*endswitch*/
return "???";
}
/*- End of function --------------------------------------------------------*/
case V8_PROTOCOL_EXTENSION:
return "Extension";
}
+ /*endswitch*/
return "Undefined";
}
/*- End of function --------------------------------------------------------*/
case (V8_PSTN_ACCESS_DCE_ON_DIGITAL | V8_PSTN_ACCESS_ANSWER_DCE_CELLULAR | V8_PSTN_ACCESS_CALL_DCE_CELLULAR):
return "DCE on digital, and answering and calling modems on cellular";
}
+ /*endswitch*/
return "PSTN access unknown";
}
/*- End of function --------------------------------------------------------*/
case 0:
return "???";
}
+ /*endswitch*/
return "???";
}
/*- End of function --------------------------------------------------------*/
case (V8_PSTN_PCM_MODEM_V91 | V8_PSTN_PCM_MODEM_V90_V92_DIGITAL | V8_PSTN_PCM_MODEM_V90_V92_ANALOGUE):
return "V.91 and V.90/V.92 digital/analogue available";
}
+ /*endswitch*/
return "PCM availability unknown";
}
/*- End of function --------------------------------------------------------*/
case 7:
return "Reserved TIA + others";
}
+ /*endswitch*/
return "???";
}
/*- End of function --------------------------------------------------------*/
span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s%s", comma, v8_modulation_to_str(modulation_schemes & (1 << i)));
comma = ", ";
}
+ /*endif*/
}
+ /*endfor*/
span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, " supported\n");
}
/*- End of function --------------------------------------------------------*/
{
if (s->result_handler)
s->result_handler(s->result_handler_user_data, &s->result);
+ /*endif*/
return 0;
}
/*- End of function --------------------------------------------------------*/
s->modulation_bytes = 1;
if (*p & 0x80)
modulations |= V8_MOD_V34HDX;
+ /*endif*/
if (*p & 0x40)
modulations |= V8_MOD_V34;
+ /*endif*/
if (*p & 0x20)
modulations |= V8_MOD_V90;
+ /*endif*/
++p;
/* Check for an extension octet */
s->modulation_bytes++;
if (*p & 0x80)
modulations |= V8_MOD_V27TER;
+ /*endif*/
if (*p & 0x40)
modulations |= V8_MOD_V29;
+ /*endif*/
if (*p & 0x04)
modulations |= V8_MOD_V17;
+ /*endif*/
if (*p & 0x02)
modulations |= V8_MOD_V22;
+ /*endif*/
if (*p & 0x01)
modulations |= V8_MOD_V32;
+ /*endif*/
++p;
/* Check for an extension octet */
s->modulation_bytes++;
if (*p & 0x80)
modulations |= V8_MOD_V21;
+ /*endif*/
if (*p & 0x40)
modulations |= V8_MOD_V23HDX;
+ /*endif*/
if (*p & 0x04)
modulations |= V8_MOD_V23;
+ /*endif*/
if (*p & 0x02)
modulations |= V8_MOD_V26BIS;
+ /*endif*/
if (*p & 0x01)
modulations |= V8_MOD_V26TER;
- ++p;
+ /*endif*/
+ ++p;
}
+ /*endif*/
}
+ /*endif*/
s->result.modulations = modulations;
v8_log_supported_modulations(s, modulations);
return p;
{
if ((s->rx_data[0] & 0x1F) == V8_CALL_FUNCTION_TAG)
process_call_function(s, &s->rx_data[0]);
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
if (s->got_cm_jm)
return;
+ /*endif*/
/* We must receive two consecutive identical CM or JM sequences to accept it. */
if (s->cm_jm_len <= 0
memcpy(s->cm_jm_data, s->rx_data, s->rx_data_ptr);
return;
}
+ /*endif*/
/* We have a matching pair of CMs or JMs, so we are happy this is correct. */
s->got_cm_jm = true;
p++;
break;
}
+ /*endswitch*/
/* Skip any future extensions we do not understand */
while ((*p & 0x38) == 0x10)
p++;
+ /*endwhile*/
}
+ /*endwhile*/
}
/*- End of function --------------------------------------------------------*/
default:
break;
}
+ /*endswitch*/
return;
}
+ /*endif*/
//span_log(&s->logging, SPAN_LOG_FLOW, "Bit %d\n", bit);
/* Wait until we sync. */
s->bit_stream = (s->bit_stream >> 1) | (bit << 19);
new_preamble_type = V8_SYNC_UNKNOWN;
break;
}
+ /*endswitch*/
if (new_preamble_type != V8_SYNC_UNKNOWN)
{
/* We have seen a fresh sync code */
tag = ">??: ";
break;
}
+ /*endswitch*/
span_log_buf(&s->logging, SPAN_LOG_FLOW, tag, s->rx_data, s->rx_data_ptr);
}
+ /*endif*/
}
+ /*endif*/
/* If we were handling a valid sync code then we should process what has been
received to date. */
switch (s->preamble_type)
cm_jm_decode(s);
break;
}
+ /*endswitch*/
s->preamble_type = new_preamble_type;
s->bit_cnt = 0;
s->rx_data_ptr = 0;
}
+ /*endif*/
if (s->preamble_type != V8_SYNC_UNKNOWN)
{
{
if (++s->zero_byte_count == 3)
s->got_cj = true;
+ /*endif*/
}
else
{
s->zero_byte_count = 0;
}
+ /*endif*/
if (s->rx_data_ptr < (int) (sizeof(s->rx_data) - 1))
s->rx_data[s->rx_data_ptr++] = data;
+ /*endif*/
s->bit_cnt = 0;
}
+ /*endif*/
}
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
s = user_data;
if (queue_read(s->tx_queue, &bit, 1) <= 0)
return SIG_STATUS_END_OF_DATA;
+ /*endif*/
return bit;
}
/*- End of function --------------------------------------------------------*/
bits[j] = byte & 1;
byte >>= 1;
}
+ /*endfor*/
bits[9] = 1;
queue_write(s->tx_queue, bits, 10);
}
+ /*endfor*/
}
/*- End of function --------------------------------------------------------*/
val = 0x05;
if (offered_modulations & V8_MOD_V90)
val |= 0x20;
+ /*endif*/
if (offered_modulations & V8_MOD_V34)
val |= 0x40;
+ /*endif*/
if (offered_modulations & V8_MOD_V34HDX)
val |= 0x80;
+ /*endif*/
buf[ptr++] = val;
if (++bytes < s->modulation_bytes)
{
val = 0x10;
if (offered_modulations & V8_MOD_V32)
val |= 0x01;
+ /*endif*/
if (offered_modulations & V8_MOD_V22)
val |= 0x02;
+ /*endif*/
if (offered_modulations & V8_MOD_V17)
val |= 0x04;
+ /*endif*/
if (offered_modulations & V8_MOD_V29)
val |= 0x40;
+ /*endif*/
if (offered_modulations & V8_MOD_V27TER)
val |= 0x80;
+ /*endif*/
buf[ptr++] = val;
}
+ /*endif*/
if (++bytes < s->modulation_bytes)
{
val = 0x10;
if (offered_modulations & V8_MOD_V26TER)
val |= 0x01;
+ /*endif*/
if (offered_modulations & V8_MOD_V26BIS)
val |= 0x02;
+ /*endif*/
if (offered_modulations & V8_MOD_V23)
val |= 0x04;
+ /*endif*/
if (offered_modulations & V8_MOD_V23HDX)
val |= 0x40;
+ /*endif*/
if (offered_modulations & V8_MOD_V21)
val |= 0x80;
+ /*endif*/
buf[ptr++] = val;
}
-
+ /*endif*/
if (s->parms.protocol)
buf[ptr++] = (s->parms.protocol << 5) | V8_PROTOCOLS_TAG;
+ /*endif*/
if (s->parms.pstn_access)
buf[ptr++] = (s->parms.pstn_access << 5) | V8_PSTN_ACCESS_TAG;
+ /*endif*/
if (s->parms.pcm_modem_availability)
buf[ptr++] = (s->parms.pcm_modem_availability << 5) | V8_PCM_MODEM_AVAILABILITY_TAG;
+ /*endif*/
if (s->parms.t66 >= 0)
buf[ptr++] = (s->parms.t66 << 5) | V8_T66_TAG;
+ /*endif*/
/* No NSF */
//buf[ptr++] = (0 << 5) | V8_NSF_TAG;
span_log_buf(&s->logging, SPAN_LOG_FLOW, (s->calling_party) ? "<CM: " : "<JM: ", &buf[1], ptr - 1);
len = 0;
if (s->modem_connect_tone_tx_on)
{
- if (s->modem_connect_tone_tx_on == (ms_to_samples(75) + 2))
+ if (s->modem_connect_tone_tx_on == (milliseconds_to_samples(75) + 2))
{
if (s->fsk_tx_on)
{
/* The initial silence is over */
s->modem_connect_tone_tx_on = 0;
}
+ /*endif*/
}
- else if (s->modem_connect_tone_tx_on == (ms_to_samples(75) + 1))
+ else if (s->modem_connect_tone_tx_on == (milliseconds_to_samples(75) + 1))
{
/* Send the ANSam tone */
len = modem_connect_tones_tx(&s->ansam_tx, amp, max_len);
if (len < max_len)
{
span_log(&s->logging, SPAN_LOG_FLOW, "ANSam or ANSam/ ended\n");
- s->modem_connect_tone_tx_on = ms_to_samples(75);
+ s->modem_connect_tone_tx_on = milliseconds_to_samples(75);
}
+ /*endif*/
}
else
{
len = s->modem_connect_tone_tx_on;
else
len = max_len;
+ /*endif*/
vec_zeroi16(amp, len);
s->modem_connect_tone_tx_on -= len;
}
+ /*endif*/
}
+ /*endif*/
if (s->fsk_tx_on && len < max_len)
{
len += fsk_tx(&s->v21tx, &[len], max_len - len);
s->fsk_tx_on = false;
//s->state = V8_PARKED;
}
+ /*endif*/
}
+ /*endif*/
if (s->state != V8_PARKED && len < max_len)
{
vec_zeroi16(&[len], max_len - len);
len = max_len;
}
+ /*endif*/
return len;
}
/*- End of function --------------------------------------------------------*/
-static void send_v92(v8_state_t *s)
+static void conditionally_send_v92(v8_state_t *s)
{
int i;
uint8_t buf[2];
- if (s->result.v92 >= 0)
+ if (s->parms.v92 >= 0)
{
/* Send 2 V.92 packets */
for (i = 0; i < 2; i++)
{
v8_put_preamble(s);
buf[0] = V8_V92_SYNC_OCTET;
- buf[1] = s->result.v92;
+ buf[1] = s->parms.v92;
span_log_buf(&s->logging, SPAN_LOG_FLOW, "<V.92: ", &buf[1], 1);
v8_put_bytes(s, buf, 2);
}
+ /*endfor*/
}
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
span_log_buf(&s->logging, SPAN_LOG_FLOW, "<CI: ", &buf[1], 1);
v8_put_bytes(s, buf, 2);
}
+ /*endfor*/
}
/*- End of function --------------------------------------------------------*/
/* Set the Te interval. The spec. says 500ms is the minimum,
but gives reasons why 1 second is a better value (V.8/8.1.1). */
s->state = V8_HEARD_ANSAM;
- s->ci_timer = ms_to_samples(1000);
+ s->ci_timer = milliseconds_to_samples(2*Te_TIMEOUT);
+ s->negotiation_timer = milliseconds_to_samples(5000);
+ v8_decode_init(s);
}
else
{
s->result.status = V8_STATUS_NON_V8_CALL;
report_event(s);
}
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
/* Wait 1 second before sending the first CI packet */
if ((s->negotiation_timer -= len) > 0)
break;
+ /*endif*/
fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]);
send_ci(s);
s->state = V8_CI_ON;
handle_modem_connect_tone(s, tone);
break;
}
+ /*endif*/
if (!s->fsk_tx_on)
{
s->state = V8_CI_OFF;
- s->ci_timer = ms_to_samples(500);
+ s->ci_timer = milliseconds_to_samples(Te_TIMEOUT);
+ s->negotiation_timer = milliseconds_to_samples(5000);
break;
}
+ /*endif*/
break;
case V8_CI_OFF:
residual_samples = modem_connect_tones_rx(&s->ansam_rx, amp, len);
handle_modem_connect_tone(s, tone);
break;
}
+ /*endif*/
if ((s->ci_timer -= len) <= 0)
{
if (++s->ci_count >= 10)
s->state = V8_CI_ON;
s->fsk_tx_on = true;
}
+ /*endif*/
}
+ /*endif*/
break;
case V8_AWAIT_ANSAM:
residual_samples = modem_connect_tones_rx(&s->ansam_rx, amp, len);
/* Check if an ANSam or ANSam/ tone has been detected */
if ((tone = modem_connect_tones_rx_get(&s->ansam_rx)) != MODEM_CONNECT_TONES_NONE)
handle_modem_connect_tone(s, tone);
+ /*endif*/
break;
case V8_HEARD_ANSAM:
/* We have heard the ANSam or ANSam/ signal, but we still need to wait for the
end of the Te timeout period to comply with the spec. */
if ((s->ci_timer -= len) <= 0)
{
- v8_decode_init(s);
- s->negotiation_timer = ms_to_samples(5000);
fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]);
- send_v92(s);
+ conditionally_send_v92(s);
send_cm_jm(s);
- s->state = V8_CM_ON;
s->fsk_tx_on = true;
+ s->state = V8_CM_ON;
}
- break;
+ /*endif*/
+ /* Fall through */
case V8_CM_ON:
residual_samples = fsk_rx(&s->v21rx, amp, len);
if (s->got_cm_jm)
s->fsk_tx_on = true;
break;
}
+ /*endif*/
if ((s->negotiation_timer -= len) <= 0)
{
/* Timeout */
s->result.status = V8_STATUS_FAILED;
report_event(s);
}
+ /*endif*/
if (queue_contents(s->tx_queue) < 10)
{
/* Send CM again */
send_cm_jm(s);
}
+ /*endif*/
break;
case V8_CJ_ON:
residual_samples = fsk_rx(&s->v21rx, amp, len);
if (!s->fsk_tx_on)
{
#if 0
- s->negotiation_timer = ms_to_samples(75);
+ s->negotiation_timer = milliseconds_to_samples(75);
s->state = V8_SIGC;
}
+ /*endif*/
break;
case V8_SIGC:
if ((s->negotiation_timer -= len) <= 0)
s->result.status = V8_STATUS_V8_CALL;
report_event(s);
}
+ /*endif*/
break;
case V8_CM_WAIT:
residual_samples = fsk_rx(&s->v21rx, amp, len);
/* Stop sending ANSam or ANSam/ and send JM instead */
fsk_tx_init(&s->v21tx, &preset_fsk_specs[FSK_V21CH2], get_bit, s);
/* Set the timeout for JM */
- s->negotiation_timer = ms_to_samples(5000);
+ s->negotiation_timer = milliseconds_to_samples(5000);
s->state = V8_JM_ON;
send_cm_jm(s);
- s->modem_connect_tone_tx_on = ms_to_samples(75);
+ s->modem_connect_tone_tx_on = milliseconds_to_samples(75);
s->fsk_tx_on = true;
break;
}
+ /*endif*/
if ((s->negotiation_timer -= len) <= 0)
{
/* Timeout */
s->result.status = V8_STATUS_FAILED;
report_event(s);
}
+ /*endif*/
break;
case V8_JM_ON:
residual_samples = fsk_rx(&s->v21rx, amp, len);
span_log(&s->logging, SPAN_LOG_FLOW, "CJ recognised\n");
/* Stop sending JM, flushing anything left in the buffer, and wait 75 ms */
queue_flush(s->tx_queue);
- s->negotiation_timer = ms_to_samples(75);
+ s->negotiation_timer = milliseconds_to_samples(75);
s->state = V8_SIGA;
break;
}
+ /*endif*/
if ((s->negotiation_timer -= len) <= 0)
{
/* Timeout */
report_event(s);
break;
}
+ /*endif*/
if (queue_contents(s->tx_queue) < 10)
{
/* Send JM */
send_cm_jm(s);
}
+ /*endif*/
break;
case V8_SIGA:
if (!s->fsk_tx_on)
s->result.status = V8_STATUS_V8_CALL;
report_event(s);
}
+ /*endif*/
break;
case V8_PARKED:
residual_samples = len;
break;
}
+ /*endswitch*/
return residual_samples;
}
/*- End of function --------------------------------------------------------*/
if (s->result.send_ci)
{
s->state = V8_WAIT_1S;
- s->negotiation_timer = ms_to_samples(1000);
+ s->negotiation_timer = milliseconds_to_samples(1000);
s->ci_count = 0;
}
else
{
s->state = V8_AWAIT_ANSAM;
}
+ /*endif*/
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;
+ s->modem_connect_tone_tx_on = milliseconds_to_samples(75) + 2;
}
else
{
/* Send the ANSam or ANSam/ tone */
s->state = V8_CM_WAIT;
- s->negotiation_timer = ms_to_samples(200 + 5000);
+ s->negotiation_timer = milliseconds_to_samples(200 + 5000);
v8_decode_init(s);
modem_connect_tones_tx_init(&s->ansam_tx, s->parms.modem_connect_tone);
- s->modem_connect_tone_tx_on = ms_to_samples(75) + 1;
+ s->modem_connect_tone_tx_on = milliseconds_to_samples(75) + 1;
}
+ /*endif*/
if (s->tx_queue)
queue_free(s->tx_queue);
+ /*endif*/
if ((s->tx_queue = queue_init(NULL, 1024, 0)) == NULL)
return -1;
+ /*endif*/
return 0;
}
/*- End of function --------------------------------------------------------*/
{
if ((s = (v8_state_t *) span_alloc(sizeof(*s))) == NULL)
return NULL;
+ /*endif*/
}
+ /*endif*/
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "V.8");
bert_set_report(bert[i], 100000, reporter, (void *) (intptr_t) i);
if ((data_modems_state[i] = data_modems_init(NULL,
calling_party,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
put_msg,
get_msg,
NULL)) == NULL)
data_modems_state_t *data_modem_state;
+int answered = false;
+int done = false;
+
+static int modem_call_control(data_modems_state_t *s, void *user_data, int op, const char *num)
+{
+ printf("\nModem control - %s", at_modem_control_to_str(op));
+ switch (op)
+ {
+ case AT_MODEM_CONTROL_CALL:
+ printf(" %s", num);
+ data_modems_call_event(s, AT_CALL_EVENT_CONNECTED);
+ break;
+ case AT_MODEM_CONTROL_ANSWER:
+ answered = true;
+ data_modems_call_event(s, AT_CALL_EVENT_ANSWERED);
+ break;
+ case AT_MODEM_CONTROL_HANGUP:
+ done = true;
+ break;
+ case AT_MODEM_CONTROL_OFFHOOK:
+ break;
+ case AT_MODEM_CONTROL_DTR:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ case AT_MODEM_CONTROL_RTS:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ case AT_MODEM_CONTROL_CTS:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ case AT_MODEM_CONTROL_CAR:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ case AT_MODEM_CONTROL_RNG:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ case AT_MODEM_CONTROL_DSR:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ case AT_MODEM_CONTROL_SETID:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ case AT_MODEM_CONTROL_RESTART:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ case AT_MODEM_CONTROL_DTE_TIMEOUT:
+ printf(" %d", (int) (intptr_t) num);
+ break;
+ }
+ /*endswitch*/
+ printf("\n");
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
static int get_msg(void *user_data, uint8_t msg[], int len)
{
return 0;
printf("Status %s\n", signal_status_to_str(len));
else
printf("Put %d '%s'\n", len, msg);
+ /*endif*/
}
/*- End of function --------------------------------------------------------*/
static void terminal_callback(void *user_data, const uint8_t msg[], int len)
{
+ data_modems_state_t *s;
+ int i;
+
+ s = (data_modems_state_t *) user_data;
printf("terminal callback %d\n", len);
+ for (i = 0; i < len; i++)
+ {
+ printf("0x%x ", msg[i]);
+ }
+ printf("\n");
+ at_interpreter(&s->at_state, msg, len);
}
/*- End of function --------------------------------------------------------*/
static int termios_callback(void *user_data, struct termios *termios)
{
+ data_modems_state_t *s;
+
+ s = (data_modems_state_t *) user_data;
printf("termios callback\n");
return 0;
}
{
for (i = 0; i < samples; i++)
wave_buffer[2*i] = amp[i];
+ /*endfor*/
}
+ /*endif*/
return out_samples;
}
/*- End of function --------------------------------------------------------*/
{
if (out_samples < samples)
memset(&[out_samples], 0, (samples - out_samples)*2);
+ /*endif*/
for (i = 0; i < samples; i++)
wave_buffer[2*i + 1] = amp[i];
+ /*endfor*/
sf_writef_short(wave_handle, wave_buffer, samples);
}
+ /*endif*/
return samples;
}
/*- End of function --------------------------------------------------------*/
/* Now set up and run the modems */
if ((data_modem_state = data_modems_init(NULL,
calling_party,
+ terminal_write,
+ NULL,
+ modem_call_control,
+ NULL,
put_msg,
get_msg,
NULL)) == NULL)
fprintf(stderr, " Cannot start the data modem\n");
exit(2);
}
+ /*endif*/
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");
fprintf(stderr, " Cannot start the socket harness\n");
exit(2);
}
+ /*endif*/
+
+ data_modems_set_at_tx_handler(data_modem_state, terminal_write, s);
wave_handle = NULL;
if (log_audio)
fprintf(stderr, " Cannot create audio file '%s'\n", OUTPUT_WAVE_FILE_NAME);
exit(2);
}
+ /*endif*/
}
+ /*endif*/
- socket_harness_run(s);
+ socket_harness_run(s, calling_party);
if (log_audio)
{
fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_WAVE_FILE_NAME);
exit(2);
}
+ /*endif*/
}
+ /*endif*/
return 0;
}
exit(2);
break;
}
+ /*endswitch*/
}
+ /*endwhile*/
if (modem_tests(use_gui, log_audio, calling_party))
exit(2);
+ /*endif*/
printf("Tests passed\n");
return 0;
}
}
/*- End of function --------------------------------------------------------*/
-int socket_harness_run(socket_harness_state_t *s)
+int terminal_write(void *user_data, const char *buf, int len)
+{
+ socket_harness_state_t *s;
+
+ s = (socket_harness_state_t *) user_data;
+ return write(s->pty_fd, buf, len);
+}
+/*- End of function --------------------------------------------------------*/
+
+int socket_harness_run(socket_harness_state_t *s, int kick)
{
struct timeval tmo;
fd_set rset;
int tx_samples;
int ret;
+ if (kick)
+ {
+ samples = 160;
+ 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: */
+ }
+ }
while (keep_running)
{
//if (s->modem->event)
fprintf(stderr, "Error: select: %s\n", strerror(errno));
return ret;
}
-
if (ret == 0)
{
/* Timeout */
modem_t modem;
} socket_harness_state_t;
-int socket_harness_run(socket_harness_state_t *s);
+int socket_harness_run(socket_harness_state_t *s, int kick);
+
+int terminal_write(void *user_data, const char *buf, int len);
socket_harness_state_t *socket_harness_init(socket_harness_state_t *s,
const char *socket_name,