Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v27ter_rx.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 * v27ter_rx.c - ITU V.27ter modem receive part | |
6 * | |
7 * Written by Steve Underwood <steveu@coppice.org> | |
8 * | |
9 * Copyright (C) 2003 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: v27ter_rx.c,v 1.131 2009/07/08 15:11:09 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/v27ter_rx.h" | |
62 | |
63 #include "spandsp/private/logging.h" | |
64 #include "spandsp/private/v27ter_rx.h" | |
65 | |
66 #if defined(SPANDSP_USE_FIXED_POINT) | |
67 #include "v27ter_rx_4800_fixed_rrc.h" | |
68 #include "v27ter_rx_2400_fixed_rrc.h" | |
69 #else | |
70 #include "v27ter_rx_4800_floating_rrc.h" | |
71 #include "v27ter_rx_2400_floating_rrc.h" | |
72 #endif | |
73 | |
74 /* V.27ter is a DPSK modem, but this code treats it like QAM. It nails down the | |
75 signal to a static constellation, even though dealing with differences is all | |
76 that is necessary. */ | |
77 | |
78 /*! The nominal frequency of the carrier, in Hertz */ | |
79 #define CARRIER_NOMINAL_FREQ 1800.0f | |
80 /*! The nominal baud or symbol rate in 2400bps mode */ | |
81 #define BAUD_RATE_2400 1200 | |
82 /*! The nominal baud or symbol rate in 4800bps mode */ | |
83 #define BAUD_RATE_4800 1600 | |
84 /*! The adaption rate coefficient for the equalizer */ | |
85 #define EQUALIZER_DELTA 0.25f | |
86 | |
87 #if defined(SPANDSP_USE_FIXED_POINT) | |
88 #define FP_FACTOR 4096 | |
89 #define FP_SHIFT_FACTOR 12 | |
90 #endif | |
91 | |
92 /* Segments of the training sequence */ | |
93 /* V.27ter defines a long and a short sequence. FAX doesn't use the | |
94 short sequence, so it is not implemented here. */ | |
95 /*! The length of training segment 3, in symbols */ | |
96 #define V27TER_TRAINING_SEG_3_LEN 50 | |
97 /*! The length of training segment 5, in symbols */ | |
98 #define V27TER_TRAINING_SEG_5_LEN 1074 | |
99 /*! The length of training segment 6, in symbols */ | |
100 #define V27TER_TRAINING_SEG_6_LEN 8 | |
101 | |
102 /*! The length of the equalizer buffer */ | |
103 #define V27TER_EQUALIZER_LEN (V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN) | |
104 | |
105 enum | |
106 { | |
107 TRAINING_STAGE_NORMAL_OPERATION = 0, | |
108 TRAINING_STAGE_SYMBOL_ACQUISITION, | |
109 TRAINING_STAGE_LOG_PHASE, | |
110 TRAINING_STAGE_WAIT_FOR_HOP, | |
111 TRAINING_STAGE_TRAIN_ON_ABAB, | |
112 TRAINING_STAGE_TEST_ONES, | |
113 TRAINING_STAGE_PARKED | |
114 }; | |
115 | |
116 #if defined(SPANDSP_USE_FIXED_POINTx) | |
117 static const complexi16_t v27ter_constellation[8] = | |
118 { | |
119 {((int)(FP_FACTOR* 1.414f), ((int)(FP_FACTOR* 0.0f)}, /* 0deg */ | |
120 {((int)(FP_FACTOR* 1.0f), ((int)(FP_FACTOR* 1.0f)}, /* 45deg */ | |
121 {((int)(FP_FACTOR* 0.0f), ((int)(FP_FACTOR* 1.414f)}, /* 90deg */ | |
122 {((int)(FP_FACTOR*-1.0f), ((int)(FP_FACTOR* 1.0f)}, /* 135deg */ | |
123 {((int)(FP_FACTOR*-1.414f), ((int)(FP_FACTOR* 0.0f)}, /* 180deg */ | |
124 {((int)(FP_FACTOR*-1.0f), ((int)(FP_FACTOR*-1.0f)}, /* 225deg */ | |
125 {((int)(FP_FACTOR* 0.0f), ((int)(FP_FACTOR*-1.414f)}, /* 270deg */ | |
126 {((int)(FP_FACTOR* 1.0f), ((int)(FP_FACTOR*-1.0f)} /* 315deg */ | |
127 }; | |
128 #else | |
129 static const complexf_t v27ter_constellation[8] = | |
130 { | |
131 { 1.414f, 0.0f}, /* 0deg */ | |
132 { 1.0f, 1.0f}, /* 45deg */ | |
133 { 0.0f, 1.414f}, /* 90deg */ | |
134 {-1.0f, 1.0f}, /* 135deg */ | |
135 {-1.414f, 0.0f}, /* 180deg */ | |
136 {-1.0f, -1.0f}, /* 225deg */ | |
137 { 0.0f, -1.414f}, /* 270deg */ | |
138 { 1.0f, -1.0f} /* 315deg */ | |
139 }; | |
140 #endif | |
141 | |
142 SPAN_DECLARE(float) v27ter_rx_carrier_frequency(v27ter_rx_state_t *s) | |
143 { | |
144 return dds_frequencyf(s->carrier_phase_rate); | |
145 } | |
146 /*- End of function --------------------------------------------------------*/ | |
147 | |
148 SPAN_DECLARE(float) v27ter_rx_symbol_timing_correction(v27ter_rx_state_t *s) | |
149 { | |
150 int steps_per_symbol; | |
151 | |
152 steps_per_symbol = (s->bit_rate == 4800) ? RX_PULSESHAPER_4800_COEFF_SETS*5 : RX_PULSESHAPER_2400_COEFF_SETS*20/3; | |
153 return (float) s->total_baud_timing_correction/(float) steps_per_symbol; | |
154 } | |
155 /*- End of function --------------------------------------------------------*/ | |
156 | |
157 SPAN_DECLARE(float) v27ter_rx_signal_power(v27ter_rx_state_t *s) | |
158 { | |
159 return power_meter_current_dbm0(&s->power) + 3.98f; | |
160 } | |
161 /*- End of function --------------------------------------------------------*/ | |
162 | |
163 SPAN_DECLARE(void) v27ter_rx_signal_cutoff(v27ter_rx_state_t *s, float cutoff) | |
164 { | |
165 /* The 0.4 factor allows for the gain of the DC blocker */ | |
166 s->carrier_on_power = (int32_t) (power_meter_level_dbm0(cutoff + 2.5f)*0.4f); | |
167 s->carrier_off_power = (int32_t) (power_meter_level_dbm0(cutoff - 2.5f)*0.4f); | |
168 } | |
169 /*- End of function --------------------------------------------------------*/ | |
170 | |
171 static void report_status_change(v27ter_rx_state_t *s, int status) | |
172 { | |
173 if (s->status_handler) | |
174 s->status_handler(s->status_user_data, status); | |
175 else if (s->put_bit) | |
176 s->put_bit(s->put_bit_user_data, status); | |
177 } | |
178 /*- End of function --------------------------------------------------------*/ | |
179 | |
180 #if defined(SPANDSP_USE_FIXED_POINTx) | |
181 SPAN_DECLARE(int) v27ter_rx_equalizer_state(v27ter_rx_state_t *s, complexi16_t **coeffs) | |
182 #else | |
183 SPAN_DECLARE(int) v27ter_rx_equalizer_state(v27ter_rx_state_t *s, complexf_t **coeffs) | |
184 #endif | |
185 { | |
186 *coeffs = s->eq_coeff; | |
187 return V27TER_EQUALIZER_LEN; | |
188 } | |
189 /*- End of function --------------------------------------------------------*/ | |
190 | |
191 static void equalizer_save(v27ter_rx_state_t *s) | |
192 { | |
193 #if defined(SPANDSP_USE_FIXED_POINTx) | |
194 cvec_copyi16(s->eq_coeff_save, s->eq_coeff, V27TER_EQUALIZER_LEN); | |
195 #else | |
196 cvec_copyf(s->eq_coeff_save, s->eq_coeff, V27TER_EQUALIZER_LEN); | |
197 #endif | |
198 } | |
199 /*- End of function --------------------------------------------------------*/ | |
200 | |
201 static void equalizer_restore(v27ter_rx_state_t *s) | |
202 { | |
203 #if defined(SPANDSP_USE_FIXED_POINTx) | |
204 cvec_copyi16(s->eq_coeff, s->eq_coeff_save, V27TER_EQUALIZER_LEN); | |
205 cvec_zeroi16(s->eq_buf, V27TER_EQUALIZER_LEN); | |
206 s->eq_delta = 32768.0f*EQUALIZER_DELTA/V27TER_EQUALIZER_LEN); | |
207 #else | |
208 cvec_copyf(s->eq_coeff, s->eq_coeff_save, V27TER_EQUALIZER_LEN); | |
209 cvec_zerof(s->eq_buf, V27TER_EQUALIZER_LEN); | |
210 s->eq_delta = EQUALIZER_DELTA/V27TER_EQUALIZER_LEN; | |
211 #endif | |
212 | |
213 s->eq_put_step = (s->bit_rate == 4800) ? RX_PULSESHAPER_4800_COEFF_SETS*5/2 : RX_PULSESHAPER_2400_COEFF_SETS*20/(3*2); | |
214 s->eq_step = 0; | |
215 } | |
216 /*- End of function --------------------------------------------------------*/ | |
217 | |
218 static void equalizer_reset(v27ter_rx_state_t *s) | |
219 { | |
220 /* Start with an equalizer based on everything being perfect. */ | |
221 #if defined(SPANDSP_USE_FIXED_POINTx) | |
222 cvec_zeroi16(s->eq_coeff, V27TER_EQUALIZER_LEN); | |
223 s->eq_coeff[V27TER_EQUALIZER_PRE_LEN] = complex_seti16(1.414f*FP_FACTOR, 0); | |
224 cvec_zeroi16(s->eq_buf, V27TER_EQUALIZER_LEN); | |
225 s->eq_delta = 32768.0f*EQUALIZER_DELTA/V27TER_EQUALIZER_LEN); | |
226 #else | |
227 cvec_zerof(s->eq_coeff, V27TER_EQUALIZER_LEN); | |
228 s->eq_coeff[V27TER_EQUALIZER_PRE_LEN] = complex_setf(1.414f, 0.0f); | |
229 cvec_zerof(s->eq_buf, V27TER_EQUALIZER_LEN); | |
230 s->eq_delta = EQUALIZER_DELTA/V27TER_EQUALIZER_LEN; | |
231 #endif | |
232 | |
233 s->eq_put_step = (s->bit_rate == 4800) ? RX_PULSESHAPER_4800_COEFF_SETS*5/2 : RX_PULSESHAPER_2400_COEFF_SETS*20/(3*2); | |
234 s->eq_step = 0; | |
235 } | |
236 /*- End of function --------------------------------------------------------*/ | |
237 | |
238 #if defined(SPANDSP_USE_FIXED_POINTx) | |
239 static __inline__ complexi16_t complex_mul_q4_12(const complexi16_t *x, const complexi16_t *y) | |
240 { | |
241 complexi16_t z; | |
242 | |
243 z.re = ((int32_t) x->re*(int32_t) y->re - (int32_t) x->im*(int32_t) y->im) >> 12; | |
244 z.im = ((int32_t) x->re*(int32_t) y->im + (int32_t) x->im*(int32_t) y->re) >> 12; | |
245 return z; | |
246 } | |
247 /*- End of function --------------------------------------------------------*/ | |
248 #endif | |
249 | |
250 #if defined(SPANDSP_USE_FIXED_POINTx) | |
251 static __inline__ complexi16_t equalizer_get(v27ter_rx_state_t *s) | |
252 #else | |
253 static __inline__ complexf_t equalizer_get(v27ter_rx_state_t *s) | |
254 #endif | |
255 { | |
256 #if defined(SPANDSP_USE_FIXED_POINTx) | |
257 complexi32_t zz; | |
258 complexi16_t z; | |
259 | |
260 /* Get the next equalized value. */ | |
261 zz = cvec_circular_dot_prodi16(s->eq_buf, s->eq_coeff, V27TER_EQUALIZER_LEN, s->eq_step); | |
262 z.re = zz.re >> FP_SHIFT_FACTOR; | |
263 z.im = zz.im >> FP_SHIFT_FACTOR; | |
264 return z; | |
265 #else | |
266 /* Get the next equalized value. */ | |
267 return cvec_circular_dot_prodf(s->eq_buf, s->eq_coeff, V27TER_EQUALIZER_LEN, s->eq_step); | |
268 #endif | |
269 } | |
270 /*- End of function --------------------------------------------------------*/ | |
271 | |
272 #if defined(SPANDSP_USE_FIXED_POINTx) | |
273 static void tune_equalizer(v27ter_rx_state_t *s, const complexi16_t *z, const complexi16_t *target) | |
274 { | |
275 complexi16_t err; | |
276 | |
277 /* Find the x and y mismatch from the exact constellation position. */ | |
278 err.re = target->re*FP_FACTOR - z->re; | |
279 err.im = target->im*FP_FACTOR - z->im; | |
280 err.re = ((int32_t) err.re*(int32_t) s->eq_delta) >> 15; | |
281 err.im = ((int32_t) err.im*(int32_t) s->eq_delta) >> 15; | |
282 cvec_circular_lmsi16(s->eq_buf, s->eq_coeff, V27TER_EQUALIZER_LEN, s->eq_step, &err); | |
283 } | |
284 #else | |
285 static void tune_equalizer(v27ter_rx_state_t *s, const complexf_t *z, const complexf_t *target) | |
286 { | |
287 complexf_t err; | |
288 | |
289 /* Find the x and y mismatch from the exact constellation position. */ | |
290 err = complex_subf(target, z); | |
291 err.re *= s->eq_delta; | |
292 err.im *= s->eq_delta; | |
293 cvec_circular_lmsf(s->eq_buf, s->eq_coeff, V27TER_EQUALIZER_LEN, s->eq_step, &err); | |
294 } | |
295 #endif | |
296 /*- End of function --------------------------------------------------------*/ | |
297 | |
298 #if defined(SPANDSP_USE_FIXED_POINTx) | |
299 static __inline__ int find_quadrant(const complexi16_t *z) | |
300 #else | |
301 static __inline__ int find_quadrant(const complexf_t *z) | |
302 #endif | |
303 { | |
304 int b1; | |
305 int b2; | |
306 | |
307 /* Split the space along the two diagonals. */ | |
308 b1 = (z->im > z->re); | |
309 b2 = (z->im < -z->re); | |
310 return (b2 << 1) | (b1 ^ b2); | |
311 } | |
312 /*- End of function --------------------------------------------------------*/ | |
313 | |
314 #if defined(SPANDSP_USE_FIXED_POINTx) | |
315 static __inline__ int find_octant(complexi16_t *z) | |
316 #else | |
317 static __inline__ int find_octant(complexf_t *z) | |
318 #endif | |
319 { | |
320 float abs_re; | |
321 float abs_im; | |
322 int b1; | |
323 int b2; | |
324 int bits; | |
325 | |
326 /* Are we near an axis or a diagonal? */ | |
327 abs_re = fabsf(z->re); | |
328 abs_im = fabsf(z->im); | |
329 if (abs_im > abs_re*0.4142136f && abs_im < abs_re*2.4142136f) | |
330 { | |
331 /* Split the space along the two axes. */ | |
332 b1 = (z->re < 0.0f); | |
333 b2 = (z->im < 0.0f); | |
334 bits = (b2 << 2) | ((b1 ^ b2) << 1) | 1; | |
335 } | |
336 else | |
337 { | |
338 /* Split the space along the two diagonals. */ | |
339 b1 = (z->im > z->re); | |
340 b2 = (z->im < -z->re); | |
341 bits = (b2 << 2) | ((b1 ^ b2) << 1); | |
342 } | |
343 return bits; | |
344 } | |
345 /*- End of function --------------------------------------------------------*/ | |
346 | |
347 #if defined(SPANDSP_USE_FIXED_POINTx) | |
348 static __inline__ void track_carrier(v27ter_rx_state_t *s, const complexi16_t *z, const complexi16_t *target) | |
349 #else | |
350 static __inline__ void track_carrier(v27ter_rx_state_t *s, const complexf_t *z, const complexf_t *target) | |
351 #endif | |
352 { | |
353 #if defined(SPANDSP_USE_FIXED_POINTx) | |
354 int32_t error; | |
355 #else | |
356 float error; | |
357 #endif | |
358 | |
359 /* For small errors the imaginary part of the difference between the actual and the target | |
360 positions is proportional to the phase error, for any particular target. However, the | |
361 different amplitudes of the various target positions scale things. */ | |
362 error = z->im*target->re - z->re*target->im; | |
363 | |
364 #if defined(SPANDSP_USE_FIXED_POINTx) | |
365 error /= (float) FP_FACTOR; | |
366 s->carrier_phase_rate += (int32_t) (s->carrier_track_i*error); | |
367 s->carrier_phase += (int32_t) (s->carrier_track_p*error); | |
368 #else | |
369 s->carrier_phase_rate += (int32_t) (s->carrier_track_i*error); | |
370 s->carrier_phase += (int32_t) (s->carrier_track_p*error); | |
371 //span_log(&s->logging, SPAN_LOG_FLOW, "Im = %15.5f f = %15.5f\n", error, dds_frequencyf(s->carrier_phase_rate)); | |
372 #endif | |
373 } | |
374 /*- End of function --------------------------------------------------------*/ | |
375 | |
376 static __inline__ int descramble(v27ter_rx_state_t *s, int in_bit) | |
377 { | |
378 int out_bit; | |
379 | |
380 out_bit = (in_bit ^ (s->scramble_reg >> 5) ^ (s->scramble_reg >> 6)) & 1; | |
381 if (s->scrambler_pattern_count >= 33) | |
382 { | |
383 out_bit ^= 1; | |
384 s->scrambler_pattern_count = 0; | |
385 } | |
386 else | |
387 { | |
388 if (s->training_stage > TRAINING_STAGE_NORMAL_OPERATION && s->training_stage < TRAINING_STAGE_TEST_ONES) | |
389 { | |
390 s->scrambler_pattern_count = 0; | |
391 } | |
392 else | |
393 { | |
394 if ((((s->scramble_reg >> 7) ^ in_bit) & ((s->scramble_reg >> 8) ^ in_bit) & ((s->scramble_reg >> 11) ^ in_bit) & 1)) | |
395 s->scrambler_pattern_count = 0; | |
396 else | |
397 s->scrambler_pattern_count++; | |
398 } | |
399 } | |
400 s->scramble_reg <<= 1; | |
401 if (s->training_stage > TRAINING_STAGE_NORMAL_OPERATION && s->training_stage < TRAINING_STAGE_TEST_ONES) | |
402 s->scramble_reg |= out_bit; | |
403 else | |
404 s->scramble_reg |= in_bit; | |
405 return out_bit; | |
406 } | |
407 /*- End of function --------------------------------------------------------*/ | |
408 | |
409 static __inline__ void put_bit(v27ter_rx_state_t *s, int bit) | |
410 { | |
411 int out_bit; | |
412 | |
413 bit &= 1; | |
414 | |
415 out_bit = descramble(s, bit); | |
416 | |
417 /* We need to strip the last part of the training before we let data | |
418 go to the application. */ | |
419 if (s->training_stage == TRAINING_STAGE_NORMAL_OPERATION) | |
420 { | |
421 s->put_bit(s->put_bit_user_data, out_bit); | |
422 } | |
423 else | |
424 { | |
425 //span_log(&s->logging, SPAN_LOG_FLOW, "Test bit %d\n", out_bit); | |
426 /* The bits during the final stage of training should be all ones. However, | |
427 buggy modems mean you cannot rely on this. Therefore we don't bother | |
428 testing for ones, but just rely on a constellation mismatch measurement. */ | |
429 } | |
430 } | |
431 /*- End of function --------------------------------------------------------*/ | |
432 | |
433 #if defined(SPANDSP_USE_FIXED_POINTx) | |
434 static void decode_baud(v27ter_rx_state_t *s, complexi16_t *z) | |
435 #else | |
436 static void decode_baud(v27ter_rx_state_t *s, complexf_t *z) | |
437 #endif | |
438 { | |
439 static const uint8_t phase_steps_4800[8] = | |
440 { | |
441 4, 0, 2, 6, 7, 3, 1, 5 | |
442 }; | |
443 static const uint8_t phase_steps_2400[4] = | |
444 { | |
445 0, 2, 3, 1 | |
446 }; | |
447 int nearest; | |
448 int raw_bits; | |
449 | |
450 if (s->bit_rate == 2400) | |
451 { | |
452 nearest = find_quadrant(z); | |
453 raw_bits = phase_steps_2400[(nearest - s->constellation_state) & 3]; | |
454 put_bit(s, raw_bits); | |
455 put_bit(s, raw_bits >> 1); | |
456 s->constellation_state = nearest; | |
457 nearest <<= 1; | |
458 } | |
459 else | |
460 { | |
461 nearest = find_octant(z); | |
462 raw_bits = phase_steps_4800[(nearest - s->constellation_state) & 7]; | |
463 put_bit(s, raw_bits); | |
464 put_bit(s, raw_bits >> 1); | |
465 put_bit(s, raw_bits >> 2); | |
466 s->constellation_state = nearest; | |
467 } | |
468 track_carrier(s, z, &v27ter_constellation[nearest]); | |
469 if (--s->eq_skip <= 0) | |
470 { | |
471 /* Once we are in the data the equalization should not need updating. | |
472 However, the line characteristics may slowly drift. We, therefore, | |
473 tune up on the occassional sample, keeping the compute down. */ | |
474 s->eq_skip = 100; | |
475 tune_equalizer(s, z, &v27ter_constellation[nearest]); | |
476 } | |
477 } | |
478 /*- End of function --------------------------------------------------------*/ | |
479 | |
480 static __inline__ void symbol_sync(v27ter_rx_state_t *s) | |
481 { | |
482 float p; | |
483 float q; | |
484 | |
485 /* This routine adapts the position of the half baud samples entering the equalizer. */ | |
486 | |
487 /* Perform a Gardner test for baud alignment */ | |
488 p = s->eq_buf[(s->eq_step - 3) & V27TER_EQUALIZER_LEN].re | |
489 - s->eq_buf[(s->eq_step - 1) & V27TER_EQUALIZER_LEN].re; | |
490 p *= s->eq_buf[(s->eq_step - 2) & V27TER_EQUALIZER_LEN].re; | |
491 | |
492 q = s->eq_buf[(s->eq_step - 3) & V27TER_EQUALIZER_LEN].im | |
493 - s->eq_buf[(s->eq_step - 1) & V27TER_EQUALIZER_LEN].im; | |
494 q *= s->eq_buf[(s->eq_step - 2) & V27TER_EQUALIZER_LEN].im; | |
495 | |
496 s->gardner_integrate += (p + q > 0.0f) ? s->gardner_step : -s->gardner_step; | |
497 | |
498 if (abs(s->gardner_integrate) >= 256) | |
499 { | |
500 /* This integrate and dump approach avoids rapid changes of the equalizer put step. | |
501 Rapid changes, without hysteresis, are bad. They degrade the equalizer performance | |
502 when the true symbol boundary is close to a sample boundary. */ | |
503 //span_log(&s->logging, SPAN_LOG_FLOW, "Hop %d\n", s->gardner_integrate); | |
504 s->eq_put_step += (s->gardner_integrate/256); | |
505 s->total_baud_timing_correction += (s->gardner_integrate/256); | |
506 if (s->qam_report) | |
507 s->qam_report(s->qam_user_data, NULL, NULL, s->gardner_integrate); | |
508 s->gardner_integrate = 0; | |
509 } | |
510 //span_log(&s->logging, SPAN_LOG_FLOW, "Gardner=%10.5f 0x%X\n", p, s->eq_put_step); | |
511 } | |
512 /*- End of function --------------------------------------------------------*/ | |
513 | |
514 #if defined(SPANDSP_USE_FIXED_POINT) | |
515 static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexi16_t *sample) | |
516 #else | |
517 static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t *sample) | |
518 #endif | |
519 { | |
520 static const int abab_pos[2] = | |
521 { | |
522 0, 4 | |
523 }; | |
524 complexf_t zz; | |
525 #if defined(SPANDSP_USE_FIXED_POINTx) | |
526 complexf_t z1; | |
527 complexi16_t z; | |
528 const complexi16_t *target; | |
529 static const complexi16_t zero = {0, 0}; | |
530 #else | |
531 complexf_t z; | |
532 const complexf_t *target; | |
533 static const complexf_t zero = {0.0f, 0.0f}; | |
534 #endif | |
535 float p; | |
536 int i; | |
537 int j; | |
538 int32_t angle; | |
539 int32_t ang; | |
540 int constellation_state; | |
541 | |
542 /* Add a sample to the equalizer's circular buffer, but don't calculate anything | |
543 at this time. */ | |
544 #if defined(SPANDSP_USE_FIXED_POINT) | |
545 s->eq_buf[s->eq_step].re = sample->re/(float) FP_FACTOR; | |
546 s->eq_buf[s->eq_step].im = sample->im/(float) FP_FACTOR; | |
547 #else | |
548 s->eq_buf[s->eq_step] = *sample; | |
549 #endif | |
550 if (++s->eq_step >= V27TER_EQUALIZER_LEN) | |
551 s->eq_step = 0; | |
552 | |
553 /* On alternate insertions we have a whole baud, and must process it. */ | |
554 if ((s->baud_half ^= 1)) | |
555 return; | |
556 | |
557 symbol_sync(s); | |
558 | |
559 z = equalizer_get(s); | |
560 | |
561 //span_log(&s->logging, SPAN_LOG_FLOW, "Equalized symbol - %15.5f %15.5f\n", z.re, z.im); | |
562 constellation_state = s->constellation_state; | |
563 switch (s->training_stage) | |
564 { | |
565 case TRAINING_STAGE_NORMAL_OPERATION: | |
566 decode_baud(s, &z); | |
567 constellation_state = (s->bit_rate == 4800) ? s->constellation_state : (s->constellation_state << 1); | |
568 target = &v27ter_constellation[constellation_state]; | |
569 break; | |
570 case TRAINING_STAGE_SYMBOL_ACQUISITION: | |
571 /* Allow time for the Gardner algorithm to settle the baud timing */ | |
572 /* Don't start narrowing the bandwidth of the Gardner algorithm too early. | |
573 Some modems are a bit wobbly when they start sending the signal. Also, we start | |
574 this analysis before our filter buffers have completely filled. */ | |
575 target = &zero; | |
576 if (++s->training_count >= 30) | |
577 { | |
578 s->gardner_step = 32; | |
579 s->training_stage = TRAINING_STAGE_LOG_PHASE; | |
580 s->angles[0] = | |
581 s->start_angles[0] = arctan2(z.im, z.re); | |
582 } | |
583 break; | |
584 case TRAINING_STAGE_LOG_PHASE: | |
585 /* Record the current alternate phase angle */ | |
586 target = &zero; | |
587 angle = arctan2(z.im, z.re); | |
588 s->angles[1] = | |
589 s->start_angles[1] = angle; | |
590 s->training_count = 1; | |
591 s->training_stage = TRAINING_STAGE_WAIT_FOR_HOP; | |
592 break; | |
593 case TRAINING_STAGE_WAIT_FOR_HOP: | |
594 target = &zero; | |
595 angle = arctan2(z.im, z.re); | |
596 /* Look for the initial ABAB sequence to display a phase reversal, which will | |
597 signal the start of the scrambled ABAB segment */ | |
598 ang = angle - s->angles[(s->training_count - 1) & 0xF]; | |
599 s->angles[(s->training_count + 1) & 0xF] = angle; | |
600 if ((ang > 0x20000000 || ang < -0x20000000) && s->training_count >= 3) | |
601 { | |
602 /* We seem to have a phase reversal */ | |
603 /* Slam the carrier frequency into line, based on the total phase drift over the last | |
604 section. Use the shift from the odd bits and the shift from the even bits to get | |
605 better jitter suppression. We need to scale here, or at the maximum specified | |
606 frequency deviation we could overflow, and get a silly answer. */ | |
607 /* Step back a few symbols so we don't get ISI distorting things. */ | |
608 i = (s->training_count - 8) & ~1; | |
609 /* Avoid the possibility of a divide by zero */ | |
610 if (i) | |
611 { | |
612 j = i & 0xF; | |
613 ang = (s->angles[j] - s->start_angles[0])/i | |
614 + (s->angles[j | 0x1] - s->start_angles[1])/i; | |
615 if (s->bit_rate == 4800) | |
616 s->carrier_phase_rate += ang/10; | |
617 else | |
618 s->carrier_phase_rate += 3*(ang/40); | |
619 } | |
620 span_log(&s->logging, SPAN_LOG_FLOW, "Coarse carrier frequency %7.2f (%d)\n", dds_frequencyf(s->carrier_phase_rate), s->training_count); | |
621 /* Check if the carrier frequency is plausible */ | |
622 if (s->carrier_phase_rate < dds_phase_ratef(CARRIER_NOMINAL_FREQ - 20.0f) | |
623 || | |
624 s->carrier_phase_rate > dds_phase_ratef(CARRIER_NOMINAL_FREQ + 20.0f)) | |
625 { | |
626 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n"); | |
627 /* Park this modem */ | |
628 s->training_stage = TRAINING_STAGE_PARKED; | |
629 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
630 break; | |
631 } | |
632 | |
633 /* Make a step shift in the phase, to pull it into line. We need to rotate the equalizer | |
634 buffer, as well as the carrier phase, for this to play out nicely. */ | |
635 angle += 0x80000000; | |
636 p = angle*2.0f*3.14159f/(65536.0f*65536.0f); | |
637 #if defined(SPANDSP_USE_FIXED_POINTx) | |
638 zz = complex_setf(cosf(p), -sinf(p)); | |
639 for (i = 0; i < V27TER_EQUALIZER_LEN; i++) | |
640 { | |
641 z1 = complex_setf(s->eq_buf[i].re, s->eq_buf[i].im); | |
642 z1 = complex_mulf(&z1, &zz); | |
643 s->eq_buf[i].re = z1.re; | |
644 s->eq_buf[i].im = z1.im; | |
645 } | |
646 #else | |
647 zz = complex_setf(cosf(p), -sinf(p)); | |
648 for (i = 0; i < V27TER_EQUALIZER_LEN; i++) | |
649 s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz); | |
650 #endif | |
651 s->carrier_phase += angle; | |
652 s->gardner_step = 2; | |
653 /* We have just seen the first element of the scrambled sequence so skip it. */ | |
654 s->training_bc = 1; | |
655 s->training_bc ^= descramble(s, 1); | |
656 descramble(s, 1); | |
657 descramble(s, 1); | |
658 constellation_state = | |
659 s->constellation_state = abab_pos[s->training_bc]; | |
660 target = &v27ter_constellation[constellation_state]; | |
661 s->training_count = 1; | |
662 s->training_stage = TRAINING_STAGE_TRAIN_ON_ABAB; | |
663 report_status_change(s, SIG_STATUS_TRAINING_IN_PROGRESS); | |
664 } | |
665 else if (++s->training_count > V27TER_TRAINING_SEG_3_LEN) | |
666 { | |
667 /* This is bogus. There are not this many bits in this section | |
668 of a real training sequence. */ | |
669 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n"); | |
670 /* Park this modem */ | |
671 s->training_stage = TRAINING_STAGE_PARKED; | |
672 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
673 } | |
674 break; | |
675 case TRAINING_STAGE_TRAIN_ON_ABAB: | |
676 /* Train on the scrambled ABAB section */ | |
677 s->training_bc ^= descramble(s, 1); | |
678 descramble(s, 1); | |
679 descramble(s, 1); | |
680 constellation_state = | |
681 s->constellation_state = abab_pos[s->training_bc]; | |
682 target = &v27ter_constellation[constellation_state]; | |
683 track_carrier(s, &z, target); | |
684 tune_equalizer(s, &z, target); | |
685 | |
686 #if defined(SPANDSP_USE_FIXED_POINTx) | |
687 s->carrier_track_i = 400 + (200000 - 400)*(float) (V27TER_TRAINING_SEG_5_LEN - s->training_count)/(float) V27TER_TRAINING_SEG_5_LEN; | |
688 s->carrier_track_p = 1000000 + (10000000 - 1000000)*(float) (V27TER_TRAINING_SEG_5_LEN - s->training_count)/(float) V27TER_TRAINING_SEG_5_LEN; | |
689 #else | |
690 s->carrier_track_i = 400.0f + (200000.0f - 400.0f)*(float) (V27TER_TRAINING_SEG_5_LEN - s->training_count)/(float) V27TER_TRAINING_SEG_5_LEN; | |
691 s->carrier_track_p = 1000000.0f + (10000000.0f - 1000000.0f)*(float) (V27TER_TRAINING_SEG_5_LEN - s->training_count)/(float) V27TER_TRAINING_SEG_5_LEN; | |
692 #endif | |
693 if (++s->training_count >= V27TER_TRAINING_SEG_5_LEN) | |
694 { | |
695 constellation_state = 4; | |
696 s->constellation_state = (s->bit_rate == 4800) ? 4 : 2; | |
697 s->training_count = 0; | |
698 s->training_stage = TRAINING_STAGE_TEST_ONES; | |
699 } | |
700 break; | |
701 case TRAINING_STAGE_TEST_ONES: | |
702 decode_baud(s, &z); | |
703 constellation_state = (s->bit_rate == 4800) ? s->constellation_state : (s->constellation_state << 1); | |
704 target = &v27ter_constellation[constellation_state]; | |
705 /* Measure the training error */ | |
706 #if defined(SPANDSP_USE_FIXED_POINTx) | |
707 z1.re = z.re/(float) FP_FACTOR; | |
708 z1.im = z.im/(float) FP_FACTOR; | |
709 zz = complex_subf(&z, target); | |
710 zz = complex_subf(&z1, &zz); | |
711 s->training_error += powerf(&zz); | |
712 #else | |
713 zz = complex_subf(&z, target); | |
714 s->training_error += powerf(&zz); | |
715 #endif | |
716 if (++s->training_count >= V27TER_TRAINING_SEG_6_LEN) | |
717 { | |
718 /* At 4800bps the symbols are 1.08238 (Euclidian) apart. | |
719 At 2400bps the symbols are 2.0 (Euclidian) apart. */ | |
720 if ((s->bit_rate == 4800 && s->training_error < V27TER_TRAINING_SEG_6_LEN*0.25f) | |
721 || | |
722 (s->bit_rate == 2400 && s->training_error < V27TER_TRAINING_SEG_6_LEN*0.5f)) | |
723 { | |
724 /* We are up and running */ | |
725 span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded at %dbps (constellation mismatch %f)\n", s->bit_rate, s->training_error); | |
726 report_status_change(s, SIG_STATUS_TRAINING_SUCCEEDED); | |
727 /* Apply some lag to the carrier off condition, to ensure the last few bits get pushed through | |
728 the processing. */ | |
729 s->signal_present = (s->bit_rate == 4800) ? 90 : 120; | |
730 s->training_stage = TRAINING_STAGE_NORMAL_OPERATION; | |
731 equalizer_save(s); | |
732 s->carrier_phase_rate_save = s->carrier_phase_rate; | |
733 s->agc_scaling_save = s->agc_scaling; | |
734 } | |
735 else | |
736 { | |
737 /* Training has failed */ | |
738 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error); | |
739 /* Park this modem */ | |
740 s->training_stage = TRAINING_STAGE_PARKED; | |
741 report_status_change(s, SIG_STATUS_TRAINING_FAILED); | |
742 } | |
743 } | |
744 break; | |
745 case TRAINING_STAGE_PARKED: | |
746 default: | |
747 /* We failed to train! */ | |
748 /* Park here until the carrier drops. */ | |
749 target = &zero; | |
750 break; | |
751 } | |
752 if (s->qam_report) | |
753 { | |
754 #if defined(SPANDSP_USE_FIXED_POINTx) | |
755 z1.re = z.re/(float) FP_FACTOR; | |
756 z1.im = z.im/(float) FP_FACTOR; | |
757 zz.re = target->re; | |
758 zz.im = target->im; | |
759 s->qam_report(s->qam_user_data, &z1, &zz, s->constellation_state); | |
760 #else | |
761 s->qam_report(s->qam_user_data, &z, target, s->constellation_state); | |
762 #endif | |
763 } | |
764 } | |
765 /*- End of function --------------------------------------------------------*/ | |
766 | |
767 static __inline__ int signal_detect(v27ter_rx_state_t *s, int16_t amp) | |
768 { | |
769 int16_t diff; | |
770 int16_t x; | |
771 int32_t power; | |
772 | |
773 /* There should be no DC in the signal, but sometimes there is. | |
774 We need to measure the power with the DC blocked, but not using | |
775 a slow to respond DC blocker. Use the most elementary HPF. */ | |
776 x = amp >> 1; | |
777 /* There could be overflow here, but it isn't a problem in practice */ | |
778 diff = x - s->last_sample; | |
779 s->last_sample = x; | |
780 power = power_meter_update(&(s->power), diff); | |
781 #if defined(IAXMODEM_STUFF) | |
782 /* Quick power drop fudge */ | |
783 diff = abs(diff); | |
784 if (10*diff < s->high_sample) | |
785 { | |
786 if (++s->low_samples > 120) | |
787 { | |
788 power_meter_init(&(s->power), 4); | |
789 s->high_sample = 0; | |
790 s->low_samples = 0; | |
791 } | |
792 } | |
793 else | |
794 { | |
795 s->low_samples = 0; | |
796 if (diff > s->high_sample) | |
797 s->high_sample = diff; | |
798 } | |
799 #endif | |
800 //span_log(&s->logging, SPAN_LOG_FLOW, "Power = %f\n", power_meter_current_dbm0(&(s->power))); | |
801 if (s->signal_present > 0) | |
802 { | |
803 /* Look for power below turn-off threshold to turn the carrier off */ | |
804 #if defined(IAXMODEM_STUFF) | |
805 if (s->carrier_drop_pending || power < s->carrier_off_power) | |
806 #else | |
807 if (power < s->carrier_off_power) | |
808 #endif | |
809 { | |
810 if (--s->signal_present <= 0) | |
811 { | |
812 /* Count down a short delay, to ensure we push the last | |
813 few bits through the filters before stopping. */ | |
814 v27ter_rx_restart(s, s->bit_rate, FALSE); | |
815 report_status_change(s, SIG_STATUS_CARRIER_DOWN); | |
816 return 0; | |
817 } | |
818 #if defined(IAXMODEM_STUFF) | |
819 /* Carrier has dropped, but the put_bit is pending the signal_present delay. */ | |
820 s->carrier_drop_pending = TRUE; | |
821 #endif | |
822 } | |
823 } | |
824 else | |
825 { | |
826 /* Look for power exceeding turn-on threshold to turn the carrier on */ | |
827 if (power < s->carrier_on_power) | |
828 return 0; | |
829 s->signal_present = 1; | |
830 #if defined(IAXMODEM_STUFF) | |
831 s->carrier_drop_pending = FALSE; | |
832 #endif | |
833 report_status_change(s, SIG_STATUS_CARRIER_UP); | |
834 } | |
835 return power; | |
836 } | |
837 /*- End of function --------------------------------------------------------*/ | |
838 | |
839 SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], int len) | |
840 { | |
841 int i; | |
842 int step; | |
843 #if defined(SPANDSP_USE_FIXED_POINT) | |
844 complexi16_t z; | |
845 complexi16_t zz; | |
846 complexi16_t sample; | |
847 int32_t v; | |
848 #else | |
849 complexf_t z; | |
850 complexf_t zz; | |
851 complexf_t sample; | |
852 float v; | |
853 #endif | |
854 int32_t power; | |
855 | |
856 if (s->bit_rate == 4800) | |
857 { | |
858 for (i = 0; i < len; i++) | |
859 { | |
860 s->rrc_filter[s->rrc_filter_step] = amp[i]; | |
861 if (++s->rrc_filter_step >= V27TER_RX_4800_FILTER_STEPS) | |
862 s->rrc_filter_step = 0; | |
863 | |
864 if ((power = signal_detect(s, amp[i])) == 0) | |
865 continue; | |
866 /* Only spend effort processing this data if the modem is not | |
867 parked, after training failure. */ | |
868 if (s->training_stage == TRAINING_STAGE_PARKED) | |
869 continue; | |
870 | |
871 /* Put things into the equalization buffer at T/2 rate. The Gardner algorithm | |
872 will fiddle the step to align this with the symbols. */ | |
873 if ((s->eq_put_step -= RX_PULSESHAPER_4800_COEFF_SETS) <= 0) | |
874 { | |
875 if (s->training_stage == TRAINING_STAGE_SYMBOL_ACQUISITION) | |
876 { | |
877 /* Only AGC during the initial training */ | |
878 #if defined(SPANDSP_USE_FIXED_POINT) | |
879 s->agc_scaling = (float) FP_FACTOR*32768.0f*(1.0f/RX_PULSESHAPER_4800_GAIN)*1.414f/sqrtf(power); | |
880 #else | |
881 s->agc_scaling = (1.0f/RX_PULSESHAPER_4800_GAIN)*1.414f/sqrtf(power); | |
882 #endif | |
883 } | |
884 /* Pulse shape while still at the carrier frequency, using a quadrature | |
885 pair of filters. This results in a properly bandpass filtered complex | |
886 signal, which can be brought directly to baseband by complex mixing. | |
887 No further filtering, to remove mixer harmonics, is needed. */ | |
888 step = -s->eq_put_step; | |
889 if (step > RX_PULSESHAPER_4800_COEFF_SETS - 1) | |
890 step = RX_PULSESHAPER_4800_COEFF_SETS - 1; | |
891 s->eq_put_step += RX_PULSESHAPER_4800_COEFF_SETS*5/2; | |
892 #if defined(SPANDSP_USE_FIXED_POINT) | |
893 v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_4800_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step); | |
894 sample.re = (v*(int32_t) s->agc_scaling) >> 15; | |
895 v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_4800_im[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step); | |
896 sample.im = (v*(int32_t) s->agc_scaling) >> 15; | |
897 z = dds_lookup_complexi16(s->carrier_phase); | |
898 zz.re = ((int32_t) sample.re*(int32_t) z.re - (int32_t) sample.im*(int32_t) z.im) >> 15; | |
899 zz.im = ((int32_t) -sample.re*(int32_t) z.im - (int32_t) sample.im*(int32_t) z.re) >> 15; | |
900 #else | |
901 v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_4800_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step); | |
902 sample.re = v*s->agc_scaling; | |
903 v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_4800_im[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step); | |
904 sample.im = v*s->agc_scaling; | |
905 z = dds_lookup_complexf(s->carrier_phase); | |
906 zz.re = sample.re*z.re - sample.im*z.im; | |
907 zz.im = -sample.re*z.im - sample.im*z.re; | |
908 #endif | |
909 process_half_baud(s, &zz); | |
910 } | |
911 #if defined(SPANDSP_USE_FIXED_POINT) | |
912 dds_advance(&s->carrier_phase, s->carrier_phase_rate); | |
913 #else | |
914 dds_advancef(&s->carrier_phase, s->carrier_phase_rate); | |
915 #endif | |
916 } | |
917 } | |
918 else | |
919 { | |
920 for (i = 0; i < len; i++) | |
921 { | |
922 s->rrc_filter[s->rrc_filter_step] = amp[i]; | |
923 if (++s->rrc_filter_step >= V27TER_RX_2400_FILTER_STEPS) | |
924 s->rrc_filter_step = 0; | |
925 | |
926 if ((power = signal_detect(s, amp[i])) == 0) | |
927 continue; | |
928 /* Only spend effort processing this data if the modem is not | |
929 parked, after training failure. */ | |
930 if (s->training_stage == TRAINING_STAGE_PARKED) | |
931 continue; | |
932 | |
933 /* Put things into the equalization buffer at T/2 rate. The Gardner algorithm | |
934 will fiddle the step to align this with the symbols. */ | |
935 if ((s->eq_put_step -= RX_PULSESHAPER_2400_COEFF_SETS) <= 0) | |
936 { | |
937 if (s->training_stage == TRAINING_STAGE_SYMBOL_ACQUISITION) | |
938 { | |
939 /* Only AGC during the initial training */ | |
940 #if defined(SPANDSP_USE_FIXED_POINT) | |
941 s->agc_scaling = (float) FP_FACTOR*32768.0f*(1.0f/RX_PULSESHAPER_2400_GAIN)*1.414f/sqrtf(power); | |
942 #else | |
943 s->agc_scaling = (1.0f/RX_PULSESHAPER_2400_GAIN)*1.414f/sqrtf(power); | |
944 #endif | |
945 } | |
946 /* Pulse shape while still at the carrier frequency, using a quadrature | |
947 pair of filters. This results in a properly bandpass filtered complex | |
948 signal, which can be brought directly to bandband by complex mixing. | |
949 No further filtering, to remove mixer harmonics, is needed. */ | |
950 step = -s->eq_put_step; | |
951 if (step > RX_PULSESHAPER_2400_COEFF_SETS - 1) | |
952 step = RX_PULSESHAPER_2400_COEFF_SETS - 1; | |
953 s->eq_put_step += RX_PULSESHAPER_2400_COEFF_SETS*20/(3*2); | |
954 #if defined(SPANDSP_USE_FIXED_POINT) | |
955 v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_2400_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step); | |
956 sample.re = (v*(int32_t) s->agc_scaling) >> 15; | |
957 v = vec_circular_dot_prodi16(s->rrc_filter, rx_pulseshaper_2400_im[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step); | |
958 sample.im = (v*(int32_t) s->agc_scaling) >> 15; | |
959 z = dds_lookup_complexi16(s->carrier_phase); | |
960 zz.re = ((int32_t) sample.re*(int32_t) z.re - (int32_t) sample.im*(int32_t) z.im) >> 15; | |
961 zz.im = ((int32_t) -sample.re*(int32_t) z.im - (int32_t) sample.im*(int32_t) z.re) >> 15; | |
962 #else | |
963 v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_2400_re[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step); | |
964 sample.re = v*s->agc_scaling; | |
965 v = vec_circular_dot_prodf(s->rrc_filter, rx_pulseshaper_2400_im[step], V27TER_RX_FILTER_STEPS, s->rrc_filter_step); | |
966 sample.im = v*s->agc_scaling; | |
967 z = dds_lookup_complexf(s->carrier_phase); | |
968 zz.re = sample.re*z.re - sample.im*z.im; | |
969 zz.im = -sample.re*z.im - sample.im*z.re; | |
970 #endif | |
971 process_half_baud(s, &zz); | |
972 } | |
973 #if defined(SPANDSP_USE_FIXED_POINT) | |
974 dds_advance(&s->carrier_phase, s->carrier_phase_rate); | |
975 #else | |
976 dds_advancef(&s->carrier_phase, s->carrier_phase_rate); | |
977 #endif | |
978 } | |
979 } | |
980 return 0; | |
981 } | |
982 /*- End of function --------------------------------------------------------*/ | |
983 | |
984 SPAN_DECLARE(int) v27ter_rx_fillin(v27ter_rx_state_t *s, int len) | |
985 { | |
986 int i; | |
987 | |
988 /* We want to sustain the current state (i.e carrier on<->carrier off), and | |
989 try to sustain the carrier phase. We should probably push the filters, as well */ | |
990 span_log(&s->logging, SPAN_LOG_FLOW, "Fill-in %d samples\n", len); | |
991 if (s->signal_present <= 0) | |
992 return 0; | |
993 if (s->training_stage == TRAINING_STAGE_PARKED) | |
994 return 0; | |
995 for (i = 0; i < len; i++) | |
996 { | |
997 #if defined(SPANDSP_USE_FIXED_POINT) | |
998 dds_advance(&s->carrier_phase, s->carrier_phase_rate); | |
999 #else | |
1000 dds_advancef(&s->carrier_phase, s->carrier_phase_rate); | |
1001 #endif | |
1002 /* Advance the symbol phase the appropriate amount */ | |
1003 if (s->bit_rate == 4800) | |
1004 { | |
1005 if ((s->eq_put_step -= RX_PULSESHAPER_4800_COEFF_SETS) <= 0) | |
1006 s->eq_put_step += RX_PULSESHAPER_4800_COEFF_SETS*5/2; | |
1007 } | |
1008 else | |
1009 { | |
1010 if ((s->eq_put_step -= RX_PULSESHAPER_2400_COEFF_SETS) <= 0) | |
1011 s->eq_put_step += RX_PULSESHAPER_2400_COEFF_SETS*20/(3*2); | |
1012 } | |
1013 /* TODO: Should we rotate any buffers */ | |
1014 } | |
1015 return 0; | |
1016 } | |
1017 /*- End of function --------------------------------------------------------*/ | |
1018 | |
1019 SPAN_DECLARE(void) v27ter_rx_set_put_bit(v27ter_rx_state_t *s, put_bit_func_t put_bit, void *user_data) | |
1020 { | |
1021 s->put_bit = put_bit; | |
1022 s->put_bit_user_data = user_data; | |
1023 } | |
1024 /*- End of function --------------------------------------------------------*/ | |
1025 | |
1026 SPAN_DECLARE(void) v27ter_rx_set_modem_status_handler(v27ter_rx_state_t *s, modem_tx_status_func_t handler, void *user_data) | |
1027 { | |
1028 s->status_handler = handler; | |
1029 s->status_user_data = user_data; | |
1030 } | |
1031 /*- End of function --------------------------------------------------------*/ | |
1032 | |
1033 SPAN_DECLARE(logging_state_t *) v27ter_rx_get_logging_state(v27ter_rx_state_t *s) | |
1034 { | |
1035 return &s->logging; | |
1036 } | |
1037 /*- End of function --------------------------------------------------------*/ | |
1038 | |
1039 SPAN_DECLARE(int) v27ter_rx_restart(v27ter_rx_state_t *s, int bit_rate, int old_train) | |
1040 { | |
1041 span_log(&s->logging, SPAN_LOG_FLOW, "Restarting V.27ter\n"); | |
1042 if (bit_rate != 4800 && bit_rate != 2400) | |
1043 return -1; | |
1044 s->bit_rate = bit_rate; | |
1045 | |
1046 #if defined(SPANDSP_USE_FIXED_POINT) | |
1047 vec_zeroi16(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
1048 #else | |
1049 vec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
1050 #endif | |
1051 s->rrc_filter_step = 0; | |
1052 | |
1053 s->scramble_reg = 0x3C; | |
1054 s->scrambler_pattern_count = 0; | |
1055 s->training_stage = TRAINING_STAGE_SYMBOL_ACQUISITION; | |
1056 s->training_bc = 0; | |
1057 s->training_count = 0; | |
1058 s->training_error = 0.0f; | |
1059 s->signal_present = 0; | |
1060 #if defined(IAXMODEM_STUFF) | |
1061 s->high_sample = 0; | |
1062 s->low_samples = 0; | |
1063 s->carrier_drop_pending = FALSE; | |
1064 #endif | |
1065 | |
1066 s->carrier_phase = 0; | |
1067 #if defined(SPANDSP_USE_FIXED_POINTx) | |
1068 s->carrier_track_i = 200000; | |
1069 s->carrier_track_p = 10000000; | |
1070 #else | |
1071 s->carrier_track_i = 200000.0f; | |
1072 s->carrier_track_p = 10000000.0f; | |
1073 #endif | |
1074 power_meter_init(&(s->power), 4); | |
1075 | |
1076 s->constellation_state = 0; | |
1077 | |
1078 if (s->old_train) | |
1079 { | |
1080 s->carrier_phase_rate = s->carrier_phase_rate_save; | |
1081 s->agc_scaling = s->agc_scaling_save; | |
1082 equalizer_restore(s); | |
1083 } | |
1084 else | |
1085 { | |
1086 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ); | |
1087 #if defined(SPANDSP_USE_FIXED_POINTx) | |
1088 s->agc_scaling = (float) FP_FACTOR*32768.0f*0.005f/RX_PULSESHAPER_4800_GAIN; | |
1089 #else | |
1090 s->agc_scaling = 0.005f/RX_PULSESHAPER_4800_GAIN; | |
1091 #endif | |
1092 equalizer_reset(s); | |
1093 } | |
1094 s->eq_skip = 0; | |
1095 s->last_sample = 0; | |
1096 | |
1097 s->gardner_integrate = 0; | |
1098 s->total_baud_timing_correction = 0; | |
1099 s->gardner_step = 512; | |
1100 s->baud_half = 0; | |
1101 | |
1102 return 0; | |
1103 } | |
1104 /*- End of function --------------------------------------------------------*/ | |
1105 | |
1106 SPAN_DECLARE(v27ter_rx_state_t *) v27ter_rx_init(v27ter_rx_state_t *s, int bit_rate, put_bit_func_t put_bit, void *user_data) | |
1107 { | |
1108 switch (bit_rate) | |
1109 { | |
1110 case 4800: | |
1111 case 2400: | |
1112 break; | |
1113 default: | |
1114 return NULL; | |
1115 } | |
1116 if (s == NULL) | |
1117 { | |
1118 if ((s = (v27ter_rx_state_t *) malloc(sizeof(*s))) == NULL) | |
1119 return NULL; | |
1120 } | |
1121 memset(s, 0, sizeof(*s)); | |
1122 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
1123 span_log_set_protocol(&s->logging, "V.27ter RX"); | |
1124 v27ter_rx_signal_cutoff(s, -45.5f); | |
1125 s->put_bit = put_bit; | |
1126 s->put_bit_user_data = user_data; | |
1127 | |
1128 v27ter_rx_restart(s, bit_rate, FALSE); | |
1129 return s; | |
1130 } | |
1131 /*- End of function --------------------------------------------------------*/ | |
1132 | |
1133 SPAN_DECLARE(int) v27ter_rx_release(v27ter_rx_state_t *s) | |
1134 { | |
1135 return 0; | |
1136 } | |
1137 /*- End of function --------------------------------------------------------*/ | |
1138 | |
1139 SPAN_DECLARE(int) v27ter_rx_free(v27ter_rx_state_t *s) | |
1140 { | |
1141 free(s); | |
1142 return 0; | |
1143 } | |
1144 /*- End of function --------------------------------------------------------*/ | |
1145 | |
1146 SPAN_DECLARE(void) v27ter_rx_set_qam_report_handler(v27ter_rx_state_t *s, qam_report_handler_t handler, void *user_data) | |
1147 { | |
1148 s->qam_report = handler; | |
1149 s->qam_user_data = user_data; | |
1150 } | |
1151 /*- End of function --------------------------------------------------------*/ | |
1152 /*- End of file ------------------------------------------------------------*/ |