*
* The actual OKI ADPCM encode and decode method is derived from freely
* available code, whose exact origins seem uncertain.
- *
- * $Id: oki_adpcm.c,v 1.32 2009/02/10 13:06:46 steveu Exp $
*/
/*! \file */
static int16_t decode(oki_adpcm_state_t *s, uint8_t adpcm)
{
- int16_t e;
+ int16_t d;
int16_t ss;
int16_t linear;
*/
ss = step_size[s->step_index];
- e = ss >> 3;
+ d = ss >> 3;
if (adpcm & 0x01)
- e += (ss >> 2);
+ d += (ss >> 2);
/*endif*/
if (adpcm & 0x02)
- e += (ss >> 1);
+ d += (ss >> 1);
/*endif*/
if (adpcm & 0x04)
- e += ss;
+ d += ss;
/*endif*/
if (adpcm & 0x08)
- e = -e;
+ d = -d;
/*endif*/
- linear = s->last + e;
+ linear = s->last + d;
/* Saturate the values to +/- 2^11 (supposed to be 12 bits) */
if (linear > 2047)
static uint8_t encode(oki_adpcm_state_t *s, int16_t linear)
{
- int16_t e;
+ int16_t d;
int16_t ss;
uint8_t adpcm;
ss = step_size[s->step_index];
- e = (linear >> 4) - s->last;
+ d = (linear >> 4) - s->last;
adpcm = (uint8_t) 0x00;
- if (e < 0)
+ if (d < 0)
{
adpcm = (uint8_t) 0x08;
- e = -e;
+ d = -d;
}
/*endif*/
- if (e >= ss)
+ if (d >= ss)
{
adpcm |= (uint8_t) 0x04;
- e -= ss;
+ d -= ss;
}
/*endif*/
- if (e >= (ss >> 1))
+ if (d >= (ss >> 1))
{
adpcm |= (uint8_t) 0x02;
- e -= ss;
+ d -= (ss >> 1);
}
/*endif*/
- if (e >= (ss >> 2))
+ if (d >= (ss >> 2))
adpcm |= (uint8_t) 0x01;
/*endif*/
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: t38_gateway.h,v 1.5 2009/11/07 08:58:58 steveu Exp $
*/
/*! \file */
*/
typedef struct
{
- /*! Core T.38 IFP support */
+ /*! \brief Core T.38 IFP support */
t38_core_state_t t38;
/*! \brief TRUE if the NSF, NSC, and NSS are to be suppressed by altering
{
/*! \brief The FAX modem set for the audio side fo the gateway. */
fax_modems_state_t modems;
- /*! \brief The current receive signal handler. Actual receiving hop between this
+ /*! \brief The current receive signal handler. Actual receiving hops between this
and a dummy receive routine. */
span_rx_handler_t *base_rx_handler;
+ span_rx_fillin_handler_t *base_rx_fillin_handler;
} t38_gateway_audio_state_t;
/*!
int supported_modems;
/*! \brief TRUE if ECM FAX mode is allowed through the gateway. */
int ecm_allowed;
+ /*! \brief Required time between T.38 transmissions, in ms. */
+ int ms_per_tx_chunk;
/*! \brief TRUE if in image data modem is to use short training. This usually
follows image_data_mode, but in ECM mode T.30 defines recovery
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: v17rx.h,v 1.2.4.1 2009/12/24 16:52:30 steveu Exp $
*/
#if !defined(_SPANDSP_PRIVATE_V17RX_H_)
/* Target length for the equalizer is about 63 taps, to deal with the worst stuff
in V.56bis. */
+/*! The length of the equalizer buffer */
+#define V17_EQUALIZER_LEN 33
+
/*! Samples before the target position in the equalizer buffer */
-#define V17_EQUALIZER_PRE_LEN 8
-/*! Samples after the target position in the equalizer buffer */
-#define V17_EQUALIZER_POST_LEN 8
+#define V17_EQUALIZER_PRE_LEN 16
/*! The number of taps in the pulse shaping/bandpass filter */
#define V17_RX_FILTER_STEPS 27
int eq_step;
/*! \brief Current write offset into the equalizer buffer. */
int eq_put_step;
- /*! \brief Symbol counter to the next equalizer update. */
+ /*! \brief Symbol count to the next equalizer update. */
int eq_skip;
/*! \brief The current half of the baud. */
/*! \brief The current delta factor for updating the equalizer coefficients. */
float eq_delta;
/*! \brief The adaptive equalizer coefficients. */
- complexi16_t eq_coeff[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN];
+ complexi16_t eq_coeff[V17_EQUALIZER_LEN];
/*! \brief A saved set of adaptive equalizer coefficients for use after restarts. */
- complexi16_t eq_coeff_save[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN];
+ complexi16_t eq_coeff_save[V17_EQUALIZER_LEN];
/*! \brief The equalizer signal buffer. */
- complexi16_t eq_buf[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN];
+ complexi16_t eq_buf[V17_EQUALIZER_LEN];
/*! Low band edge filter for symbol sync. */
int32_t symbol_sync_low[2];
/*! \brief The current delta factor for updating the equalizer coefficients. */
float eq_delta;
/*! \brief The adaptive equalizer coefficients. */
- complexf_t eq_coeff[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN];
+ complexf_t eq_coeff[V17_EQUALIZER_LEN];
/*! \brief A saved set of adaptive equalizer coefficients for use after restarts. */
- complexf_t eq_coeff_save[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN];
+ complexf_t eq_coeff_save[V17_EQUALIZER_LEN];
/*! \brief The equalizer signal buffer. */
- complexf_t eq_buf[V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN];
+ complexf_t eq_buf[V17_EQUALIZER_LEN];
/*! Low band edge filter for symbol sync. */
float symbol_sync_low[2];
\param len The number of samples to fake.
\return The number of samples unprocessed.
*/
-SPAN_DECLARE(int) v17_rx_fillin(v17_rx_state_t *s, int len);
+SPAN_DECLARE_NONSTD(int) v17_rx_fillin(v17_rx_state_t *s, int len);
/*! Get a snapshot of the current equalizer coefficients.
\brief Get a snapshot of the current equalizer coefficients.
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: t38_gateway.c,v 1.171.4.2 2009/12/19 10:44:10 steveu Exp $
*/
/*! \file */
/* This is the target time per transmission chunk. The actual
packet timing will sync to the data octets. */
/*! The default number of milliseconds per transmitted IFP when sending bulk T.38 data */
-#define MS_PER_TX_CHUNK 30
+#define DEFAULT_MS_PER_TX_CHUNK 30
+
/*! The number of bytes which must be in the audio to T.38 HDLC buffer before we start
outputting them as IFP messages. */
#define HDLC_START_BUFFER_LEVEL 8
static void non_ecm_push_residue(t38_gateway_state_t *s);
static void tone_detected(void *user_data, int tone, int level, int delay);
-static void set_rx_handler(t38_gateway_state_t *s, span_rx_handler_t *handler, void *user_data)
+static void set_rx_handler(t38_gateway_state_t *s, span_rx_handler_t *handler, span_rx_fillin_handler_t *fillin_handler, void *user_data)
{
if (s->audio.modems.rx_handler != span_dummy_rx)
+ {
s->audio.modems.rx_handler = handler;
+ s->audio.modems.rx_fillin_handler = fillin_handler;
+ }
s->audio.base_rx_handler = handler;
+ s->audio.base_rx_fillin_handler = fillin_handler;
s->audio.modems.rx_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
static void set_rx_active(t38_gateway_state_t *s, int active)
{
s->audio.modems.rx_handler = (active) ? s->audio.base_rx_handler : span_dummy_rx;
+ s->audio.modems.rx_fillin_handler = (active) ? s->audio.base_rx_fillin_handler : span_dummy_rx_fillin;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v17_v21_rx_fillin(void *user_data, int len)
+{
+ t38_gateway_state_t *t;
+ fax_modems_state_t *s;
+
+ t = (t38_gateway_state_t *) user_data;
+ s = &t->audio.modems;
+ v17_rx_fillin(&s->v17_rx, len);
+ fsk_rx_fillin(&s->v21_rx, len);
+ return 0;
}
/*- End of function --------------------------------------------------------*/
/* The fast modem has trained, so we no longer need to run the slow
one in parallel. */
span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.17 + V.21 to V.17 (%.2fdBm0)\n", v17_rx_signal_power(&s->v17_rx));
- set_rx_handler(t, (span_rx_handler_t *) &v17_rx, &s->v17_rx);
+ set_rx_handler(t, (span_rx_handler_t *) &v17_rx, (span_rx_fillin_handler_t *) &v17_rx_fillin, &s->v17_rx);
}
else
{
if (s->rx_signal_present)
{
span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.17 + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx));
- set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, &s->v21_rx);
+ set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
}
/*endif*/
}
}
/*- End of function --------------------------------------------------------*/
+static int v27ter_v21_rx_fillin(void *user_data, int len)
+{
+ t38_gateway_state_t *t;
+ fax_modems_state_t *s;
+
+ t = (t38_gateway_state_t *) user_data;
+ s = &t->audio.modems;
+ v27ter_rx_fillin(&s->v27ter_rx, len);
+ fsk_rx_fillin(&s->v21_rx, len);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
static int v27ter_v21_rx(void *user_data, const int16_t amp[], int len)
{
t38_gateway_state_t *t;
/* The fast modem has trained, so we no longer need to run the slow
one in parallel. */
span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.27ter + V.21 to V.27ter (%.2fdBm0)\n", v27ter_rx_signal_power(&s->v27ter_rx));
- set_rx_handler(t, (span_rx_handler_t *) &v27ter_rx, &s->v27ter_rx);
+ set_rx_handler(t, (span_rx_handler_t *) &v27ter_rx, (span_rx_fillin_handler_t *) &v27ter_v21_rx_fillin, &s->v27ter_rx);
}
else
{
if (s->rx_signal_present)
{
span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.27ter + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx));
- set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, &s->v21_rx);
+ set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
}
/*endif*/
}
}
/*- End of function --------------------------------------------------------*/
+static int v29_v21_rx_fillin(void *user_data, int len)
+{
+ t38_gateway_state_t *t;
+ fax_modems_state_t *s;
+
+ t = (t38_gateway_state_t *) user_data;
+ s = &t->audio.modems;
+ v29_rx_fillin(&s->v29_rx, len);
+ fsk_rx_fillin(&s->v21_rx, len);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
static int v29_v21_rx(void *user_data, const int16_t amp[], int len)
{
t38_gateway_state_t *t;
/* The fast modem has trained, so we no longer need to run the slow
one in parallel. */
span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.29 + V.21 to V.29 (%.2fdBm0)\n", v29_rx_signal_power(&s->v29_rx));
- set_rx_handler(t, (span_rx_handler_t *) &v29_rx, &s->v29_rx);
+ set_rx_handler(t, (span_rx_handler_t *) &v29_rx, (span_rx_fillin_handler_t *) &v29_rx_fillin, &s->v29_rx);
}
else
{
if (s->rx_signal_present)
{
span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.29 + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx));
- set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, &s->v21_rx);
+ set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
}
/*endif*/
}
{
case T30_DIS:
/* Make sure the V.8 capability doesn't pass through. If it
- did then two V.34 capable FAX machines might start some
+ did, two V.34 capable FAX machines might start some
V.8 re-negotiation. */
buf[3] &= ~DISBIT6;
break;
if (len >= 6)
{
j = (buf[5] & (DISBIT7 | DISBIT6 | DISBIT5)) >> 4;
- span_log(&s->logging, SPAN_LOG_FLOW, "Min bits test = 0x%X\n", buf[5]);
s->core.min_row_bits = (s->core.fast_bit_rate*minimum_scan_line_times[j])/1000;
}
else
{
int octets;
- octets = MS_PER_TX_CHUNK*bit_rate/(8*1000);
+ octets = s->core.ms_per_tx_chunk*bit_rate/(8*1000);
if (octets < 1)
octets = 1;
/*endif*/
case T38_V17_RX:
v17_rx_restart(&s->audio.modems.v17_rx, s->core.fast_bit_rate, s->core.short_train);
v17_rx_set_put_bit(&s->audio.modems.v17_rx, put_bit_func, put_bit_user_data);
- set_rx_handler(s, (span_rx_handler_t *) &v17_v21_rx, s);
+ set_rx_handler(s, &v17_v21_rx, &v17_v21_rx_fillin, s);
s->core.fast_rx_active = T38_V17_RX;
break;
case T38_V27TER_RX:
v27ter_rx_restart(&s->audio.modems.v27ter_rx, s->core.fast_bit_rate, FALSE);
v27ter_rx_set_put_bit(&s->audio.modems.v27ter_rx, put_bit_func, put_bit_user_data);
- set_rx_handler(s, (span_rx_handler_t *) &v27ter_v21_rx, s);
+ set_rx_handler(s, &v27ter_v21_rx, &v27ter_v21_rx_fillin, s);
s->core.fast_rx_active = T38_V27TER_RX;
break;
case T38_V29_RX:
v29_rx_restart(&s->audio.modems.v29_rx, s->core.fast_bit_rate, FALSE);
v29_rx_set_put_bit(&s->audio.modems.v29_rx, put_bit_func, put_bit_user_data);
- set_rx_handler(s, (span_rx_handler_t *) &v29_v21_rx, s);
+ set_rx_handler(s, &v29_v21_rx, &v29_v21_rx_fillin, s);
s->core.fast_rx_active = T38_V29_RX;
break;
default:
- set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, &(s->audio.modems.v21_rx));
+ set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &(s->audio.modems.v21_rx));
s->core.fast_rx_active = T38_NONE;
break;
}
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len)
+static void update_rx_timing(t38_gateway_state_t *s, int len)
{
- int i;
-
-#if defined(LOG_FAX_AUDIO)
- if (s->audio.modems.audio_rx_log >= 0)
- write(s->audio.modems.audio_rx_log, amp, len*sizeof(int16_t));
- /*endif*/
-#endif
if (s->core.samples_to_timeout > 0)
{
if ((s->core.samples_to_timeout -= len) <= 0)
/*endif*/
}
/*endif*/
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len)
+{
+ int i;
+
+#if defined(LOG_FAX_AUDIO)
+ if (s->audio.modems.audio_rx_log >= 0)
+ write(s->audio.modems.audio_rx_log, amp, len*sizeof(int16_t));
+ /*endif*/
+#endif
+ update_rx_timing(s, len);
for (i = 0; i < len; i++)
amp[i] = dc_restore(&(s->audio.modems.dc_restore), amp[i]);
/*endfor*/
SPAN_DECLARE_NONSTD(int) t38_gateway_rx_fillin(t38_gateway_state_t *s, int len)
{
- /* TODO: handle things properly */
+ /* To mitigate the effect of lost packets on a packet network we should
+ try to sustain the status quo. If there is no receive modem running, keep
+ things that way. If there is a receive modem running, try to sustain its
+ operation, without causing a phase hop, or letting its adaptive functions
+ diverge. */
+#if defined(LOG_FAX_AUDIO)
+ if (s->audio.modems.audio_rx_log >= 0)
+ {
+ int i;
+#if defined(_MSC_VER)
+ int16_t *amp = (int16_t *) _alloca(sizeof(int16_t)*len);
+#else
+ int16_t amp[len];
+#endif
+
+ vec_zeroi16(amp, len);
+ write(s->modems.audio_rx_log, amp, len*sizeof(int16_t));
+ }
+#endif
+ update_rx_timing(s, len);
+ /* TODO: handle the modems properly */
+ s->audio.modems.rx_fillin_handler(s->audio.modems.rx_user_data, len);
return 0;
}
/*- End of function --------------------------------------------------------*/
t38_gateway_t38_init(s, tx_packet_handler, tx_packet_user_data);
set_rx_active(s, TRUE);
- t38_gateway_set_supported_modems(s, T30_SUPPORT_V27TER | T30_SUPPORT_V29);
+ t38_gateway_set_supported_modems(s, T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17);
t38_gateway_set_nsx_suppression(s, (const uint8_t *) "\x00\x00\x00", 3, (const uint8_t *) "\x00\x00\x00", 3);
s->core.to_t38.octets_per_data_packet = 1;
s->core.ecm_allowed = TRUE;
+ s->core.ms_per_tx_chunk = DEFAULT_MS_PER_TX_CHUNK;
t38_non_ecm_buffer_init(&s->core.non_ecm_to_modem, FALSE, 0);
restart_rx_modem(s);
s->core.timed_mode = TIMED_MODE_STARTUP;
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: v17rx.c,v 1.153.4.6 2009/12/28 12:20:46 steveu Exp $
*/
/*! \file */
/*! The 16 bit pattern used in the bridge section of the training sequence */
#define V17_BRIDGE_WORD 0x8880
-/*! The length of the equalizer buffer */
-#define V17_EQUALIZER_LEN (V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN)
-
enum
{
TRAINING_STAGE_NORMAL_OPERATION = 0,
s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1;
s->eq_step = 0;
+ s->eq_skip = 0;
}
/*- End of function --------------------------------------------------------*/
s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1;
s->eq_step = 0;
+ s->eq_skip = 0;
}
/*- End of function --------------------------------------------------------*/
Passband Timing Recovery in an All-Digital Modem Receiver
IEEE TRANSACTIONS ON COMMUNICATIONS, VOL. COM-26, NO. 5, MAY 1978 */
- /* This is slightly rearranged for figure 3b of the Godard paper, as this saves a couple of
+ /* This is slightly rearranged from figure 3b of the Godard paper, as this saves a couple of
maths operations */
#if defined(SPANDSP_USE_FIXED_POINTx)
/* TODO: The scalings used here need more thorough evaluation, to see if overflows are possible. */
s->symbol_sync_dc_filter[0] = v;
/* A little integration will now filter away much of the HF noise */
s->baud_phase -= p;
- if (abs(s->baud_phase) > 100*FP_FACTOR)
+ v = labs(s->baud_phase);
+ if (v > 100*FP_FACTOR)
{
- if (s->baud_phase > 0)
- i = (s->baud_phase > 1000*FP_FACTOR) ? 15 : 1;
- else
- i = (s->baud_phase < -1000*FP_FACTOR) ? -15 : -1;
+ i = (v > 1000*FP_FACTOR) ? 15 : 1;
+ if (s->baud_phase < 0)
+ i = -i;
//printf("v = %10.5f %5d - %f %f %d %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction);
s->eq_put_step += i;
s->total_baud_timing_correction += i;
s->symbol_sync_dc_filter[0] = v;
/* A little integration will now filter away much of the HF noise */
s->baud_phase -= p;
- if (fabsf(s->baud_phase) > 100.0f)
+ v = fabsf(s->baud_phase);
+ if (v > 100.0f)
{
- if (s->baud_phase > 0.0f)
- i = (s->baud_phase > 1000.0f) ? 15 : 1;
- else
- i = (s->baud_phase < -1000.0f) ? -15 : -1;
+ i = (v > 1000.0f) ? 15 : 1;
+ if (s->baud_phase < 0.0f)
+ i = -i;
//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;
if (s->training_count == 100)
{
i = s->training_count;
- /* Avoid the possibility of a divide by zero */
- if (i)
- {
- j = i & 0xF;
- ang = (s->angles[j] - s->start_angles[0])/i
- + (s->angles[j | 0x1] - s->start_angles[1])/i;
- s->carrier_phase_rate += 3*(ang/20);
- //span_log(&s->logging, SPAN_LOG_FLOW, "Angles %x, %x, %x, %x, dist %d\n", s->angles[j], s->start_angles[0], s->angles[j | 0x1], s->start_angles[1], i);
-
- s->start_angles[0] = s->angles[j];
- s->start_angles[1] = s->angles[j | 0x1];
- }
+ j = i & 0xF;
+ ang = (s->angles[j] - s->start_angles[0])/i
+ + (s->angles[j | 0x1] - s->start_angles[1])/i;
+ s->carrier_phase_rate += 3*(ang/20);
+ //span_log(&s->logging, SPAN_LOG_FLOW, "Angles %x, %x, %x, %x, dist %d\n", s->angles[j], s->start_angles[0], s->angles[j | 0x1], s->start_angles[1], i);
+
+ s->start_angles[0] = s->angles[j];
+ s->start_angles[1] = s->angles[j | 0x1];
//span_log(&s->logging, SPAN_LOG_FLOW, "%d %d %d %d %d\n", s->angles[s->training_count & 0xF], s->start_angles[0], s->angles[(s->training_count | 0x1) & 0xF], s->start_angles[1], s->training_count);
span_log(&s->logging, SPAN_LOG_FLOW, "First coarse carrier frequency %7.2f (%d)\n", dds_frequencyf(s->carrier_phase_rate), s->training_count);
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) v17_rx_fillin(v17_rx_state_t *s, int len)
+SPAN_DECLARE_NONSTD(int) v17_rx_fillin(v17_rx_state_t *s, int len)
{
int i;
s->distances[0] = 0;
s->trellis_ptr = 14;
- 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->carrier_phase = 0;
power_meter_init(&(s->power), 4);
if (s->short_train)
{
s->carrier_phase_rate = s->carrier_phase_rate_save;
- s->agc_scaling = s->agc_scaling_save;
equalizer_restore(s);
+ s->agc_scaling = s->agc_scaling_save;
/* Don't allow any frequency correction at all, until we start to pull the phase in. */
#if defined(SPANDSP_USE_FIXED_POINTx)
s->carrier_track_i = 0;
s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ);
equalizer_reset(s);
#if defined(SPANDSP_USE_FIXED_POINTx)
- s->agc_scaling_save = 0;
+ //s->agc_scaling_save = 0;
s->agc_scaling = (float) FP_FACTOR*32768.0f*0.0017f/RX_PULSESHAPER_GAIN;
s->carrier_track_i = 5000;
s->carrier_track_p = 40000;
#else
- s->agc_scaling_save = 0.0f;
+ //s->agc_scaling_save = 0.0f;
s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN;
s->carrier_track_i = 5000.0f;
s->carrier_track_p = 40000.0f;
#endif
}
s->last_sample = 0;
+ 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));
/* Initialise the working data for symbol timing synchronisation */
#if defined(SPANDSP_USE_FIXED_POINTx)
s->short_train = FALSE;
//s->scrambler_tap = 18 - 1;
v17_rx_signal_cutoff(s, -45.5f);
- s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN;
+#if defined(SPANDSP_USE_FIXED_POINTx)
+ s->agc_scaling_save = 0;
+ s->agc_scaling = (float) FP_FACTOR*32768.0f*0.0017f/RX_PULSESHAPER_GAIN;
+#else
s->agc_scaling_save = 0.0f;
+ s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN;
+#endif
s->carrier_phase_rate_save = dds_phase_ratef(CARRIER_NOMINAL_FREQ);
v17_rx_restart(s, bit_rate, s->short_train);
return s;
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: oki_adpcm_tests.c,v 1.37 2009/05/30 15:23:14 steveu Exp $
*/
/*! \file */
int hist_out;
oki_adpcm_state_t *oki_enc_state;
oki_adpcm_state_t *oki_dec_state;
+ oki_adpcm_state_t *oki_dec_state2;
int xx;
int total_pre_samples;
int total_compressed_bytes;
int total_post_samples;
+ int successive_08_bytes;
+ int successive_80_bytes;
+ int encoded_fd;
+ const char *encoded_file_name;
const char *in_file_name;
int log_encoded_data;
int opt;
bit_rate = 32000;
+ encoded_file_name = NULL;
in_file_name = IN_FILE_NAME;
log_encoded_data = FALSE;
- while ((opt = getopt(argc, argv, "2i:l")) != -1)
+ while ((opt = getopt(argc, argv, "2d:i:l")) != -1)
{
switch (opt)
{
case '2':
bit_rate = 24000;
break;
+ case 'd':
+ encoded_file_name = optarg;
+ break;
case 'i':
in_file_name = optarg;
break;
}
}
- if ((inhandle = sf_open_telephony_read(in_file_name, 1)) == NULL)
+ if (encoded_file_name)
{
- fprintf(stderr, " Cannot open audio file '%s'\n", in_file_name);
- exit(2);
+ if ((encoded_fd = open(encoded_file_name, O_RDONLY)) < 0)
+ {
+ fprintf(stderr, " Cannot open encoded file '%s'\n", encoded_file_name);
+ exit(2);
+ }
+ }
+ else
+ {
+ if ((inhandle = sf_open_telephony_read(in_file_name, 1)) == NULL)
+ {
+ fprintf(stderr, " Cannot open audio file '%s'\n", in_file_name);
+ exit(2);
+ }
+ if ((oki_enc_state = oki_adpcm_init(NULL, bit_rate)) == NULL)
+ {
+ fprintf(stderr, " Cannot create encoder\n");
+ exit(2);
+ }
}
+
if ((outhandle = sf_open_telephony_write(OUT_FILE_NAME, 1)) == NULL)
{
fprintf(stderr, " Cannot create audio file '%s'\n", OUT_FILE_NAME);
exit(2);
}
-
- if ((oki_enc_state = oki_adpcm_init(NULL, bit_rate)) == NULL)
+ if ((oki_dec_state = oki_adpcm_init(NULL, bit_rate)) == NULL)
{
- fprintf(stderr, " Cannot create encoder\n");
+ fprintf(stderr, " Cannot create decoder\n");
exit(2);
}
-
- if ((oki_dec_state = oki_adpcm_init(NULL, bit_rate)) == NULL)
+ if ((oki_dec_state2 = oki_adpcm_init(NULL, bit_rate)) == NULL)
{
fprintf(stderr, " Cannot create decoder\n");
exit(2);
total_pre_samples = 0;
total_compressed_bytes = 0;
total_post_samples = 0;
- while ((frames = sf_readf_short(inhandle, pre_amp, 159)))
+ if (encoded_file_name)
{
- total_pre_samples += frames;
- oki_bytes = oki_adpcm_encode(oki_enc_state, oki_data, pre_amp, frames);
- if (log_encoded_data)
- write(1, oki_data, oki_bytes);
- total_compressed_bytes += oki_bytes;
- dec_frames = oki_adpcm_decode(oki_dec_state, post_amp, oki_data, oki_bytes);
- total_post_samples += dec_frames;
- for (i = 0; i < frames; i++)
- {
- history[hist_in++] = pre_amp[i];
- if (hist_in >= HIST_LEN)
- hist_in = 0;
- pre_energy += (double) pre_amp[i] * (double) pre_amp[i];
- }
- for (i = 0; i < dec_frames; i++)
+ /* Decode a file of OKI ADPCM code to a linear wave file */
+ while ((oki_bytes = read(encoded_fd, oki_data, 80)) > 0)
{
- post_energy += (double) post_amp[i] * (double) post_amp[i];
- xx = post_amp[i] - history[hist_out++];
- if (hist_out >= HIST_LEN)
- hist_out = 0;
- diff_energy += (double) xx * (double) xx;
- //post_amp[i] = xx;
+ total_compressed_bytes += oki_bytes;
+ dec_frames = oki_adpcm_decode(oki_dec_state, post_amp, oki_data, oki_bytes);
+ total_post_samples += dec_frames;
+ for (i = 0; i < dec_frames; i++)
+ {
+ post_energy += (double) post_amp[i] * (double) post_amp[i];
+ xx = post_amp[i] - history[hist_out++];
+ if (hist_out >= HIST_LEN)
+ hist_out = 0;
+ diff_energy += (double) xx * (double) xx;
+ }
+ outframes = sf_writef_short(outhandle, post_amp, dec_frames);
}
- outframes = sf_writef_short(outhandle, post_amp, dec_frames);
- }
- if (sf_close(inhandle) != 0)
- {
- fprintf(stderr, " Cannot close audio file '%s'\n", in_file_name);
- exit(2);
+ close(encoded_fd);
}
- if (sf_close(outhandle) != 0)
+ else
{
- fprintf(stderr, " Cannot close audio file '%s'\n", OUT_FILE_NAME);
- exit(2);
- }
- oki_adpcm_release(oki_enc_state);
- oki_adpcm_release(oki_dec_state);
+ /* Perform a linear wave file -> OKI ADPCM -> linear wave file cycle. Along the way
+ check the decoder resets on the sequence specified for this codec, and the gain
+ and worst case sample distortion. */
+ successive_08_bytes = 0;
+ successive_80_bytes = 0;
+ while ((frames = sf_readf_short(inhandle, pre_amp, 159)))
+ {
+ total_pre_samples += frames;
+ oki_bytes = oki_adpcm_encode(oki_enc_state, oki_data, pre_amp, frames);
+ if (log_encoded_data)
+ write(1, oki_data, oki_bytes);
+ total_compressed_bytes += oki_bytes;
+ /* Look for a condition which is defined as something which should cause a reset of
+ the decoder (48 samples of 0, 8, 0, 8, etc.), and verify that it really does. Use
+ a second decode, which we feed byte by byte, for this. */
+ for (i = 0; i < oki_bytes; i++)
+ {
+ oki_adpcm_decode(oki_dec_state2, post_amp, &oki_data[i], 1);
+ if (oki_data[i] == 0x08)
+ successive_08_bytes++;
+ else
+ successive_08_bytes = 0;
+ if (oki_data[i] == 0x80)
+ successive_80_bytes++;
+ else
+ successive_80_bytes = 0;
+ if (successive_08_bytes == 24 || successive_80_bytes == 24)
+ {
+ if (oki_dec_state2->step_index != 0)
+ {
+ fprintf(stderr, "Decoder reset failure\n");
+ exit(2);
+ }
+ }
+ }
+ dec_frames = oki_adpcm_decode(oki_dec_state, post_amp, oki_data, oki_bytes);
+ total_post_samples += dec_frames;
+ for (i = 0; i < frames; i++)
+ {
+ history[hist_in++] = pre_amp[i];
+ if (hist_in >= HIST_LEN)
+ hist_in = 0;
+ pre_energy += (double) pre_amp[i] * (double) pre_amp[i];
+ }
+ for (i = 0; i < dec_frames; i++)
+ {
+ post_energy += (double) post_amp[i] * (double) post_amp[i];
+ xx = post_amp[i] - history[hist_out++];
+ if (hist_out >= HIST_LEN)
+ hist_out = 0;
+ diff_energy += (double) xx * (double) xx;
+ //post_amp[i] = xx;
+ }
+ outframes = sf_writef_short(outhandle, post_amp, dec_frames);
+ }
+ printf("Pre samples: %d\n", total_pre_samples);
+ printf("Compressed bytes: %d\n", total_compressed_bytes);
+ printf("Post samples: %d\n", total_post_samples);
+
+ printf("Output energy is %f%% of input energy.\n", 100.0*post_energy/pre_energy);
+ printf("Residual energy is %f%% of the total.\n", 100.0*diff_energy/post_energy);
+ if (bit_rate == 32000)
+ {
+ if (fabs(1.0 - post_energy/pre_energy) > 0.01
+ ||
+ fabs(diff_energy/post_energy) > 0.01)
+ {
+ printf("Tests failed.\n");
+ exit(2);
+ }
+ }
+ else
+ {
+ if (fabs(1.0 - post_energy/pre_energy) > 0.11
+ ||
+ fabs(diff_energy/post_energy) > 0.05)
+ {
+ printf("Tests failed.\n");
+ exit(2);
+ }
+ }
- printf("Pre samples: %d\n", total_pre_samples);
- printf("Compressed bytes: %d\n", total_compressed_bytes);
- printf("Post samples: %d\n", total_post_samples);
- printf("Output energy is %f%% of input energy.\n", 100.0*post_energy/pre_energy);
- printf("Residual energy is %f%% of the total.\n", 100.0*diff_energy/post_energy);
- if (bit_rate == 32000)
- {
- if (fabs(1.0 - post_energy/pre_energy) > 0.05
- ||
- fabs(diff_energy/post_energy) > 0.03)
+ oki_adpcm_release(oki_enc_state);
+ if (sf_close(inhandle) != 0)
{
- printf("Tests failed.\n");
+ fprintf(stderr, " Cannot close audio file '%s'\n", in_file_name);
exit(2);
}
}
- else
+ oki_adpcm_release(oki_dec_state);
+ oki_adpcm_release(oki_dec_state2);
+ if (sf_close(outhandle) != 0)
{
- if (fabs(1.0 - post_energy/pre_energy) > 0.20
- ||
- fabs(diff_energy/post_energy) > 0.10)
- {
- printf("Tests failed.\n");
- exit(2);
- }
+ fprintf(stderr, " Cannot close audio file '%s'\n", OUT_FILE_NAME);
+ exit(2);
}
printf("Tests passed.\n");