git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@9493 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Anthony Minessale committed 2008-09-09 17:04:42 +00:00
1 parent bc5cf34915
commit ea964d8fe6
79 files changed
+2804 -1642

No files matched your search

+344 -129
View File
@@ -22,7 +22,7 @@
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: v29rx.c,v 1.124 2008/07/17 19:12:27 steveu Exp $
* $Id: v29rx.c,v 1.135 2008/09/09 16:13:12 steveu Exp $
*/
/*! \file */
@@ -48,6 +48,8 @@
#include "spandsp/complex.h"
#include "spandsp/vector_float.h"
#include "spandsp/complex_vector_float.h"
#include "spandsp/vector_int.h"
#include "spandsp/complex_vector_int.h"
#include "spandsp/async.h"
#include "spandsp/power_meter.h"
#include "spandsp/arctan2.h"
@@ -67,6 +69,11 @@
#define BAUD_RATE 2400
#define EQUALIZER_DELTA 0.21f
#if defined(SPANDSP_USE_FIXED_POINT)
#define FP_FACTOR 4096
#define FP_SHIFT_FACTOR 12
#endif
/* Segments of the training sequence */
#define V29_TRAINING_SEG_2_LEN 128
#define V29_TRAINING_SEG_3_LEN 384
@@ -109,13 +116,23 @@ static const uint8_t space_map_9600[20][20] =
};
/* Coefficients for the band edge symbol timing synchroniser (alpha = 0.99) */
#define SYNC_LOW_BAND_EDGE_COEFF_0 1.829281f /* 2*alpha*cos(low_edge) */
#define SYNC_LOW_BAND_EDGE_COEFF_1 -0.980100f /* -alpha^2 */
#define SYNC_HIGH_BAND_EDGE_COEFF_0 -1.285907f /* 2*alpha*cos(high_edge) */
#define SYNC_HIGH_BAND_EDGE_COEFF_1 -0.980100f /* -alpha^2 */
#define SYNC_CROSS_CORR_COEFF_A -0.932131f /* -alpha^2*sin(freq_diff) */
#define SYNC_CROSS_CORR_COEFF_B 0.752802f /* alpha*sin(high_edge) */
#define SYNC_CROSS_CORR_COEFF_C -0.378857f /* -alpha*sin(low_edge) */
#if defined(SPANDSP_USE_FIXED_POINT)
#define SYNC_LOW_BAND_EDGE_COEFF_0 ((int)(FP_FACTOR* 1.829281f)) /* 2*alpha*cos(low_edge) */
#define SYNC_LOW_BAND_EDGE_COEFF_1 ((int)(FP_FACTOR*-0.980100f)) /* -alpha^2 */
#define SYNC_HIGH_BAND_EDGE_COEFF_0 ((int)(FP_FACTOR*-1.285907f)) /* 2*alpha*cos(high_edge) */
#define SYNC_HIGH_BAND_EDGE_COEFF_1 ((int)(FP_FACTOR*-0.980100f)) /* -alpha^2 */
#define SYNC_CROSS_CORR_COEFF_A ((int)(FP_FACTOR*-0.932131f)) /* -alpha^2*sin(freq_diff) */
#define SYNC_CROSS_CORR_COEFF_B ((int)(FP_FACTOR* 0.752802f)) /* alpha*sin(high_edge) */
#define SYNC_CROSS_CORR_COEFF_C ((int)(FP_FACTOR*-0.378857f)) /* -alpha*sin(low_edge) */
#else
#define SYNC_LOW_BAND_EDGE_COEFF_0 1.829281f /* 2*alpha*cos(low_edge) */
#define SYNC_LOW_BAND_EDGE_COEFF_1 -0.980100f /* -alpha^2 */
#define SYNC_HIGH_BAND_EDGE_COEFF_0 -1.285907f /* 2*alpha*cos(high_edge) */
#define SYNC_HIGH_BAND_EDGE_COEFF_1 -0.980100f /* -alpha^2 */
#define SYNC_CROSS_CORR_COEFF_A -0.932131f /* -alpha^2*sin(freq_diff) */
#define SYNC_CROSS_CORR_COEFF_B 0.752802f /* alpha*sin(high_edge) */
#define SYNC_CROSS_CORR_COEFF_C -0.378857f /* -alpha*sin(low_edge) */
#endif
float v29_rx_carrier_frequency(v29_rx_state_t *s)
{
@@ -143,19 +160,17 @@ float v29_rx_signal_power(v29_rx_state_t *s)
}
/*- End of function --------------------------------------------------------*/
#if defined(SPANDSP_USE_FIXED_POINT)
int v29_rx_equalizer_state(v29_rx_state_t *s, complexi16_t **coeffs)
#else
int v29_rx_equalizer_state(v29_rx_state_t *s, complexf_t **coeffs)
#endif
{
*coeffs = s->eq_coeff;
return V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN;
}
/*- End of function --------------------------------------------------------*/
static void equalizer_save(v29_rx_state_t *s)
{
cvec_copyf(s->eq_coeff_save, s->eq_coeff, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
}
/*- End of function --------------------------------------------------------*/
static void report_status_change(v29_rx_state_t *s, int status)
{
if (s->status_handler)
@@ -165,51 +180,105 @@ static void report_status_change(v29_rx_state_t *s, int status)
}
/*- End of function --------------------------------------------------------*/
static void equalizer_save(v29_rx_state_t *s)
{
#if defined(SPANDSP_USE_FIXED_POINT)
cvec_copyi16(s->eq_coeff_save, s->eq_coeff, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
#else
cvec_copyf(s->eq_coeff_save, s->eq_coeff, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
#endif
}
/*- End of function --------------------------------------------------------*/
static void equalizer_restore(v29_rx_state_t *s)
{
#if defined(SPANDSP_USE_FIXED_POINT)
cvec_copyi16(s->eq_coeff, s->eq_coeff_save, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
cvec_zeroi16(s->eq_buf, V29_EQUALIZER_MASK);
s->eq_delta = 32768.0f*EQUALIZER_DELTA/(V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
#else
cvec_copyf(s->eq_coeff, s->eq_coeff_save, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
cvec_zerof(s->eq_buf, V29_EQUALIZER_MASK);
s->eq_delta = EQUALIZER_DELTA/(V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
#endif
s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1;
s->eq_step = 0;
s->eq_delta = EQUALIZER_DELTA/(V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
}
/*- End of function --------------------------------------------------------*/
static void equalizer_reset(v29_rx_state_t *s)
{
/* Start with an equalizer based on everything being perfect */
#if defined(SPANDSP_USE_FIXED_POINT)
cvec_zeroi16(s->eq_coeff, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
s->eq_coeff[V29_EQUALIZER_PRE_LEN] = complex_seti16(3*FP_FACTOR, 0*FP_FACTOR);
cvec_zeroi16(s->eq_buf, V29_EQUALIZER_MASK);
s->eq_delta = 32768.0f*EQUALIZER_DELTA/(V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
#else
cvec_zerof(s->eq_coeff, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
s->eq_coeff[V29_EQUALIZER_PRE_LEN] = complex_setf(3.0f, 0.0f);
cvec_zerof(s->eq_buf, V29_EQUALIZER_MASK);
s->eq_delta = EQUALIZER_DELTA/(V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
#endif
s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1;
s->eq_step = 0;
s->eq_delta = EQUALIZER_DELTA/(V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
}
/*- End of function --------------------------------------------------------*/
#if defined(SPANDSP_USE_FIXED_POINT)
static __inline__ complexi16_t complex_mul_q4_12(const complexi16_t *x, const complexi16_t *y)
{
complexi16_t z;
z.re = ((int32_t) x->re*(int32_t) y->re - (int32_t) x->im*(int32_t) y->im) >> 12;
z.im = ((int32_t) x->re*(int32_t) y->im + (int32_t) x->im*(int32_t) y->re) >> 12;
return z;
}
/*- End of function --------------------------------------------------------*/
#endif
#if defined(SPANDSP_USE_FIXED_POINT)
static __inline__ complexi16_t equalizer_get(v29_rx_state_t *s)
#else
static __inline__ complexf_t equalizer_get(v29_rx_state_t *s)
#endif
{
int i;
int p;
#if defined(SPANDSP_USE_FIXED_POINT)
complexi16_t z;
complexi16_t z1;
#else
complexf_t z;
complexf_t z1;
#endif
/* Get the next equalized value. */
z = complex_setf(0.0f, 0.0f);
p = s->eq_step - 1;
#if defined(SPANDSP_USE_FIXED_POINT)
z = complex_seti16(0, 0);
for (i = 0; i < V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN; i++)
{
p = (p - 1) & V29_EQUALIZER_MASK;
z1 = complex_mul_q4_12(&s->eq_coeff[i], &s->eq_buf[p]);
z = complex_addi16(&z, &z1);
}
#else
z = complex_setf(0.0f, 0.0f);
for (i = 0; i < V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN; i++)
{
p = (p - 1) & V29_EQUALIZER_MASK;
z1 = complex_mulf(&s->eq_coeff[i], &s->eq_buf[p]);
z = complex_addf(&z, &z1);
}
#endif
return z;
}
/*- End of function --------------------------------------------------------*/
#if defined(SPANDSP_USE_FIXED_POINTy)
#if defined(SPANDSP_USE_FIXED_POINT)
static void tune_equalizer(v29_rx_state_t *s, const complexi16_t *z, const complexi16_t *target)
#else
static void tune_equalizer(v29_rx_state_t *s, const complexf_t *z, const complexf_t *target)
@@ -217,24 +286,42 @@ static void tune_equalizer(v29_rx_state_t *s, const complexf_t *z, const complex
{
int i;
int p;
#if defined(SPANDSP_USE_FIXED_POINT)
complexi16_t ez;
complexi16_t z1;
#else
complexf_t ez;
complexf_t z1;
#endif
/* Find the x and y mismatch from the exact constellation position. */
#if defined(SPANDSP_USE_FIXED_POINT)
ez.re = target->re*FP_FACTOR - z->re;
ez.im = target->im*FP_FACTOR - z->im;
ez.re = ((int32_t) ez.re*(int32_t) s->eq_delta) >> 15;
ez.im = ((int32_t) ez.im*(int32_t) s->eq_delta) >> 15;
#else
ez = complex_subf(target, z);
ez.re *= s->eq_delta;
ez.im *= s->eq_delta;
#endif
p = s->eq_step - 1;
for (i = 0; i < V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN; i++)
{
p = (p - 1) & V29_EQUALIZER_MASK;
#if defined(SPANDSP_USE_FIXED_POINT)
z1 = complex_conji16(&s->eq_buf[p]);
z1 = complex_mul_q4_12(&ez, &z1);
s->eq_coeff[i] = complex_addi16(&s->eq_coeff[i], &z1);
#else
z1 = complex_conjf(&s->eq_buf[p]);
z1 = complex_mulf(&ez, &z1);
s->eq_coeff[i] = complex_addf(&s->eq_coeff[i], &z1);
/* Leak a little to tame uncontrolled wandering */
s->eq_coeff[i].re *= 0.9999f;
s->eq_coeff[i].im *= 0.9999f;
#endif
}
}
/*- End of function --------------------------------------------------------*/
@@ -253,7 +340,7 @@ static int scrambled_training_bit(v29_rx_state_t *s)
}
/*- End of function --------------------------------------------------------*/
#if defined(SPANDSP_USE_FIXED_POINTy)
#if defined(SPANDSP_USE_FIXED_POINT)
static __inline__ int find_quadrant(const complexi16_t *z)
#else
static __inline__ int find_quadrant(const complexf_t *z)
@@ -269,7 +356,7 @@ static __inline__ int find_quadrant(const complexf_t *z)
}
/*- End of function --------------------------------------------------------*/
#if defined(SPANDSP_USE_FIXED_POINTy)
#if defined(SPANDSP_USE_FIXED_POINT)
static __inline__ void track_carrier(v29_rx_state_t *s, const complexi16_t *z, const complexi16_t *target)
#else
static __inline__ void track_carrier(v29_rx_state_t *s, const complexf_t *z, const complexf_t *target)
@@ -290,7 +377,12 @@ static __inline__ void track_carrier(v29_rx_state_t *s, const complexf_t *z, con
different amplitudes of the various target positions scale things. This isn't all bad,
as the angular error for the larger amplitude constellation points is probably
a more reliable indicator, and we are weighting it as such. */
#if defined(SPANDSP_USE_FIXED_POINT)
error = z->im*target->re - z->re*target->im;
error /= (float) FP_FACTOR;
#else
error = z->im*target->re - z->re*target->im;
#endif
/* Use a proportional-integral approach to tracking the carrier. The PI
parameters are coarser at first, until we get precisely on target. Then,
@@ -326,7 +418,11 @@ static __inline__ void put_bit(v29_rx_state_t *s, int bit)
}
/*- End of function --------------------------------------------------------*/
#if defined(SPANDSP_USE_FIXED_POINT)
static void decode_baud(v29_rx_state_t *s, complexi16_t *z)
#else
static void decode_baud(v29_rx_state_t *s, complexf_t *z)
#endif
{
static const uint8_t phase_steps_9600[8] =
{
@@ -342,57 +438,51 @@ static void decode_baud(v29_rx_state_t *s, complexf_t *z)
int re;
int im;
switch (s->bit_rate)
if (s->bit_rate == 4800)
{
case 9600:
default:
/* 4800 is a special case. */
nearest = find_quadrant(z) << 1;
raw_bits = phase_steps_4800[((nearest - s->constellation_state) >> 1) & 3];
put_bit(s, raw_bits);
put_bit(s, raw_bits >> 1);
}
else
{
/* 9600 and 7200 are quite similar. */
#if defined(SPANDSP_USE_FIXED_POINT)
re = (z->re + 5*FP_FACTOR) >> (FP_SHIFT_FACTOR - 1);
im = (z->im + 5*FP_FACTOR) >> (FP_SHIFT_FACTOR - 1);
#else
re = (int) ((z->re + 5.0f)*2.0f);
im = (int) ((z->im + 5.0f)*2.0f);
#endif
if (re > 19)
re = 19;
else if (re < 0)
re = 0;
im = (int) ((z->im + 5.0f)*2.0f);
if (im > 19)
im = 19;
else if (im < 0)
im = 0;
nearest = space_map_9600[re][im];
/* Deal with the amplitude bit */
put_bit(s, nearest >> 3);
if (s->bit_rate == 9600)
{
/* Send out the top (amplitude) bit. */
put_bit(s, nearest >> 3);
}
else
{
/* We can reuse the space map for 9600, but drop the top bit. */
nearest &= 7;
}
raw_bits = phase_steps_9600[(nearest - s->constellation_state) & 7];
for (i = 0; i < 3; i++)
{
put_bit(s, raw_bits);
raw_bits >>= 1;
}
break;
case 7200:
/* We can reuse the space map for 9600, but drop the top bit */
re = (int) ((z->re + 5.0f)*2.0f);
if (re > 19)
re = 19;
else if (re < 0)
re = 0;
im = (int) ((z->im + 5.0f)*2.0f);
if (im > 19)
im = 19;
else if (im < 0)
im = 0;
nearest = space_map_9600[re][im] & 7;
raw_bits = phase_steps_9600[(nearest - s->constellation_state) & 7];
for (i = 0; i < 3; i++)
{
put_bit(s, raw_bits);
raw_bits >>= 1;
}
break;
case 4800:
nearest = find_quadrant(z) << 1;
raw_bits = phase_steps_4800[((nearest - s->constellation_state) >> 1) & 3];
put_bit(s, raw_bits);
put_bit(s, raw_bits >> 1);
break;
}
track_carrier(s, z, &v29_9600_constellation[nearest]);
if (--s->eq_skip <= 0)
{
@@ -406,7 +496,73 @@ static void decode_baud(v29_rx_state_t *s, complexf_t *z)
}
/*- End of function --------------------------------------------------------*/
static __inline__ void symbol_sync(v29_rx_state_t *s)
{
int i;
#if defined(SPANDSP_USE_FIXED_POINT)
int32_t v;
int32_t p;
#else
float v;
float p;
#endif
/* This routine adapts the position of the half baud samples entering the equalizer. */
#if defined(SPANDSP_USE_FIXED_POINT)
/* TODO: The scalings used here need more thorough evaluation, to see if overflows are possible. */
/* Cross correlate */
v = (((s->symbol_sync_low[1] >> 5)*(s->symbol_sync_high[1] >> 4)) >> 15)*SYNC_CROSS_CORR_COEFF_A
+ (((s->symbol_sync_low[0] >> 5)*(s->symbol_sync_high[1] >> 4)) >> 15)*SYNC_CROSS_CORR_COEFF_B
+ (((s->symbol_sync_low[1] >> 5)*(s->symbol_sync_high[0] >> 4)) >> 15)*SYNC_CROSS_CORR_COEFF_C;
/* Filter away any DC component */
p = v - s->symbol_sync_dc_filter[1];
s->symbol_sync_dc_filter[1] = s->symbol_sync_dc_filter[0];
s->symbol_sync_dc_filter[0] = v;
/* A little integration will now filter away much of the noise */
s->baud_phase -= p;
if (abs(s->baud_phase) > 50*FP_FACTOR)
{
if (s->baud_phase > 0)
i = (s->baud_phase > 1000*FP_FACTOR) ? 5 : 1;
else
i = (s->baud_phase < -1000*FP_FACTOR) ? -5 : -1;
//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;
}
#else
/* Cross correlate */
v = s->symbol_sync_low[1]*s->symbol_sync_high[1]*SYNC_CROSS_CORR_COEFF_A
+ s->symbol_sync_low[0]*s->symbol_sync_high[1]*SYNC_CROSS_CORR_COEFF_B
+ s->symbol_sync_low[1]*s->symbol_sync_high[0]*SYNC_CROSS_CORR_COEFF_C;
/* Filter away any DC component */
p = v - s->symbol_sync_dc_filter[1];
s->symbol_sync_dc_filter[1] = s->symbol_sync_dc_filter[0];
s->symbol_sync_dc_filter[0] = v;
/* A little integration will now filter away much of the noise */
s->baud_phase -= p;
if (fabsf(s->baud_phase) > 50.0f)
{
if (s->baud_phase > 0.0f)
i = (s->baud_phase > 1000.0f) ? 5 : 1;
else
i = (s->baud_phase < -1000.0f) ? -5 : -1;
//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;
}
#endif
}
/*- End of function --------------------------------------------------------*/
#if defined(SPANDSP_USE_FIXED_POINT)
static void process_half_baud(v29_rx_state_t *s, complexi16_t *sample)
#else
static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
#endif
{
static const int cdcd_pos[6] =
{
@@ -414,14 +570,17 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
0, 3,
0, 2
};
complexf_t z;
complexf_t zz;
#if defined(SPANDSP_USE_FIXED_POINTy)
#if defined(SPANDSP_USE_FIXED_POINT)
complexf_t z1;
complexi16_t z;
const complexi16_t *target;
static const complexi16_t zero = {0, 0};
#else
complexf_t z;
const complexf_t *target;
static const complexf_t zero = {0.0f, 0.0f};
#endif
float v;
float p;
int bit;
int i;
@@ -429,8 +588,7 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
int32_t angle;
int32_t ang;
/* This routine processes every half a baud, as we put things into the equalizer at the T/2 rate.
This routine adapts the position of the half baud samples, which the caller takes. */
/* This routine processes every half a baud, as we put things into the equalizer at the T/2 rate. */
/* Add a sample to the equalizer's circular buffer, but don't calculate anything
at this time. */
@@ -442,29 +600,7 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
return;
/* Symbol timing synchronisation */
/* Cross correlate */
v = s->symbol_sync_low[1]*s->symbol_sync_high[1]*SYNC_CROSS_CORR_COEFF_A
+ s->symbol_sync_low[0]*s->symbol_sync_high[1]*SYNC_CROSS_CORR_COEFF_B
+ s->symbol_sync_low[1]*s->symbol_sync_high[0]*SYNC_CROSS_CORR_COEFF_C;
/* Filter away any DC component */
p = v - s->symbol_sync_dc_filter[1];
s->symbol_sync_dc_filter[1] = s->symbol_sync_dc_filter[0];
s->symbol_sync_dc_filter[0] = v;
/* A little integration will now filter away much of the noise */
s->baud_phase -= p;
if (fabsf(s->baud_phase) > 30.0f)
{
if (s->baud_phase > 0.0f)
i = (s->baud_phase > 1000.0f) ? 5 : 1;
else
i = (s->baud_phase < -1000.0f) ? -5 : -1;
//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;
}
symbol_sync(s);
z = equalizer_get(s);
@@ -477,25 +613,27 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
break;
case TRAINING_STAGE_SYMBOL_ACQUISITION:
/* Allow time for symbol synchronisation to settle the symbol timing. */
target = &z;
target = &zero;
if (++s->training_count >= 60)
{
/* Record the current phase angle */
s->training_stage = TRAINING_STAGE_LOG_PHASE;
s->angles[0] =
s->start_angles[0] = arctan2(z.im, z.re);
if (s->agc_scaling_save == 0.0f)
s->agc_scaling_save = s->agc_scaling;
}
break;
case TRAINING_STAGE_LOG_PHASE:
/* Record the current alternate phase angle */
target = &z;
target = &zero;
s->angles[1] =
s->start_angles[1] = arctan2(z.im, z.re);
s->training_count = 1;
s->training_stage = TRAINING_STAGE_WAIT_FOR_CDCD;
break;
case TRAINING_STAGE_WAIT_FOR_CDCD:
target = &z;
target = &zero;
angle = arctan2(z.im, z.re);
/* Look for the initial ABAB sequence to display a phase reversal, which will
signal the start of the scrambled CDCD segment */
@@ -524,24 +662,36 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
||
s->carrier_phase_rate > dds_phase_ratef(CARRIER_NOMINAL_FREQ + 20.0f))
{
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, PUTBIT_TRAINING_FAILED);
break;
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n");
/* Park this modem */
s->agc_scaling_save = 0.0f;
s->training_stage = TRAINING_STAGE_PARKED;
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
break;
}
/* 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. */
p = angle*2.0f*3.14159f/(65536.0f*65536.0f);
#if defined(SPANDSP_USE_FIXED_POINT)
zz = complex_setf(cosf(p), -sinf(p));
for (i = 0; i <= V29_EQUALIZER_MASK; i++)
{
z1 = complex_setf(s->eq_buf[i].re, s->eq_buf[i].im);
z1 = complex_mulf(&z1, &zz);
s->eq_buf[i].re = z1.re;
s->eq_buf[i].im = z1.im;
}
#else
zz = complex_setf(cosf(p), -sinf(p));
for (i = 0; i <= V29_EQUALIZER_MASK; i++)
s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz);
#endif
s->carrier_phase += angle;
/* We have just seen the first bit of the scrambled sequence, so skip it. */
bit = scrambled_training_bit(s);
s->training_count = 1;
s->training_stage = TRAINING_STAGE_TRAIN_ON_CDCD;
report_status_change(s, PUTBIT_TRAINING_IN_PROGRESS);
report_status_change(s, SIG_STATUS_TRAINING_IN_PROGRESS);
break;
}
if (++s->training_count > V29_TRAINING_SEG_2_LEN)
@@ -550,8 +700,9 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
of a real training sequence. */
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n");
/* Park this modem */
s->agc_scaling_save = 0.0f;
s->training_stage = TRAINING_STAGE_PARKED;
report_status_change(s, PUTBIT_TRAINING_FAILED);
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
}
break;
case TRAINING_STAGE_TRAIN_ON_CDCD:
@@ -579,8 +730,17 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
track_carrier(s, &z, target);
tune_equalizer(s, &z, target);
/* Measure the training error */
#if defined(SPANDSP_USE_FIXED_POINT)
z1.re = z.re/(float) FP_FACTOR;
z1.im = z.im/(float) FP_FACTOR;
zz.re = target->re;
zz.im = target->im;
zz = complex_subf(&z1, &zz);
s->training_error += powerf(&zz);
#else
zz = complex_subf(&z, target);
s->training_error += powerf(&zz);
#endif
if (++s->training_count >= V29_TRAINING_SEG_3_LEN)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Constellation mismatch %f\n", s->training_error);
@@ -595,8 +755,9 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (convergence failed)\n");
/* Park this modem */
s->agc_scaling_save = 0.0f;
s->training_stage = TRAINING_STAGE_PARKED;
report_status_change(s, PUTBIT_TRAINING_FAILED);
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
}
}
break;
@@ -607,15 +768,24 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
decode_baud(s, &z);
target = &v29_9600_constellation[s->constellation_state];
/* Measure the training error */
#if defined(SPANDSP_USE_FIXED_POINT)
z1.re = z.re/(float) FP_FACTOR;
z1.im = z.im/(float) FP_FACTOR;
zz.re = target->re;
zz.im = target->im;
zz = complex_subf(&z1, &zz);
s->training_error += powerf(&zz);
#else
zz = complex_subf(&z, target);
s->training_error += powerf(&zz);
#endif
if (++s->training_count >= V29_TRAINING_SEG_4_LEN)
{
if (s->training_error < 50.0f)
{
/* We are up and running */
span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded (constellation mismatch %f)\n", s->training_error);
report_status_change(s, PUTBIT_TRAINING_SUCCEEDED);
report_status_change(s, SIG_STATUS_TRAINING_SUCCEEDED);
/* Apply some lag to the carrier off condition, to ensure the last few bits get pushed through
the processing. */
s->signal_present = 60;
@@ -629,8 +799,9 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
/* Training has failed */
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error);
/* Park this modem */
report_status_change(s, PUTBIT_TRAINING_FAILED);
s->agc_scaling_save = 0.0f;
s->training_stage = TRAINING_STAGE_PARKED;
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
}
}
break;
@@ -638,11 +809,21 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
default:
/* We failed to train! */
/* Park here until the carrier drops. */
target = &z;
target = &zero;
break;
}
if (s->qam_report)
{
#if defined(SPANDSP_USE_FIXED_POINT)
z1.re = z.re/(float) FP_FACTOR;
z1.im = z.im/(float) FP_FACTOR;
zz.re = target->re;
zz.im = target->im;
s->qam_report(s->qam_user_data, &z1, &zz, s->constellation_state);
#else
s->qam_report(s->qam_user_data, &z, target, s->constellation_state);
#endif
}
}
/*- End of function --------------------------------------------------------*/
@@ -653,14 +834,19 @@ int v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
int step;
int16_t x;
int32_t diff;
#if defined(SPANDSP_USE_FIXED_POINT)
complexi16_t z;
complexi16_t zz;
complexi16_t sample;
int32_t v;
float y;
#else
complexf_t z;
complexf_t zz;
complexf_t sample;
#if defined(SPANDSP_USE_FIXED_POINT)
complexi_t zi;
float v;
#endif
int32_t power;
float v;
for (i = 0; i < len; i++)
{
@@ -709,7 +895,7 @@ int v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
/* Count down a short delay, to ensure we push the last
few bits through the filters before stopping. */
v29_rx_restart(s, s->bit_rate, FALSE);
report_status_change(s, PUTBIT_CARRIER_DOWN);
report_status_change(s, SIG_STATUS_CARRIER_DOWN);
continue;
}
#if defined(IAXMODEM_STUFF)
@@ -728,7 +914,7 @@ int v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
#if defined(IAXMODEM_STUFF)
s->carrier_drop_pending = FALSE;
#endif
report_status_change(s, PUTBIT_CARRIER_UP);
report_status_change(s, SIG_STATUS_CARRIER_UP);
}
if (s->training_stage == TRAINING_STAGE_PARKED)
continue;
@@ -741,18 +927,29 @@ int v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
if (step < 0)
step += RX_PULSESHAPER_COEFF_SETS;
#if defined(SPANDSP_USE_FIXED_POINT)
zi.re = (int32_t) rx_pulseshaper[step][0].re*(int32_t) s->rrc_filter[s->rrc_filter_step];
v = (int32_t) rx_pulseshaper[step][0].re*(int32_t) s->rrc_filter[s->rrc_filter_step];
for (j = 1; j < V29_RX_FILTER_STEPS; j++)
zi.re += (int32_t) rx_pulseshaper[step][j].re*(int32_t) s->rrc_filter[j + s->rrc_filter_step];
sample.re = zi.re*s->agc_scaling;
v += (int32_t) rx_pulseshaper[step][j].re*(int32_t) s->rrc_filter[j + s->rrc_filter_step];
y = v*s->agc_scaling;
sample.re = y;
#else
zz.re = rx_pulseshaper[step][0].re*s->rrc_filter[s->rrc_filter_step];
v = rx_pulseshaper[step][0].re*s->rrc_filter[s->rrc_filter_step];
for (j = 1; j < V29_RX_FILTER_STEPS; j++)
zz.re += rx_pulseshaper[step][j].re*s->rrc_filter[j + s->rrc_filter_step];
sample.re = zz.re*s->agc_scaling;
v += rx_pulseshaper[step][j].re*s->rrc_filter[j + s->rrc_filter_step];
sample.re = v*s->agc_scaling;
#endif
/* Symbol timing synchronisation band edge filters */
#if defined(SPANDSP_USE_FIXED_POINT)
/* Low Nyquist band edge filter */
v = ((s->symbol_sync_low[0]*SYNC_LOW_BAND_EDGE_COEFF_0) >> 12) + ((s->symbol_sync_low[1]*SYNC_LOW_BAND_EDGE_COEFF_1) >> 12) + sample.re;
s->symbol_sync_low[1] = s->symbol_sync_low[0];
s->symbol_sync_low[0] = v;
/* High Nyquist band edge filter */
v = ((s->symbol_sync_high[0]*SYNC_HIGH_BAND_EDGE_COEFF_0) >> 12) + ((s->symbol_sync_high[1]*SYNC_HIGH_BAND_EDGE_COEFF_1) >> 12) + sample.re;
s->symbol_sync_high[1] = s->symbol_sync_high[0];
s->symbol_sync_high[0] = v;
#else
/* Low Nyquist band edge filter */
v = s->symbol_sync_low[0]*SYNC_LOW_BAND_EDGE_COEFF_0 + s->symbol_sync_low[1]*SYNC_LOW_BAND_EDGE_COEFF_1 + sample.re;
s->symbol_sync_low[1] = s->symbol_sync_low[0];
@@ -761,16 +958,19 @@ int v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
v = s->symbol_sync_high[0]*SYNC_HIGH_BAND_EDGE_COEFF_0 + s->symbol_sync_high[1]*SYNC_HIGH_BAND_EDGE_COEFF_1 + sample.re;
s->symbol_sync_high[1] = s->symbol_sync_high[0];
s->symbol_sync_high[0] = v;
#endif
/* Put things into the equalization buffer at T/2 rate. The symbol synchronisation
will fiddle the step to align this with the symbols. */
if (s->eq_put_step <= 0)
{
if (s->training_stage == TRAINING_STAGE_SYMBOL_ACQUISITION)
{
/* Only AGC during the initial training */
/* Only AGC until we have locked down the setting. */
#if defined(SPANDSP_USE_FIXED_POINT)
if (s->agc_scaling_save == 0.0f)
s->agc_scaling = (float) FP_FACTOR*(1.0f/RX_PULSESHAPER_GAIN)*5.0f*0.25f/sqrtf(power);
#else
if (s->agc_scaling_save == 0.0f)
s->agc_scaling = (1.0f/RX_PULSESHAPER_GAIN)*5.0f*0.25f/sqrtf(power);
}
#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.
@@ -778,24 +978,24 @@ int v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
step = -s->eq_put_step;
if (step > RX_PULSESHAPER_COEFF_SETS - 1)
step = RX_PULSESHAPER_COEFF_SETS - 1;
#if defined(SPANDSP_USE_FIXED_POINT)
zi.im = (int32_t) rx_pulseshaper[step][0].im*(int32_t) s->rrc_filter[s->rrc_filter_step];
for (j = 1; j < V29_RX_FILTER_STEPS; j++)
zi.im += (int32_t) rx_pulseshaper[step][j].im*(int32_t) s->rrc_filter[j + s->rrc_filter_step];
sample.im = zi.im*s->agc_scaling;
#else
zz.im = rx_pulseshaper[step][0].im*s->rrc_filter[s->rrc_filter_step];
for (j = 1; j < V29_RX_FILTER_STEPS; j++)
zz.im += rx_pulseshaper[step][j].im*s->rrc_filter[j + s->rrc_filter_step];
sample.im = zz.im*s->agc_scaling;
#endif
s->eq_put_step += RX_PULSESHAPER_COEFF_SETS*10/(3*2);
/* Shift to baseband - since this is done in a full complex form, the
result is clean, and requires no further filtering, apart from the
equalizer. */
#if defined(SPANDSP_USE_FIXED_POINT)
v = (int32_t) rx_pulseshaper[step][0].im*(int32_t) s->rrc_filter[s->rrc_filter_step];
for (j = 1; j < V29_RX_FILTER_STEPS; j++)
v += (int32_t) rx_pulseshaper[step][j].im*(int32_t) s->rrc_filter[j + s->rrc_filter_step];
sample.im = v*s->agc_scaling;
z = dds_lookup_complexi16(s->carrier_phase);
zz.re = ((int32_t) sample.re*(int32_t) z.re - (int32_t) sample.im*(int32_t) z.im) >> 15;
zz.im = ((int32_t) -sample.re*(int32_t) z.im - (int32_t) sample.im*(int32_t) z.re) >> 15;
#else
v = rx_pulseshaper[step][0].im*s->rrc_filter[s->rrc_filter_step];
for (j = 1; j < V29_RX_FILTER_STEPS; j++)
v += rx_pulseshaper[step][j].im*s->rrc_filter[j + s->rrc_filter_step];
sample.im = v*s->agc_scaling;
z = dds_lookup_complexf(s->carrier_phase);
zz.re = sample.re*z.re - sample.im*z.im;
zz.im = -sample.re*z.im - sample.im*z.re;
#endif
process_half_baud(s, &zz);
}
dds_advancef(&(s->carrier_phase), s->carrier_phase_rate);
@@ -837,7 +1037,7 @@ int v29_rx_restart(v29_rx_state_t *s, int bit_rate, int old_train)
s->bit_rate = bit_rate;
#if defined(SPANDSP_USE_FIXED_POINT)
memset(s->rrc_filter, 0, sizeof(s->rrc_filter));
vec_zeroi16(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
#else
vec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
#endif
@@ -856,8 +1056,6 @@ int v29_rx_restart(v29_rx_state_t *s, int bit_rate, int old_train)
s->old_train = old_train;
s->carrier_phase = 0;
s->carrier_track_i = 8000.0f;
s->carrier_track_p = 8000000.0f;
power_meter_init(&(s->power), 4);
@@ -872,13 +1070,29 @@ int v29_rx_restart(v29_rx_state_t *s, int bit_rate, int old_train)
else
{
s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ);
s->agc_scaling_save = 0.0f;
#if defined(SPANDSP_USE_FIXED_POINT)
s->agc_scaling = (float) FP_FACTOR*0.0017f/RX_PULSESHAPER_GAIN;
#else
s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN;
#endif
equalizer_reset(s);
}
s->eq_skip = 0;
s->carrier_track_i = 8000.0f;
s->carrier_track_p = 8000000.0f;
s->last_sample = 0;
s->eq_skip = 0;
/* Initialise the working data for symbol timing synchronisation */
#if defined(SPANDSP_USE_FIXED_POINT)
s->symbol_sync_low[0] = 0;
s->symbol_sync_low[1] = 0;
s->symbol_sync_high[0] = 0;
s->symbol_sync_high[1] = 0;
s->symbol_sync_dc_filter[0] = 0;
s->symbol_sync_dc_filter[1] = 0;
s->baud_phase = 0;
#else
s->symbol_sync_low[0] = 0.0f;
s->symbol_sync_low[1] = 0.0f;
s->symbol_sync_high[0] = 0.0f;
@@ -886,6 +1100,7 @@ int v29_rx_restart(v29_rx_state_t *s, int bit_rate, int old_train)
s->symbol_sync_dc_filter[0] = 0.0f;
s->symbol_sync_dc_filter[1] = 0.0f;
s->baud_phase = 0.0f;
#endif
s->baud_half = 0;
s->total_baud_timing_correction = 0;