]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Some modem cleanups, and movement towards efficient fixed point modem
authorSteve Underwood <steveu@coppice.org>
Sat, 21 Jul 2012 11:47:45 +0000 (19:47 +0800)
committerSteve Underwood <steveu@coppice.org>
Sat, 21 Jul 2012 11:47:45 +0000 (19:47 +0800)
implementations for platforms with slow floating point

libs/spandsp/src/spandsp/private/v22bis.h
libs/spandsp/src/spandsp/private/v27ter_rx.h
libs/spandsp/src/spandsp/private/v29rx.h
libs/spandsp/src/spandsp/v27ter_rx.h
libs/spandsp/src/spandsp/v29rx.h
libs/spandsp/src/v17tx.c
libs/spandsp/src/v22bis_rx.c
libs/spandsp/src/v22bis_tx.c
libs/spandsp/src/v27ter_rx.c
libs/spandsp/src/v29rx.c

index 01c37e9aea9f3eacd2e44b37981bbfa6c86c3d70..9ac200d44e9467d1ee4f5f494fec800ebf27c8f7 100644 (file)
 #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. */
index c491c905df2ea48e973060c75e947dee0475a278..92bf937d833fc8ea29287aff7d732f2b01a5d3ed 100644 (file)
@@ -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
index c86c87fe2e07e78840d04a4a06180a9872e7d955..17e900582e621a03cd29bceab9849705b6d462ff 100644 (file)
@@ -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;
index 8e8de0d0738ceb16121b011ffcc699fbf117fa11..6b464b3d422de8af7597849bb6d85c3eb78a5885 100644 (file)
@@ -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);
index 8c89407ce3a673b46e46800807e5a70cfbdfa65c..bdcd23f944719d497e234c02aac2d32656ed4fda 100644 (file)
@@ -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);
index ca4eba1c68186b66efb0ac89fe28da4a18b057bd..56d3fa13a0fd3e49b4eacac6ad5607f151c74fd8 100644 (file)
 #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)
             {
index 85c0b9e10c9d2e3bce5800af10aef4782a33c809..85303220f526facea9255c87a7823305d15e4246 100644 (file)
 #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;
+    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;
 
-    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;
-    }
+    /* 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
+            {
+#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);
+                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);
             }
+            /* 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;
 
index 2f8c724f6b706707b22cd0c199d59f9e5dbb0122..5cbef4d7d8826bcf0bcff077a9cf301209fba290 100644 (file)
@@ -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
index 5ae87dc44d365afeecd887591c691f339e6e8812..b38481b87a5ec4216165465e2041ce61396a40f5 100644 (file)
 #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
 /*! 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);
     }
index cfc60aeabbc0243d20649f22502427c0c196f6b0..a8a1ed9c9455c7419fb398c69ee6ac10c9196ec8 100644 (file)
 #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
 
 /*! 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 --------------------------------------------------------*/