Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v17rx.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 * v17rx.c - ITU V.17 modem receive part | |
| 6 * | |
| 7 * Written by Steve Underwood <steveu@coppice.org> | |
| 8 * | |
| 9 * Copyright (C) 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: v17rx.c,v 1.153.4.6 2009/12/28 12:20:46 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 #include "spandsp/v17tx.h" | |
| 62 #include "spandsp/v17rx.h" | |
| 63 | |
| 64 #include "spandsp/private/logging.h" | |
| 65 #include "spandsp/private/v17rx.h" | |
| 66 | |
| 67 #include "v17_v32bis_tx_constellation_maps.h" | |
| 68 #include "v17_v32bis_rx_constellation_maps.h" | |
| 69 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 70 #include "v17_v32bis_rx_fixed_rrc.h" | |
| 71 #else | |
| 72 #include "v17_v32bis_rx_floating_rrc.h" | |
| 73 #endif | |
| 74 | |
| 75 /*! The nominal frequency of the carrier, in Hertz */ | |
| 76 #define CARRIER_NOMINAL_FREQ 1800.0f | |
| 77 /*! The nominal baud or symbol rate */ | |
| 78 #define BAUD_RATE 2400 | |
| 79 /*! The adaption rate coefficient for the equalizer during initial training */ | |
| 80 #define EQUALIZER_DELTA 0.21f | |
| 81 /*! The adaption rate coefficient for the equalizer during continuous fine tuning */ | |
| 82 #define EQUALIZER_SLOW_ADAPT_RATIO 0.1f | |
| 83 | |
| 84 /* Segments of the training sequence */ | |
| 85 /*! The length of training segment 1, in symbols */ | |
| 86 #define V17_TRAINING_SEG_1_LEN 256 | |
| 87 /*! The length of training segment 2 in long training mode, in symbols */ | |
| 88 #define V17_TRAINING_SEG_2_LEN 2976 | |
| 89 /*! The length of training segment 2 in short training mode, in symbols */ | |
| 90 #define V17_TRAINING_SHORT_SEG_2_LEN 38 | |
| 91 /*! The length of training segment 3, in symbols */ | |
| 92 #define V17_TRAINING_SEG_3_LEN 64 | |
| 93 /*! The length of training segment 4A, in symbols */ | |
| 94 #define V17_TRAINING_SEG_4A_LEN 15 | |
| 95 /*! The length of training segment 4, in symbols */ | |
| 96 #define V17_TRAINING_SEG_4_LEN 48 | |
| 97 | |
| 98 /*! The 16 bit pattern used in the bridge section of the training sequence */ | |
| 99 #define V17_BRIDGE_WORD 0x8880 | |
| 100 | |
| 101 /*! The length of the equalizer buffer */ | |
| 102 #define V17_EQUALIZER_LEN (V17_EQUALIZER_PRE_LEN + 1 + V17_EQUALIZER_POST_LEN) | |
| 103 | |
| 104 enum | |
| 105 { | |
| 106 TRAINING_STAGE_NORMAL_OPERATION = 0, | |
| 107 TRAINING_STAGE_SYMBOL_ACQUISITION, | |
| 108 TRAINING_STAGE_LOG_PHASE, | |
| 109 TRAINING_STAGE_SHORT_WAIT_FOR_CDBA, | |
| 110 TRAINING_STAGE_WAIT_FOR_CDBA, | |
| 111 TRAINING_STAGE_COARSE_TRAIN_ON_CDBA, | |
| 112 TRAINING_STAGE_FINE_TRAIN_ON_CDBA, | |
| 113 TRAINING_STAGE_SHORT_TRAIN_ON_CDBA_AND_TEST, | |
| 114 TRAINING_STAGE_TRAIN_ON_CDBA_AND_TEST, | |
| 115 TRAINING_STAGE_BRIDGE, | |
| 116 TRAINING_STAGE_TCM_WINDUP, | |
| 117 TRAINING_STAGE_TEST_ONES, | |
| 118 TRAINING_STAGE_PARKED | |
| 119 }; | |
| 120 | |
| 121 /* Coefficients for the band edge symbol timing synchroniser (alpha = 0.99) */ | |
| 122 /* low_edge = 2.0f*M_PI*(CARRIER_NOMINAL_FREQ - BAUD_RATE/2.0f)/SAMPLE_RATE; */ | |
| 123 /* high_edge = 2.0f*M_PI*(CARRIER_NOMINAL_FREQ + BAUD_RATE/2.0f)/SAMPLE_RATE; */ | |
| 124 #define SIN_LOW_BAND_EDGE 0.453990499f | |
| 125 #define COS_LOW_BAND_EDGE 0.891006542f | |
| 126 #define SIN_HIGH_BAND_EDGE 0.707106781f | |
| 127 #define COS_HIGH_BAND_EDGE -0.707106781f | |
| 128 #define ALPHA 0.99f | |
| 129 | |
| 130 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 131 #define SYNC_LOW_BAND_EDGE_COEFF_0 ((int)(FP_FACTOR*(2.0f*ALPHA*COS_LOW_BAND_EDGE))) | |
| 132 #define SYNC_LOW_BAND_EDGE_COEFF_1 ((int)(FP_FACTOR*(-ALPHA*ALPHA))) | |
| 133 #define SYNC_LOW_BAND_EDGE_COEFF_2 ((int)(FP_FACTOR*(-ALPHA*SIN_LOW_BAND_EDGE))) | |
| 134 #define SYNC_HIGH_BAND_EDGE_COEFF_0 ((int)(FP_FACTOR*(2.0f*ALPHA*COS_HIGH_BAND_EDGE))) | |
| 135 #define SYNC_HIGH_BAND_EDGE_COEFF_1 ((int)(FP_FACTOR*(-ALPHA*ALPHA))) | |
| 136 #define SYNC_HIGH_BAND_EDGE_COEFF_2 ((int)(FP_FACTOR*(-ALPHA*SIN_HIGH_BAND_EDGE))) | |
| 137 #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)))) | |
| 138 #else | |
| 139 #define SYNC_LOW_BAND_EDGE_COEFF_0 (2.0f*ALPHA*COS_LOW_BAND_EDGE) | |
| 140 #define SYNC_LOW_BAND_EDGE_COEFF_1 (-ALPHA*ALPHA) | |
| 141 #define SYNC_LOW_BAND_EDGE_COEFF_2 (-ALPHA*SIN_LOW_BAND_EDGE) | |
| 142 #define SYNC_HIGH_BAND_EDGE_COEFF_0 (2.0f*ALPHA*COS_HIGH_BAND_EDGE) | |
| 143 #define SYNC_HIGH_BAND_EDGE_COEFF_1 (-ALPHA*ALPHA) | |
| 144 #define SYNC_HIGH_BAND_EDGE_COEFF_2 (-ALPHA*SIN_HIGH_BAND_EDGE) | |
| 145 #define SYNC_MIXED_EDGES_COEFF_3 (-ALPHA*ALPHA*(SIN_HIGH_BAND_EDGE*COS_LOW_BAND_EDGE - SIN_LOW_BAND_EDGE*COS_HIGH_BAND_EDGE)) | |
| 146 #endif | |
| 147 | |
| 148 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 149 static const int constellation_spacing[4] = | |
| 150 { | |
| 151 ((int)(FP_FACTOR*1.414f), | |
| 152 ((int)(FP_FACTOR*2.0f)}, | |
| 153 ((int)(FP_FACTOR*2.828f)}, | |
| 154 ((int)(FP_FACTOR*4.0f)}, | |
| 155 }; | |
| 156 #else | |
| 157 static const float constellation_spacing[4] = | |
| 158 { | |
| 159 1.414f, | |
| 160 2.0f, | |
| 161 2.828f, | |
| 162 4.0f | |
| 163 }; | |
| 164 #endif | |
| 165 | |
| 166 SPAN_DECLARE(float) v17_rx_carrier_frequency(v17_rx_state_t *s) | |
| 167 { | |
| 168 return dds_frequencyf(s->carrier_phase_rate); | |
| 169 } | |
| 170 /*- End of function --------------------------------------------------------*/ | |
| 171 | |
| 172 SPAN_DECLARE(float) v17_rx_symbol_timing_correction(v17_rx_state_t *s) | |
| 173 { | |
| 174 return (float) s->total_baud_timing_correction/((float) RX_PULSESHAPER_COEFF_SETS*10.0f/3.0f); | |
| 175 } | |
| 176 /*- End of function --------------------------------------------------------*/ | |
| 177 | |
| 178 SPAN_DECLARE(float) v17_rx_signal_power(v17_rx_state_t *s) | |
| 179 { | |
| 180 return power_meter_current_dbm0(&s->power) + 3.98f; | |
| 181 } | |
| 182 /*- End of function --------------------------------------------------------*/ | |
| 183 | |
| 184 SPAN_DECLARE(void) v17_rx_signal_cutoff(v17_rx_state_t *s, float cutoff) | |
| 185 { | |
| 186 /* The 0.4 factor allows for the gain of the DC blocker */ | |
| 187 s->carrier_on_power = (int32_t) (power_meter_level_dbm0(cutoff + 2.5f)*0.4f); | |
| 188 s->carrier_off_power = (int32_t) (power_meter_level_dbm0(cutoff - 2.5f)*0.4f); | |
| 189 } | |
| 190 /*- End of function --------------------------------------------------------*/ | |
| 191 | |
| 192 static void report_status_change(v17_rx_state_t *s, int status) | |
| 193 { | |
| 194 if (s->status_handler) | |
| 195 s->status_handler(s->status_user_data, status); | |
| 196 else if (s->put_bit) | |
| 197 s->put_bit(s->put_bit_user_data, status); | |
| 198 } | |
| 199 /*- End of function --------------------------------------------------------*/ | |
| 200 | |
| 201 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 202 SPAN_DECLARE(int) v17_rx_equalizer_state(v17_rx_state_t *s, complexi16_t **coeffs) | |
| 203 #else | |
| 204 SPAN_DECLARE(int) v17_rx_equalizer_state(v17_rx_state_t *s, complexf_t **coeffs) | |
| 205 #endif | |
| 206 { | |
| 207 *coeffs = s->eq_coeff; | |
| 208 return V17_EQUALIZER_LEN; | |
| 209 } | |
| 210 /*- End of function --------------------------------------------------------*/ | |
| 211 | |
| 212 static void equalizer_save(v17_rx_state_t *s) | |
| 213 { | |
| 214 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 215 cvec_copyi16(s->eq_coeff_save, s->eq_coeff, V17_EQUALIZER_LEN); | |
| 216 #else | |
| 217 cvec_copyf(s->eq_coeff_save, s->eq_coeff, V17_EQUALIZER_LEN); | |
| 218 #endif | |
| 219 } | |
| 220 /*- End of function --------------------------------------------------------*/ | |
| 221 | |
| 222 static void equalizer_restore(v17_rx_state_t *s) | |
| 223 { | |
| 224 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 225 cvec_copyi16(s->eq_coeff, s->eq_coeff_save, V17_EQUALIZER_LEN); | |
| 226 cvec_zeroi16(s->eq_buf, V17_EQUALIZER_LEN); | |
| 227 s->eq_delta = 32768.0f*EQUALIZER_SLOW_ADAPT_RATIO*EQUALIZER_DELTA/V17_EQUALIZER_LEN; | |
| 228 #else | |
| 229 cvec_copyf(s->eq_coeff, s->eq_coeff_save, V17_EQUALIZER_LEN); | |
| 230 cvec_zerof(s->eq_buf, V17_EQUALIZER_LEN); | |
| 231 s->eq_delta = EQUALIZER_SLOW_ADAPT_RATIO*EQUALIZER_DELTA/V17_EQUALIZER_LEN; | |
| 232 #endif | |
| 233 | |
| 234 s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1; | |
| 235 s->eq_step = 0; | |
| 236 } | |
| 237 /*- End of function --------------------------------------------------------*/ | |
| 238 | |
| 239 static void equalizer_reset(v17_rx_state_t *s) | |
| 240 { | |
| 241 /* Start with an equalizer based on everything being perfect */ | |
| 242 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 243 cvec_zeroi16(s->eq_coeff, V17_EQUALIZER_LEN); | |
| 244 s->eq_coeff[V17_EQUALIZER_PRE_LEN] = complex_seti16(3*FP_FACTOR, 0); | |
| 245 cvec_zeroi16(s->eq_buf, V17_EQUALIZER_LEN); | |
| 246 s->eq_delta = 32768.0f*EQUALIZER_DELTA/V17_EQUALIZER_LEN; | |
| 247 #else | |
| 248 cvec_zerof(s->eq_coeff, V17_EQUALIZER_LEN); | |
| 249 s->eq_coeff[V17_EQUALIZER_PRE_LEN] = complex_setf(3.0f, 0.0f); | |
| 250 cvec_zerof(s->eq_buf, V17_EQUALIZER_LEN); | |
| 251 s->eq_delta = EQUALIZER_DELTA/V17_EQUALIZER_LEN; | |
| 252 #endif | |
| 253 | |
| 254 s->eq_put_step = RX_PULSESHAPER_COEFF_SETS*10/(3*2) - 1; | |
| 255 s->eq_step = 0; | |
| 256 } | |
| 257 /*- End of function --------------------------------------------------------*/ | |
| 258 | |
| 259 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 260 static __inline__ complexi16_t equalizer_get(v17_rx_state_t *s) | |
| 261 #else | |
| 262 static __inline__ complexf_t equalizer_get(v17_rx_state_t *s) | |
| 263 #endif | |
| 264 { | |
| 265 return cvec_circular_dot_prodf(s->eq_buf, s->eq_coeff, V17_EQUALIZER_LEN, s->eq_step); | |
| 266 } | |
| 267 /*- End of function --------------------------------------------------------*/ | |
| 268 | |
| 269 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 270 static void tune_equalizer(v17_rx_state_t *s, const complexi16_t *z, const complexi16_t *target) | |
| 271 { | |
| 272 complexi16_t err; | |
| 273 | |
| 274 /* Find the x and y mismatch from the exact constellation position. */ | |
| 275 err.re = target->re*FP_FACTOR - z->re; | |
| 276 err.im = target->im*FP_FACTOR - z->im; | |
| 277 //span_log(&s->logging, SPAN_LOG_FLOW, "Equalizer error %f\n", sqrt(err.re*err.re + err.im*err.im)); | |
| 278 err.re = ((int32_t) err.re*(int32_t) s->eq_delta) >> 15; | |
| 279 err.im = ((int32_t) err.im*(int32_t) s->eq_delta) >> 15; | |
| 280 cvec_circular_lmsi16(s->eq_buf, s->eq_coeff, V17_EQUALIZER_LEN, s->eq_step, &err); | |
| 281 } | |
| 282 #else | |
| 283 static void tune_equalizer(v17_rx_state_t *s, const complexf_t *z, const complexf_t *target) | |
| 284 { | |
| 285 complexf_t err; | |
| 286 | |
| 287 /* Find the x and y mismatch from the exact constellation position. */ | |
| 288 err = complex_subf(target, z); | |
| 289 //span_log(&s->logging, SPAN_LOG_FLOW, "Equalizer error %f\n", sqrt(err.re*err.re + err.im*err.im)); | |
| 290 err.re *= s->eq_delta; | |
| 291 err.im *= s->eq_delta; | |
| 292 cvec_circular_lmsf(s->eq_buf, s->eq_coeff, V17_EQUALIZER_LEN, s->eq_step, &err); | |
| 293 } | |
| 294 #endif | |
| 295 | |
| 296 static int descramble(v17_rx_state_t *s, int in_bit) | |
| 297 { | |
| 298 int out_bit; | |
| 299 | |
| 300 //out_bit = (in_bit ^ (s->scramble_reg >> s->scrambler_tap) ^ (s->scramble_reg >> (23 - 1))) & 1; | |
| 301 out_bit = (in_bit ^ (s->scramble_reg >> (18 - 1)) ^ (s->scramble_reg >> (23 - 1))) & 1; | |
| 302 s->scramble_reg <<= 1; | |
| 303 if (s->training_stage > TRAINING_STAGE_NORMAL_OPERATION && s->training_stage < TRAINING_STAGE_TCM_WINDUP) | |
| 304 s->scramble_reg |= out_bit; | |
| 305 else | |
| 306 s->scramble_reg |= (in_bit & 1); | |
| 307 return out_bit; | |
| 308 } | |
| 309 /*- End of function --------------------------------------------------------*/ | |
| 310 | |
| 311 static void track_carrier(v17_rx_state_t *s, const complexf_t *z, const complexf_t *target) | |
| 312 { | |
| 313 float error; | |
| 314 | |
| 315 /* For small errors the imaginary part of the difference between the actual and the target | |
| 316 positions is proportional to the phase error, for any particular target. However, the | |
| 317 different amplitudes of the various target positions scale things. */ | |
| 318 error = z->im*target->re - z->re*target->im; | |
| 319 | |
| 320 s->carrier_phase_rate += (int32_t) (s->carrier_track_i*error); | |
| 321 s->carrier_phase += (int32_t) (s->carrier_track_p*error); | |
| 322 //span_log(&s->logging, SPAN_LOG_FLOW, "Im = %15.5f f = %15.5f\n", error, dds_frequencyf(s->carrier_phase_rate)); | |
| 323 //printf("XXX Im = %15.5f f = %15.5f %f %f %f %f (%f %f)\n", error, dds_frequencyf(s->carrier_phase_rate), target->re, target->im, z->re, z->im, s->carrier_track_i, s->carrier_track_p); | |
| 324 } | |
| 325 /*- End of function --------------------------------------------------------*/ | |
| 326 | |
| 327 static __inline__ void put_bit(v17_rx_state_t *s, int bit) | |
| 328 { | |
| 329 int out_bit; | |
| 330 | |
| 331 /* We need to strip the last part of the training - the test period of all 1s - | |
| 332 before we let data go to the application. */ | |
| 333 if (s->training_stage == TRAINING_STAGE_NORMAL_OPERATION) | |
| 334 { | |
| 335 out_bit = descramble(s, bit); | |
| 336 s->put_bit(s->put_bit_user_data, out_bit); | |
| 337 } | |
| 338 else if (s->training_stage == TRAINING_STAGE_TEST_ONES) | |
| 339 { | |
| 340 /* The bits during the final stage of training should be all ones. However, | |
| 341 buggy modems mean you cannot rely on this. Therefore we don't bother | |
| 342 testing for ones, but just rely on a constellation mismatch measurement. */ | |
| 343 out_bit = descramble(s, bit); | |
| 344 //span_log(&s->logging, SPAN_LOG_FLOW, "A 1 is really %d\n", out_bit); | |
| 345 } | |
| 346 } | |
| 347 /*- End of function --------------------------------------------------------*/ | |
| 348 | |
| 349 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 350 static __inline__ uint32_t dist_sq(const complexi_t *x, const complexi_t *y) | |
| 351 { | |
| 352 return (x->re - y->re)*(x->re - y->re) + (x->im - y->im)*(x->im - y->im); | |
| 353 } | |
| 354 /*- End of function --------------------------------------------------------*/ | |
| 355 #else | |
| 356 static __inline__ float dist_sq(const complexf_t *x, const complexf_t *y) | |
| 357 { | |
| 358 return (x->re - y->re)*(x->re - y->re) + (x->im - y->im)*(x->im - y->im); | |
| 359 } | |
| 360 /*- End of function --------------------------------------------------------*/ | |
| 361 #endif | |
| 362 | |
| 363 static int decode_baud(v17_rx_state_t *s, complexf_t *z) | |
| 364 { | |
| 365 static const uint8_t v32bis_4800_differential_decoder[4][4] = | |
| 366 { | |
| 367 {2, 3, 0, 1}, | |
| 368 {0, 2, 1, 3}, | |
| 369 {3, 1, 2, 0}, | |
| 370 {1, 0, 3, 2} | |
| 371 }; | |
| 372 static const uint8_t v17_differential_decoder[4][4] = | |
| 373 { | |
| 374 {0, 1, 2, 3}, | |
| 375 {3, 0, 1, 2}, | |
| 376 {2, 3, 0, 1}, | |
| 377 {1, 2, 3, 0} | |
| 378 }; | |
| 379 static const uint8_t tcm_paths[8][4] = | |
| 380 { | |
| 381 {0, 6, 2, 4}, | |
| 382 {6, 0, 4, 2}, | |
| 383 {2, 4, 0, 6}, | |
| 384 {4, 2, 6, 0}, | |
| 385 {1, 3, 7, 5}, | |
| 386 {5, 7, 3, 1}, | |
| 387 {7, 5, 1, 3}, | |
| 388 {3, 1, 5, 7} | |
| 389 }; | |
| 390 int nearest; | |
| 391 int i; | |
| 392 int j; | |
| 393 int k; | |
| 394 int re; | |
| 395 int im; | |
| 396 int raw; | |
| 397 int constellation_state; | |
| 398 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 399 #define DIST_FACTOR 2048 /* Something less than sqrt(0xFFFFFFFF/10)/10 */ | |
| 400 complexi_t zi; | |
| 401 uint32_t distances[8]; | |
| 402 uint32_t new_distances[8]; | |
| 403 uint32_t min; | |
| 404 complexi_t ci; | |
| 405 #else | |
| 406 float distances[8]; | |
| 407 float new_distances[8]; | |
| 408 float min; | |
| 409 #endif | |
| 410 | |
| 411 re = (int) ((z->re + 9.0f)*2.0f); | |
| 412 if (re > 35) | |
| 413 re = 35; | |
| 414 else if (re < 0) | |
| 415 re = 0; | |
| 416 im = (int) ((z->im + 9.0f)*2.0f); | |
| 417 if (im > 35) | |
| 418 im = 35; | |
| 419 else if (im < 0) | |
| 420 im = 0; | |
| 421 | |
| 422 if (s->bits_per_symbol == 2) | |
| 423 { | |
| 424 /* 4800bps V.32bis mode, without trellis coding */ | |
| 425 nearest = constel_map_4800[re][im]; | |
| 426 raw = v32bis_4800_differential_decoder[s->diff][nearest]; | |
| 427 s->diff = nearest; | |
| 428 put_bit(s, raw); | |
| 429 put_bit(s, raw >> 1); | |
| 430 return nearest; | |
| 431 } | |
| 432 | |
| 433 /* Find a set of 8 candidate constellation positions, that are the closest | |
| 434 to the target, with different patterns in the last 3 bits. */ | |
| 435 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 436 min = 0xFFFFFFFF; | |
| 437 zi = complex_seti(z->re*DIST_FACTOR, z->im*DIST_FACTOR); | |
| 438 #else | |
| 439 min = 9999999.0f; | |
| 440 #endif | |
| 441 j = 0; | |
| 442 for (i = 0; i < 8; i++) | |
| 443 { | |
| 444 nearest = constel_maps[s->space_map][re][im][i]; | |
| 445 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 446 ci = complex_seti(s->constellation[nearest].re*DIST_FACTOR, | |
| 447 s->constellation[nearest].im*DIST_FACTOR); | |
| 448 distances[i] = dist_sq(&ci, &zi); | |
| 449 #else | |
| 450 distances[i] = dist_sq(&s->constellation[nearest], z); | |
| 451 #endif | |
| 452 if (min > distances[i]) | |
| 453 { | |
| 454 min = distances[i]; | |
| 455 j = i; | |
| 456 } | |
| 457 } | |
| 458 /* Use the nearest of these soft-decisions as the basis for DFE */ | |
| 459 constellation_state = constel_maps[s->space_map][re][im][j]; | |
| 460 /* Control the equalizer, carrier tracking, etc. based on the non-trellis | |
| 461 corrected information. The trellis correct stuff comes out a bit late. */ | |
| 462 track_carrier(s, z, &s->constellation[constellation_state]); | |
| 463 //tune_equalizer(s, z, &s->constellation[constellation_state]); | |
| 464 | |
| 465 /* Now do the trellis decoding */ | |
| 466 | |
| 467 /* TODO: change to processing blocks of stored symbols here, instead of processing | |
| 468 one symbol at a time, to speed up the processing. */ | |
| 469 | |
| 470 /* Update the minimum accumulated distance to each of the 8 states */ | |
| 471 if (++s->trellis_ptr >= V17_TRELLIS_STORAGE_DEPTH) | |
| 472 s->trellis_ptr = 0; | |
| 473 for (i = 0; i < 4; i++) | |
| 474 { | |
| 475 min = distances[tcm_paths[i][0]] + s->distances[0]; | |
| 476 k = 0; | |
| 477 for (j = 1; j < 4; j++) | |
| 478 { | |
| 479 if (min > distances[tcm_paths[i][j]] + s->distances[j << 1]) | |
| 480 { | |
| 481 min = distances[tcm_paths[i][j]] + s->distances[j << 1]; | |
| 482 k = j; | |
| 483 } | |
| 484 } | |
| 485 /* Use an elementary IIR filter to track the distance to date. */ | |
| 486 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 487 new_distances[i] = s->distances[k << 1]*9/10 + distances[tcm_paths[i][k]]*1/10; | |
| 488 #else | |
| 489 new_distances[i] = s->distances[k << 1]*0.9f + distances[tcm_paths[i][k]]*0.1f; | |
| 490 #endif | |
| 491 s->full_path_to_past_state_locations[s->trellis_ptr][i] = constel_maps[s->space_map][re][im][tcm_paths[i][k]]; | |
| 492 s->past_state_locations[s->trellis_ptr][i] = k << 1; | |
| 493 } | |
| 494 for (i = 4; i < 8; i++) | |
| 495 { | |
| 496 min = distances[tcm_paths[i][0]] + s->distances[1]; | |
| 497 k = 0; | |
| 498 for (j = 1; j < 4; j++) | |
| 499 { | |
| 500 if (min > distances[tcm_paths[i][j]] + s->distances[(j << 1) + 1]) | |
| 501 { | |
| 502 min = distances[tcm_paths[i][j]] + s->distances[(j << 1) + 1]; | |
| 503 k = j; | |
| 504 } | |
| 505 } | |
| 506 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 507 new_distances[i] = s->distances[(k << 1) + 1]*9/10 + distances[tcm_paths[i][k]]*1/10; | |
| 508 #else | |
| 509 new_distances[i] = s->distances[(k << 1) + 1]*0.9f + distances[tcm_paths[i][k]]*0.1f; | |
| 510 #endif | |
| 511 s->full_path_to_past_state_locations[s->trellis_ptr][i] = constel_maps[s->space_map][re][im][tcm_paths[i][k]]; | |
| 512 s->past_state_locations[s->trellis_ptr][i] = (k << 1) + 1; | |
| 513 } | |
| 514 memcpy(s->distances, new_distances, sizeof(s->distances)); | |
| 515 | |
| 516 /* Find the minimum distance to date. This is the start of the path back to the result. */ | |
| 517 min = s->distances[0]; | |
| 518 k = 0; | |
| 519 for (i = 1; i < 8; i++) | |
| 520 { | |
| 521 if (min > s->distances[i]) | |
| 522 { | |
| 523 min = s->distances[i]; | |
| 524 k = i; | |
| 525 } | |
| 526 } | |
| 527 /* Trace back through every time step, starting with the current one, and find the | |
| 528 state from which the path came one step before. At the end of this search, the | |
| 529 last state found also points to the constellation point at that state. This is the | |
| 530 output of the trellis. */ | |
| 531 for (i = 0, j = s->trellis_ptr; i < V17_TRELLIS_LOOKBACK_DEPTH - 1; i++) | |
| 532 { | |
| 533 k = s->past_state_locations[j][k]; | |
| 534 if (--j < 0) | |
| 535 j = V17_TRELLIS_STORAGE_DEPTH - 1; | |
| 536 } | |
| 537 nearest = s->full_path_to_past_state_locations[j][k] >> 1; | |
| 538 | |
| 539 /* Differentially decode */ | |
| 540 raw = (nearest & 0x3C) | v17_differential_decoder[s->diff][nearest & 0x03]; | |
| 541 s->diff = nearest & 0x03; | |
| 542 for (i = 0; i < s->bits_per_symbol; i++) | |
| 543 { | |
| 544 put_bit(s, raw); | |
| 545 raw >>= 1; | |
| 546 } | |
| 547 return constellation_state; | |
| 548 } | |
| 549 /*- End of function --------------------------------------------------------*/ | |
| 550 | |
| 551 static __inline__ void symbol_sync(v17_rx_state_t *s) | |
| 552 { | |
| 553 int i; | |
| 554 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 555 int32_t v; | |
| 556 int32_t p; | |
| 557 #else | |
| 558 float v; | |
| 559 float p; | |
| 560 #endif | |
| 561 | |
| 562 /* This routine adapts the position of the half baud samples entering the equalizer. */ | |
| 563 | |
| 564 /* This symbol sync scheme is based on the technique first described by Dominique Godard in | |
| 565 Passband Timing Recovery in an All-Digital Modem Receiver | |
| 566 IEEE TRANSACTIONS ON COMMUNICATIONS, VOL. COM-26, NO. 5, MAY 1978 */ | |
| 567 | |
| 568 /* This is slightly rearranged for figure 3b of the Godard paper, as this saves a couple of | |
| 569 maths operations */ | |
| 570 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 571 /* TODO: The scalings used here need more thorough evaluation, to see if overflows are possible. */ | |
| 572 /* Cross correlate */ | |
| 573 v = (((s->symbol_sync_low[1] >> 5)*(s->symbol_sync_high[0] >> 4)) >> 15)*SYNC_LOW_BAND_EDGE_COEFF_2 | |
| 574 - (((s->symbol_sync_low[0] >> 5)*(s->symbol_sync_high[1] >> 4)) >> 15)*SYNC_HIGH_BAND_EDGE_COEFF_2 | |
| 575 + (((s->symbol_sync_low[1] >> 5)*(s->symbol_sync_high[1] >> 4)) >> 15)*SYNC_MIXED_EDGES_COEFF_3; | |
| 576 /* Filter away any DC component */ | |
| 577 p = v - s->symbol_sync_dc_filter[1]; | |
| 578 s->symbol_sync_dc_filter[1] = s->symbol_sync_dc_filter[0]; | |
| 579 s->symbol_sync_dc_filter[0] = v; | |
| 580 /* A little integration will now filter away much of the HF noise */ | |
| 581 s->baud_phase -= p; | |
| 582 if (abs(s->baud_phase) > 100*FP_FACTOR) | |
| 583 { | |
| 584 if (s->baud_phase > 0) | |
| 585 i = (s->baud_phase > 1000*FP_FACTOR) ? 15 : 1; | |
| 586 else | |
| 587 i = (s->baud_phase < -1000*FP_FACTOR) ? -15 : -1; | |
| 588 //printf("v = %10.5f %5d - %f %f %d %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction); | |
| 589 s->eq_put_step += i; | |
| 590 s->total_baud_timing_correction += i; | |
| 591 } | |
| 592 #else | |
| 593 /* Cross correlate */ | |
| 594 v = s->symbol_sync_low[1]*s->symbol_sync_high[0]*SYNC_LOW_BAND_EDGE_COEFF_2 | |
| 595 - s->symbol_sync_low[0]*s->symbol_sync_high[1]*SYNC_HIGH_BAND_EDGE_COEFF_2 | |
| 596 + s->symbol_sync_low[1]*s->symbol_sync_high[1]*SYNC_MIXED_EDGES_COEFF_3; | |
| 597 /* Filter away any DC component */ | |
| 598 p = v - s->symbol_sync_dc_filter[1]; | |
| 599 s->symbol_sync_dc_filter[1] = s->symbol_sync_dc_filter[0]; | |
| 600 s->symbol_sync_dc_filter[0] = v; | |
| 601 /* A little integration will now filter away much of the HF noise */ | |
| 602 s->baud_phase -= p; | |
| 603 if (fabsf(s->baud_phase) > 100.0f) | |
| 604 { | |
| 605 if (s->baud_phase > 0.0f) | |
| 606 i = (s->baud_phase > 1000.0f) ? 15 : 1; | |
| 607 else | |
| 608 i = (s->baud_phase < -1000.0f) ? -15 : -1; | |
| 609 //printf("v = %10.5f %5d - %f %f %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction); | |
| 610 s->eq_put_step += i; | |
| 611 s->total_baud_timing_correction += i; | |
| 612 } | |
| 613 #endif | |
| 614 } | |
| 615 /*- End of function --------------------------------------------------------*/ | |
| 616 | |
| 617 static void process_half_baud(v17_rx_state_t *s, const complexf_t *sample) | |
| 618 { | |
| 619 static const complexf_t cdba[4] = | |
| 620 { | |
| 621 { 6.0f, 2.0f}, | |
| 622 {-2.0f, 6.0f}, | |
| 623 { 2.0f, -6.0f}, | |
| 624 {-6.0f, -2.0f} | |
| 625 }; | |
| 626 complexf_t z; | |
| 627 complexf_t zz; | |
| 628 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 629 const complexi_t *target; | |
| 630 static const complexi16_t zero = {0, 0}; | |
| 631 #else | |
| 632 const complexf_t *target; | |
| 633 static const complexf_t zero = {0, 0}; | |
| 634 #endif | |
| 635 float p; | |
| 636 int bit; | |
| 637 int i; | |
| 638 int j; | |
| 639 int32_t angle; | |
| 640 int32_t ang; | |
| 641 int constellation_state; | |
| 642 | |
| 643 /* This routine processes every half a baud, as we put things into the equalizer at the T/2 rate. */ | |
| 644 | |
| 645 /* Add a sample to the equalizer's circular buffer, but don't calculate anything at this time. */ | |
| 646 s->eq_buf[s->eq_step] = *sample; | |
| 647 if (++s->eq_step >= V17_EQUALIZER_LEN) | |
| 648 s->eq_step = 0; | |
| 649 | |
| 650 /* On alternate insertions we have a whole baud and must process it. */ | |
| 651 if ((s->baud_half ^= 1)) | |
| 652 return; | |
| 653 | |
| 654 /* Symbol timing synchronisation */ | |
| 655 symbol_sync(s); | |
| 656 | |
| 657 z = equalizer_get(s); | |
| 658 | |
| 659 constellation_state = 0; | |
| 660 switch (s->training_stage) | |
| 661 { | |
| 662 case TRAINING_STAGE_NORMAL_OPERATION: | |
| 663 /* Normal operation. */ | |
| 664 constellation_state = decode_baud(s, &z); | |
| 665 target = &s->constellation[constellation_state]; | |
| 666 break; | |
| 667 case TRAINING_STAGE_SYMBOL_ACQUISITION: | |
| 668 /* Allow time for the symbol synchronisation to settle the symbol timing. */ | |
| 669 target = &zero; | |
| 670 if (++s->training_count >= 100) | |
| 671 { | |
| 672 /* Record the current phase angle */ | |
| 673 s->angles[0] = | |
| 674 s->start_angles[0] = arctan2(z.im, z.re); | |
| 675 s->training_stage = TRAINING_STAGE_LOG_PHASE; | |
| 676 if (s->agc_scaling_save == 0.0f) | |
| 677 s->agc_scaling_save = s->agc_scaling; | |
| 678 } | |
| 679 break; | |
| 680 case TRAINING_STAGE_LOG_PHASE: | |
| 681 /* Record the current alternate phase angle */ | |
| 682 target = &zero; | |
| 683 angle = arctan2(z.im, z.re); | |
| 684 s->training_count = 1; | |
| 685 if (s->short_train) | |
| 686 { | |
| 687 /* We should already know the accurate carrier frequency. All we need to sort | |
| 688 out is the phase. */ | |
| 689 /* Check if we just saw A or B */ | |
| 690 if ((uint32_t) (angle - s->start_angles[0]) < 0x80000000U) | |
| 691 { | |
| 692 angle = s->start_angles[0]; | |
| 693 s->angles[0] = 0xC0000000 + 219937506; | |
| 694 s->angles[1] = 0x80000000 + 219937506; | |
| 695 } | |
| 696 else | |
| 697 { | |
| 698 s->angles[0] = 0x80000000 + 219937506; | |
| 699 s->angles[1] = 0xC0000000 + 219937506; | |
| 700 } | |
| 701 /* Make a step shift in the phase, to pull it into line. We need to rotate the equalizer | |
| 702 buffer, as well as the carrier phase, for this to play out nicely. */ | |
| 703 /* angle is now the difference between where A is, and where it should be */ | |
| 704 p = 3.14159f + angle*2.0f*3.14159f/(65536.0f*65536.0f) - 0.321751f; | |
| 705 span_log(&s->logging, SPAN_LOG_FLOW, "Spin (short) by %.5f rads\n", p); | |
| 706 zz = complex_setf(cosf(p), -sinf(p)); | |
| 707 for (i = 0; i < V17_EQUALIZER_LEN; i++) | |
| 708 s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz); | |
| 709 s->carrier_phase += (0x80000000 + angle - 219937506); | |
| 710 | |
| 711 s->carrier_track_p = 500000.0f; | |
| 712 | |
| 713 s->training_stage = TRAINING_STAGE_SHORT_WAIT_FOR_CDBA; | |
| 714 } | |
| 715 else | |
| 716 { | |
| 717 s->angles[1] = | |
| 718 s->start_angles[1] = angle; | |
| 719 s->training_stage = TRAINING_STAGE_WAIT_FOR_CDBA; | |
| 720 } | |
| 721 break; | |
| 722 case TRAINING_STAGE_WAIT_FOR_CDBA: | |
| 723 target = &zero; | |
| 724 angle = arctan2(z.im, z.re); | |
| 725 /* Look for the initial ABAB sequence to display a phase reversal, which will | |
| 726 signal the start of the scrambled CDBA segment */ | |
| 727 ang = angle - s->angles[(s->training_count - 1) & 0xF]; | |
| 728 s->angles[(s->training_count + 1) & 0xF] = angle; | |
| 729 | |
| 730 /* Do a coarse frequency adjustment about half way through the reversals, as if we wait until | |
| 731 the end, we might have rotated too far to correct properly. */ | |
| 732 if (s->training_count == 100) | |
| 733 { | |
| 734 i = s->training_count; | |
| 735 /* Avoid the possibility of a divide by zero */ | |
| 736 if (i) | |
| 737 { | |
| 738 j = i & 0xF; | |
| 739 ang = (s->angles[j] - s->start_angles[0])/i | |
| 740 + (s->angles[j | 0x1] - s->start_angles[1])/i; | |
| 741 s->carrier_phase_rate += 3*(ang/20); | |
| 742 //span_log(&s->logging, SPAN_LOG_FLOW, "Angles %x, %x, %x, %x, dist %d\n", s->angles[j], s->start_angles[0], s->angles[j | 0x1], s->start_angles[1], i); | |
| 743 | |
| 744 s->start_angles[0] = s->angles[j]; | |
| 745 s->start_angles[1] = s->angles[j | 0x1]; | |
| 746 } | |
| 747 //span_log(&s->logging, SPAN_LOG_FLOW, "%d %d %d %d %d\n", s->angles[s->training_count & 0xF], s->start_angles[0], s->angles[(s->training_count | 0x1) & 0xF], s->start_angles[1], s->training_count); | |
| 748 span_log(&s->logging, SPAN_LOG_FLOW, "First coarse carrier frequency %7.2f (%d)\n", dds_frequencyf(s->carrier_phase_rate), s->training_count); | |
| 749 | |
| 750 } | |
| 751 if ((ang > 0x40000000 || ang < -0x40000000) && s->training_count >= 13) | |
| 752 { | |
| 753 span_log(&s->logging, SPAN_LOG_FLOW, "We seem to have a reversal at symbol %d\n", s->training_count); | |
| 754 /* We seem to have a phase reversal */ | |
| 755 /* Slam the carrier frequency into line, based on the total phase drift over the last | |
| 756 section. Use the shift from the odd bits and the shift from the even bits to get | |
| 757 better jitter suppression. */ | |
| 758 /* TODO: We are supposed to deal with frequancy errors up to +-8Hz. Over 200+ | |
| 759 symbols that is more than half a cycle. We get confused an do crazy things. | |
| 760 We can only cope with errors up to 5Hz right now. We need to implement | |
| 761 greater tolerance to be compliant, although it doesn't really matter much | |
| 762 these days. */ | |
| 763 /* Step back a few symbols so we don't get ISI distorting things. */ | |
| 764 i = (s->training_count - 8) & ~1; | |
| 765 /* Avoid the possibility of a divide by zero */ | |
| 766 if (i - 100 + 8) | |
| 767 { | |
| 768 j = i & 0xF; | |
| 769 ang = (s->angles[j] - s->start_angles[0])/(i - 100 + 8) | |
| 770 + (s->angles[j | 0x1] - s->start_angles[1])/(i - 100 + 8); | |
| 771 s->carrier_phase_rate += 3*(ang/20); | |
| 772 span_log(&s->logging, SPAN_LOG_FLOW, "Angles %x, %x, %x, %x, dist %d\n", s->angles[j], s->start_angles[0], s->angles[j | 0x1], s->start_angles[1], i); | |
| 773 } | |
| 774 //span_log(&s->logging, SPAN_LOG_FLOW, "%d %d %d %d %d\n", s->angles[s->training_count & 0xF], s->start_angles[0], s->angles[(s->training_count | 0x1) & 0xF], s->start_angles[1], s->training_count); | |
| 775 span_log(&s->logging, SPAN_LOG_FLOW, "Second coarse carrier frequency %7.2f (%d)\n", dds_frequencyf(s->carrier_phase_rate), s->training_count); | |
| 776 /* Check if the carrier frequency is plausible */ | |
| 777 if (s->carrier_phase_rate < dds_phase_ratef(CARRIER_NOMINAL_FREQ - 20.0f) | |
| 778 || | |
| 779 s->carrier_phase_rate > dds_phase_ratef(CARRIER_NOMINAL_FREQ + 20.0f)) | |
| 780 { | |
| 781 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n"); | |
| 782 /* Park this modem */ | |
| 783 s->agc_scaling_save = 0.0f; | |
| 784 s->training_stage = TRAINING_STAGE_PARKED; | |
| 785 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 786 break; | |
| 787 } | |
| 788 | |
| 789 /* Make a step shift in the phase, to pull it into line. We need to rotate the equalizer buffer, | |
| 790 as well as the carrier phase, for this to play out nicely. */ | |
| 791 /* angle is now the difference between where C is, and where it should be */ | |
| 792 p = angle*2.0f*3.14159f/(65536.0f*65536.0f) - 0.321751f; | |
| 793 span_log(&s->logging, SPAN_LOG_FLOW, "Spin (long) by %.5f rads\n", p); | |
| 794 zz = complex_setf(cosf(p), -sinf(p)); | |
| 795 for (i = 0; i < V17_EQUALIZER_LEN; i++) | |
| 796 s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz); | |
| 797 s->carrier_phase += (angle - 219937506); | |
| 798 | |
| 799 /* We have just seen the first symbol of the scrambled sequence, so skip it. */ | |
| 800 bit = descramble(s, 1); | |
| 801 bit = (bit << 1) | descramble(s, 1); | |
| 802 target = &cdba[bit]; | |
| 803 s->training_count = 1; | |
| 804 s->training_stage = TRAINING_STAGE_COARSE_TRAIN_ON_CDBA; | |
| 805 report_status_change(s, SIG_STATUS_TRAINING_IN_PROGRESS); | |
| 806 break; | |
| 807 } | |
| 808 if (++s->training_count > V17_TRAINING_SEG_1_LEN) | |
| 809 { | |
| 810 /* This is bogus. There are not this many bits in this section | |
| 811 of a real training sequence. Note that this might be TEP. */ | |
| 812 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n"); | |
| 813 /* Park this modem */ | |
| 814 s->agc_scaling_save = 0.0f; | |
| 815 s->training_stage = TRAINING_STAGE_PARKED; | |
| 816 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 817 } | |
| 818 break; | |
| 819 case TRAINING_STAGE_COARSE_TRAIN_ON_CDBA: | |
| 820 /* Train on the scrambled CDBA section. */ | |
| 821 bit = descramble(s, 1); | |
| 822 bit = (bit << 1) | descramble(s, 1); | |
| 823 target = &cdba[bit]; | |
| 824 track_carrier(s, &z, target); | |
| 825 tune_equalizer(s, &z, target); | |
| 826 #if defined(IAXMODEM_STUFF) | |
| 827 zz = complex_subf(&z, target); | |
| 828 s->training_error = powerf(&zz); | |
| 829 if (++s->training_count == V17_TRAINING_SEG_2_LEN - 2000 || s->training_error < 1.0f || s->training_error > 200.0f) | |
| 830 #else | |
| 831 if (++s->training_count == V17_TRAINING_SEG_2_LEN - 2000) | |
| 832 #endif | |
| 833 { | |
| 834 /* Now the equaliser adaption should be getting somewhere, slow it down, or it will never | |
| 835 tune very well on a noisy signal. */ | |
| 836 s->eq_delta *= EQUALIZER_SLOW_ADAPT_RATIO; | |
| 837 s->carrier_track_i = 1000.0f; | |
| 838 s->training_stage = TRAINING_STAGE_FINE_TRAIN_ON_CDBA; | |
| 839 } | |
| 840 break; | |
| 841 case TRAINING_STAGE_FINE_TRAIN_ON_CDBA: | |
| 842 /* Train on the scrambled CDBA section. */ | |
| 843 bit = descramble(s, 1); | |
| 844 bit = (bit << 1) | descramble(s, 1); | |
| 845 target = &cdba[bit]; | |
| 846 /* By this point the training should be comming into focus. */ | |
| 847 track_carrier(s, &z, target); | |
| 848 tune_equalizer(s, &z, target); | |
| 849 if (++s->training_count >= V17_TRAINING_SEG_2_LEN - 48) | |
| 850 { | |
| 851 s->training_error = 0.0f; | |
| 852 s->carrier_track_i = 100.0f; | |
| 853 s->carrier_track_p = 500000.0f; | |
| 854 s->training_stage = TRAINING_STAGE_TRAIN_ON_CDBA_AND_TEST; | |
| 855 } | |
| 856 break; | |
| 857 case TRAINING_STAGE_TRAIN_ON_CDBA_AND_TEST: | |
| 858 /* Continue training on the scrambled CDBA section, but measure the quality of training too. */ | |
| 859 bit = descramble(s, 1); | |
| 860 bit = (bit << 1) | descramble(s, 1); | |
| 861 target = &cdba[bit]; | |
| 862 //span_log(&s->logging, SPAN_LOG_FLOW, "%5d [%15.5f, %15.5f] [%15.5f, %15.5f]\n", s->training_count, z.re, z.im, cdba[bit].re, cdba[bit].im); | |
| 863 /* We ignore the last few symbols because it seems some modems do not end this | |
| 864 part properly, and it throws things off. */ | |
| 865 if (++s->training_count < V17_TRAINING_SEG_2_LEN - 20) | |
| 866 { | |
| 867 track_carrier(s, &z, target); | |
| 868 tune_equalizer(s, &z, target); | |
| 869 /* Measure the training error */ | |
| 870 zz = complex_subf(&z, &cdba[bit]); | |
| 871 s->training_error += powerf(&zz); | |
| 872 } | |
| 873 else if (s->training_count >= V17_TRAINING_SEG_2_LEN) | |
| 874 { | |
| 875 span_log(&s->logging, SPAN_LOG_FLOW, "Long training error %f\n", s->training_error); | |
| 876 if (s->training_error < 20.0f*1.414f*constellation_spacing[s->space_map]) | |
| 877 { | |
| 878 s->training_count = 0; | |
| 879 s->training_error = 0.0f; | |
| 880 s->training_stage = TRAINING_STAGE_BRIDGE; | |
| 881 } | |
| 882 else | |
| 883 { | |
| 884 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (convergence failed)\n"); | |
| 885 /* Park this modem */ | |
| 886 s->agc_scaling_save = 0.0f; | |
| 887 s->training_stage = TRAINING_STAGE_PARKED; | |
| 888 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 889 } | |
| 890 } | |
| 891 break; | |
| 892 case TRAINING_STAGE_BRIDGE: | |
| 893 descramble(s, V17_BRIDGE_WORD >> ((s->training_count & 0x7) << 1)); | |
| 894 descramble(s, V17_BRIDGE_WORD >> (((s->training_count & 0x7) << 1) + 1)); | |
| 895 target = &z; | |
| 896 if (++s->training_count >= V17_TRAINING_SEG_3_LEN) | |
| 897 { | |
| 898 s->training_count = 0; | |
| 899 s->training_error = 0.0f; | |
| 900 if (s->bits_per_symbol == 2) | |
| 901 { | |
| 902 /* Restart the differential decoder */ | |
| 903 /* There is no trellis, so go straight to processing decoded data */ | |
| 904 s->diff = (s->short_train) ? 0 : 1; | |
| 905 s->training_stage = TRAINING_STAGE_TEST_ONES; | |
| 906 } | |
| 907 else | |
| 908 { | |
| 909 /* Wait for the trellis to wind up */ | |
| 910 s->training_stage = TRAINING_STAGE_TCM_WINDUP; | |
| 911 } | |
| 912 } | |
| 913 break; | |
| 914 case TRAINING_STAGE_SHORT_WAIT_FOR_CDBA: | |
| 915 /* Look for the initial ABAB sequence to display a phase reversal, which will | |
| 916 signal the start of the scrambled CDBA segment */ | |
| 917 angle = arctan2(z.im, z.re); | |
| 918 ang = angle - s->angles[s->training_count & 1]; | |
| 919 if (ang > 0x40000000 || ang < -0x40000000) | |
| 920 { | |
| 921 /* We seem to have a phase reversal */ | |
| 922 /* We have just seen the first symbol of the scrambled sequence, so skip it. */ | |
| 923 bit = descramble(s, 1); | |
| 924 bit = (bit << 1) | descramble(s, 1); | |
| 925 target = &cdba[bit]; | |
| 926 s->training_count = 1; | |
| 927 s->training_error = 0.0f; | |
| 928 s->training_stage = TRAINING_STAGE_SHORT_TRAIN_ON_CDBA_AND_TEST; | |
| 929 break; | |
| 930 } | |
| 931 target = &cdba[(s->training_count & 1) + 2]; | |
| 932 track_carrier(s, &z, target); | |
| 933 if (++s->training_count > V17_TRAINING_SEG_1_LEN) | |
| 934 { | |
| 935 /* This is bogus. There are not this many bits in this section | |
| 936 of a real training sequence. Note that this might be TEP. */ | |
| 937 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n"); | |
| 938 /* Park this modem */ | |
| 939 s->training_stage = TRAINING_STAGE_PARKED; | |
| 940 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 941 } | |
| 942 break; | |
| 943 case TRAINING_STAGE_SHORT_TRAIN_ON_CDBA_AND_TEST: | |
| 944 /* Short retrain on the scrambled CDBA section, but measure the quality of training too. */ | |
| 945 bit = descramble(s, 1); | |
| 946 bit = (bit << 1) | descramble(s, 1); | |
| 947 //span_log(&s->logging, SPAN_LOG_FLOW, "%5d [%15.5f, %15.5f] [%15.5f, %15.5f] %d\n", s->training_count, z.re, z.im, cdba[bit].re, cdba[bit].im, arctan2(z.im, z.re)); | |
| 948 target = &cdba[bit]; | |
| 949 track_carrier(s, &z, target); | |
| 950 //tune_equalizer(s, &z, target); | |
| 951 /* Measure the training error */ | |
| 952 if (s->training_count > 8) | |
| 953 { | |
| 954 zz = complex_subf(&z, &cdba[bit]); | |
| 955 s->training_error += powerf(&zz); | |
| 956 } | |
| 957 if (++s->training_count >= V17_TRAINING_SHORT_SEG_2_LEN) | |
| 958 { | |
| 959 span_log(&s->logging, SPAN_LOG_FLOW, "Short training error %f\n", s->training_error); | |
| 960 s->carrier_track_i = 100.0f; | |
| 961 s->carrier_track_p = 500000.0f; | |
| 962 /* TODO: This was increased by a factor of 10 after studying real world failures. | |
| 963 However, it is not clear why this is an improvement, If something gives | |
| 964 a huge training error, surely it shouldn't decode too well? */ | |
| 965 if (s->training_error < (V17_TRAINING_SHORT_SEG_2_LEN - 8)*4.0f*constellation_spacing[s->space_map]) | |
| 966 { | |
| 967 s->training_count = 0; | |
| 968 if (s->bits_per_symbol == 2) | |
| 969 { | |
| 970 /* There is no trellis, so go straight to processing decoded data */ | |
| 971 /* Restart the differential decoder */ | |
| 972 s->diff = (s->short_train) ? 0 : 1; | |
| 973 s->training_error = 0.0f; | |
| 974 s->training_stage = TRAINING_STAGE_TEST_ONES; | |
| 975 } | |
| 976 else | |
| 977 { | |
| 978 /* Wait for the trellis to wind up */ | |
| 979 s->training_stage = TRAINING_STAGE_TCM_WINDUP; | |
| 980 } | |
| 981 report_status_change(s, SIG_STATUS_TRAINING_IN_PROGRESS); | |
| 982 } | |
| 983 else | |
| 984 { | |
| 985 span_log(&s->logging, SPAN_LOG_FLOW, "Short training failed (convergence failed)\n"); | |
| 986 /* Park this modem */ | |
| 987 s->training_stage = TRAINING_STAGE_PARKED; | |
| 988 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 989 } | |
| 990 } | |
| 991 break; | |
| 992 case TRAINING_STAGE_TCM_WINDUP: | |
| 993 /* We need to wait 15 bauds while the trellis fills up. */ | |
| 994 //span_log(&s->logging, SPAN_LOG_FLOW, "%5d %15.5f, %15.5f\n", s->training_count, z.re, z.im); | |
| 995 constellation_state = decode_baud(s, &z); | |
| 996 target = &s->constellation[constellation_state]; | |
| 997 /* Measure the training error */ | |
| 998 zz = complex_subf(&z, target); | |
| 999 s->training_error += powerf(&zz); | |
| 1000 if (++s->training_count >= V17_TRAINING_SEG_4A_LEN) | |
| 1001 { | |
| 1002 s->training_count = 0; | |
| 1003 s->training_error = 0.0f; | |
| 1004 /* Restart the differential decoder */ | |
| 1005 s->diff = (s->short_train) ? 0 : 1; | |
| 1006 s->training_stage = TRAINING_STAGE_TEST_ONES; | |
| 1007 } | |
| 1008 break; | |
| 1009 case TRAINING_STAGE_TEST_ONES: | |
| 1010 /* We are in the test phase, where we check that we can receive reliably. | |
| 1011 We should get a run of 1's, 48 symbols long. */ | |
| 1012 //span_log(&s->logging, SPAN_LOG_FLOW, "%5d %15.5f, %15.5f\n", s->training_count, z.re, z.im); | |
| 1013 constellation_state = decode_baud(s, &z); | |
| 1014 target = &s->constellation[constellation_state]; | |
| 1015 /* Measure the training error */ | |
| 1016 zz = complex_subf(&z, target); | |
| 1017 s->training_error += powerf(&zz); | |
| 1018 if (++s->training_count >= V17_TRAINING_SEG_4_LEN) | |
| 1019 { | |
| 1020 if (s->training_error < V17_TRAINING_SEG_4_LEN*constellation_spacing[s->space_map]) | |
| 1021 { | |
| 1022 /* We are up and running */ | |
| 1023 span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded at %dbps (constellation mismatch %f)\n", s->bit_rate, s->training_error); | |
| 1024 report_status_change(s, SIG_STATUS_TRAINING_SUCCEEDED); | |
| 1025 /* Apply some lag to the carrier off condition, to ensure the last few bits get pushed through | |
| 1026 the processing. */ | |
| 1027 s->signal_present = 60; | |
| 1028 equalizer_save(s); | |
| 1029 s->carrier_phase_rate_save = s->carrier_phase_rate; | |
| 1030 s->short_train = TRUE; | |
| 1031 s->training_stage = TRAINING_STAGE_NORMAL_OPERATION; | |
| 1032 } | |
| 1033 else | |
| 1034 { | |
| 1035 /* Training has failed */ | |
| 1036 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error); | |
| 1037 /* Park this modem */ | |
| 1038 if (!s->short_train) | |
| 1039 s->agc_scaling_save = 0.0f; | |
| 1040 s->training_stage = TRAINING_STAGE_PARKED; | |
| 1041 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
| 1042 } | |
| 1043 } | |
| 1044 break; | |
| 1045 case TRAINING_STAGE_PARKED: | |
| 1046 default: | |
| 1047 /* We failed to train! */ | |
| 1048 /* Park here until the carrier drops. */ | |
| 1049 target = &zero; | |
| 1050 break; | |
| 1051 } | |
| 1052 if (s->qam_report) | |
| 1053 s->qam_report(s->qam_user_data, &z, target, constellation_state); | |
| 1054 } | |
| 1055 /*- End of function --------------------------------------------------------*/ | |
| 1056 | |
| 1057 static __inline__ int signal_detect(v17_rx_state_t *s, int16_t amp) | |
| 1058 { | |
| 1059 int16_t diff; | |
| 1060 int16_t x; | |
| 1061 int32_t power; | |
| 1062 | |
| 1063 /* There should be no DC in the signal, but sometimes there is. | |
| 1064 We need to measure the power with the DC blocked, but not using | |
| 1065 a slow to respond DC blocker. Use the most elementary HPF. */ | |
| 1066 x = amp >> 1; | |
| 1067 /* There could be overflow here, but it isn't a problem in practice */ | |
| 1068 diff = x - s->last_sample; | |
| 1069 s->last_sample = x; | |
| 1070 power = power_meter_update(&(s->power), diff); | |
| 1071 #if defined(IAXMODEM_STUFF) | |
| 1072 /* Quick power drop fudge */ | |
| 1073 diff = abs(diff); | |
| 1074 if (10*diff < s->high_sample) | |
| 1075 { | |
| 1076 if (++s->low_samples > 120) | |
| 1077 { | |
| 1078 power_meter_init(&(s->power), 4); | |
| 1079 s->high_sample = 0; | |
| 1080 s->low_samples = 0; | |
| 1081 } | |
| 1082 } | |
| 1083 else | |
| 1084 { | |
| 1085 s->low_samples = 0; | |
| 1086 if (diff > s->high_sample) | |
| 1087 s->high_sample = diff; | |
| 1088 } | |
| 1089 #endif | |
| 1090 if (s->signal_present > 0) | |
| 1091 { | |
| 1092 /* Look for power below turn-off threshold to turn the carrier off */ | |
| 1093 #if defined(IAXMODEM_STUFF) | |
| 1094 if (s->carrier_drop_pending || power < s->carrier_off_power) | |
| 1095 #else | |
| 1096 if (power < s->carrier_off_power) | |
| 1097 #endif | |
| 1098 { | |
| 1099 if (--s->signal_present <= 0) | |
| 1100 { | |
| 1101 /* Count down a short delay, to ensure we push the last | |
| 1102 few bits through the filters before stopping. */ | |
| 1103 v17_rx_restart(s, s->bit_rate, s->short_train); | |
| 1104 report_status_change(s, SIG_STATUS_CARRIER_DOWN); | |
| 1105 return 0; | |
| 1106 } | |
| 1107 #if defined(IAXMODEM_STUFF) | |
| 1108 /* Carrier has dropped, but the put_bit is pending the signal_present delay. */ | |
| 1109 s->carrier_drop_pending = TRUE; | |
| 1110 #endif | |
| 1111 } | |
| 1112 } | |
| 1113 else | |
| 1114 { | |
| 1115 /* Look for power exceeding turn-on threshold to turn the carrier on */ | |
| 1116 if (power < s->carrier_on_power) | |
| 1117 return 0; | |
| 1118 s->signal_present = 1; | |
| 1119 #if defined(IAXMODEM_STUFF) | |
| 1120 s->carrier_drop_pending = FALSE; | |
| 1121 #endif | |
| 1122 report_status_change(s, SIG_STATUS_CARRIER_UP); | |
| 1123 } | |
| 1124 return power; | |
| 1125 } | |
| 1126 /*- End of function --------------------------------------------------------*/ | |
| 1127 | |
| 1128 SPAN_DECLARE_NONSTD(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len) | |
| 1129 { | |
| 1130 int i; | |
| 1131 int step; | |
| 1132 complexf_t z; | |
| 1133 complexf_t zz; | |
| 1134 complexf_t sample; | |
| 1135 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1136 int32_t vi; | |
| 1137 #endif | |
| 1138 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 1139 int32_t v; | |
| 1140 #else | |
| 1141 float v; | |
| 1142 #endif | |
| 1143 int32_t power; | |
| 1144 | |
| 1145 for (i = 0; i < len; i++) | |
| 1146 { | |
| 1147 s->rrc_filter[s->rrc_filter_step] = amp[i]; | |
| 1148 if (++s->rrc_filter_step >= V17_RX_FILTER_STEPS) | |
| 1149 s->rrc_filter_step = 0; | |
| 1150 | |
| 1151 if ((power = signal_detect(s, amp[i])) == 0) | |
| 1152 continue; | |
| 1153 if (s->training_stage == TRAINING_STAGE_PARKED) | |
| 1154 continue; | |
| 1155 /* Only spend effort processing this data if the modem is not | |
| 1156 parked, after training failure. */ | |
| 1157 s->eq_put_step -= RX_PULSESHAPER_COEFF_SETS; | |
| 1158 step = -s->eq_put_step; | |
| 1159 if (step > RX_PULSESHAPER_COEFF_SETS - 1) | |
| 1160 step = RX_PULSESHAPER_COEFF_SETS - 1; | |
| 1161 if (step < 0) | |
| 1162 step += RX_PULSESHAPER_COEFF_SETS; | |
| 1163 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1164 vi = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_re[step], V17_RX_FILTER_STEPS, s->rrc_filter_step); | |
| 1165 //sample.re = (vi*(int32_t) s->agc_scaling) >> 15; | |
| 1166 sample.re = vi*s->agc_scaling; | |
| 1167 #else | |
| 1168 v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_re[step], V17_RX_FILTER_STEPS, s->rrc_filter_step); | |
| 1169 sample.re = v*s->agc_scaling; | |
| 1170 #endif | |
| 1171 /* Symbol timing synchronisation band edge filters */ | |
| 1172 /* Low Nyquist band edge filter */ | |
| 1173 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; | |
| 1174 s->symbol_sync_low[1] = s->symbol_sync_low[0]; | |
| 1175 s->symbol_sync_low[0] = v; | |
| 1176 /* High Nyquist band edge filter */ | |
| 1177 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; | |
| 1178 s->symbol_sync_high[1] = s->symbol_sync_high[0]; | |
| 1179 s->symbol_sync_high[0] = v; | |
| 1180 | |
| 1181 /* Put things into the equalization buffer at T/2 rate. The symbol sync. | |
| 1182 will fiddle the step to align this with the symbols. */ | |
| 1183 if (s->eq_put_step <= 0) | |
| 1184 { | |
| 1185 /* Only AGC until we have locked down the setting. */ | |
| 1186 if (s->agc_scaling_save == 0.0f) | |
| 1187 s->agc_scaling = (1.0f/RX_PULSESHAPER_GAIN)*2.17f/sqrtf(power); | |
| 1188 /* Pulse shape while still at the carrier frequency, using a quadrature | |
| 1189 pair of filters. This results in a properly bandpass filtered complex | |
| 1190 signal, which can be brought directly to baseband by complex mixing. | |
| 1191 No further filtering, to remove mixer harmonics, is needed. */ | |
| 1192 step = -s->eq_put_step; | |
| 1193 if (step > RX_PULSESHAPER_COEFF_SETS - 1) | |
| 1194 step = RX_PULSESHAPER_COEFF_SETS - 1; | |
| 1195 s->eq_put_step += RX_PULSESHAPER_COEFF_SETS*10/(3*2); | |
| 1196 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1197 vi = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_im[step], V17_RX_FILTER_STEPS, s->rrc_filter_step); | |
| 1198 //sample.im = (vi*(int32_t) s->agc_scaling) >> 15; | |
| 1199 sample.im = vi*s->agc_scaling; | |
| 1200 z = dds_lookup_complexf(s->carrier_phase); | |
| 1201 zz.re = sample.re*z.re - sample.im*z.im; | |
| 1202 zz.im = -sample.re*z.im - sample.im*z.re; | |
| 1203 #else | |
| 1204 v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_im[step], V17_RX_FILTER_STEPS, s->rrc_filter_step); | |
| 1205 sample.im = v*s->agc_scaling; | |
| 1206 z = dds_lookup_complexf(s->carrier_phase); | |
| 1207 zz.re = sample.re*z.re - sample.im*z.im; | |
| 1208 zz.im = -sample.re*z.im - sample.im*z.re; | |
| 1209 #endif | |
| 1210 process_half_baud(s, &zz); | |
| 1211 } | |
| 1212 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1213 dds_advance(&s->carrier_phase, s->carrier_phase_rate); | |
| 1214 #else | |
| 1215 dds_advancef(&s->carrier_phase, s->carrier_phase_rate); | |
| 1216 #endif | |
| 1217 } | |
| 1218 return 0; | |
| 1219 } | |
| 1220 /*- End of function --------------------------------------------------------*/ | |
| 1221 | |
| 1222 SPAN_DECLARE(int) v17_rx_fillin(v17_rx_state_t *s, int len) | |
| 1223 { | |
| 1224 int i; | |
| 1225 | |
| 1226 /* We want to sustain the current state (i.e carrier on<->carrier off), and | |
| 1227 try to sustain the carrier phase. We should probably push the filters, as well */ | |
| 1228 span_log(&s->logging, SPAN_LOG_FLOW, "Fill-in %d samples\n", len); | |
| 1229 if (s->signal_present <= 0) | |
| 1230 return 0; | |
| 1231 if (s->training_stage == TRAINING_STAGE_PARKED) | |
| 1232 return 0; | |
| 1233 for (i = 0; i < len; i++) | |
| 1234 { | |
| 1235 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1236 dds_advance(&s->carrier_phase, s->carrier_phase_rate); | |
| 1237 #else | |
| 1238 dds_advancef(&s->carrier_phase, s->carrier_phase_rate); | |
| 1239 #endif | |
| 1240 /* Advance the symbol phase the appropriate amount */ | |
| 1241 s->eq_put_step -= RX_PULSESHAPER_COEFF_SETS; | |
| 1242 if (s->eq_put_step <= 0) | |
| 1243 s->eq_put_step += RX_PULSESHAPER_COEFF_SETS*10/(3*2); | |
| 1244 /* TODO: Should we rotate any buffers */ | |
| 1245 } | |
| 1246 return 0; | |
| 1247 } | |
| 1248 /*- End of function --------------------------------------------------------*/ | |
| 1249 | |
| 1250 SPAN_DECLARE(void) v17_rx_set_put_bit(v17_rx_state_t *s, put_bit_func_t put_bit, void *user_data) | |
| 1251 { | |
| 1252 s->put_bit = put_bit; | |
| 1253 s->put_bit_user_data = user_data; | |
| 1254 } | |
| 1255 /*- End of function --------------------------------------------------------*/ | |
| 1256 | |
| 1257 SPAN_DECLARE(void) v17_rx_set_modem_status_handler(v17_rx_state_t *s, modem_tx_status_func_t handler, void *user_data) | |
| 1258 { | |
| 1259 s->status_handler = handler; | |
| 1260 s->status_user_data = user_data; | |
| 1261 } | |
| 1262 /*- End of function --------------------------------------------------------*/ | |
| 1263 | |
| 1264 SPAN_DECLARE(logging_state_t *) v17_rx_get_logging_state(v17_rx_state_t *s) | |
| 1265 { | |
| 1266 return &s->logging; | |
| 1267 } | |
| 1268 /*- End of function --------------------------------------------------------*/ | |
| 1269 | |
| 1270 SPAN_DECLARE(int) v17_rx_restart(v17_rx_state_t *s, int bit_rate, int short_train) | |
| 1271 { | |
| 1272 int i; | |
| 1273 | |
| 1274 span_log(&s->logging, SPAN_LOG_FLOW, "Restarting V.17, %dbps, %s training\n", bit_rate, (short_train) ? "short" : "long"); | |
| 1275 switch (bit_rate) | |
| 1276 { | |
| 1277 case 14400: | |
| 1278 s->constellation = v17_v32bis_14400_constellation; | |
| 1279 s->space_map = 0; | |
| 1280 s->bits_per_symbol = 6; | |
| 1281 break; | |
| 1282 case 12000: | |
| 1283 s->constellation = v17_v32bis_12000_constellation; | |
| 1284 s->space_map = 1; | |
| 1285 s->bits_per_symbol = 5; | |
| 1286 break; | |
| 1287 case 9600: | |
| 1288 s->constellation = v17_v32bis_9600_constellation; | |
| 1289 s->space_map = 2; | |
| 1290 s->bits_per_symbol = 4; | |
| 1291 break; | |
| 1292 case 7200: | |
| 1293 s->constellation = v17_v32bis_7200_constellation; | |
| 1294 s->space_map = 3; | |
| 1295 s->bits_per_symbol = 3; | |
| 1296 break; | |
| 1297 case 4800: | |
| 1298 /* This does not exist in the V.17 spec as a valid mode of operation. | |
| 1299 However, it does exist in V.32bis, so it is here for completeness. */ | |
| 1300 s->constellation = v17_v32bis_4800_constellation; | |
| 1301 s->space_map = 0; | |
| 1302 s->bits_per_symbol = 2; | |
| 1303 break; | |
| 1304 default: | |
| 1305 return -1; | |
| 1306 } | |
| 1307 s->bit_rate = bit_rate; | |
| 1308 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 1309 vec_zeroi16(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
| 1310 #else | |
| 1311 vec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
| 1312 #endif | |
| 1313 s->rrc_filter_step = 0; | |
| 1314 | |
| 1315 s->diff = 1; | |
| 1316 s->scramble_reg = 0x2ECDD5; | |
| 1317 s->training_stage = TRAINING_STAGE_SYMBOL_ACQUISITION; | |
| 1318 s->training_count = 0; | |
| 1319 s->training_error = 0.0f; | |
| 1320 s->signal_present = 0; | |
| 1321 #if defined(IAXMODEM_STUFF) | |
| 1322 s->high_sample = 0; | |
| 1323 s->low_samples = 0; | |
| 1324 s->carrier_drop_pending = FALSE; | |
| 1325 #endif | |
| 1326 if (short_train != 2) | |
| 1327 s->short_train = short_train; | |
| 1328 memset(s->start_angles, 0, sizeof(s->start_angles)); | |
| 1329 memset(s->angles, 0, sizeof(s->angles)); | |
| 1330 | |
| 1331 /* Initialise the TCM decoder parameters. */ | |
| 1332 /* The accumulated distance vectors are set so state zero starts | |
| 1333 at a value of zero, and all others start larger. This forces the | |
| 1334 initial paths to merge at the zero states. */ | |
| 1335 for (i = 0; i < 8; i++) | |
| 1336 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 1337 s->distances[i] = 99*DIST_FACTOR*DIST_FACTOR; | |
| 1338 #else | |
| 1339 s->distances[i] = 99.0f; | |
| 1340 #endif | |
| 1341 memset(s->full_path_to_past_state_locations, 0, sizeof(s->full_path_to_past_state_locations)); | |
| 1342 memset(s->past_state_locations, 0, sizeof(s->past_state_locations)); | |
| 1343 s->distances[0] = 0; | |
| 1344 s->trellis_ptr = 14; | |
| 1345 | |
| 1346 span_log(&s->logging, SPAN_LOG_FLOW, "Phase rates %f %f\n", dds_frequencyf(s->carrier_phase_rate), dds_frequencyf(s->carrier_phase_rate_save)); | |
| 1347 s->carrier_phase = 0; | |
| 1348 power_meter_init(&(s->power), 4); | |
| 1349 | |
| 1350 if (s->short_train) | |
| 1351 { | |
| 1352 s->carrier_phase_rate = s->carrier_phase_rate_save; | |
| 1353 s->agc_scaling = s->agc_scaling_save; | |
| 1354 equalizer_restore(s); | |
| 1355 /* Don't allow any frequency correction at all, until we start to pull the phase in. */ | |
| 1356 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 1357 s->carrier_track_i = 0; | |
| 1358 s->carrier_track_p = 40000; | |
| 1359 #else | |
| 1360 s->carrier_track_i = 0.0f; | |
| 1361 s->carrier_track_p = 40000.0f; | |
| 1362 #endif | |
| 1363 } | |
| 1364 else | |
| 1365 { | |
| 1366 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ); | |
| 1367 equalizer_reset(s); | |
| 1368 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 1369 s->agc_scaling_save = 0; | |
| 1370 s->agc_scaling = (float) FP_FACTOR*32768.0f*0.0017f/RX_PULSESHAPER_GAIN; | |
| 1371 s->carrier_track_i = 5000; | |
| 1372 s->carrier_track_p = 40000; | |
| 1373 #else | |
| 1374 s->agc_scaling_save = 0.0f; | |
| 1375 s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN; | |
| 1376 s->carrier_track_i = 5000.0f; | |
| 1377 s->carrier_track_p = 40000.0f; | |
| 1378 #endif | |
| 1379 } | |
| 1380 s->last_sample = 0; | |
| 1381 | |
| 1382 /* Initialise the working data for symbol timing synchronisation */ | |
| 1383 #if defined(SPANDSP_USE_FIXED_POINTx) | |
| 1384 for (i = 0; i < 2; i++) | |
| 1385 { | |
| 1386 s->symbol_sync_low[i] = 0; | |
| 1387 s->symbol_sync_high[i] = 0; | |
| 1388 s->symbol_sync_dc_filter[i] = 0; | |
| 1389 } | |
| 1390 s->baud_phase = 0; | |
| 1391 #else | |
| 1392 for (i = 0; i < 2; i++) | |
| 1393 { | |
| 1394 s->symbol_sync_low[i] = 0.0f; | |
| 1395 s->symbol_sync_high[i] = 0.0f; | |
| 1396 s->symbol_sync_dc_filter[i] = 0.0f; | |
| 1397 } | |
| 1398 s->baud_phase = 0.0f; | |
| 1399 #endif | |
| 1400 s->baud_half = 0; | |
| 1401 | |
| 1402 s->total_baud_timing_correction = 0; | |
| 1403 | |
| 1404 return 0; | |
| 1405 } | |
| 1406 /*- End of function --------------------------------------------------------*/ | |
| 1407 | |
| 1408 SPAN_DECLARE(v17_rx_state_t *) v17_rx_init(v17_rx_state_t *s, int bit_rate, put_bit_func_t put_bit, void *user_data) | |
| 1409 { | |
| 1410 switch (bit_rate) | |
| 1411 { | |
| 1412 case 14400: | |
| 1413 case 12000: | |
| 1414 case 9600: | |
| 1415 case 7200: | |
| 1416 case 4800: | |
| 1417 /* 4800 is an extension of V.17, to provide full converage of the V.32bis modes */ | |
| 1418 break; | |
| 1419 default: | |
| 1420 return NULL; | |
| 1421 } | |
| 1422 if (s == NULL) | |
| 1423 { | |
| 1424 if ((s = (v17_rx_state_t *) malloc(sizeof(*s))) == NULL) | |
| 1425 return NULL; | |
| 1426 } | |
| 1427 memset(s, 0, sizeof(*s)); | |
| 1428 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
| 1429 span_log_set_protocol(&s->logging, "V.17 RX"); | |
| 1430 s->put_bit = put_bit; | |
| 1431 s->put_bit_user_data = user_data; | |
| 1432 s->short_train = FALSE; | |
| 1433 //s->scrambler_tap = 18 - 1; | |
| 1434 v17_rx_signal_cutoff(s, -45.5f); | |
| 1435 s->agc_scaling = 0.0017f/RX_PULSESHAPER_GAIN; | |
| 1436 s->agc_scaling_save = 0.0f; | |
| 1437 s->carrier_phase_rate_save = dds_phase_ratef(CARRIER_NOMINAL_FREQ); | |
| 1438 v17_rx_restart(s, bit_rate, s->short_train); | |
| 1439 return s; | |
| 1440 } | |
| 1441 /*- End of function --------------------------------------------------------*/ | |
| 1442 | |
| 1443 SPAN_DECLARE(int) v17_rx_release(v17_rx_state_t *s) | |
| 1444 { | |
| 1445 return 0; | |
| 1446 } | |
| 1447 /*- End of function --------------------------------------------------------*/ | |
| 1448 | |
| 1449 SPAN_DECLARE(int) v17_rx_free(v17_rx_state_t *s) | |
| 1450 { | |
| 1451 free(s); | |
| 1452 return 0; | |
| 1453 } | |
| 1454 /*- End of function --------------------------------------------------------*/ | |
| 1455 | |
| 1456 SPAN_DECLARE(void) v17_rx_set_qam_report_handler(v17_rx_state_t *s, qam_report_handler_t handler, void *user_data) | |
| 1457 { | |
| 1458 s->qam_report = handler; | |
| 1459 s->qam_user_data = user_data; | |
| 1460 } | |
| 1461 /*- End of function --------------------------------------------------------*/ | |
| 1462 /*- End of file ------------------------------------------------------------*/ |
