Some modem cleanups, and movement towards efficient fixed point modem
implementations for platforms with slow floating point
This commit is contained in:
parent
5922cb59d5
commit
e58b2e7d97
|
@ -26,10 +26,10 @@
|
|||
#if !defined(_SPANDSP_PRIVATE_V22BIS_H_)
|
||||
#define _SPANDSP_PRIVATE_V22BIS_H_
|
||||
|
||||
/*! The number of steps to the left and to the right of the target position in the equalizer buffer. */
|
||||
#define V22BIS_EQUALIZER_LEN 7
|
||||
/*! One less than a power of 2 >= (2*V22BIS_EQUALIZER_LEN + 1) */
|
||||
#define V22BIS_EQUALIZER_MASK 15
|
||||
/*! The length of the equalizer buffer */
|
||||
#define V22BIS_EQUALIZER_LEN 17
|
||||
/*! Samples before the target position in the equalizer buffer */
|
||||
#define V22BIS_EQUALIZER_PRE_LEN 8
|
||||
|
||||
/*! The number of taps in the transmit pulse shaping filter */
|
||||
#define V22BIS_TX_FILTER_STEPS 9
|
||||
|
@ -139,24 +139,24 @@ struct v22bis_state_s
|
|||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
/*! \brief The scaling factor accessed by the AGC algorithm. */
|
||||
float agc_scaling;
|
||||
int16_t agc_scaling;
|
||||
/*! \brief The root raised cosine (RRC) pulse shaping filter buffer. */
|
||||
int16_t rrc_filter[V22BIS_RX_FILTER_STEPS];
|
||||
|
||||
/*! \brief The current delta factor for updating the equalizer coefficients. */
|
||||
float eq_delta;
|
||||
int16_t eq_delta;
|
||||
/*! \brief The adaptive equalizer coefficients. */
|
||||
complexi_t eq_coeff[2*V22BIS_EQUALIZER_LEN + 1];
|
||||
complexi16_t eq_coeff[V22BIS_EQUALIZER_LEN];
|
||||
/*! \brief The equalizer signal buffer. */
|
||||
complexi_t eq_buf[V22BIS_EQUALIZER_MASK + 1];
|
||||
complexi16_t eq_buf[V22BIS_EQUALIZER_LEN];
|
||||
|
||||
/*! \brief A measure of how much mismatch there is between the real constellation,
|
||||
and the decoded symbol positions. */
|
||||
float training_error;
|
||||
int32_t training_error;
|
||||
/*! \brief The proportional part of the carrier tracking filter. */
|
||||
float carrier_track_p;
|
||||
int32_t carrier_track_p;
|
||||
/*! \brief The integral part of the carrier tracking filter. */
|
||||
float carrier_track_i;
|
||||
int32_t carrier_track_i;
|
||||
#else
|
||||
/*! \brief The scaling factor accessed by the AGC algorithm. */
|
||||
float agc_scaling;
|
||||
|
@ -166,9 +166,9 @@ struct v22bis_state_s
|
|||
/*! \brief The current delta factor for updating the equalizer coefficients. */
|
||||
float eq_delta;
|
||||
/*! \brief The adaptive equalizer coefficients. */
|
||||
complexf_t eq_coeff[2*V22BIS_EQUALIZER_LEN + 1];
|
||||
complexf_t eq_coeff[V22BIS_EQUALIZER_LEN];
|
||||
/*! \brief The equalizer signal buffer. */
|
||||
complexf_t eq_buf[V22BIS_EQUALIZER_MASK + 1];
|
||||
complexf_t eq_buf[V22BIS_EQUALIZER_LEN];
|
||||
|
||||
/*! \brief A measure of how much mismatch there is between the real constellation,
|
||||
and the decoded symbol positions. */
|
||||
|
@ -202,7 +202,7 @@ struct v22bis_state_s
|
|||
/* Transmit section */
|
||||
struct
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
/*! \brief The guard tone level. */
|
||||
int16_t guard_tone_gain;
|
||||
/*! \brief The gain factor needed to achieve the specified output power. */
|
||||
|
|
|
@ -76,22 +76,22 @@ struct v27ter_rx_state_s
|
|||
int16_t agc_scaling_save;
|
||||
|
||||
/*! \brief The current delta factor for updating the equalizer coefficients. */
|
||||
float eq_delta;
|
||||
int16_t eq_delta;
|
||||
/*! \brief The adaptive equalizer coefficients. */
|
||||
/*complexi16_t*/ complexf_t eq_coeff[V27TER_EQUALIZER_LEN];
|
||||
complexi16_t eq_coeff[V27TER_EQUALIZER_LEN];
|
||||
/*! \brief A saved set of adaptive equalizer coefficients for use after restarts. */
|
||||
/*complexi16_t*/ complexf_t eq_coeff_save[V27TER_EQUALIZER_LEN];
|
||||
complexi16_t eq_coeff_save[V27TER_EQUALIZER_LEN];
|
||||
/*! \brief The equalizer signal buffer. */
|
||||
/*complexi16_t*/ complexf_t eq_buf[V27TER_EQUALIZER_LEN];
|
||||
complexi16_t eq_buf[V27TER_EQUALIZER_LEN];
|
||||
|
||||
/*! \brief A measure of how much mismatch there is between the real constellation,
|
||||
and the decoded symbol positions. */
|
||||
float training_error;
|
||||
int32_t training_error;
|
||||
|
||||
/*! \brief The proportional part of the carrier tracking filter. */
|
||||
float carrier_track_p;
|
||||
int32_t carrier_track_p;
|
||||
/*! \brief The integral part of the carrier tracking filter. */
|
||||
float carrier_track_i;
|
||||
int32_t carrier_track_i;
|
||||
/*! \brief The root raised cosine (RRC) pulse shaping filter buffer. */
|
||||
int16_t rrc_filter[V27TER_RX_FILTER_STEPS];
|
||||
#else
|
||||
|
|
|
@ -87,7 +87,7 @@ struct v29_rx_state_s
|
|||
|
||||
/*! \brief A measure of how much mismatch there is between the real constellation,
|
||||
and the decoded symbol positions. */
|
||||
float training_error;
|
||||
int32_t training_error;
|
||||
|
||||
/*! \brief The proportional part of the carrier tracking filter. */
|
||||
int32_t carrier_track_p;
|
||||
|
|
|
@ -126,7 +126,7 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx_fillin(v27ter_rx_state_t *s, int len);
|
|||
\brief Get a snapshot of the current equalizer coefficients.
|
||||
\param coeffs The vector of complex coefficients.
|
||||
\return The number of coefficients in the vector. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
SPAN_DECLARE(int) v27ter_rx_equalizer_state(v27ter_rx_state_t *s, complexi16_t **coeffs);
|
||||
#else
|
||||
SPAN_DECLARE(int) v27ter_rx_equalizer_state(v27ter_rx_state_t *s, complexf_t **coeffs);
|
||||
|
|
|
@ -118,7 +118,7 @@ scrambler register) cannot be trusted for the test. The receive modem,
|
|||
therefore, only tests that bits starting at bit 24 are really ones.
|
||||
*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
typedef void (*qam_report_handler_t)(void *user_data, const complexi16_t *constel, const complexi16_t *target, int symbol);
|
||||
#else
|
||||
typedef void (*qam_report_handler_t)(void *user_data, const complexf_t *constel, const complexf_t *target, int symbol);
|
||||
|
|
|
@ -53,13 +53,19 @@
|
|||
#include "spandsp/dds.h"
|
||||
#include "spandsp/power_meter.h"
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
#define SPANDSP_USE_FIXED_POINTx
|
||||
#endif
|
||||
|
||||
#include "spandsp/v17tx.h"
|
||||
|
||||
#include "spandsp/private/logging.h"
|
||||
#include "spandsp/private/v17tx.h"
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
#define SPANDSP_USE_FIXED_POINTx
|
||||
#define FP_SCALE(x) ((int16_t) x)
|
||||
#else
|
||||
#define FP_SCALE(x) (x)
|
||||
#endif
|
||||
|
||||
#include "v17_v32bis_tx_constellation_maps.h"
|
||||
|
@ -229,6 +235,11 @@ static __inline__ complexf_t getbaud(v17_tx_state_t *s)
|
|||
int i;
|
||||
int bit;
|
||||
int bits;
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static const complexi16_t zero = {0, 0};
|
||||
#else
|
||||
static const complexf_t zero = {0.0f, 0.0f};
|
||||
#endif
|
||||
|
||||
if (s->in_training)
|
||||
{
|
||||
|
@ -251,11 +262,7 @@ static __inline__ complexf_t getbaud(v17_tx_state_t *s)
|
|||
{
|
||||
/* The shutdown sequence is 32 bauds of all 1's, then 48 bauds
|
||||
of silence */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
return complex_seti16(0, 0);
|
||||
#else
|
||||
return complex_setf(0.0f, 0.0f);
|
||||
#endif
|
||||
return zero;
|
||||
}
|
||||
if (s->training_step == V17_TRAINING_SHUTDOWN_END)
|
||||
{
|
||||
|
|
|
@ -73,9 +73,12 @@
|
|||
#include "spandsp/private/v22bis.h"
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#include "v22bis_rx_1200_floating_rrc.h"
|
||||
#include "v22bis_rx_2400_floating_rrc.h"
|
||||
#define FP_SHIFT_FACTOR 10
|
||||
#define FP_SCALE FP_Q_6_10
|
||||
#include "v22bis_rx_1200_fixed_rrc.h"
|
||||
#include "v22bis_rx_2400_fixed_rrc.h"
|
||||
#else
|
||||
#define FP_SCALE(x) (x)
|
||||
#include "v22bis_rx_1200_floating_rrc.h"
|
||||
#include "v22bis_rx_2400_floating_rrc.h"
|
||||
#endif
|
||||
|
@ -170,10 +173,14 @@ void v22bis_report_status_change(v22bis_state_t *s, int status)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
SPAN_DECLARE(int) v22bis_rx_equalizer_state(v22bis_state_t *s, complexi16_t **coeffs)
|
||||
#else
|
||||
SPAN_DECLARE(int) v22bis_rx_equalizer_state(v22bis_state_t *s, complexf_t **coeffs)
|
||||
#endif
|
||||
{
|
||||
*coeffs = s->rx.eq_coeff;
|
||||
return 2*V22BIS_EQUALIZER_LEN + 1;
|
||||
return V22BIS_EQUALIZER_LEN;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
|
@ -181,13 +188,17 @@ void v22bis_equalizer_coefficient_reset(v22bis_state_t *s)
|
|||
{
|
||||
/* Start with an equalizer based on everything being perfect */
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
cvec_zeroi16(s->rx.eq_coeff, 2*V22BIS_EQUALIZER_LEN + 1);
|
||||
s->rx.eq_coeff[V22BIS_EQUALIZER_LEN] = complex_seti16(3*FP_FACTOR, 0*FP_FACTOR);
|
||||
s->rx.eq_delta = 32768.0f*EQUALIZER_DELTA/(2*V22BIS_EQUALIZER_LEN + 1);
|
||||
static const complexi16_t x = {FP_Q_6_10(3.0f), FP_Q_6_10(0.0f)};
|
||||
|
||||
cvec_zeroi16(s->rx.eq_coeff, V22BIS_EQUALIZER_LEN);
|
||||
s->rx.eq_coeff[V22BIS_EQUALIZER_PRE_LEN] = x;
|
||||
s->rx.eq_delta = 32.0f*EQUALIZER_DELTA/V22BIS_EQUALIZER_LEN;
|
||||
#else
|
||||
cvec_zerof(s->rx.eq_coeff, 2*V22BIS_EQUALIZER_LEN + 1);
|
||||
s->rx.eq_coeff[V22BIS_EQUALIZER_LEN] = complex_setf(3.0f, 0.0f);
|
||||
s->rx.eq_delta = EQUALIZER_DELTA/(2*V22BIS_EQUALIZER_LEN + 1);
|
||||
static const complexf_t x = {3.0f, 0.0f};
|
||||
|
||||
cvec_zerof(s->rx.eq_coeff, V22BIS_EQUALIZER_LEN);
|
||||
s->rx.eq_coeff[V22BIS_EQUALIZER_PRE_LEN] = x;
|
||||
s->rx.eq_delta = EQUALIZER_DELTA/V22BIS_EQUALIZER_LEN;
|
||||
#endif
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
@ -196,73 +207,99 @@ static void equalizer_reset(v22bis_state_t *s)
|
|||
{
|
||||
v22bis_equalizer_coefficient_reset(s);
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
cvec_zeroi16(s->rx.eq_buf, V22BIS_EQUALIZER_MASK + 1);
|
||||
cvec_zeroi16(s->rx.eq_buf, V22BIS_EQUALIZER_LEN);
|
||||
#else
|
||||
cvec_zerof(s->rx.eq_buf, V22BIS_EQUALIZER_MASK + 1);
|
||||
cvec_zerof(s->rx.eq_buf, V22BIS_EQUALIZER_LEN);
|
||||
#endif
|
||||
s->rx.eq_put_step = 20 - 1;
|
||||
s->rx.eq_step = 0;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static complexf_t equalizer_get(v22bis_state_t *s)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static __inline__ complexi16_t equalizer_get(v22bis_state_t *s)
|
||||
{
|
||||
int i;
|
||||
int p;
|
||||
complexf_t z;
|
||||
complexf_t z1;
|
||||
complexi32_t zz;
|
||||
complexi16_t z;
|
||||
|
||||
/* Get the next equalized value. */
|
||||
z = complex_setf(0.0f, 0.0f);
|
||||
p = s->rx.eq_step - 1;
|
||||
for (i = 0; i < 2*V22BIS_EQUALIZER_LEN + 1; i++)
|
||||
{
|
||||
p = (p - 1) & V22BIS_EQUALIZER_MASK;
|
||||
z1 = complex_mulf(&s->rx.eq_coeff[i], &s->rx.eq_buf[p]);
|
||||
z = complex_addf(&z, &z1);
|
||||
}
|
||||
zz = cvec_circular_dot_prodi16(s->rx.eq_buf, s->rx.eq_coeff, V22BIS_EQUALIZER_LEN, s->rx.eq_step);
|
||||
z.re = zz.re >> FP_SHIFT_FACTOR;
|
||||
z.im = zz.im >> FP_SHIFT_FACTOR;
|
||||
return z;
|
||||
}
|
||||
#else
|
||||
static __inline__ complexf_t equalizer_get(v22bis_state_t *s)
|
||||
{
|
||||
/* Get the next equalized value. */
|
||||
return cvec_circular_dot_prodf(s->rx.eq_buf, s->rx.eq_coeff, V22BIS_EQUALIZER_LEN, s->rx.eq_step);
|
||||
}
|
||||
#endif
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static void tune_equalizer(v22bis_state_t *s, const complexf_t *z, const complexf_t *target)
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
static void tune_equalizer(v22bis_state_t *s, const complexi16_t *z, const complexi16_t *target)
|
||||
{
|
||||
int i;
|
||||
int p;
|
||||
complexf_t ez;
|
||||
complexf_t z1;
|
||||
complexi16_t err;
|
||||
|
||||
/* Find the x and y mismatch from the exact constellation position. */
|
||||
ez = complex_subf(target, z);
|
||||
ez.re *= s->rx.eq_delta;
|
||||
ez.im *= s->rx.eq_delta;
|
||||
|
||||
p = s->rx.eq_step - 1;
|
||||
for (i = 0; i < 2*V22BIS_EQUALIZER_LEN + 1; i++)
|
||||
{
|
||||
p = (p - 1) & V22BIS_EQUALIZER_MASK;
|
||||
z1 = complex_conjf(&s->rx.eq_buf[p]);
|
||||
z1 = complex_mulf(&ez, &z1);
|
||||
s->rx.eq_coeff[i] = complex_addf(&s->rx.eq_coeff[i], &z1);
|
||||
/* If we don't leak a little bit we seem to get some wandering adaption */
|
||||
s->rx.eq_coeff[i].re *= 0.9999f;
|
||||
s->rx.eq_coeff[i].im *= 0.9999f;
|
||||
}
|
||||
err = complex_subi16(target, z);
|
||||
err.re = ((int32_t) err.re*s->rx.eq_delta) >> 5;
|
||||
err.im = ((int32_t) err.im*s->rx.eq_delta) >> 5;
|
||||
//cvec_circular_lmsi16(s->rx.eq_buf, s->rx.eq_coeff, V22BIS_EQUALIZER_LEN, s->rx.eq_step, &err);
|
||||
}
|
||||
#else
|
||||
static void tune_equalizer(v22bis_state_t *s, const complexf_t *z, const complexf_t *target)
|
||||
{
|
||||
complexf_t err;
|
||||
|
||||
/* Find the x and y mismatch from the exact constellation position. */
|
||||
err = complex_subf(target, z);
|
||||
err.re *= s->rx.eq_delta;
|
||||
err.im *= s->rx.eq_delta;
|
||||
cvec_circular_lmsf(s->rx.eq_buf, s->rx.eq_coeff, V22BIS_EQUALIZER_LEN, s->rx.eq_step, &err);
|
||||
}
|
||||
#endif
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
static __inline__ void track_carrier(v22bis_state_t *s, const complexi16_t *z, const complexi16_t *target)
|
||||
#else
|
||||
static __inline__ void track_carrier(v22bis_state_t *s, const complexf_t *z, const complexf_t *target)
|
||||
#endif
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
int32_t error;
|
||||
#else
|
||||
float error;
|
||||
#endif
|
||||
|
||||
/* For small errors the imaginary part of the difference between the actual and the target
|
||||
positions is proportional to the phase error, for any particular target. However, the
|
||||
different amplitudes of the various target positions scale things. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
error = ((int32_t) z->im*target->re - (int32_t) z->re*target->im) >> FP_SHIFT_FACTOR;
|
||||
s->rx.carrier_phase_rate += (s->rx.carrier_track_i*error);
|
||||
s->rx.carrier_phase += (s->rx.carrier_track_p*error);
|
||||
//span_log(&s->logging,
|
||||
// SPAN_LOG_FLOW,
|
||||
// "CARR: Im = %15.5f f = %15.5f - %10d %10d\n",
|
||||
// error/1024.0f,
|
||||
// dds_frequency(s->rx.carrier_phase_rate),
|
||||
// (s->rx.carrier_track_i*error),
|
||||
// (s->rx.carrier_track_p*error));
|
||||
#else
|
||||
error = z->im*target->re - z->re*target->im;
|
||||
|
||||
s->rx.carrier_phase_rate += (int32_t) (s->rx.carrier_track_i*error);
|
||||
s->rx.carrier_phase += (int32_t) (s->rx.carrier_track_p*error);
|
||||
//span_log(&s->logging, SPAN_LOG_FLOW, "Im = %15.5f f = %15.5f\n", error, dds_frequencyf(s->rx.carrier_phase_rate));
|
||||
//span_log(&s->logging,
|
||||
// SPAN_LOG_FLOW,
|
||||
// "CARR: Im = %15.5f f = %15.5f - %10d %10d\n",
|
||||
// error,
|
||||
// dds_frequencyf(s->rx.carrier_phase_rate),
|
||||
// (int32_t) (s->rx.carrier_track_i*error),
|
||||
// (int32_t) (s->rx.carrier_track_p*error));
|
||||
#endif
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
|
@ -339,40 +376,61 @@ static int decode_baudx(v22bis_state_t *s, int nearest)
|
|||
|
||||
static __inline__ void symbol_sync(v22bis_state_t *s)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
int32_t p;
|
||||
int32_t q;
|
||||
complexi16_t a;
|
||||
complexi16_t b;
|
||||
complexi16_t c;
|
||||
static const complexi16_t x = {FP_Q_1_15(0.894427f), FP_Q_1_15(0.44721f)};
|
||||
#else
|
||||
float p;
|
||||
float q;
|
||||
complexf_t zz;
|
||||
complexf_t a;
|
||||
complexf_t b;
|
||||
complexf_t c;
|
||||
static const complexf_t x = {0.894427f, 0.44721f};
|
||||
#endif
|
||||
int aa[3];
|
||||
int i;
|
||||
int j;
|
||||
|
||||
/* This routine adapts the position of the half baud samples entering the equalizer. */
|
||||
|
||||
/* Perform a Gardner test for baud alignment on the three most recent samples. */
|
||||
for (i = 0, j = s->rx.eq_step; i < 3; i++)
|
||||
{
|
||||
if (--j < 0)
|
||||
j = V22BIS_EQUALIZER_LEN - 1;
|
||||
aa[i] = j;
|
||||
}
|
||||
if (s->rx.sixteen_way_decisions)
|
||||
{
|
||||
p = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].re
|
||||
- s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].re;
|
||||
p *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].re;
|
||||
p = s->rx.eq_buf[aa[2]].re - s->rx.eq_buf[aa[0]].re;
|
||||
p *= s->rx.eq_buf[aa[1]].re;
|
||||
|
||||
q = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].im
|
||||
- s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].im;
|
||||
q *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].im;
|
||||
q = s->rx.eq_buf[aa[2]].im - s->rx.eq_buf[aa[0]].im;
|
||||
q *= s->rx.eq_buf[aa[1]].im;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Rotate the points to the 45 degree positions, to maximise the effectiveness of
|
||||
the Gardner algorithm. This is particularly significant at the start of operation
|
||||
to pull things in quickly. */
|
||||
zz = complex_setf(0.894427, 0.44721f);
|
||||
a = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK], &zz);
|
||||
b = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK], &zz);
|
||||
c = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK], &zz);
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
a = complex_mul_q1_15(&s->rx.eq_buf[aa[2]], &x);
|
||||
b = complex_mul_q1_15(&s->rx.eq_buf[aa[1]], &x);
|
||||
c = complex_mul_q1_15(&s->rx.eq_buf[aa[0]], &x);
|
||||
#else
|
||||
a = complex_mulf(&s->rx.eq_buf[aa[2]], &x);
|
||||
b = complex_mulf(&s->rx.eq_buf[aa[1]], &x);
|
||||
c = complex_mulf(&s->rx.eq_buf[aa[0]], &x);
|
||||
#endif
|
||||
p = (a.re - c.re)*b.re;
|
||||
q = (a.im - c.im)*b.im;
|
||||
}
|
||||
|
||||
s->rx.gardner_integrate += (p + q > 0.0f) ? s->rx.gardner_step : -s->rx.gardner_step;
|
||||
s->rx.gardner_integrate += (p + q > 0) ? s->rx.gardner_step : -s->rx.gardner_step;
|
||||
|
||||
if (abs(s->rx.gardner_integrate) >= 16)
|
||||
{
|
||||
|
@ -389,11 +447,23 @@ static __inline__ void symbol_sync(v22bis_state_t *s)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
static __inline__ void process_half_baud(v22bis_state_t *s, const complexi16_t *sample)
|
||||
#else
|
||||
static __inline__ void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
|
||||
#endif
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
complexi16_t z;
|
||||
complexi16_t zz;
|
||||
const complexi16_t *target;
|
||||
static const complexi16_t x = {FP_Q_1_15(0.894427f), FP_Q_1_15(0.44721f)};
|
||||
#else
|
||||
complexf_t z;
|
||||
complexf_t zz;
|
||||
const complexf_t *target;
|
||||
static const complexf_t x = {0.894427f, 0.44721f};
|
||||
#endif
|
||||
int re;
|
||||
int im;
|
||||
int nearest;
|
||||
|
@ -406,7 +476,8 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
|
|||
/* Add a sample to the equalizer's circular buffer, but don't calculate anything
|
||||
at this time. */
|
||||
s->rx.eq_buf[s->rx.eq_step] = z;
|
||||
s->rx.eq_step = (s->rx.eq_step + 1) & V22BIS_EQUALIZER_MASK;
|
||||
if (++s->rx.eq_step >= V22BIS_EQUALIZER_LEN)
|
||||
s->rx.eq_step = 0;
|
||||
|
||||
/* On alternate insertions we have a whole baud and must process it. */
|
||||
if ((s->rx.baud_phase ^= 1))
|
||||
|
@ -419,12 +490,17 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
|
|||
/* Find the constellation point */
|
||||
if (s->rx.sixteen_way_decisions)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
re = (z.re + FP_Q_6_10(3.0f)) >> FP_SHIFT_FACTOR;
|
||||
im = (z.im + FP_Q_6_10(3.0f)) >> FP_SHIFT_FACTOR;
|
||||
#else
|
||||
re = (int) (z.re + 3.0f);
|
||||
im = (int) (z.im + 3.0f);
|
||||
#endif
|
||||
if (re > 5)
|
||||
re = 5;
|
||||
else if (re < 0)
|
||||
re = 0;
|
||||
im = (int) (z.im + 3.0f);
|
||||
if (im > 5)
|
||||
im = 5;
|
||||
else if (im < 0)
|
||||
|
@ -433,13 +509,16 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* Rotate to 45 degrees, to make the slicing trivial */
|
||||
zz = complex_setf(0.894427, 0.44721f);
|
||||
zz = complex_mulf(&z, &zz);
|
||||
/* Rotate to 45 degrees, to make the slicing trivial. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
zz = complex_mul_q1_15(&z, &x);
|
||||
#else
|
||||
zz = complex_mulf(&z, &x);
|
||||
#endif
|
||||
nearest = 0x01;
|
||||
if (zz.re < 0.0f)
|
||||
if (zz.re < 0)
|
||||
nearest |= 0x04;
|
||||
if (zz.im < 0.0f)
|
||||
if (zz.im < 0)
|
||||
{
|
||||
nearest ^= 0x04;
|
||||
nearest |= 0x08;
|
||||
|
@ -493,10 +572,7 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
|
|||
error could be higher. */
|
||||
s->rx.gardner_step = 4;
|
||||
s->rx.pattern_repeats = 0;
|
||||
if (s->calling_party)
|
||||
s->rx.training = V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES;
|
||||
else
|
||||
s->rx.training = V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200;
|
||||
s->rx.training = (s->calling_party) ? V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES : V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200;
|
||||
/* Be pessimistic and see what the handshake brings */
|
||||
s->negotiated_bit_rate = 1200;
|
||||
break;
|
||||
|
@ -612,7 +688,11 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
|
|||
s->tx.training = V22BIS_TX_TRAINING_STAGE_TIMED_S11;
|
||||
/* Normal reception starts immediately */
|
||||
s->rx.training = V22BIS_RX_TRAINING_STAGE_NORMAL_OPERATION;
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
s->rx.carrier_track_i = 8;
|
||||
#else
|
||||
s->rx.carrier_track_i = 8000.0f;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -636,7 +716,11 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
|
|||
s->rx.sixteen_way_decisions = TRUE;
|
||||
s->rx.training = V22BIS_RX_TRAINING_STAGE_WAIT_FOR_SCRAMBLED_ONES_AT_2400;
|
||||
s->rx.pattern_repeats = 0;
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
s->rx.carrier_track_i = 8;
|
||||
#else
|
||||
s->rx.carrier_track_i = 8000.0f;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -698,12 +782,20 @@ SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int l
|
|||
{
|
||||
int i;
|
||||
int step;
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
complexi16_t z;
|
||||
complexi16_t zz;
|
||||
complexi16_t sample;
|
||||
int32_t ii;
|
||||
int32_t qq;
|
||||
#else
|
||||
complexf_t z;
|
||||
complexf_t zz;
|
||||
int32_t power;
|
||||
complexf_t sample;
|
||||
float ii;
|
||||
float qq;
|
||||
#endif
|
||||
int32_t power;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
|
@ -720,7 +812,7 @@ SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int l
|
|||
if (s->calling_party)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
ii = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_2400_re[6], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
ii = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_2400_re[6], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step) >> 15;
|
||||
#else
|
||||
ii = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_2400_re[6], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
#endif
|
||||
|
@ -728,12 +820,12 @@ SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int l
|
|||
else
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
ii = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_1200_re[6], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
ii = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_1200_re[6], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step) >> 15;
|
||||
#else
|
||||
ii = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_1200_re[6], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
#endif
|
||||
}
|
||||
power = power_meter_update(&(s->rx.rx_power), (int16_t) ii);
|
||||
power = power_meter_update(&s->rx.rx_power, (int16_t) ii);
|
||||
if (s->rx.signal_present)
|
||||
{
|
||||
/* Look for power below the carrier off point */
|
||||
|
@ -752,58 +844,75 @@ SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int l
|
|||
s->rx.signal_present = TRUE;
|
||||
v22bis_report_status_change(s, SIG_STATUS_CARRIER_UP);
|
||||
}
|
||||
if (s->rx.training != V22BIS_RX_TRAINING_STAGE_PARKED)
|
||||
/* Only spend effort processing this data if the modem is not parked, after
|
||||
a training failure. */
|
||||
if (s->rx.training == V22BIS_RX_TRAINING_STAGE_PARKED)
|
||||
continue;
|
||||
|
||||
/* Put things into the equalization buffer at T/2 rate. The Gardner algorithm
|
||||
will fiddle the step to align this with the symbols. */
|
||||
if ((s->rx.eq_put_step -= PULSESHAPER_COEFF_SETS) <= 0)
|
||||
{
|
||||
/* Only spend effort processing this data if the modem is not
|
||||
parked, after a training failure. */
|
||||
z = dds_complexf(&s->rx.carrier_phase, s->rx.carrier_phase_rate);
|
||||
if (s->rx.training == V22BIS_RX_TRAINING_STAGE_SYMBOL_ACQUISITION)
|
||||
{
|
||||
/* Only AGC during the initial symbol acquisition, and then lock the gain. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
s->rx.agc_scaling = saturate16(((int32_t) (1024.0f*1024.0f*0.18f*3.60f))/fixed_sqrt32(power));
|
||||
#else
|
||||
s->rx.agc_scaling = 0.18f*3.60f/sqrtf(power);
|
||||
#endif
|
||||
}
|
||||
/* Put things into the equalization buffer at T/2 rate. The Gardner algorithm
|
||||
will fiddle the step to align this with the symbols. */
|
||||
if ((s->rx.eq_put_step -= PULSESHAPER_COEFF_SETS) <= 0)
|
||||
/* 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 bandband by complex mixing.
|
||||
No further filtering, to remove mixer harmonics, is needed. */
|
||||
step = -s->rx.eq_put_step;
|
||||
if (step > PULSESHAPER_COEFF_SETS - 1)
|
||||
step = PULSESHAPER_COEFF_SETS - 1;
|
||||
s->rx.eq_put_step += PULSESHAPER_COEFF_SETS*40/(3*2);
|
||||
if (s->calling_party)
|
||||
{
|
||||
/* 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 bandband by complex mixing.
|
||||
No further filtering, to remove mixer harmonics, is needed. */
|
||||
step = -s->rx.eq_put_step;
|
||||
if (step > PULSESHAPER_COEFF_SETS - 1)
|
||||
step = PULSESHAPER_COEFF_SETS - 1;
|
||||
s->rx.eq_put_step += PULSESHAPER_COEFF_SETS*40/(3*2);
|
||||
if (s->calling_party)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
ii = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_2400_re[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
qq = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_2400_im[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
ii = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_2400_re[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step) >> 15;
|
||||
qq = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_2400_im[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step) >> 15;
|
||||
#else
|
||||
ii = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_2400_re[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
qq = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_2400_im[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
ii = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_2400_re[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
qq = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_2400_im[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
ii = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_1200_re[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
qq = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_1200_im[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
#else
|
||||
ii = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_1200_re[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
qq = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_1200_im[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
#endif
|
||||
}
|
||||
sample.re = ii*s->rx.agc_scaling;
|
||||
sample.im = qq*s->rx.agc_scaling;
|
||||
/* 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. */
|
||||
zz.re = sample.re*z.re - sample.im*z.im;
|
||||
zz.im = -sample.re*z.im - sample.im*z.re;
|
||||
process_half_baud(s, &zz);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
ii = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_1200_re[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step) >> 15;
|
||||
qq = vec_circular_dot_prodi16(s->rx.rrc_filter, rx_pulseshaper_1200_im[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step) >> 15;
|
||||
#else
|
||||
ii = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_1200_re[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
qq = vec_circular_dot_prodf(s->rx.rrc_filter, rx_pulseshaper_1200_im[step], V22BIS_RX_FILTER_STEPS, s->rx.rrc_filter_step);
|
||||
#endif
|
||||
}
|
||||
/* 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_POINTx)
|
||||
sample.re = (ii*s->rx.agc_scaling) >> FP_SHIFT_FACTOR;
|
||||
sample.im = (qq*s->rx.agc_scaling) >> FP_SHIFT_FACTOR;
|
||||
z = dds_lookup_complexi16(s->rx.carrier_phase);
|
||||
zz.re = ((int32_t) sample.re*z.re - (int32_t) sample.im*z.im) >> 15;
|
||||
zz.im = ((int32_t) -sample.re*z.im - (int32_t) sample.im*z.re) >> 15;
|
||||
#else
|
||||
sample.re = ii*s->rx.agc_scaling;
|
||||
sample.im = qq*s->rx.agc_scaling;
|
||||
z = dds_lookup_complexf(s->rx.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);
|
||||
}
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
dds_advance(&s->rx.carrier_phase, s->rx.carrier_phase_rate);
|
||||
#else
|
||||
dds_advancef(&s->rx.carrier_phase, s->rx.carrier_phase_rate);
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -835,8 +944,10 @@ int v22bis_rx_restart(v22bis_state_t *s)
|
|||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
vec_zeroi16(s->rx.rrc_filter, sizeof(s->rx.rrc_filter)/sizeof(s->rx.rrc_filter[0]));
|
||||
s->rx.training_error = 0;
|
||||
#else
|
||||
vec_zerof(s->rx.rrc_filter, sizeof(s->rx.rrc_filter)/sizeof(s->rx.rrc_filter[0]));
|
||||
s->rx.training_error = 0.0f;
|
||||
#endif
|
||||
s->rx.rrc_filter_step = 0;
|
||||
s->rx.scramble_reg = 0;
|
||||
|
@ -847,9 +958,13 @@ int v22bis_rx_restart(v22bis_state_t *s)
|
|||
|
||||
s->rx.carrier_phase_rate = dds_phase_ratef((s->calling_party) ? 2400.0f : 1200.0f);
|
||||
s->rx.carrier_phase = 0;
|
||||
power_meter_init(&(s->rx.rx_power), 5);
|
||||
power_meter_init(&s->rx.rx_power, 5);
|
||||
v22bis_rx_signal_cutoff(s, -45.5f);
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->rx.agc_scaling = (float) (1024.0f*1024.0f)*0.0005f*0.025f;
|
||||
#else
|
||||
s->rx.agc_scaling = 0.0005f*0.025f;
|
||||
#endif
|
||||
|
||||
s->rx.constellation_state = 0;
|
||||
s->rx.sixteen_way_decisions = FALSE;
|
||||
|
@ -861,11 +976,15 @@ int v22bis_rx_restart(v22bis_state_t *s)
|
|||
s->rx.gardner_integrate = 0;
|
||||
s->rx.gardner_step = 256;
|
||||
s->rx.baud_phase = 0;
|
||||
s->rx.training_error = 0.0f;
|
||||
s->rx.total_baud_timing_correction = 0;
|
||||
/* We want the carrier to pull in faster on the answerer side, as it has very little time to adapt. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
s->rx.carrier_track_i = (s->calling_party) ? 8 : 40;
|
||||
s->rx.carrier_track_p = 8000;
|
||||
#else
|
||||
s->rx.carrier_track_i = (s->calling_party) ? 8000.0f : 40000.0f;
|
||||
s->rx.carrier_track_p = 8000000.0f;
|
||||
#endif
|
||||
|
||||
s->negotiated_bit_rate = 1200;
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
#include "spandsp/private/logging.h"
|
||||
#include "spandsp/private/v22bis.h"
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
#define FP_SCALE FP_Q_6_10
|
||||
#include "v22bis_tx_fixed_rrc.h"
|
||||
#else
|
||||
|
@ -248,7 +248,7 @@ static const int phase_steps[4] =
|
|||
1, 0, 2, 3
|
||||
};
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
const complexi16_t v22bis_constellation[16] =
|
||||
#else
|
||||
const complexf_t v22bis_constellation[16] =
|
||||
|
@ -314,7 +314,7 @@ static __inline__ int get_scrambled_bit(v22bis_state_t *s)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static complexi16_t training_get(v22bis_state_t *s)
|
||||
#else
|
||||
static complexf_t training_get(v22bis_state_t *s)
|
||||
|
@ -417,13 +417,13 @@ static complexf_t training_get(v22bis_state_t *s)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static complexi16_t getbaud(v22bis_state_t *s)
|
||||
#else
|
||||
static complexf_t getbaud(v22bis_state_t *s)
|
||||
#endif
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static const complexi16_t zero = {0, 0};
|
||||
#else
|
||||
static const complexf_t zero = {0.0f, 0.0f};
|
||||
|
@ -464,7 +464,7 @@ static complexf_t getbaud(v22bis_state_t *s)
|
|||
|
||||
SPAN_DECLARE_NONSTD(int) v22bis_tx(v22bis_state_t *s, int16_t amp[], int len)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
complexi16_t v;
|
||||
complexi32_t x;
|
||||
complexi32_t z;
|
||||
|
@ -490,7 +490,7 @@ SPAN_DECLARE_NONSTD(int) v22bis_tx(v22bis_state_t *s, int16_t amp[], int len)
|
|||
if (++s->tx.rrc_filter_step >= V22BIS_TX_FILTER_STEPS)
|
||||
s->tx.rrc_filter_step = 0;
|
||||
}
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
/* Root raised cosine pulse shaping at baseband */
|
||||
x.re = vec_circular_dot_prodi16(s->tx.rrc_filter_re, tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->tx.baud_phase], V22BIS_TX_FILTER_STEPS, s->tx.rrc_filter_step) >> 14;
|
||||
x.im = vec_circular_dot_prodi16(s->tx.rrc_filter_im, tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->tx.baud_phase], V22BIS_TX_FILTER_STEPS, s->tx.rrc_filter_step) >> 14;
|
||||
|
@ -551,7 +551,7 @@ SPAN_DECLARE(void) v22bis_tx_power(v22bis_state_t *s, float power)
|
|||
}
|
||||
sig_gain = 0.4490f*powf(10.0f, (sig_power - DBM0_MAX_POWER)/20.0f)*32768.0f/TX_PULSESHAPER_GAIN;
|
||||
guard_tone_gain = powf(10.0f, (guard_tone_power - DBM0_MAX_POWER)/20.0f)*32768.0f;
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->tx.gain = (int16_t) sig_gain;
|
||||
s->tx.guard_tone_gain = (int16_t) guard_tone_gain;
|
||||
#else
|
||||
|
@ -563,7 +563,7 @@ SPAN_DECLARE(void) v22bis_tx_power(v22bis_state_t *s, float power)
|
|||
|
||||
static int v22bis_tx_restart(v22bis_state_t *s)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
vec_zeroi16(s->tx.rrc_filter_re, sizeof(s->tx.rrc_filter_re)/sizeof(s->tx.rrc_filter_re[0]));
|
||||
vec_zeroi16(s->tx.rrc_filter_im, sizeof(s->tx.rrc_filter_im)/sizeof(s->tx.rrc_filter_im[0]));
|
||||
#else
|
||||
|
|
|
@ -65,11 +65,13 @@
|
|||
#include "spandsp/private/v27ter_rx.h"
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
#define FP_SCALE FP_Q_6_10
|
||||
#define FP_SCALE FP_Q_6_10
|
||||
#define FP_FACTOR 4096
|
||||
#define FP_SHIFT_FACTOR 12
|
||||
#include "v27ter_rx_4800_fixed_rrc.h"
|
||||
#include "v27ter_rx_2400_fixed_rrc.h"
|
||||
#else
|
||||
#define FP_SCALE(x) (x)
|
||||
#define FP_SCALE(x) (x)
|
||||
#include "v27ter_rx_4800_floating_rrc.h"
|
||||
#include "v27ter_rx_2400_floating_rrc.h"
|
||||
#endif
|
||||
|
@ -87,11 +89,6 @@
|
|||
/*! The adaption rate coefficient for the equalizer */
|
||||
#define EQUALIZER_DELTA 0.25f
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
#define FP_FACTOR 4096
|
||||
#define FP_SHIFT_FACTOR 12
|
||||
#endif
|
||||
|
||||
/* Segments of the training sequence */
|
||||
/* V.27ter defines a long and a short sequence. FAX doesn't use the
|
||||
short sequence, so it is not implemented here. */
|
||||
|
@ -113,7 +110,7 @@ enum
|
|||
TRAINING_STAGE_PARKED
|
||||
};
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static const complexi16_t v27ter_constellation[8] =
|
||||
#else
|
||||
static const complexf_t v27ter_constellation[8] =
|
||||
|
@ -167,7 +164,7 @@ static void report_status_change(v27ter_rx_state_t *s, int status)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
SPAN_DECLARE(int) v27ter_rx_equalizer_state(v27ter_rx_state_t *s, complexi16_t **coeffs)
|
||||
#else
|
||||
SPAN_DECLARE(int) v27ter_rx_equalizer_state(v27ter_rx_state_t *s, complexf_t **coeffs)
|
||||
|
@ -180,7 +177,7 @@ SPAN_DECLARE(int) v27ter_rx_equalizer_state(v27ter_rx_state_t *s, complexf_t **c
|
|||
|
||||
static void equalizer_save(v27ter_rx_state_t *s)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
cvec_copyi16(s->eq_coeff_save, s->eq_coeff, V27TER_EQUALIZER_LEN);
|
||||
#else
|
||||
cvec_copyf(s->eq_coeff_save, s->eq_coeff, V27TER_EQUALIZER_LEN);
|
||||
|
@ -190,7 +187,7 @@ static void equalizer_save(v27ter_rx_state_t *s)
|
|||
|
||||
static void equalizer_restore(v27ter_rx_state_t *s)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
cvec_copyi16(s->eq_coeff, s->eq_coeff_save, V27TER_EQUALIZER_LEN);
|
||||
cvec_zeroi16(s->eq_buf, V27TER_EQUALIZER_LEN);
|
||||
s->eq_delta = 32768.0f*EQUALIZER_DELTA/V27TER_EQUALIZER_LEN;
|
||||
|
@ -208,7 +205,7 @@ static void equalizer_restore(v27ter_rx_state_t *s)
|
|||
static void equalizer_reset(v27ter_rx_state_t *s)
|
||||
{
|
||||
/* Start with an equalizer based on everything being perfect. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static const complexi16_t x = {FP_SCALE(1.414f), FP_SCALE(0.0f)};
|
||||
|
||||
cvec_zeroi16(s->eq_coeff, V27TER_EQUALIZER_LEN);
|
||||
|
@ -229,50 +226,36 @@ static void equalizer_reset(v27ter_rx_state_t *s)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
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_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static __inline__ complexi16_t equalizer_get(v27ter_rx_state_t *s)
|
||||
#else
|
||||
static __inline__ complexf_t equalizer_get(v27ter_rx_state_t *s)
|
||||
#endif
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
complexi32_t zz;
|
||||
complexi16_t z;
|
||||
|
||||
/* Get the next equalized value. */
|
||||
zz = cvec_circular_dot_prodi16(s->eq_buf, s->eq_coeff, V27TER_EQUALIZER_LEN, s->eq_step);
|
||||
z.re = zz.re >> FP_SHIFT_FACTOR;
|
||||
z.im = zz.im >> FP_SHIFT_FACTOR;
|
||||
z.re = zz.re >> 14;
|
||||
z.im = zz.im >> 14;
|
||||
return z;
|
||||
}
|
||||
#else
|
||||
static __inline__ complexf_t equalizer_get(v27ter_rx_state_t *s)
|
||||
{
|
||||
/* Get the next equalized value. */
|
||||
return cvec_circular_dot_prodf(s->eq_buf, s->eq_coeff, V27TER_EQUALIZER_LEN, s->eq_step);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static void tune_equalizer(v27ter_rx_state_t *s, const complexi16_t *z, const complexi16_t *target)
|
||||
{
|
||||
complexi16_t err;
|
||||
|
||||
/* Find the x and y mismatch from the exact constellation position. */
|
||||
err.re = target->re*FP_FACTOR - z->re;
|
||||
err.im = target->im*FP_FACTOR - z->im;
|
||||
err.re = ((int32_t) err.re*(int32_t) s->eq_delta) >> 15;
|
||||
err.im = ((int32_t) err.im*(int32_t) s->eq_delta) >> 15;
|
||||
err = complex_subi16(target, z);
|
||||
err.re = ((int32_t) err.re*s->eq_delta) >> 13;
|
||||
err.im = ((int32_t) err.im*s->eq_delta) >> 13;
|
||||
cvec_circular_lmsi16(s->eq_buf, s->eq_coeff, V27TER_EQUALIZER_LEN, s->eq_step, &err);
|
||||
}
|
||||
#else
|
||||
|
@ -289,7 +272,7 @@ static void tune_equalizer(v27ter_rx_state_t *s, const complexf_t *z, const comp
|
|||
#endif
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#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)
|
||||
|
@ -305,25 +288,36 @@ static __inline__ int find_quadrant(const complexf_t *z)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static __inline__ int find_octant(complexi16_t *z)
|
||||
#else
|
||||
static __inline__ int find_octant(complexf_t *z)
|
||||
#endif
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
int32_t abs_re;
|
||||
int32_t abs_im;
|
||||
#else
|
||||
float abs_re;
|
||||
float abs_im;
|
||||
#endif
|
||||
int b1;
|
||||
int b2;
|
||||
int bits;
|
||||
|
||||
/* Are we near an axis or a diagonal? */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
abs_re = abs(z->re);
|
||||
abs_im = abs(z->im);
|
||||
if (abs_im*1000 > abs_re*414 && abs_im*1000 < abs_re*2414)
|
||||
#else
|
||||
abs_re = fabsf(z->re);
|
||||
abs_im = fabsf(z->im);
|
||||
if (abs_im > abs_re*0.4142136f && abs_im < abs_re*2.4142136f)
|
||||
#endif
|
||||
{
|
||||
/* Split the space along the two axes. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
b1 = (z->re < 0);
|
||||
b2 = (z->im < 0);
|
||||
#else
|
||||
|
@ -343,13 +337,13 @@ static __inline__ int find_octant(complexf_t *z)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static __inline__ void track_carrier(v27ter_rx_state_t *s, const complexi16_t *z, const complexi16_t *target)
|
||||
#else
|
||||
static __inline__ void track_carrier(v27ter_rx_state_t *s, const complexf_t *z, const complexf_t *target)
|
||||
#endif
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
int32_t error;
|
||||
#else
|
||||
float error;
|
||||
|
@ -358,13 +352,12 @@ static __inline__ void track_carrier(v27ter_rx_state_t *s, const complexf_t *z,
|
|||
/* For small errors the imaginary part of the difference between the actual and the target
|
||||
positions is proportional to the phase error, for any particular target. However, the
|
||||
different amplitudes of the various target positions scale things. */
|
||||
error = z->im*target->re - z->re*target->im;
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
error /= (float) FP_FACTOR;
|
||||
s->carrier_phase_rate += (int32_t) (s->carrier_track_i*error);
|
||||
s->carrier_phase += (int32_t) (s->carrier_track_p*error);
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
error = ((int32_t) z->im*target->re - (int32_t) z->re*target->im) >> 10;
|
||||
s->carrier_phase_rate += ((s->carrier_track_i*error) >> FP_SHIFT_FACTOR);
|
||||
s->carrier_phase += ((s->carrier_track_p*error) >> FP_SHIFT_FACTOR);
|
||||
#else
|
||||
error = z->im*target->re - z->re*target->im;
|
||||
s->carrier_phase_rate += (int32_t) (s->carrier_track_i*error);
|
||||
s->carrier_phase += (int32_t) (s->carrier_track_p*error);
|
||||
//span_log(&s->logging, SPAN_LOG_FLOW, "Im = %15.5f f = %15.5f\n", error, dds_frequencyf(s->carrier_phase_rate));
|
||||
|
@ -429,7 +422,7 @@ static __inline__ void put_bit(v27ter_rx_state_t *s, int bit)
|
|||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static void decode_baud(v27ter_rx_state_t *s, complexi16_t *z)
|
||||
#else
|
||||
static void decode_baud(v27ter_rx_state_t *s, complexf_t *z)
|
||||
|
@ -478,8 +471,13 @@ static void decode_baud(v27ter_rx_state_t *s, complexf_t *z)
|
|||
|
||||
static __inline__ void symbol_sync(v27ter_rx_state_t *s)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
int32_t p;
|
||||
int32_t q;
|
||||
#else
|
||||
float p;
|
||||
float q;
|
||||
#endif
|
||||
|
||||
/* This routine adapts the position of the half baud samples entering the equalizer. */
|
||||
|
||||
|
@ -492,7 +490,7 @@ static __inline__ void symbol_sync(v27ter_rx_state_t *s)
|
|||
- s->eq_buf[(s->eq_step - 1) & (V27TER_EQUALIZER_LEN - 1)].im;
|
||||
q *= s->eq_buf[(s->eq_step - 2) & (V27TER_EQUALIZER_LEN - 1)].im;
|
||||
|
||||
s->gardner_integrate += (p + q > 0.0f) ? s->gardner_step : -s->gardner_step;
|
||||
s->gardner_integrate += (p + q > 0) ? s->gardner_step : -s->gardner_step;
|
||||
|
||||
if (abs(s->gardner_integrate) >= 256)
|
||||
{
|
||||
|
@ -516,22 +514,19 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexi16_
|
|||
static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t *sample)
|
||||
#endif
|
||||
{
|
||||
static const int abab_pos[2] =
|
||||
{
|
||||
0, 4
|
||||
};
|
||||
complexf_t zz;
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
complexf_t z1;
|
||||
static const int abab_pos[2] = {0, 4};
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
complexi16_t z;
|
||||
complexi16_t z16;
|
||||
const complexi16_t *target;
|
||||
static const complexi16_t zero = {0, 0};
|
||||
#else
|
||||
float p;
|
||||
complexf_t z;
|
||||
complexf_t zz;
|
||||
const complexf_t *target;
|
||||
static const complexf_t zero = {0.0f, 0.0f};
|
||||
#endif
|
||||
float p;
|
||||
int i;
|
||||
int j;
|
||||
int32_t angle;
|
||||
|
@ -540,12 +535,7 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
|
||||
/* Add a sample to the equalizer's circular buffer, but don't calculate anything
|
||||
at this time. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->eq_buf[s->eq_step].re = sample->re/(float) FP_FACTOR;
|
||||
s->eq_buf[s->eq_step].im = sample->im/(float) FP_FACTOR;
|
||||
#else
|
||||
s->eq_buf[s->eq_step] = *sample;
|
||||
#endif
|
||||
if (++s->eq_step >= V27TER_EQUALIZER_LEN)
|
||||
s->eq_step = 0;
|
||||
|
||||
|
@ -583,9 +573,8 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
case TRAINING_STAGE_LOG_PHASE:
|
||||
/* Record the current alternate phase angle */
|
||||
target = &zero;
|
||||
angle = arctan2(z.im, z.re);
|
||||
s->angles[1] =
|
||||
s->start_angles[1] = angle;
|
||||
s->start_angles[1] = arctan2(z.im, z.re);
|
||||
s->training_count = 1;
|
||||
s->training_stage = TRAINING_STAGE_WAIT_FOR_HOP;
|
||||
break;
|
||||
|
@ -609,8 +598,7 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
if (i)
|
||||
{
|
||||
j = i & 0xF;
|
||||
ang = (s->angles[j] - s->start_angles[0])/i
|
||||
+ (s->angles[j | 0x1] - s->start_angles[1])/i;
|
||||
ang = (s->angles[j] - s->start_angles[0])/i + (s->angles[j | 0x1] - s->start_angles[1])/i;
|
||||
if (s->bit_rate == 4800)
|
||||
s->carrier_phase_rate += ang/10;
|
||||
else
|
||||
|
@ -622,27 +610,22 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
||
|
||||
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, SIG_STATUS_TRAINING_FAILED);
|
||||
break;
|
||||
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);
|
||||
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. */
|
||||
angle += 0x80000000;
|
||||
p = angle*2.0f*3.14159f/(65536.0f*65536.0f);
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
zz = complex_setf(cosf(p), -sinf(p));
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
z16 = complex_seti16(fixed_cos(angle >> 16), -fixed_sin(angle >> 16));
|
||||
for (i = 0; i < V27TER_EQUALIZER_LEN; 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;
|
||||
}
|
||||
s->eq_buf[i] = complex_mul_q1_15(&s->eq_buf[i], &z16);
|
||||
#else
|
||||
p = angle*2.0f*3.14159f/(65536.0f*65536.0f);
|
||||
zz = complex_setf(cosf(p), -sinf(p));
|
||||
for (i = 0; i < V27TER_EQUALIZER_LEN; i++)
|
||||
s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz);
|
||||
|
@ -682,7 +665,7 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
track_carrier(s, &z, target);
|
||||
tune_equalizer(s, &z, target);
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->carrier_track_i = 400 + (200000 - 400)*(float) (V27TER_TRAINING_SEG_5_LEN - s->training_count)/(float) V27TER_TRAINING_SEG_5_LEN;
|
||||
s->carrier_track_p = 1000000 + (10000000 - 1000000)*(float) (V27TER_TRAINING_SEG_5_LEN - s->training_count)/(float) V27TER_TRAINING_SEG_5_LEN;
|
||||
#else
|
||||
|
@ -702,13 +685,9 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
constellation_state = (s->bit_rate == 4800) ? s->constellation_state : (s->constellation_state << 1);
|
||||
target = &v27ter_constellation[constellation_state];
|
||||
/* Measure the training error */
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
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);
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
z16 = complex_subi16(&z, target);
|
||||
s->training_error += poweri16(&z16);
|
||||
#else
|
||||
zz = complex_subf(&z, target);
|
||||
s->training_error += powerf(&zz);
|
||||
|
@ -717,12 +696,20 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
{
|
||||
/* At 4800bps the symbols are 1.08238 (Euclidian) apart.
|
||||
At 2400bps the symbols are 2.0 (Euclidian) apart. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
if ((s->bit_rate == 4800 && s->training_error < V27TER_TRAINING_SEG_6_LEN*FP_FACTOR*FP_FACTOR/4)
|
||||
||
|
||||
(s->bit_rate == 2400 && s->training_error < V27TER_TRAINING_SEG_6_LEN*FP_FACTOR*FP_FACTOR/2))
|
||||
{
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded at %dbps (constellation mismatch %d)\n", s->bit_rate, s->training_error);
|
||||
#else
|
||||
if ((s->bit_rate == 4800 && s->training_error < V27TER_TRAINING_SEG_6_LEN*0.25f)
|
||||
||
|
||||
(s->bit_rate == 2400 && s->training_error < V27TER_TRAINING_SEG_6_LEN*0.5f))
|
||||
{
|
||||
/* We are up and running */
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded at %dbps (constellation mismatch %f)\n", s->bit_rate, s->training_error);
|
||||
#endif
|
||||
/* We are up and running */
|
||||
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. */
|
||||
|
@ -735,7 +722,11 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
else
|
||||
{
|
||||
/* Training has failed */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %d)\n", s->training_error);
|
||||
#else
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error);
|
||||
#endif
|
||||
/* Park this modem */
|
||||
s->training_stage = TRAINING_STAGE_PARKED;
|
||||
report_status_change(s, SIG_STATUS_TRAINING_FAILED);
|
||||
|
@ -750,17 +741,7 @@ static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t
|
|||
break;
|
||||
}
|
||||
if (s->qam_report)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
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 --------------------------------------------------------*/
|
||||
|
||||
|
@ -777,7 +758,7 @@ static __inline__ int signal_detect(v27ter_rx_state_t *s, int16_t amp)
|
|||
/* There could be overflow here, but it isn't a problem in practice */
|
||||
diff = x - s->last_sample;
|
||||
s->last_sample = x;
|
||||
power = power_meter_update(&(s->power), diff);
|
||||
power = power_meter_update(&s->power, diff);
|
||||
#if defined(IAXMODEM_STUFF)
|
||||
/* Quick power drop fudge */
|
||||
diff = abs(diff);
|
||||
|
@ -785,7 +766,7 @@ static __inline__ int signal_detect(v27ter_rx_state_t *s, int16_t amp)
|
|||
{
|
||||
if (++s->low_samples > 120)
|
||||
{
|
||||
power_meter_init(&(s->power), 4);
|
||||
power_meter_init(&s->power, 4);
|
||||
s->high_sample = 0;
|
||||
s->low_samples = 0;
|
||||
}
|
||||
|
@ -797,7 +778,7 @@ static __inline__ int signal_detect(v27ter_rx_state_t *s, int16_t amp)
|
|||
s->high_sample = diff;
|
||||
}
|
||||
#endif
|
||||
//span_log(&s->logging, SPAN_LOG_FLOW, "Power = %f\n", power_meter_current_dbm0(&(s->power)));
|
||||
//span_log(&s->logging, SPAN_LOG_FLOW, "Power = %f\n", power_meter_current_dbm0(&s->power));
|
||||
if (s->signal_present > 0)
|
||||
{
|
||||
/* Look for power below turn-off threshold to turn the carrier off */
|
||||
|
@ -867,7 +848,7 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], in
|
|||
parked, after training failure. */
|
||||
if (s->training_stage == TRAINING_STAGE_PARKED)
|
||||
continue;
|
||||
|
||||
|
||||
/* Put things into the equalization buffer at T/2 rate. The Gardner algorithm
|
||||
will fiddle the step to align this with the symbols. */
|
||||
if ((s->eq_put_step -= RX_PULSESHAPER_4800_COEFF_SETS) <= 0)
|
||||
|
@ -876,7 +857,7 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], in
|
|||
{
|
||||
/* Only AGC during the initial training */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->agc_scaling = (float) FP_FACTOR*32768.0f*(1.0f/RX_PULSESHAPER_4800_GAIN)*1.414f/sqrtf(power);
|
||||
s->agc_scaling = saturate16(((int32_t) (1024.0f*FP_FACTOR*1.414f))/fixed_sqrt32(power));
|
||||
#else
|
||||
s->agc_scaling = (1.0f/RX_PULSESHAPER_4800_GAIN)*1.414f/sqrtf(power);
|
||||
#endif
|
||||
|
@ -890,13 +871,13 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], in
|
|||
step = RX_PULSESHAPER_4800_COEFF_SETS - 1;
|
||||
s->eq_put_step += RX_PULSESHAPER_4800_COEFF_SETS*5/2;
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_4800_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.re = (v*(int32_t) s->agc_scaling) >> 15;
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_4800_im[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.im = (v*(int32_t) s->agc_scaling) >> 15;
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_4800_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step) >> 15;
|
||||
sample.re = (v*s->agc_scaling) >> 10;
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_4800_im[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step) >> 15;
|
||||
sample.im = (v*s->agc_scaling) >> 10;
|
||||
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;
|
||||
zz.re = ((int32_t) sample.re*z.re - (int32_t) sample.im*z.im) >> 15;
|
||||
zz.im = ((int32_t) -sample.re*z.im - (int32_t) sample.im*z.re) >> 15;
|
||||
#else
|
||||
v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_4800_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.re = v*s->agc_scaling;
|
||||
|
@ -929,7 +910,7 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], in
|
|||
parked, after training failure. */
|
||||
if (s->training_stage == TRAINING_STAGE_PARKED)
|
||||
continue;
|
||||
|
||||
|
||||
/* Put things into the equalization buffer at T/2 rate. The Gardner algorithm
|
||||
will fiddle the step to align this with the symbols. */
|
||||
if ((s->eq_put_step -= RX_PULSESHAPER_2400_COEFF_SETS) <= 0)
|
||||
|
@ -938,7 +919,7 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], in
|
|||
{
|
||||
/* Only AGC during the initial training */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->agc_scaling = (float) FP_FACTOR*32768.0f*(1.0f/RX_PULSESHAPER_2400_GAIN)*1.414f/sqrtf(power);
|
||||
s->agc_scaling = saturate16(((int32_t) (1024.0f*FP_FACTOR*1.414f))/fixed_sqrt32(power));
|
||||
#else
|
||||
s->agc_scaling = (1.0f/RX_PULSESHAPER_2400_GAIN)*1.414f/sqrtf(power);
|
||||
#endif
|
||||
|
@ -952,13 +933,13 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], in
|
|||
step = RX_PULSESHAPER_2400_COEFF_SETS - 1;
|
||||
s->eq_put_step += RX_PULSESHAPER_2400_COEFF_SETS*20/(3*2);
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_2400_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.re = (v*(int32_t) s->agc_scaling) >> 15;
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_2400_im[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.im = (v*(int32_t) s->agc_scaling) >> 15;
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_2400_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step) >> 15;
|
||||
sample.re = (v*s->agc_scaling) >> 10;
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_2400_im[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step) >> 15;
|
||||
sample.im = (v*s->agc_scaling) >> 10;
|
||||
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;
|
||||
zz.re = ((int32_t) sample.re*z.re - (int32_t) sample.im*z.im) >> 15;
|
||||
zz.im = ((int32_t) -sample.re*z.im - (int32_t) sample.im*z.re) >> 15;
|
||||
#else
|
||||
v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_2400_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.re = v*s->agc_scaling;
|
||||
|
@ -1045,8 +1026,10 @@ SPAN_DECLARE(int) v27ter_rx_restart(v27ter_rx_state_t *s, int bit_rate, int old_
|
|||
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
vec_zeroi16(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
|
||||
s->training_error = 0;
|
||||
#else
|
||||
vec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
|
||||
s->training_error = 0.0f;
|
||||
#endif
|
||||
s->rrc_filter_step = 0;
|
||||
|
||||
|
@ -1055,7 +1038,6 @@ SPAN_DECLARE(int) v27ter_rx_restart(v27ter_rx_state_t *s, int bit_rate, int old_
|
|||
s->training_stage = TRAINING_STAGE_SYMBOL_ACQUISITION;
|
||||
s->training_bc = 0;
|
||||
s->training_count = 0;
|
||||
s->training_error = 0.0f;
|
||||
s->signal_present = 0;
|
||||
#if defined(IAXMODEM_STUFF)
|
||||
s->high_sample = 0;
|
||||
|
@ -1064,14 +1046,14 @@ SPAN_DECLARE(int) v27ter_rx_restart(v27ter_rx_state_t *s, int bit_rate, int old_
|
|||
#endif
|
||||
|
||||
s->carrier_phase = 0;
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->carrier_track_i = 200000;
|
||||
s->carrier_track_p = 10000000;
|
||||
#else
|
||||
s->carrier_track_i = 200000.0f;
|
||||
s->carrier_track_p = 10000000.0f;
|
||||
#endif
|
||||
power_meter_init(&(s->power), 4);
|
||||
power_meter_init(&s->power, 4);
|
||||
|
||||
s->constellation_state = 0;
|
||||
|
||||
|
@ -1084,10 +1066,10 @@ SPAN_DECLARE(int) v27ter_rx_restart(v27ter_rx_state_t *s, int bit_rate, int old_
|
|||
else
|
||||
{
|
||||
s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ);
|
||||
#if defined(SPANDSP_USE_FIXED_POINTx)
|
||||
s->agc_scaling = (float) FP_FACTOR*32768.0f*0.005f/RX_PULSESHAPER_4800_GAIN;
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->agc_scaling = (float) (1024.0f*FP_FACTOR)*1.414f/283.0f;
|
||||
#else
|
||||
s->agc_scaling = 0.005f/RX_PULSESHAPER_4800_GAIN;
|
||||
s->agc_scaling = (1.0f/RX_PULSESHAPER_4800_GAIN)*1.414f/283.0f;
|
||||
#endif
|
||||
equalizer_reset(s);
|
||||
}
|
||||
|
|
|
@ -63,10 +63,15 @@
|
|||
#include "spandsp/private/logging.h"
|
||||
#include "spandsp/private/v29rx.h"
|
||||
|
||||
#include "v29tx_constellation_maps.h"
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
#define FP_SCALE FP_Q_4_12
|
||||
#define FP_FACTOR 4096
|
||||
#define FP_SHIFT_FACTOR 12
|
||||
#include "v29tx_constellation_maps.h"
|
||||
#include "v29rx_fixed_rrc.h"
|
||||
#else
|
||||
#define FP_SCALE(x) (x)
|
||||
#include "v29tx_constellation_maps.h"
|
||||
#include "v29rx_floating_rrc.h"
|
||||
#endif
|
||||
|
||||
|
@ -77,11 +82,6 @@
|
|||
/*! The adaption rate coefficient for the equalizer */
|
||||
#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 */
|
||||
/*! The length of training segment 2, in symbols */
|
||||
#define V29_TRAINING_SEG_2_LEN 128
|
||||
|
@ -136,13 +136,13 @@ static const uint8_t space_map_9600[20][20] =
|
|||
#define ALPHA 0.99f
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
#define SYNC_LOW_BAND_EDGE_COEFF_0 ((int)(FP_FACTOR*(2.0f*ALPHA*COS_LOW_BAND_EDGE)))
|
||||
#define SYNC_LOW_BAND_EDGE_COEFF_1 ((int)(FP_FACTOR*(-ALPHA*ALPHA)))
|
||||
#define SYNC_LOW_BAND_EDGE_COEFF_2 ((int)(FP_FACTOR*(-ALPHA*SIN_LOW_BAND_EDGE)))
|
||||
#define SYNC_HIGH_BAND_EDGE_COEFF_0 ((int)(FP_FACTOR*(2.0f*ALPHA*COS_HIGH_BAND_EDGE)))
|
||||
#define SYNC_HIGH_BAND_EDGE_COEFF_1 ((int)(FP_FACTOR*(-ALPHA*ALPHA)))
|
||||
#define SYNC_HIGH_BAND_EDGE_COEFF_2 ((int)(FP_FACTOR*(-ALPHA*SIN_HIGH_BAND_EDGE)))
|
||||
#define SYNC_MIXED_EDGES_COEFF_3 ((int)(FP_FACTOR*(-ALPHA*ALPHA*(SIN_HIGH_BAND_EDGE*COS_LOW_BAND_EDGE - SIN_LOW_BAND_EDGE*COS_HIGH_BAND_EDGE))))
|
||||
#define SYNC_LOW_BAND_EDGE_COEFF_0 FP_Q_6_10(2.0f*ALPHA*COS_LOW_BAND_EDGE)
|
||||
#define SYNC_LOW_BAND_EDGE_COEFF_1 FP_Q_6_10(-ALPHA*ALPHA)
|
||||
#define SYNC_LOW_BAND_EDGE_COEFF_2 FP_Q_6_10(-ALPHA*SIN_LOW_BAND_EDGE)
|
||||
#define SYNC_HIGH_BAND_EDGE_COEFF_0 FP_Q_6_10(2.0f*ALPHA*COS_HIGH_BAND_EDGE)
|
||||
#define SYNC_HIGH_BAND_EDGE_COEFF_1 FP_Q_6_10(-ALPHA*ALPHA)
|
||||
#define SYNC_HIGH_BAND_EDGE_COEFF_2 FP_Q_6_10(-ALPHA*SIN_HIGH_BAND_EDGE)
|
||||
#define SYNC_MIXED_EDGES_COEFF_3 FP_Q_6_10(-ALPHA*ALPHA*(SIN_HIGH_BAND_EDGE*COS_LOW_BAND_EDGE - SIN_LOW_BAND_EDGE*COS_HIGH_BAND_EDGE))
|
||||
#else
|
||||
#define SYNC_LOW_BAND_EDGE_COEFF_0 (2.0f*ALPHA*COS_LOW_BAND_EDGE)
|
||||
#define SYNC_LOW_BAND_EDGE_COEFF_1 (-ALPHA*ALPHA)
|
||||
|
@ -230,7 +230,7 @@ static void equalizer_reset(v29_rx_state_t *s)
|
|||
{
|
||||
/* Start with an equalizer based on everything being perfect */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
static const complexi16_t x = {3*FP_FACTOR, 0*FP_FACTOR};
|
||||
static const complexi16_t x = {FP_SCALE(3.0f), FP_SCALE(0.0f)};
|
||||
|
||||
cvec_zeroi16(s->eq_coeff, V29_EQUALIZER_LEN);
|
||||
s->eq_coeff[V29_EQUALIZER_PRE_LEN] = x;
|
||||
|
@ -250,25 +250,9 @@ static void equalizer_reset(v29_rx_state_t *s)
|
|||
}
|
||||
/*- 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) >> FP_SHIFT_FACTOR;
|
||||
z.im = ((int32_t) x->re*(int32_t) y->im + (int32_t) x->im*(int32_t) y->re) >> FP_SHIFT_FACTOR;
|
||||
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
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
complexi32_t zz;
|
||||
complexi16_t z;
|
||||
|
||||
|
@ -277,11 +261,14 @@ static __inline__ complexf_t equalizer_get(v29_rx_state_t *s)
|
|||
z.re = zz.re >> FP_SHIFT_FACTOR;
|
||||
z.im = zz.im >> FP_SHIFT_FACTOR;
|
||||
return z;
|
||||
}
|
||||
#else
|
||||
static __inline__ complexf_t equalizer_get(v29_rx_state_t *s)
|
||||
{
|
||||
/* Get the next equalized value. */
|
||||
return cvec_circular_dot_prodf(s->eq_buf, s->eq_coeff, V29_EQUALIZER_LEN, s->eq_step);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
|
@ -290,10 +277,9 @@ static void tune_equalizer(v29_rx_state_t *s, const complexi16_t *z, const compl
|
|||
complexi16_t err;
|
||||
|
||||
/* Find the x and y mismatch from the exact constellation position. */
|
||||
err.re = target->re*FP_FACTOR - z->re;
|
||||
err.im = target->im*FP_FACTOR - z->im;
|
||||
err.re = ((int32_t) err.re*(int32_t) s->eq_delta) >> 15;
|
||||
err.im = ((int32_t) err.im*(int32_t) s->eq_delta) >> 15;
|
||||
err = complex_subi16(target, z);
|
||||
err.re = ((int32_t) err.re*s->eq_delta) >> 15;
|
||||
err.im = ((int32_t) err.im*s->eq_delta) >> 15;
|
||||
cvec_circular_lmsi16(s->eq_buf, s->eq_coeff, V29_EQUALIZER_LEN, s->eq_step, &err);
|
||||
}
|
||||
#else
|
||||
|
@ -365,8 +351,11 @@ 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 = (((int32_t) z->im*target->re) >> FP_SHIFT_FACTOR) - (((int32_t) z->re*target->im) >> FP_SHIFT_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,
|
||||
the filter will be damped more to keep us on target. */
|
||||
|
@ -562,18 +551,19 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
|
|||
0, 3,
|
||||
0, 2
|
||||
};
|
||||
complexf_t zz;
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
complexf_t z1;
|
||||
uint16_t ip;
|
||||
complexi16_t z;
|
||||
complexi16_t z16;
|
||||
const complexi16_t *target;
|
||||
static const complexi16_t zero = {0, 0};
|
||||
#else
|
||||
float p;
|
||||
complexf_t z;
|
||||
complexf_t zz;
|
||||
const complexf_t *target;
|
||||
static const complexf_t zero = {0.0f, 0.0f};
|
||||
#endif
|
||||
float p;
|
||||
int bit;
|
||||
int i;
|
||||
int j;
|
||||
|
@ -671,17 +661,15 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
|
|||
}
|
||||
/* 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));
|
||||
ip = angle >> 16;
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Spin by %d\n", ip);
|
||||
z16 = complex_seti16(fixed_cos(ip), -fixed_sin(ip));
|
||||
for (i = 0; i < V29_EQUALIZER_LEN; 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;
|
||||
}
|
||||
s->eq_buf[i] = complex_mul_q1_15(&s->eq_buf[i], &z16);
|
||||
#else
|
||||
p = angle*2.0f*3.14159f/(65536.0f*65536.0f);
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Spin by %.5f rads\n", p);
|
||||
zz = complex_setf(cosf(p), -sinf(p));
|
||||
for (i = 0; i < V29_EQUALIZER_LEN; i++)
|
||||
s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz);
|
||||
|
@ -743,23 +731,26 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
|
|||
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);
|
||||
z16 = complex_subi16(&z, target);
|
||||
s->training_error += poweri16(&z16);
|
||||
#else
|
||||
zz = complex_subf(&z, target);
|
||||
s->training_error += powerf(&zz);
|
||||
#endif
|
||||
if (++s->training_count >= V29_TRAINING_SEG_3_LEN)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Constellation mismatch %d\n", s->training_error);
|
||||
if (s->training_error < 48*2*FP_FACTOR*FP_FACTOR)
|
||||
{
|
||||
s->training_error = 0;
|
||||
#else
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Constellation mismatch %f\n", s->training_error);
|
||||
if (s->training_error < 48.0f*2.0f)
|
||||
{
|
||||
s->training_count = 0;
|
||||
s->training_error = 0.0f;
|
||||
#endif
|
||||
s->training_count = 0;
|
||||
s->constellation_state = 0;
|
||||
s->training_stage = TRAINING_STAGE_TEST_ONES;
|
||||
}
|
||||
|
@ -785,22 +776,26 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
|
|||
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);
|
||||
z16 = complex_subi16(&z, target);
|
||||
s->training_error += poweri16(&z16);
|
||||
#else
|
||||
zz = complex_subf(&z, target);
|
||||
s->training_error += powerf(&zz);
|
||||
#endif
|
||||
if (++s->training_count >= V29_TRAINING_SEG_4_LEN)
|
||||
{
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
if (s->training_error < 48*FP_FACTOR*FP_FACTOR)
|
||||
#else
|
||||
if (s->training_error < 48.0f)
|
||||
#endif
|
||||
{
|
||||
/* We are up and running */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded at %dbps (constellation mismatch %d)\n", s->bit_rate, s->training_error);
|
||||
#else
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded at %dbps (constellation mismatch %f)\n", s->bit_rate, s->training_error);
|
||||
#endif
|
||||
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. */
|
||||
|
@ -812,12 +807,12 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* Training has failed */
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error);
|
||||
/* Park this modem */
|
||||
/* Training has failed. Park this modem */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %d)\n", s->training_error);
|
||||
s->agc_scaling_save = 0;
|
||||
#else
|
||||
span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error);
|
||||
s->agc_scaling_save = 0.0f;
|
||||
#endif
|
||||
s->training_stage = TRAINING_STAGE_PARKED;
|
||||
|
@ -833,17 +828,7 @@ static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
|
|||
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 --------------------------------------------------------*/
|
||||
|
||||
|
@ -860,7 +845,7 @@ static __inline__ int signal_detect(v29_rx_state_t *s, int16_t amp)
|
|||
/* There could be overflow here, but it isn't a problem in practice */
|
||||
diff = x - s->last_sample;
|
||||
s->last_sample = x;
|
||||
power = power_meter_update(&(s->power), diff);
|
||||
power = power_meter_update(&s->power, diff);
|
||||
#if defined(IAXMODEM_STUFF)
|
||||
/* Quick power drop fudge */
|
||||
diff = abs(diff);
|
||||
|
@ -868,7 +853,7 @@ static __inline__ int signal_detect(v29_rx_state_t *s, int16_t amp)
|
|||
{
|
||||
if (++s->low_samples > 120)
|
||||
{
|
||||
power_meter_init(&(s->power), 4);
|
||||
power_meter_init(&s->power, 4);
|
||||
s->high_sample = 0;
|
||||
s->low_samples = 0;
|
||||
}
|
||||
|
@ -957,21 +942,20 @@ SPAN_DECLARE_NONSTD(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
|
|||
else if (step > RX_PULSESHAPER_COEFF_SETS - 1)
|
||||
step = RX_PULSESHAPER_COEFF_SETS - 1;
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_re[step], V29_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.re = (v*s->agc_scaling) >> 15;
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_re[step], V29_RX_FILTER_STEPS, s->rrc_filter_step) >> 15;
|
||||
sample.re = (v*s->agc_scaling) >> 10;
|
||||
#else
|
||||
v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_re[step], V29_RX_FILTER_STEPS, 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) >> FP_SHIFT_FACTOR) + ((s->symbol_sync_low[1]*SYNC_LOW_BAND_EDGE_COEFF_1) >> FP_SHIFT_FACTOR) + sample.re;
|
||||
v = ((s->symbol_sync_low[0]*SYNC_LOW_BAND_EDGE_COEFF_0) >> 10) + ((s->symbol_sync_low[1]*SYNC_LOW_BAND_EDGE_COEFF_1) >> 10) + 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) >> FP_SHIFT_FACTOR) + ((s->symbol_sync_high[1]*SYNC_HIGH_BAND_EDGE_COEFF_1) >> FP_SHIFT_FACTOR) + sample.re;
|
||||
v = ((s->symbol_sync_high[0]*SYNC_HIGH_BAND_EDGE_COEFF_0) >> 10) + ((s->symbol_sync_high[1]*SYNC_HIGH_BAND_EDGE_COEFF_1) >> 10) + sample.re;
|
||||
s->symbol_sync_high[1] = s->symbol_sync_high[0];
|
||||
s->symbol_sync_high[0] = v;
|
||||
#else
|
||||
|
@ -991,10 +975,10 @@ SPAN_DECLARE_NONSTD(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
|
|||
/* Only AGC until we have locked down the setting. */
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
if (s->agc_scaling_save == 0)
|
||||
s->agc_scaling = (float) FP_FACTOR*32768.0f*(1.0f/RX_PULSESHAPER_GAIN)*5.0f*0.25f/sqrtf(power);
|
||||
s->agc_scaling = saturate16(((int32_t) (1024.0f*FP_FACTOR*1.25f))/fixed_sqrt32(power));
|
||||
#else
|
||||
if (s->agc_scaling_save == 0.0f)
|
||||
s->agc_scaling = (1.0f/RX_PULSESHAPER_GAIN)*5.0f*0.25f/sqrtf(power);
|
||||
s->agc_scaling = (1.0f/RX_PULSESHAPER_GAIN)*1.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
|
||||
|
@ -1002,11 +986,11 @@ SPAN_DECLARE_NONSTD(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
|
|||
No further filtering, to remove mixer harmonics, is needed. */
|
||||
s->eq_put_step += RX_PULSESHAPER_COEFF_SETS*10/(3*2);
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_im[step], V29_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.im = (v*s->agc_scaling) >> 15;
|
||||
v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_im[step], V29_RX_FILTER_STEPS, s->rrc_filter_step) >> 15;
|
||||
sample.im = (v*s->agc_scaling) >> 10;
|
||||
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;
|
||||
zz.re = ((int32_t) sample.re*z.re - (int32_t) sample.im*z.im) >> 15;
|
||||
zz.im = ((int32_t) -sample.re*z.im - (int32_t) sample.im*z.re) >> 15;
|
||||
#else
|
||||
v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_im[step], V29_RX_FILTER_STEPS, s->rrc_filter_step);
|
||||
sample.im = v*s->agc_scaling;
|
||||
|
@ -1115,7 +1099,7 @@ SPAN_DECLARE(int) v29_rx_restart(v29_rx_state_t *s, int bit_rate, int old_train)
|
|||
|
||||
s->carrier_phase = 0;
|
||||
|
||||
power_meter_init(&(s->power), 4);
|
||||
power_meter_init(&s->power, 4);
|
||||
|
||||
s->constellation_state = 0;
|
||||
|
||||
|
@ -1131,10 +1115,10 @@ SPAN_DECLARE(int) v29_rx_restart(v29_rx_state_t *s, int bit_rate, int old_train)
|
|||
equalizer_reset(s);
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
s->agc_scaling_save = 0;
|
||||
s->agc_scaling = (float) FP_FACTOR*32768.0f*0.0017f/RX_PULSESHAPER_GAIN;
|
||||
s->agc_scaling = (float) (1024.0f*FP_FACTOR)*1.25f/735.0f;
|
||||
#else
|
||||
s->agc_scaling_save = 0.0f;
|
||||
s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN;
|
||||
s->agc_scaling = (1.0f/RX_PULSESHAPER_GAIN)*1.25f/735.0f;
|
||||
#endif
|
||||
}
|
||||
#if defined(SPANDSP_USE_FIXED_POINT)
|
||||
|
@ -1168,7 +1152,6 @@ SPAN_DECLARE(int) v29_rx_restart(v29_rx_state_t *s, int bit_rate, int old_train)
|
|||
s->baud_half = 0;
|
||||
|
||||
s->total_baud_timing_correction = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
|
Loading…
Reference in New Issue