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 ------------------------------------------------------------*/ |