Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v29rx.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
| author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
|---|---|
| date | Fri, 25 Jun 2010 15:50:58 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 3:c6c5a16ce2f2 | 4:26cd8f1ef0b1 |
|---|---|
| 1 #define IAXMODEM_STUFF | |
| 2 /* | |
| 3 * SpanDSP - a series of DSP components for telephony | |
| 4 * | |
| 5 * v29rx.c - ITU V.29 modem receive part | |
| 6 * | |
| 7 * Written by Steve Underwood <steveu@coppice.org> | |
| 8 * | |
| 9 * Copyright (C) 2003, 2004, 2005, 2006, 2007 Steve Underwood | |
| 10 * | |
| 11 * All rights reserved. | |
| 12 * | |
| 13 * This program is free software; you can redistribute it and/or modify | |
| 14 * it under the terms of the GNU Lesser General Public License version 2.1, | |
| 15 * as published by the Free Software Foundation. | |
| 16 * | |
| 17 * This program is distributed in the hope that it will be useful, | |
| 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 20 * GNU Lesser General Public License for more details. | |
| 21 * | |
| 22 * You should have received a copy of the GNU Lesser General Public | |
| 23 * License along with this program; if not, write to the Free Software | |
| 24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 25 * | |
| 26 * $Id: v29rx.c,v 1.167.4.5 2009/12/28 12:20:47 steveu Exp $ | |
| 27 */ | |
| 28 | |
| 29 /*! \file */ | |
| 30 | |
| 31 #if defined(HAVE_CONFIG_H) | |
| 32 #include "config.h" | |
| 33 #endif | |
| 34 | |
| 35 #include <stdlib.h> | |
| 36 #include <inttypes.h> | |
| 37 #include <string.h> | |
| 38 #include <stdio.h> | |
| 39 #if defined(HAVE_TGMATH_H) | |
| 40 #include <tgmath.h> | |
| 41 #endif | |
| 42 #if defined(HAVE_MATH_H) | |
| 43 #include <math.h> | |
| 44 #endif | |
| 45 #include "floating_fudge.h" | |
| 46 | |
| 47 #include "spandsp/telephony.h" | |
| 48 #include "spandsp/logging.h" | |
| 49 #include "spandsp/complex.h" | |
| 50 #include "spandsp/vector_float.h" | |
| 51 #include "spandsp/complex_vector_float.h" | |
| 52 #include "spandsp/vector_int.h" | |
| 53 #include "spandsp/complex_vector_int.h" | |
| 54 #include "spandsp/async.h" | |
| 55 #include "spandsp/power_meter.h" | |
| 56 #include "spandsp/arctan2.h" | |
| 57 #include "spandsp/dds.h" | |
| 58 #include "spandsp/complex_filters.h" | |
| 59 | |
| 60 #include "spandsp/v29rx.h" | |
| 61 | |
| 62 #include "spandsp/private/logging.h" | |
| 63 #include "spandsp/private/v29rx.h" | |
| 64 | |
| 65 #include "v29tx_constellation_maps.h" | |
| 66 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 67 #include "v29rx_fixed_rrc.h" | |
| 68 #else | |
| 69 #include "v29rx_floating_rrc.h" | |
| 70 #endif | |
| 71 | |
| 72 /*! The nominal frequency of the carrier, in Hertz */ | |
| 73 #define CARRIER_NOMINAL_FREQ 1700.0f | |
| 74 /*! The nominal baud or symbol rate */ | |
| 75 #define BAUD_RATE 2400 | |
| 76 /*! The adaption rate coefficient for the equalizer */ | |
| 77 #define EQUALIZER_DELTA 0.21f | |
| 78 | |
| 79 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 80 #define FP_FACTOR 4096 | |
| 81 #define FP_SHIFT_FACTOR 12 | |
| 82 #endif | |
| 83 | |
| 84 /* Segments of the training sequence */ | |
| 85 /*! The length of training segment 2, in symbols */ | |
| 86 #define V29_TRAINING_SEG_2_LEN 128 | |
| 87 /*! The length of training segment 3, in symbols */ | |
| 88 #define V29_TRAINING_SEG_3_LEN 384 | |
| 89 /*! The length of training segment 4, in symbols */ | |
| 90 #define V29_TRAINING_SEG_4_LEN 48 | |
| 91 | |
| 92 /*! The length of the equalizer buffer */ | |
| 93 #define V29_EQUALIZER_LEN (V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN) | |
| 94 | |
| 95 enum | |
| 96 { | |
| 97 TRAINING_STAGE_NORMAL_OPERATION = 0, | |
| 98 TRAINING_STAGE_SYMBOL_ACQUISITION, | |
| 99 TRAINING_STAGE_LOG_PHASE, | |
| 100 TRAINING_STAGE_WAIT_FOR_CDCD, | |
| 101 TRAINING_STAGE_TRAIN_ON_CDCD, | |
| 102 TRAINING_STAGE_TRAIN_ON_CDCD_AND_TEST, | |
| 103 TRAINING_STAGE_TEST_ONES, | |
| 104 TRAINING_STAGE_PARKED | |
| 105 }; | |
| 106 | |
| 107 static const uint8_t space_map_9600[20][20] = | |
| 108 { | |
| 109 {13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11}, | |
| 110 {13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11}, | |
| 111 {13, 13, 13, 13, 13, 13, 13, 4, 4, 4, 4, 4, 4, 11, 11, 11, 11, 11, 11, 11}, | |
| 112 {13, 13, 13, 13, 13, 13, 13, 4, 4, 4, 4, 4, 4, 11, 11, 11, 11, 11, 11, 11}, | |
| 113 {13, 13, 13, 13, 13, 13, 13, 4, 4, 4, 4, 4, 4, 11, 11, 11, 11, 11, 11, 11}, | |
| 114 {13, 13, 13, 13, 13, 13, 13, 5, 4, 4, 4, 4, 3, 11, 11, 11, 11, 11, 11, 11}, | |
| 115 {14, 13, 13, 13, 13, 13, 5, 5, 5, 5, 3, 3, 3, 3, 11, 11, 11, 11, 11, 10}, | |
| 116 {14, 14, 6, 6, 6, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 2, 2, 2, 10, 10}, | |
| 117 {14, 14, 6, 6, 6, 6, 5, 5, 5, 5, 3, 3, 3, 3, 2, 2, 2, 2, 10, 10}, | |
| 118 {14, 14, 6, 6, 6, 6, 5, 5, 5, 5, 3, 3, 3, 3, 2, 2, 2, 2, 10, 10}, | |
| 119 {14, 14, 6, 6, 6, 6, 7, 7, 7, 7, 1, 1, 1, 1, 2, 2, 2, 2, 10, 10}, | |
| 120 {14, 14, 6, 6, 6, 6, 7, 7, 7, 7, 1, 1, 1, 1, 2, 2, 2, 2, 10, 10}, | |
| 121 {14, 14, 6, 6, 6, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 2, 2, 2, 10, 10}, | |
| 122 {14, 15, 15, 15, 15, 15, 7, 7, 7, 7, 1, 1, 1, 1, 9, 9, 9, 9, 9, 10}, | |
| 123 {15, 15, 15, 15, 15, 15, 15, 7, 0, 0, 0, 0, 1, 9, 9, 9, 9, 9, 9, 9}, | |
| 124 {15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9}, | |
| 125 {15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9}, | |
| 126 {15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9}, | |
| 127 {15, 15, 15, 15, 15, 15, 15, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9}, | |
| 128 {15, 15, 15, 15, 15, 15, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9} | |
| 129 }; | |
| 130 | |
| 131 /* Coefficients for the band edge symbol timing synchroniser (alpha = 0.99) */ | |
| 132 /* low_edge = 2.0f*M_PI*(CARRIER_NOMINAL_FREQ - BAUD_RATE/2.0f)/SAMPLE_RATE; */ | |
| 133 /* high_edge = 2.0f*M_PI*(CARRIER_NOMINAL_FREQ + BAUD_RATE/2.0f)/SAMPLE_RATE; */ | |
| 134 #define SIN_LOW_BAND_EDGE 0.382683432f | |
| 135 #define COS_LOW_BAND_EDGE 0.923879533f | |
| 136 #define SIN_HIGH_BAND_EDGE 0.760405966f | |
| 137 #define COS_HIGH_BAND_EDGE -0.649448048f | |
| 138 #define ALPHA 0.99f | |
| 139 | |
| 140 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 141 #define SYNC_LOW_BAND_EDGE_COEFF_0 ((int)(FP_FACTOR*(2.0f*ALPHA*COS_LOW_BAND_EDGE))) | |
| 142 #define SYNC_LOW_BAND_EDGE_COEFF_1 ((int)(FP_FACTOR*(-ALPHA*ALPHA))) | |
| 143 #define SYNC_LOW_BAND_EDGE_COEFF_2 ((int)(FP_FACTOR*(-ALPHA*SIN_LOW_BAND_EDGE))) | |
| 144 #define SYNC_HIGH_BAND_EDGE_COEFF_0 ((int)(FP_FACTOR*(2.0f*ALPHA*COS_HIGH_BAND_EDGE))) | |
| 145 #define SYNC_HIGH_BAND_EDGE_COEFF_1 ((int)(FP_FACTOR*(-ALPHA*ALPHA))) | |
| 146 #define SYNC_HIGH_BAND_EDGE_COEFF_2 ((int)(FP_FACTOR*(-ALPHA*SIN_HIGH_BAND_EDGE))) | |
| 147 #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)))) | |
| 148 #else | |
| 149 #define SYNC_LOW_BAND_EDGE_COEFF_0 (2.0f*ALPHA*COS_LOW_BAND_EDGE) | |
| 150 #define SYNC_LOW_BAND_EDGE_COEFF_1 (-ALPHA*ALPHA) | |
| 151 #define SYNC_LOW_BAND_EDGE_COEFF_2 (-ALPHA*SIN_LOW_BAND_EDGE) | |
| 152 #define SYNC_HIGH_BAND_EDGE_COEFF_0 (2.0f*ALPHA*COS_HIGH_BAND_EDGE) | |
| 153 #define SYNC_HIGH_BAND_EDGE_COEFF_1 (-ALPHA*ALPHA) | |
| 154 #define SYNC_HIGH_BAND_EDGE_COEFF_2 (-ALPHA*SIN_HIGH_BAND_EDGE) | |
| 155 #define SYNC_MIXED_EDGES_COEFF_3 (-ALPHA*ALPHA*(SIN_HIGH_BAND_EDGE*COS_LOW_BAND_EDGE - SIN_LOW_BAND_EDGE*COS_HIGH_BAND_EDGE)) | |
| 156 #endif | |
| 157 | |
| 158 SPAN_DECLARE(float) v29_rx_carrier_frequency(v29_rx_state_t *s) | |
| 159 { | |
| 160 return dds_frequencyf(s->carrier_phase_rate); | |
| 161 } | |
| 162 /*- End of function --------------------------------------------------------*/ | |
| 163 | |
| 164 SPAN_DECLARE(float) v29_rx_symbol_timing_correction(v29_rx_state_t *s) | |
| 165 { | |
| 166 return (float) s->total_baud_timing_correction/((float) RX_PULSESHAPER_COEFF_SETS*10.0f/3.0f); | |
| 167 } | |
| 168 /*- End of function --------------------------------------------------------*/ | |
| 169 | |
| 170 SPAN_DECLARE(float) v29_rx_signal_power(v29_rx_state_t *s) | |
| 171 { | |
| 172 return power_meter_current_dbm0(&s->power) + 3.98f; | |
| 173 } | |
| 174 /*- End of function --------------------------------------------------------*/ | |
| 175 | |
| 176 SPAN_DECLARE(void) v29_rx_signal_cutoff(v29_rx_state_t *s, float cutoff) | |
| 177 { | |
| 178 /* The 0.4 factor allows for the gain of the DC blocker */ | |
| 179 s->carrier_on_power = (int32_t) (power_meter_level_dbm0(cutoff + 2.5f)*0.4f); | |
| 180 s->carrier_off_power = (int32_t) (power_meter_level_dbm0(cutoff - 2.5f)*0.4f); | |
| 181 } | |
| 182 /*- End of function --------------------------------------------------------*/ | |
| 183 | |
| 184 static void report_status_change(v29_rx_state_t *s, int status) | |
| 185 { | |
| 186 if (s->status_handler) | |
| 187 s->status_handler(s->status_user_data, status); | |
| 188 else if (s->put_bit) | |
| 189 s->put_bit(s->put_bit_user_data, status); | |
| 190 } | |
| 191 /*- End of function --------------------------------------------------------*/ | |
| 192 | |
| 193 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 194 SPAN_DECLARE(int) v29_rx_equalizer_state(v29_rx_state_t *s, complexi16_t **coeffs) | |
| 195 #else | |
| 196 SPAN_DECLARE(int) v29_rx_equalizer_state(v29_rx_state_t *s, complexf_t **coeffs) | |
| 197 #endif | |
| 198 { | |
| 199 *coeffs = s->eq_coeff; | |
| 200 return V29_EQUALIZER_LEN; | |
| 201 } | |
| 202 /*- End of function --------------------------------------------------------*/ | |
| 203 | |
| 204 static void equalizer_save(v29_rx_state_t *s) | |
| 205 { | |
| 206 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 207 cvec_copyi16(s->eq_coeff_save, s->eq_coeff, V29_EQUALIZER_LEN); | |
| 208 #else | |
| 209 cvec_copyf(s->eq_coeff_save, s->eq_coeff, V29_EQUALIZER_LEN); | |
| 210 #endif | |
| 211 } | |
| 212 /*- End of function --------------------------------------------------------*/ | |
| 213 | |
| 214 static void equalizer_restore(v29_rx_state_t *s) | |
| 215 { | |
| 216 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 217 cvec_copyi16(s->eq_coeff, s->eq_coeff_save, V29_EQUALIZER_LEN); | |
| 218 cvec_zeroi16(s->eq_buf, V29_EQUALIZER_LEN); | |
| 219 s->eq_delta = 32768.0f*EQUALIZER_DELTA/V29_EQUALIZER_LEN; | |
| 220 #else | |
| 221 cvec_copyf(s->eq_coeff, s->eq_coeff_save, V29_EQUALIZER_LEN); | |
| 222 cvec_zerof(s->eq_buf, V29_EQUALIZER_LEN); | |
| 223 s->eq_delta = EQUALIZER_DELTA/V29_EQUALIZER_LEN; | |
| 224 #endif | |
| 225 | |
| 226 s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1; | |
| 227 s->eq_step = 0; | |
| 228 } | |
| 229 /*- End of function --------------------------------------------------------*/ | |
| 230 | |
| 231 static void equalizer_reset(v29_rx_state_t *s) | |
| 232 { | |
| 233 /* Start with an equalizer based on everything being perfect */ | |
| 234 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 235 cvec_zeroi16(s->eq_coeff, V29_EQUALIZER_LEN); | |
| 236 s->eq_coeff[V29_EQUALIZER_POST_LEN] = complex_seti16(3*FP_FACTOR, 0*FP_FACTOR); | |
| 237 cvec_zeroi16(s->eq_buf, V29_EQUALIZER_LEN); | |
| 238 s->eq_delta = 32768.0f*EQUALIZER_DELTA/V29_EQUALIZER_LEN; | |
| 239 #else | |
| 240 cvec_zerof(s->eq_coeff, V29_EQUALIZER_LEN); | |
| 241 s->eq_coeff[V29_EQUALIZER_POST_LEN] = complex_setf(3.0f, 0.0f); | |
| 242 cvec_zerof(s->eq_buf, V29_EQUALIZER_LEN); | |
| 243 s->eq_delta = EQUALIZER_DELTA/V29_EQUALIZER_LEN; | |
| 244 #endif | |
| 245 | |
| 246 s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1; | |
| 247 s->eq_step = 0; | |
| 248 } | |
| 249 /*- End of function --------------------------------------------------------*/ | |
| 250 | |
| 251 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 252 static __inline__ complexi16_t complex_mul_q4_12(const complexi16_t *x, const complexi16_t *y) | |
| 253 { | |
| 254 complexi16_t z; | |
| 255 | |
| 256 z.re = ((int32_t) x->re*(int32_t) y->re - (int32_t) x->im*(int32_t) y->im) >> FP_SHIFT_FACTOR; | |
| 257 z.im = ((int32_t) x->re*(int32_t) y->im + (int32_t) x->im*(int32_t) y->re) >> FP_SHIFT_FACTOR; | |
| 258 return z; | |
| 259 } | |
| 260 /*- End of function --------------------------------------------------------*/ | |
| 261 #endif | |
| 262 | |
| 263 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 264 static __inline__ complexi16_t equalizer_get(v29_rx_state_t *s) | |
| 265 #else | |
| 266 static __inline__ complexf_t equalizer_get(v29_rx_state_t *s) | |
| 267 #endif | |
| 268 { | |
| 269 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 270 complexi32_t zz; | |
| 271 complexi16_t z; | |
| 272 | |
| 273 /* Get the next equalized value. */ | |
| 274 zz = cvec_circular_dot_prodi16(s->eq_buf, s->eq_coeff, V29_EQUALIZER_LEN, s->eq_step); | |
| 275 z.re = zz.re >> FP_SHIFT_FACTOR; | |
| 276 z.im = zz.im >> FP_SHIFT_FACTOR; | |
| 277 return z; | |
| 278 #else | |
| 279 /* Get the next equalized value. */ | |
| 280 return cvec_circular_dot_prodf(s->eq_buf, s->eq_coeff, V29_EQUALIZER_LEN, s->eq_step); | |
| 281 #endif | |
| 282 } | |
| 283 /*- End of function --------------------------------------------------------*/ | |
| 284 | |
| 285 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 286 static void tune_equalizer(v29_rx_state_t *s, const complexi16_t *z, const complexi16_t *target) | |
| 287 { | |
| 288 complexi16_t err; | |
| 289 | |
| 290 /* Find the x and y mismatch from the exact constellation position. */ | |
| 291 err.re = target->re*FP_FACTOR - z->re; | |
| 292 err.im = target->im*FP_FACTOR - z->im; | |
| 293 err.re = ((int32_t) err.re*(int32_t) s->eq_delta) >> 15; | |
| 294 err.im = ((int32_t) err.im*(int32_t) s->eq_delta) >> 15; | |
| 295 cvec_circular_lmsi16(s->eq_buf, s->eq_coeff, V29_EQUALIZER_LEN, s->eq_step, &err); | |
| 296 } | |
| 297 #else | |
| 298 static void tune_equalizer(v29_rx_state_t *s, const complexf_t *z, const complexf_t *target) | |
| 299 { | |
| 300 complexf_t err; | |
| 301 | |
| 302 /* Find the x and y mismatch from the exact constellation position. */ | |
| 303 err = complex_subf(target, z); | |
| 304 err.re *= s->eq_delta; | |
| 305 err.im *= s->eq_delta; | |
| 306 cvec_circular_lmsf(s->eq_buf, s->eq_coeff, V29_EQUALIZER_LEN, s->eq_step, &err); | |
| 307 } | |
| 308 #endif | |
| 309 /*- End of function --------------------------------------------------------*/ | |
| 310 | |
| 311 static int scrambled_training_bit(v29_rx_state_t *s) | |
| 312 { | |
| 313 int bit; | |
| 314 | |
| 315 /* Segment 3 of the training sequence - the scrambled CDCD part. */ | |
| 316 /* Apply the 1 + x^-6 + x^-7 scrambler */ | |
| 317 bit = s->training_scramble_reg & 1; | |
| 318 s->training_scramble_reg >>= 1; | |
| 319 if (bit ^ (s->training_scramble_reg & 1)) | |
| 320 s->training_scramble_reg |= 0x40; | |
| 321 return bit; | |
| 322 } | |
| 323 /*- End of function --------------------------------------------------------*/ | |
| 324 | |
| 325 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 326 static __inline__ int find_quadrant(const complexi16_t *z) | |
| 327 #else | |
| 328 static __inline__ int find_quadrant(const complexf_t *z) | |
| 329 #endif | |
| 330 { | |
| 331 int b1; | |
| 332 int b2; | |
| 333 | |
| 334 /* Split the space along the two diagonals. */ | |
| 335 b1 = (z->im > z->re); | |
| 336 b2 = (z->im < -z->re); | |
| 337 return (b2 << 1) | (b1 ^ b2); | |
| 338 } | |
| 339 /*- End of function --------------------------------------------------------*/ | |
| 340 | |
| 341 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 342 static __inline__ void track_carrier(v29_rx_state_t *s, const complexi16_t *z, const complexi16_t *target) | |
| 343 #else | |
| 344 static __inline__ void track_carrier(v29_rx_state_t *s, const complexf_t *z, const complexf_t *target) | |
| 345 #endif | |
| 346 { | |
| 347 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 348 int32_t error; | |
| 349 #else | |
| 350 float error; | |
| 351 #endif | |
| 352 | |
| 353 /* The initial coarse carrier frequency and phase estimation should have | |
| 354 got us in the right ballpark. Now we need to fine tune fairly quickly, | |
| 355 to get the receovered carrier more precisely on target. Then we need to | |
| 356 fine tune in a more damped way to keep us on target. The goal is to have | |
| 357 things running really well by the time the training is complete. | |
| 358 We assume the frequency of the oscillators at the two ends drift only | |
| 359 very slowly. The PSTN has rather limited doppler problems. :-) Any | |
| 360 remaining FDM in the network should also drift slowly. */ | |
| 361 /* For small errors the imaginary part of the difference between the actual and the target | |
| 362 positions is proportional to the phase error, for any particular target. However, the | |
| 363 different amplitudes of the various target positions scale things. This isn't all bad, | |
| 364 as the angular error for the larger amplitude constellation points is probably | |
| 365 a more reliable indicator, and we are weighting it as such. */ | |
| 366 error = z->im*target->re - z->re*target->im; | |
| 367 | |
| 368 /* Use a proportional-integral approach to tracking the carrier. The PI | |
| 369 parameters are coarser at first, until we get precisely on target. Then, | |
| 370 the filter will be damped more to keep us on target. */ | |
| 371 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 372 s->carrier_phase_rate += ((s->carrier_track_i*error) >> FP_SHIFT_FACTOR); | |
| 373 s->carrier_phase += ((s->carrier_track_p*error) >> FP_SHIFT_FACTOR); | |
| 374 #else | |
| 375 s->carrier_phase_rate += (int32_t) (s->carrier_track_i*error); | |
| 376 s->carrier_phase += (int32_t) (s->carrier_track_p*error); | |
| 377 //span_log(&s->logging, SPAN_LOG_FLOW, "Im = %15.5f f = %15.5f\n", error, dds_frequencyf(s->carrier_phase_rate)); | |
| 378 #endif | |
| 379 } | |
| 380 /*- End of function --------------------------------------------------------*/ | |
| 381 | |
| 382 static __inline__ void put_bit(v29_rx_state_t *s, int bit) | |
| 383 { | |
| 384 int out_bit; | |
| 385 | |
| 386 bit &= 1; | |
| 387 | |
| 388 /* Descramble the bit */ | |
| 389 out_bit = (bit ^ (s->scramble_reg >> 17) ^ (s->scramble_reg >> 22)) & 1; | |
| 390 s->scramble_reg = (s->scramble_reg << 1) | bit; | |
| 391 | |
| 392 /* We need to strip the last part of the training - the test period of all 1s - | |
| 393 before we let data go to the application. */ | |
| 394 if (s->training_stage == TRAINING_STAGE_NORMAL_OPERATION) | |
| 395 { | |
| 396 s->put_bit(s->put_bit_user_data, out_bit); | |
| 397 } | |
| 398 else | |
| 399 { | |
| 400 /* The bits during the final stage of training should be all ones. However, | |
| 401 buggy modems mean you cannot rely on this. Therefore we don't bother | |
| 402 testing for ones, but just rely on a constellation mismatch measurement. */ | |
| 403 } | |
| 404 } | |
| 405 /*- End of function --------------------------------------------------------*/ | |
| 406 | |
| 407 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 408 static void decode_baud(v29_rx_state_t *s, complexi16_t *z) | |
| 409 #else | |
| 410 static void decode_baud(v29_rx_state_t *s, complexf_t *z) | |
| 411 #endif | |
| 412 { | |
| 413 static const uint8_t phase_steps_9600[8] = | |
| 414 { | |
| 415 4, 0, 2, 6, 7, 3, 1, 5 | |
| 416 }; | |
| 417 static const uint8_t phase_steps_4800[4] = | |
| 418 { | |
| 419 0, 2, 3, 1 | |
| 420 }; | |
| 421 int nearest; | |
| 422 int raw_bits; | |
| 423 int i; | |
| 424 int re; | |
| 425 int im; | |
| 426 | |
| 427 if (s->bit_rate == 4800) | |
| 428 { | |
| 429 /* 4800 is a special case. */ | |
| 430 nearest = find_quadrant(z) << 1; | |
| 431 raw_bits = phase_steps_4800[((nearest - s->constellation_state) >> 1) & 3]; | |
| 432 put_bit(s, raw_bits); | |
| 433 put_bit(s, raw_bits >> 1); | |
| 434 } | |
| 435 else | |
| 436 { | |
| 437 /* 9600 and 7200 are quite similar. */ | |
| 438 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 439 re = (z->re + 5*FP_FACTOR) >> (FP_SHIFT_FACTOR - 1); | |
| 440 im = (z->im + 5*FP_FACTOR) >> (FP_SHIFT_FACTOR - 1); | |
| 441 #else | |
| 442 re = (int) ((z->re + 5.0f)*2.0f); | |
| 443 im = (int) ((z->im + 5.0f)*2.0f); | |
| 444 #endif | |
| 445 if (re > 19) | |
| 446 re = 19; | |
| 447 else if (re < 0) | |
| 448 re = 0; | |
| 449 if (im > 19) | |
| 450 im = 19; | |
| 451 else if (im < 0) | |
| 452 im = 0; | |
| 453 nearest = space_map_9600[re][im]; | |
| 454 if (s->bit_rate == 9600) | |
| 455 { | |
| 456 /* Send out the top (amplitude) bit. */ | |
| 457 put_bit(s, nearest >> 3); | |
| 458 } | |
| 459 else | |
| 460 { | |
| 461 /* We can reuse the space map for 9600, but drop the top bit. */ | |
| 462 nearest &= 7; | |
| 463 } | |
| 464 raw_bits = phase_steps_9600[(nearest - s->constellation_state) & 7]; | |
| 465 for (i = 0; i < 3; i++) | |
| 466 { | |
| 467 put_bit(s, raw_bits); | |
| 468 raw_bits >>= 1; | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 track_carrier(s, z, &v29_9600_constellation[nearest]); | |
| 473 if (--s->eq_skip <= 0) | |
| 474 { | |
| 475 /* Once we are in the data the equalization should not need updating. | |
| 476 However, the line characteristics may slowly drift. We, therefore, | |
| 477 tune up on the occassional sample, keeping the compute down. */ | |
| 478 s->eq_skip = 10; | |
| 479 tune_equalizer(s, z, &v29_9600_constellation[nearest]); | |
| 480 } | |
| 481 s->constellation_state = nearest; | |
| 482 } | |
| 483 /*- End of function --------------------------------------------------------*/ | |
| 484 | |
| 485 static __inline__ void symbol_sync(v29_rx_state_t *s) | |
| 486 { | |
| 487 int i; | |
| 488 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 489 int32_t v; | |
| 490 int32_t p; | |
| 491 #else | |
| 492 float v; | |
| 493 float p; | |
| 494 #endif | |
| 495 | |
| 496 /* This routine adapts the position of the half baud samples entering the equalizer. */ | |
| 497 | |
| 498 /* This symbol sync scheme is based on the technique first described by Dominique Godard in | |
| 499 Passband Timing Recovery in an All-Digital Modem Receiver | |
| 500 IEEE TRANSACTIONS ON COMMUNICATIONS, VOL. COM-26, NO. 5, MAY 1978 */ | |
| 501 | |
| 502 /* This is slightly rearranged for figure 3b of the Godard paper, as this saves a couple of | |
| 503 maths operations */ | |
| 504 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 505 /* TODO: The scalings used here need more thorough evaluation, to see if overflows are possible. */ | |
| 506 /* Cross correlate */ | |
| 507 v = (((s->symbol_sync_low[1] >> 5)*(s->symbol_sync_high[0] >> 4)) >> 15)*SYNC_LOW_BAND_EDGE_COEFF_2 | |
| 508 - (((s->symbol_sync_low[0] >> 5)*(s->symbol_sync_high[1] >> 4)) >> 15)*SYNC_HIGH_BAND_EDGE_COEFF_2 | |
| 509 + (((s->symbol_sync_low[1] >> 5)*(s->symbol_sync_high[1] >> 4)) >> 15)*SYNC_MIXED_EDGES_COEFF_3; | |
| 510 /* Filter away any DC component */ | |
| 511 p = v - s->symbol_sync_dc_filter[1]; | |
| 512 s->symbol_sync_dc_filter[1] = s->symbol_sync_dc_filter[0]; | |
| 513 s->symbol_sync_dc_filter[0] = v; | |
| 514 /* A little integration will now filter away much of the HF noise */ | |
| 515 s->baud_phase -= p; | |
| 516 if (abs(s->baud_phase) > 30*FP_FACTOR) | |
| 517 { | |
| 518 if (s->baud_phase > 0) | |
| 519 i = (s->baud_phase > 1000*FP_FACTOR) ? 5 : 1; | |
| 520 else | |
| 521 i = (s->baud_phase < -1000*FP_FACTOR) ? -5 : -1; | |
| 522 //printf("v = %10.5f %5d - %f %f %d %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction); | |
| 523 s->eq_put_step += i; | |
| 524 s->total_baud_timing_correction += i; | |
| 525 } | |
| 526 #else | |
| 527 /* Cross correlate */ | |
| 528 v = s->symbol_sync_low[1]*s->symbol_sync_high[0]*SYNC_LOW_BAND_EDGE_COEFF_2 | |
| 529 - s->symbol_sync_low[0]*s->symbol_sync_high[1]*SYNC_HIGH_BAND_EDGE_COEFF_2 | |
| 530 + s->symbol_sync_low[1]*s->symbol_sync_high[1]*SYNC_MIXED_EDGES_COEFF_3; | |
| 531 /* Filter away any DC component */ | |
| 532 p = v - s->symbol_sync_dc_filter[1]; | |
| 533 s->symbol_sync_dc_filter[1] = s->symbol_sync_dc_filter[0]; | |
| 534 s->symbol_sync_dc_filter[0] = v; | |
| 535 /* A little integration will now filter away much of the HF noise */ | |
| 536 s->baud_phase -= p; | |
| 537 if (fabsf(s->baud_phase) > 30.0f) | |
| 538 { | |
| 539 if (s->baud_phase > 0.0f) | |
| 540 i = (s->baud_phase > 1000.0f) ? 5 : 1; | |
| 541 else | |
| 542 i = (s->baud_phase < -1000.0f) ? -5 : -1; | |
| 543 //printf("v = %10.5f %5d - %f %f %d %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction); | |
| 544 s->eq_put_step += i; | |
| 545 s->total_baud_timing_correction += i; | |
| 546 } | |
| 547 #endif | |
| 548 } | |
| 549 /*- End of function --------------------------------------------------------*/ | |
| 550 | |
| 551 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 552 static void process_half_baud(v29_rx_state_t *s, complexi16_t *sample) | |
| 553 #else | |
| 554 static void process_half_baud(v29_rx_state_t *s, complexf_t *sample) | |
| 555 #endif | |
| 556 { | |
| 557 static const int cdcd_pos[6] = | |
| 558 { | |
| 559 0, 11, | |
| 560 0, 3, | |
| 561 0, 2 | |
| 562 }; | |
| 563 complexf_t zz; | |
| 564 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 565 complexf_t z1; | |
| 566 complexi16_t z; | |
| 567 const complexi16_t *target; | |
| 568 static const complexi16_t zero = {0, 0}; | |
| 569 #else | |
| 570 complexf_t z; | |
| 571 const complexf_t *target; | |
| 572 static const complexf_t zero = {0.0f, 0.0f}; | |
| 573 #endif | |
| 574 float p; | |
| 575 int bit; | |
| 576 int i; | |
| 577 int j; | |
| 578 int32_t angle; | |
| 579 int32_t ang; | |
| 580 | |
| 581 /* This routine processes every half a baud, as we put things into the equalizer at the T/2 rate. */ | |
| 582 | |
| 583 /* Add a sample to the equalizer's circular buffer, but don't calculate anything at this time. */ | |
| 584 s->eq_buf[s->eq_step] = *sample; | |
| 585 if (++s->eq_step >= V29_EQUALIZER_LEN) | |
| 586 s->eq_step = 0; | |
| 587 | |
| 588 /* On alternate insertions we have a whole baud, and must process it. */ | |
| 589 if ((s->baud_half ^= 1)) | |
| 590 return; | |
| 591 | |
| 592 /* Symbol timing synchronisation */ | |
| 593 symbol_sync(s); | |
| 594 | |
| 595 z = equalizer_get(s); | |
| 596 | |
| 597 switch (s->training_stage) | |
| 598 { | |
| 599 case TRAINING_STAGE_NORMAL_OPERATION: | |
| 600 /* Normal operation. */ | |
| 601 decode_baud(s, &z); | |
| 602 target = &v29_9600_constellation[s->constellation_state]; | |
| 603 break; | |
| 604 case TRAINING_STAGE_SYMBOL_ACQUISITION: | |
| 605 /* Allow time for symbol synchronisation to settle the symbol timing. */ | |
| 606 target = &zero; | |
| 607 if (++s->training_count >= 60) | |
| 608 { | |
| 609 /* Record the current phase angle */ | |
| 610 s->training_stage = TRAINING_STAGE_LOG_PHASE; | |
| 611 s->angles[0] = | |
| 612 s->start_angles[0] = arctan2(z.im, z.re); | |
| 613 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 614 if (s->agc_scaling_save == 0) | |
| 615 s->agc_scaling_save = s->agc_scaling; | |
| 616 #else | |
| 617 if (s->agc_scaling_save == 0.0f) | |
| 618 s->agc_scaling_save = s->agc_scaling; | |
| 619 #endif | |
| 620 } | |
| 621 break; | |
| 622 case TRAINING_STAGE_LOG_PHASE: | |
| 623 /* Record the current alternate phase angle */ | |
| 624 target = &zero; | |
| 625 s->angles[1] = | |
| 626 s->start_angles[1] = arctan2(z.im, z.re); | |
| 627 s->training_count = 1; | |
| 628 s->training_stage = TRAINING_STAGE_WAIT_FOR_CDCD; | |
| 629 break; | |
| 630 case TRAINING_STAGE_WAIT_FOR_CDCD: | |
| 631 target = &zero; | |
| 632 angle = arctan2(z.im, z.re); | |
| 633 /* Look for the initial ABAB sequence to display a phase reversal, which will | |
| 634 signal the start of the scrambled CDCD segment */ | |
| 635 ang = angle - s->angles[(s->training_count - 1) & 0xF]; | |
| 636 s->angles[(s->training_count + 1) & 0xF] = angle; | |
| 637 if ((ang > 0x20000000 || ang < -0x20000000) && s->training_count >= 13) | |
| 638 { | |
| 639 /* We seem to have a phase reversal */ | |
| 640 /* Slam the carrier frequency into line, based on the total phase drift over the last | |
| 641 section. Use the shift from the odd bits and the shift from the even bits to get | |
| 642 better jitter suppression. We need to scale here, or at the maximum specified | |
| 643 frequency deviation we could overflow, and get a silly answer. */ | |
| 644 /* Step back a few symbols so we don't get ISI distorting things. */ | |
| 645 i = (s->training_count - 8) & ~1; | |
| 646 /* Avoid the possibility of a divide by zero */ | |
| 647 if (i) | |
| 648 { | |
| 649 j = i & 0xF; | |
| 650 ang = (s->angles[j] - s->start_angles[0])/i | |
| 651 + (s->angles[j | 0x1] - s->start_angles[1])/i; | |
| 652 s->carrier_phase_rate += 3*(ang/20); | |
| 653 } | |
| 654 span_log(&s->logging, SPAN_LOG_FLOW, "Coarse carrier frequency %7.2f\n", dds_frequencyf(s->carrier_phase_rate)); | |
| 655 /* Check if the carrier frequency is plausible */ | |
| 656 if (s->carrier_phase_rate < dds_phase_ratef(CARRIER_NOMINAL_FREQ - 20.0f) | |
| 657 || | |
| 658 s->carrier_phase_rate > dds_phase_ratef(CARRIER_NOMINAL_FREQ + 20.0f)) | |
| 659 { | |
| 660 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n"); | |
| 661 /* Park this modem */ | |
| 662 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 663 s->agc_scaling_save = 0; | |
| 664 #else | |
| 665 s->agc_scaling_save = 0.0f; | |
| 666 #endif | |
| 667 s->training_stage = TRAINING_STAGE_PARKED; | |
| 668 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 669 break; | |
| 670 } | |
| 671 /* Make a step shift in the phase, to pull it into line. We need to rotate the equalizer | |
| 672 buffer, as well as the carrier phase, for this to play out nicely. */ | |
| 673 p = angle*2.0f*3.14159f/(65536.0f*65536.0f); | |
| 674 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 675 zz = complex_setf(cosf(p), -sinf(p)); | |
| 676 for (i = 0; i < V29_EQUALIZER_LEN; i++) | |
| 677 { | |
| 678 z1 = complex_setf(s->eq_buf[i].re, s->eq_buf[i].im); | |
| 679 z1 = complex_mulf(&z1, &zz); | |
| 680 s->eq_buf[i].re = z1.re; | |
| 681 s->eq_buf[i].im = z1.im; | |
| 682 } | |
| 683 #else | |
| 684 zz = complex_setf(cosf(p), -sinf(p)); | |
| 685 for (i = 0; i < V29_EQUALIZER_LEN; i++) | |
| 686 s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz); | |
| 687 #endif | |
| 688 s->carrier_phase += angle; | |
| 689 /* We have just seen the first bit of the scrambled sequence, so skip it. */ | |
| 690 bit = scrambled_training_bit(s); | |
| 691 s->constellation_state = cdcd_pos[s->training_cd + bit]; | |
| 692 target = &v29_9600_constellation[s->constellation_state]; | |
| 693 s->training_count = 1; | |
| 694 s->training_stage = TRAINING_STAGE_TRAIN_ON_CDCD; | |
| 695 report_status_change(s, SIG_STATUS_TRAINING_IN_PROGRESS); | |
| 696 break; | |
| 697 } | |
| 698 if (++s->training_count > V29_TRAINING_SEG_2_LEN) | |
| 699 { | |
| 700 /* This is bogus. There are not this many bauds in this section | |
| 701 of a real training sequence. */ | |
| 702 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n"); | |
| 703 /* Park this modem */ | |
| 704 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 705 s->agc_scaling_save = 0; | |
| 706 #else | |
| 707 s->agc_scaling_save = 0.0f; | |
| 708 #endif | |
| 709 s->training_stage = TRAINING_STAGE_PARKED; | |
| 710 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 711 } | |
| 712 break; | |
| 713 case TRAINING_STAGE_TRAIN_ON_CDCD: | |
| 714 /* Train on the scrambled CDCD section. */ | |
| 715 bit = scrambled_training_bit(s); | |
| 716 //span_log(&s->logging, SPAN_LOG_FLOW, "%5d %15.5f, %15.5f %15.5f, %15.5f\n", s->training_count, z.re, z.im, v29_9600_constellation[cdcd_pos[s->training_cd + bit]].re, v29_9600_constellation[cdcd_pos[s->training_cd + bit]].im); | |
| 717 s->constellation_state = cdcd_pos[s->training_cd + bit]; | |
| 718 target = &v29_9600_constellation[s->constellation_state]; | |
| 719 track_carrier(s, &z, target); | |
| 720 tune_equalizer(s, &z, target); | |
| 721 if (++s->training_count >= V29_TRAINING_SEG_3_LEN - 48) | |
| 722 { | |
| 723 s->training_stage = TRAINING_STAGE_TRAIN_ON_CDCD_AND_TEST; | |
| 724 s->training_error = 0.0f; | |
| 725 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 726 s->carrier_track_i = 200; | |
| 727 s->carrier_track_p = 1000000; | |
| 728 #else | |
| 729 s->carrier_track_i = 200.0f; | |
| 730 s->carrier_track_p = 1000000.0f; | |
| 731 #endif | |
| 732 } | |
| 733 break; | |
| 734 case TRAINING_STAGE_TRAIN_ON_CDCD_AND_TEST: | |
| 735 /* Continue training on the scrambled CDCD section, but measure the quality of training too. */ | |
| 736 bit = scrambled_training_bit(s); | |
| 737 //span_log(&s->logging, SPAN_LOG_FLOW, "%5d %15.5f, %15.5f %15.5f, %15.5f\n", s->training_count, z.re, z.im, v29_9600_constellation[cdcd_pos[s->training_cd + bit]].re, v29_9600_constellation[cdcd_pos[s->training_cd + bit]].im); | |
| 738 s->constellation_state = cdcd_pos[s->training_cd + bit]; | |
| 739 target = &v29_9600_constellation[s->constellation_state]; | |
| 740 track_carrier(s, &z, target); | |
| 741 tune_equalizer(s, &z, target); | |
| 742 /* Measure the training error */ | |
| 743 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 744 z1.re = z.re/(float) FP_FACTOR; | |
| 745 z1.im = z.im/(float) FP_FACTOR; | |
| 746 zz.re = target->re; | |
| 747 zz.im = target->im; | |
| 748 zz = complex_subf(&z1, &zz); | |
| 749 s->training_error += powerf(&zz); | |
| 750 #else | |
| 751 zz = complex_subf(&z, target); | |
| 752 s->training_error += powerf(&zz); | |
| 753 #endif | |
| 754 if (++s->training_count >= V29_TRAINING_SEG_3_LEN) | |
| 755 { | |
| 756 span_log(&s->logging, SPAN_LOG_FLOW, "Constellation mismatch %f\n", s->training_error); | |
| 757 if (s->training_error < 48.0f*2.0f) | |
| 758 { | |
| 759 s->training_count = 0; | |
| 760 s->training_error = 0.0f; | |
| 761 s->constellation_state = 0; | |
| 762 s->training_stage = TRAINING_STAGE_TEST_ONES; | |
| 763 } | |
| 764 else | |
| 765 { | |
| 766 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (convergence failed)\n"); | |
| 767 /* Park this modem */ | |
| 768 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 769 s->agc_scaling_save = 0; | |
| 770 #else | |
| 771 s->agc_scaling_save = 0.0f; | |
| 772 #endif | |
| 773 s->training_stage = TRAINING_STAGE_PARKED; | |
| 774 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 775 } | |
| 776 } | |
| 777 break; | |
| 778 case TRAINING_STAGE_TEST_ONES: | |
| 779 /* We are in the test phase, where we check that we can receive reliably. | |
| 780 We should get a run of 1's, 48 symbols (192 bits at 9600bps) long. */ | |
| 781 //span_log(&s->logging, SPAN_LOG_FLOW, "%5d %15.5f, %15.5f\n", s->training_count, z.re, z.im); | |
| 782 decode_baud(s, &z); | |
| 783 target = &v29_9600_constellation[s->constellation_state]; | |
| 784 /* Measure the training error */ | |
| 785 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 786 z1.re = z.re/(float) FP_FACTOR; | |
| 787 z1.im = z.im/(float) FP_FACTOR; | |
| 788 zz.re = target->re; | |
| 789 zz.im = target->im; | |
| 790 zz = complex_subf(&z1, &zz); | |
| 791 s->training_error += powerf(&zz); | |
| 792 #else | |
| 793 zz = complex_subf(&z, target); | |
| 794 s->training_error += powerf(&zz); | |
| 795 #endif | |
| 796 if (++s->training_count >= V29_TRAINING_SEG_4_LEN) | |
| 797 { | |
| 798 if (s->training_error < 48.0f) | |
| 799 { | |
| 800 /* We are up and running */ | |
| 801 span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded at %dbps (constellation mismatch %f)\n", s->bit_rate, s->training_error); | |
| 802 report_status_change(s, SIG_STATUS_TRAINING_SUCCEEDED); | |
| 803 /* Apply some lag to the carrier off condition, to ensure the last few bits get pushed through | |
| 804 the processing. */ | |
| 805 s->signal_present = 60; | |
| 806 s->training_stage = TRAINING_STAGE_NORMAL_OPERATION; | |
| 807 equalizer_save(s); | |
| 808 s->carrier_phase_rate_save = s->carrier_phase_rate; | |
| 809 s->agc_scaling_save = s->agc_scaling; | |
| 810 } | |
| 811 else | |
| 812 { | |
| 813 /* Training has failed */ | |
| 814 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error); | |
| 815 /* Park this modem */ | |
| 816 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 817 s->agc_scaling_save = 0; | |
| 818 #else | |
| 819 s->agc_scaling_save = 0.0f; | |
| 820 #endif | |
| 821 s->training_stage = TRAINING_STAGE_PARKED; | |
| 822 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 823 } | |
| 824 } | |
| 825 break; | |
| 826 case TRAINING_STAGE_PARKED: | |
| 827 default: | |
| 828 /* We failed to train! */ | |
| 829 /* Park here until the carrier drops. */ | |
| 830 target = &zero; | |
| 831 break; | |
| 832 } | |
| 833 if (s->qam_report) | |
| 834 { | |
| 835 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 836 z1.re = z.re/(float) FP_FACTOR; | |
| 837 z1.im = z.im/(float) FP_FACTOR; | |
| 838 zz.re = target->re; | |
| 839 zz.im = target->im; | |
| 840 s->qam_report(s->qam_user_data, &z1, &zz, s->constellation_state); | |
| 841 #else | |
| 842 s->qam_report(s->qam_user_data, &z, target, s->constellation_state); | |
| 843 #endif | |
| 844 } | |
| 845 } | |
| 846 /*- End of function --------------------------------------------------------*/ | |
| 847 | |
| 848 static __inline__ int signal_detect(v29_rx_state_t *s, int16_t amp) | |
| 849 { | |
| 850 int16_t diff; | |
| 851 int16_t x; | |
| 852 int32_t power; | |
| 853 | |
| 854 /* There should be no DC in the signal, but sometimes there is. | |
| 855 We need to measure the power with the DC blocked, but not using | |
| 856 a slow to respond DC blocker. Use the most elementary HPF. */ | |
| 857 x = amp >> 1; | |
| 858 /* There could be overflow here, but it isn't a problem in practice */ | |
| 859 diff = x - s->last_sample; | |
| 860 s->last_sample = x; | |
| 861 power = power_meter_update(&(s->power), diff); | |
| 862 #if defined(IAXMODEM_STUFF) | |
| 863 /* Quick power drop fudge */ | |
| 864 diff = abs(diff); | |
| 865 if (10*diff < s->high_sample) | |
| 866 { | |
| 867 if (++s->low_samples > 120) | |
| 868 { | |
| 869 power_meter_init(&(s->power), 4); | |
| 870 s->high_sample = 0; | |
| 871 s->low_samples = 0; | |
| 872 } | |
| 873 } | |
| 874 else | |
| 875 { | |
| 876 s->low_samples = 0; | |
| 877 if (diff > s->high_sample) | |
| 878 s->high_sample = diff; | |
| 879 } | |
| 880 #endif | |
| 881 if (s->signal_present > 0) | |
| 882 { | |
| 883 /* Look for power below turn-off threshold to turn the carrier off */ | |
| 884 #if defined(IAXMODEM_STUFF) | |
| 885 if (s->carrier_drop_pending || power < s->carrier_off_power) | |
| 886 #else | |
| 887 if (power < s->carrier_off_power) | |
| 888 #endif | |
| 889 { | |
| 890 if (--s->signal_present <= 0) | |
| 891 { | |
| 892 /* Count down a short delay, to ensure we push the last | |
| 893 few bits through the filters before stopping. */ | |
| 894 v29_rx_restart(s, s->bit_rate, FALSE); | |
| 895 report_status_change(s, SIG_STATUS_CARRIER_DOWN); | |
| 896 return 0; | |
| 897 } | |
| 898 #if defined(IAXMODEM_STUFF) | |
| 899 /* Carrier has dropped, but the put_bit is | |
| 900 pending the signal_present delay. */ | |
| 901 s->carrier_drop_pending = TRUE; | |
| 902 #endif | |
| 903 } | |
| 904 } | |
| 905 else | |
| 906 { | |
| 907 /* Look for power exceeding turn-on threshold to turn the carrier on */ | |
| 908 if (power < s->carrier_on_power) | |
| 909 return 0; | |
| 910 s->signal_present = 1; | |
| 911 #if defined(IAXMODEM_STUFF) | |
| 912 s->carrier_drop_pending = FALSE; | |
| 913 #endif | |
| 914 report_status_change(s, SIG_STATUS_CARRIER_UP); | |
| 915 } | |
| 916 return power; | |
| 917 } | |
| 918 /*- End of function --------------------------------------------------------*/ | |
| 919 | |
| 920 SPAN_DECLARE_NONSTD(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len) | |
| 921 { | |
| 922 int i; | |
| 923 int step; | |
| 924 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 925 complexi16_t z; | |
| 926 complexi16_t zz; | |
| 927 complexi16_t sample; | |
| 928 int32_t v; | |
| 929 #else | |
| 930 complexf_t z; | |
| 931 complexf_t zz; | |
| 932 complexf_t sample; | |
| 933 float v; | |
| 934 #endif | |
| 935 int32_t power; | |
| 936 | |
| 937 for (i = 0; i < len; i++) | |
| 938 { | |
| 939 s->rrc_filter[s->rrc_filter_step] = amp[i]; | |
| 940 if (++s->rrc_filter_step >= V29_RX_FILTER_STEPS) | |
| 941 s->rrc_filter_step = 0; | |
| 942 | |
| 943 if ((power = signal_detect(s, amp[i])) == 0) | |
| 944 continue; | |
| 945 if (s->training_stage == TRAINING_STAGE_PARKED) | |
| 946 continue; | |
| 947 /* Only spend effort processing this data if the modem is not | |
| 948 parked, after training failure. */ | |
| 949 s->eq_put_step -= RX_PULSESHAPER_COEFF_SETS; | |
| 950 step = -s->eq_put_step; | |
| 951 if (step > RX_PULSESHAPER_COEFF_SETS - 1) | |
| 952 step = RX_PULSESHAPER_COEFF_SETS - 1; | |
| 953 if (step < 0) | |
| 954 step += RX_PULSESHAPER_COEFF_SETS; | |
| 955 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 956 v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_re[step], V29_RX_FILTER_STEPS, s->rrc_filter_step); | |
| 957 sample.re = (v*s->agc_scaling) >> 15; | |
| 958 #else | |
| 959 v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_re[step], V29_RX_FILTER_STEPS, s->rrc_filter_step); | |
| 960 sample.re = v*s->agc_scaling; | |
| 961 #endif | |
| 962 | |
| 963 /* Symbol timing synchronisation band edge filters */ | |
| 964 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 965 /* Low Nyquist band edge filter */ | |
| 966 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; | |
| 967 s->symbol_sync_low[1] = s->symbol_sync_low[0]; | |
| 968 s->symbol_sync_low[0] = v; | |
| 969 /* High Nyquist band edge filter */ | |
| 970 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; | |
| 971 s->symbol_sync_high[1] = s->symbol_sync_high[0]; | |
| 972 s->symbol_sync_high[0] = v; | |
| 973 #else | |
| 974 /* Low Nyquist band edge filter */ | |
| 975 v = s->symbol_sync_low[0]*SYNC_LOW_BAND_EDGE_COEFF_0 + s->symbol_sync_low[1]*SYNC_LOW_BAND_EDGE_COEFF_1 + sample.re; | |
| 976 s->symbol_sync_low[1] = s->symbol_sync_low[0]; | |
| 977 s->symbol_sync_low[0] = v; | |
| 978 /* High Nyquist band edge filter */ | |
| 979 v = s->symbol_sync_high[0]*SYNC_HIGH_BAND_EDGE_COEFF_0 + s->symbol_sync_high[1]*SYNC_HIGH_BAND_EDGE_COEFF_1 + sample.re; | |
| 980 s->symbol_sync_high[1] = s->symbol_sync_high[0]; | |
| 981 s->symbol_sync_high[0] = v; | |
| 982 #endif | |
| 983 /* Put things into the equalization buffer at T/2 rate. The symbol synchronisation | |
| 984 will fiddle the step to align this with the symbols. */ | |
| 985 if (s->eq_put_step <= 0) | |
| 986 { | |
| 987 /* Only AGC until we have locked down the setting. */ | |
| 988 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 989 if (s->agc_scaling_save == 0) | |
| 990 s->agc_scaling = (float) FP_FACTOR*32768.0f*(1.0f/RX_PULSESHAPER_GAIN)*5.0f*0.25f/sqrtf(power); | |
| 991 #else | |
| 992 if (s->agc_scaling_save == 0.0f) | |
| 993 s->agc_scaling = (1.0f/RX_PULSESHAPER_GAIN)*5.0f*0.25f/sqrtf(power); | |
| 994 #endif | |
| 995 /* Pulse shape while still at the carrier frequency, using a quadrature | |
| 996 pair of filters. This results in a properly bandpass filtered complex | |
| 997 signal, which can be brought directly to baseband by complex mixing. | |
| 998 No further filtering, to remove mixer harmonics, is needed. */ | |
| 999 step = -s->eq_put_step; | |
| 1000 if (step > RX_PULSESHAPER_COEFF_SETS - 1) | |
| 1001 step = RX_PULSESHAPER_COEFF_SETS - 1; | |
| 1002 s->eq_put_step += RX_PULSESHAPER_COEFF_SETS*10/(3*2); | |
| 1003 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1004 v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_im[step], V29_RX_FILTER_STEPS, s->rrc_filter_step); | |
| 1005 sample.im = (v*s->agc_scaling) >> 15; | |
| 1006 z = dds_lookup_complexi16(s->carrier_phase); | |
| 1007 zz.re = ((int32_t) sample.re*(int32_t) z.re - (int32_t) sample.im*(int32_t) z.im) >> 15; | |
| 1008 zz.im = ((int32_t) -sample.re*(int32_t) z.im - (int32_t) sample.im*(int32_t) z.re) >> 15; | |
| 1009 #else | |
| 1010 v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_im[step], V29_RX_FILTER_STEPS, s->rrc_filter_step); | |
| 1011 sample.im = v*s->agc_scaling; | |
| 1012 z = dds_lookup_complexf(s->carrier_phase); | |
| 1013 zz.re = sample.re*z.re - sample.im*z.im; | |
| 1014 zz.im = -sample.re*z.im - sample.im*z.re; | |
| 1015 #endif | |
| 1016 process_half_baud(s, &zz); | |
| 1017 } | |
| 1018 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1019 dds_advance(&s->carrier_phase, s->carrier_phase_rate); | |
| 1020 #else | |
| 1021 dds_advancef(&s->carrier_phase, s->carrier_phase_rate); | |
| 1022 #endif | |
| 1023 } | |
| 1024 return 0; | |
| 1025 } | |
| 1026 /*- End of function --------------------------------------------------------*/ | |
| 1027 | |
| 1028 SPAN_DECLARE(int) v29_rx_fillin(v29_rx_state_t *s, int len) | |
| 1029 { | |
| 1030 int i; | |
| 1031 | |
| 1032 /* We want to sustain the current state (i.e carrier on<->carrier off), and | |
| 1033 try to sustain the carrier phase. We should probably push the filters, as well */ | |
| 1034 span_log(&s->logging, SPAN_LOG_FLOW, "Fill-in %d samples\n", len); | |
| 1035 if (s->signal_present <= 0) | |
| 1036 return 0; | |
| 1037 if (s->training_stage == TRAINING_STAGE_PARKED) | |
| 1038 return 0; | |
| 1039 for (i = 0; i < len; i++) | |
| 1040 { | |
| 1041 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1042 dds_advance(&s->carrier_phase, s->carrier_phase_rate); | |
| 1043 #else | |
| 1044 dds_advancef(&s->carrier_phase, s->carrier_phase_rate); | |
| 1045 #endif | |
| 1046 /* Advance the symbol phase the appropriate amount */ | |
| 1047 s->eq_put_step -= RX_PULSESHAPER_COEFF_SETS; | |
| 1048 if (s->eq_put_step <= 0) | |
| 1049 s->eq_put_step += RX_PULSESHAPER_COEFF_SETS*10/(3*2); | |
| 1050 /* TODO: Should we rotate any buffers */ | |
| 1051 } | |
| 1052 return 0; | |
| 1053 } | |
| 1054 /*- End of function --------------------------------------------------------*/ | |
| 1055 | |
| 1056 SPAN_DECLARE(void) v29_rx_set_put_bit(v29_rx_state_t *s, put_bit_func_t put_bit, void *user_data) | |
| 1057 { | |
| 1058 s->put_bit = put_bit; | |
| 1059 s->put_bit_user_data = user_data; | |
| 1060 } | |
| 1061 /*- End of function --------------------------------------------------------*/ | |
| 1062 | |
| 1063 SPAN_DECLARE(void) v29_rx_set_modem_status_handler(v29_rx_state_t *s, modem_tx_status_func_t handler, void *user_data) | |
| 1064 { | |
| 1065 s->status_handler = handler; | |
| 1066 s->status_user_data = user_data; | |
| 1067 } | |
| 1068 /*- End of function --------------------------------------------------------*/ | |
| 1069 | |
| 1070 SPAN_DECLARE(logging_state_t *) v29_rx_get_logging_state(v29_rx_state_t *s) | |
| 1071 { | |
| 1072 return &s->logging; | |
| 1073 } | |
| 1074 /*- End of function --------------------------------------------------------*/ | |
| 1075 | |
| 1076 SPAN_DECLARE(int) v29_rx_restart(v29_rx_state_t *s, int bit_rate, int old_train) | |
| 1077 { | |
| 1078 int i; | |
| 1079 | |
| 1080 switch (bit_rate) | |
| 1081 { | |
| 1082 case 9600: | |
| 1083 s->training_cd = 0; | |
| 1084 break; | |
| 1085 case 7200: | |
| 1086 s->training_cd = 2; | |
| 1087 break; | |
| 1088 case 4800: | |
| 1089 s->training_cd = 4; | |
| 1090 break; | |
| 1091 default: | |
| 1092 return -1; | |
| 1093 } | |
| 1094 s->bit_rate = bit_rate; | |
| 1095 | |
| 1096 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1097 vec_zeroi16(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
| 1098 #else | |
| 1099 vec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
| 1100 #endif | |
| 1101 s->rrc_filter_step = 0; | |
| 1102 | |
| 1103 s->scramble_reg = 0; | |
| 1104 s->training_scramble_reg = 0x2A; | |
| 1105 s->training_stage = TRAINING_STAGE_SYMBOL_ACQUISITION; | |
| 1106 s->training_count = 0; | |
| 1107 s->signal_present = 0; | |
| 1108 #if defined(IAXMODEM_STUFF) | |
| 1109 s->high_sample = 0; | |
| 1110 s->low_samples = 0; | |
| 1111 s->carrier_drop_pending = FALSE; | |
| 1112 #endif | |
| 1113 s->old_train = old_train; | |
| 1114 | |
| 1115 s->carrier_phase = 0; | |
| 1116 | |
| 1117 power_meter_init(&(s->power), 4); | |
| 1118 | |
| 1119 s->constellation_state = 0; | |
| 1120 | |
| 1121 if (s->old_train) | |
| 1122 { | |
| 1123 s->carrier_phase_rate = s->carrier_phase_rate_save; | |
| 1124 s->agc_scaling = s->agc_scaling_save; | |
| 1125 equalizer_restore(s); | |
| 1126 } | |
| 1127 else | |
| 1128 { | |
| 1129 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ); | |
| 1130 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1131 s->agc_scaling_save = 0; | |
| 1132 s->agc_scaling = (float) FP_FACTOR*32768.0f*0.0017f/RX_PULSESHAPER_GAIN; | |
| 1133 #else | |
| 1134 s->agc_scaling_save = 0.0f; | |
| 1135 s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN; | |
| 1136 #endif | |
| 1137 equalizer_reset(s); | |
| 1138 } | |
| 1139 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1140 s->carrier_track_i = 8000; | |
| 1141 s->carrier_track_p = 8000000; | |
| 1142 #else | |
| 1143 s->carrier_track_i = 8000.0f; | |
| 1144 s->carrier_track_p = 8000000.0f; | |
| 1145 #endif | |
| 1146 s->last_sample = 0; | |
| 1147 s->eq_skip = 0; | |
| 1148 | |
| 1149 /* Initialise the working data for symbol timing synchronisation */ | |
| 1150 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1151 for (i = 0; i < 2; i++) | |
| 1152 { | |
| 1153 s->symbol_sync_low[i] = 0; | |
| 1154 s->symbol_sync_high[i] = 0; | |
| 1155 s->symbol_sync_dc_filter[i] = 0; | |
| 1156 } | |
| 1157 s->baud_phase = 0; | |
| 1158 #else | |
| 1159 for (i = 0; i < 2; i++) | |
| 1160 { | |
| 1161 s->symbol_sync_low[i] = 0.0f; | |
| 1162 s->symbol_sync_high[i] = 0.0f; | |
| 1163 s->symbol_sync_dc_filter[i] = 0.0f; | |
| 1164 } | |
| 1165 s->baud_phase = 0.0f; | |
| 1166 #endif | |
| 1167 s->baud_half = 0; | |
| 1168 | |
| 1169 s->total_baud_timing_correction = 0; | |
| 1170 | |
| 1171 return 0; | |
| 1172 } | |
| 1173 /*- End of function --------------------------------------------------------*/ | |
| 1174 | |
| 1175 SPAN_DECLARE(v29_rx_state_t *) v29_rx_init(v29_rx_state_t *s, int bit_rate, put_bit_func_t put_bit, void *user_data) | |
| 1176 { | |
| 1177 switch (bit_rate) | |
| 1178 { | |
| 1179 case 9600: | |
| 1180 case 7200: | |
| 1181 case 4800: | |
| 1182 break; | |
| 1183 default: | |
| 1184 return NULL; | |
| 1185 } | |
| 1186 if (s == NULL) | |
| 1187 { | |
| 1188 if ((s = (v29_rx_state_t *) malloc(sizeof(*s))) == NULL) | |
| 1189 return NULL; | |
| 1190 } | |
| 1191 memset(s, 0, sizeof(*s)); | |
| 1192 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
| 1193 span_log_set_protocol(&s->logging, "V.29 RX"); | |
| 1194 s->put_bit = put_bit; | |
| 1195 s->put_bit_user_data = user_data; | |
| 1196 /* The V.29 spec says the thresholds should be -31dBm and -26dBm, but that makes little | |
| 1197 sense. V.17 uses -48dBm and -43dBm, and there seems no good reason to cut off at a | |
| 1198 higher level (though at 9600bps and 7200bps, TCM should put V.17 sensitivity several | |
| 1199 dB ahead of V.29). */ | |
| 1200 /* The thresholds should be on at -26dBm0 and off at -31dBm0 */ | |
| 1201 v29_rx_signal_cutoff(s, -28.5f); | |
| 1202 | |
| 1203 v29_rx_restart(s, bit_rate, FALSE); | |
| 1204 return s; | |
| 1205 } | |
| 1206 /*- End of function --------------------------------------------------------*/ | |
| 1207 | |
| 1208 SPAN_DECLARE(int) v29_rx_release(v29_rx_state_t *s) | |
| 1209 { | |
| 1210 return 0; | |
| 1211 } | |
| 1212 /*- End of function --------------------------------------------------------*/ | |
| 1213 | |
| 1214 SPAN_DECLARE(int) v29_rx_free(v29_rx_state_t *s) | |
| 1215 { | |
| 1216 free(s); | |
| 1217 return 0; | |
| 1218 } | |
| 1219 /*- End of function --------------------------------------------------------*/ | |
| 1220 | |
| 1221 SPAN_DECLARE(void) v29_rx_set_qam_report_handler(v29_rx_state_t *s, qam_report_handler_t handler, void *user_data) | |
| 1222 { | |
| 1223 s->qam_report = handler; | |
| 1224 s->qam_user_data = user_data; | |
| 1225 } | |
| 1226 /*- End of function --------------------------------------------------------*/ | |
| 1227 /*- End of file ------------------------------------------------------------*/ |
