Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v22bis_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 /* | |
2 * SpanDSP - a series of DSP components for telephony | |
3 * | |
4 * v22bis_rx.c - ITU V.22bis modem receive part | |
5 * | |
6 * Written by Steve Underwood <steveu@coppice.org> | |
7 * | |
8 * Copyright (C) 2004 Steve Underwood | |
9 * | |
10 * All rights reserved. | |
11 * | |
12 * This program is free software; you can redistribute it and/or modify | |
13 * it under the terms of the GNU Lesser General Public License version 2.1, | |
14 * as published by the Free Software Foundation. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU Lesser General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU Lesser General Public | |
22 * License along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 * | |
25 * $Id: v22bis_rx.c,v 1.69 2009/11/04 15:52:06 steveu Exp $ | |
26 */ | |
27 | |
28 /*! \file */ | |
29 | |
30 /* THIS IS A WORK IN PROGRESS - It is basically functional, but it is not feature | |
31 complete, and doesn't reliably sync over the signal and noise level ranges it | |
32 should. There are some nasty inefficiencies too! | |
33 TODO: | |
34 Better noise performance | |
35 Retrain is incomplete | |
36 Rate change is not implemented | |
37 Remote loopback is not implemented */ | |
38 | |
39 #if defined(HAVE_CONFIG_H) | |
40 #include "config.h" | |
41 #endif | |
42 | |
43 #include <inttypes.h> | |
44 #include <string.h> | |
45 #include <stdio.h> | |
46 #include <stdlib.h> | |
47 #if defined(HAVE_TGMATH_H) | |
48 #include <tgmath.h> | |
49 #endif | |
50 #if defined(HAVE_MATH_H) | |
51 #include <math.h> | |
52 #endif | |
53 #include "floating_fudge.h" | |
54 | |
55 #include "spandsp/telephony.h" | |
56 #include "spandsp/logging.h" | |
57 #include "spandsp/complex.h" | |
58 #include "spandsp/vector_float.h" | |
59 #include "spandsp/complex_vector_float.h" | |
60 #include "spandsp/async.h" | |
61 #include "spandsp/power_meter.h" | |
62 #include "spandsp/arctan2.h" | |
63 #include "spandsp/dds.h" | |
64 #include "spandsp/complex_filters.h" | |
65 | |
66 #include "spandsp/v29rx.h" | |
67 #include "spandsp/v22bis.h" | |
68 | |
69 #include "spandsp/private/logging.h" | |
70 #include "spandsp/private/v22bis.h" | |
71 | |
72 #if defined(SPANDSP_USE_FIXED_POINTx) | |
73 #include "v22bis_rx_1200_floating_rrc.h" | |
74 #include "v22bis_rx_2400_floating_rrc.h" | |
75 #else | |
76 #include "v22bis_rx_1200_floating_rrc.h" | |
77 #include "v22bis_rx_2400_floating_rrc.h" | |
78 #endif | |
79 | |
80 #define ms_to_symbols(t) (((t)*600)/1000) | |
81 | |
82 /*! The adaption rate coefficient for the equalizer */ | |
83 #define EQUALIZER_DELTA 0.25f | |
84 /*! The number of phase shifted coefficient set for the pulse shaping/bandpass filter */ | |
85 #define PULSESHAPER_COEFF_SETS 12 | |
86 | |
87 /* | |
88 The basic method used by the V.22bis receiver is: | |
89 | |
90 Put each sample into the pulse-shaping and phase shift filter buffer | |
91 | |
92 At T/2 rate: | |
93 Filter and demodulate the contents of the input filter buffer, producing a sample | |
94 in the equalizer filter buffer. | |
95 | |
96 Tune the symbol timing based on the latest 3 samples in the equalizer buffer. This | |
97 updates the decision points for taking the T/2 samples. | |
98 | |
99 Equalize the contents of the equalizer buffer, producing a demodulated constellation | |
100 point. | |
101 | |
102 Find the nearest constellation point to the received position. This is our received | |
103 symbol. | |
104 | |
105 Tune the local carrier, based on the angular mismatch between the actual signal and | |
106 the decision. | |
107 | |
108 Tune the equalizer, based on the mismatch between the actual signal and the decision. | |
109 | |
110 Descramble and output the bits represented by the decision. | |
111 */ | |
112 | |
113 static const uint8_t space_map_v22bis[6][6] = | |
114 { | |
115 {11, 9, 9, 6, 6, 7}, | |
116 {10, 8, 8, 4, 4, 5}, | |
117 {10, 8, 8, 4, 4, 5}, | |
118 {13, 12, 12, 0, 0, 2}, | |
119 {13, 12, 12, 0, 0, 2}, | |
120 {15, 14, 14, 1, 1, 3} | |
121 }; | |
122 | |
123 static const uint8_t phase_steps[4] = | |
124 { | |
125 1, 0, 2, 3 | |
126 }; | |
127 | |
128 static const uint8_t ones[] = | |
129 { | |
130 0, 1, 1, 2, | |
131 1, 2, 2, 3, | |
132 1, 2, 2, 3, | |
133 2, 3, 3, 4 | |
134 }; | |
135 | |
136 SPAN_DECLARE(float) v22bis_rx_carrier_frequency(v22bis_state_t *s) | |
137 { | |
138 return dds_frequencyf(s->rx.carrier_phase_rate); | |
139 } | |
140 /*- End of function --------------------------------------------------------*/ | |
141 | |
142 SPAN_DECLARE(float) v22bis_rx_symbol_timing_correction(v22bis_state_t *s) | |
143 { | |
144 return (float) s->rx.total_baud_timing_correction/((float) PULSESHAPER_COEFF_SETS*40.0f/(3.0f*2.0f)); | |
145 } | |
146 /*- End of function --------------------------------------------------------*/ | |
147 | |
148 SPAN_DECLARE(float) v22bis_rx_signal_power(v22bis_state_t *s) | |
149 { | |
150 return power_meter_current_dbm0(&s->rx.rx_power) + 6.34f; | |
151 } | |
152 /*- End of function --------------------------------------------------------*/ | |
153 | |
154 SPAN_DECLARE(void) v22bis_rx_signal_cutoff(v22bis_state_t *s, float cutoff) | |
155 { | |
156 s->rx.carrier_on_power = (int32_t) (power_meter_level_dbm0(cutoff + 2.5f)*0.232f); | |
157 s->rx.carrier_off_power = (int32_t) (power_meter_level_dbm0(cutoff - 2.5f)*0.232f); | |
158 } | |
159 /*- End of function --------------------------------------------------------*/ | |
160 | |
161 void v22bis_report_status_change(v22bis_state_t *s, int status) | |
162 { | |
163 if (s->status_handler) | |
164 s->status_handler(s->status_user_data, status); | |
165 else if (s->put_bit) | |
166 s->put_bit(s->put_bit_user_data, status); | |
167 } | |
168 /*- End of function --------------------------------------------------------*/ | |
169 | |
170 SPAN_DECLARE(int) v22bis_rx_equalizer_state(v22bis_state_t *s, complexf_t **coeffs) | |
171 { | |
172 *coeffs = s->rx.eq_coeff; | |
173 return 2*V22BIS_EQUALIZER_LEN + 1; | |
174 } | |
175 /*- End of function --------------------------------------------------------*/ | |
176 | |
177 void v22bis_equalizer_coefficient_reset(v22bis_state_t *s) | |
178 { | |
179 /* Start with an equalizer based on everything being perfect */ | |
180 #if defined(SPANDSP_USE_FIXED_POINTx) | |
181 cvec_zeroi16(s->rx.eq_coeff, 2*V22BIS_EQUALIZER_LEN + 1); | |
182 s->rx.eq_coeff[V22BIS_EQUALIZER_LEN] = complex_seti16(3*FP_FACTOR, 0*FP_FACTOR); | |
183 s->rx.eq_delta = 32768.0f*EQUALIZER_DELTA/(2*V22BIS_EQUALIZER_LEN + 1); | |
184 #else | |
185 cvec_zerof(s->rx.eq_coeff, 2*V22BIS_EQUALIZER_LEN + 1); | |
186 s->rx.eq_coeff[V22BIS_EQUALIZER_LEN] = complex_setf(3.0f, 0.0f); | |
187 s->rx.eq_delta = EQUALIZER_DELTA/(2*V22BIS_EQUALIZER_LEN + 1); | |
188 #endif | |
189 } | |
190 /*- End of function --------------------------------------------------------*/ | |
191 | |
192 static void equalizer_reset(v22bis_state_t *s) | |
193 { | |
194 v22bis_equalizer_coefficient_reset(s); | |
195 #if defined(SPANDSP_USE_FIXED_POINTx) | |
196 cvec_zeroi16(s->rx.eq_buf, V22BIS_EQUALIZER_MASK + 1); | |
197 #else | |
198 cvec_zerof(s->rx.eq_buf, V22BIS_EQUALIZER_MASK + 1); | |
199 #endif | |
200 s->rx.eq_put_step = 20 - 1; | |
201 s->rx.eq_step = 0; | |
202 } | |
203 /*- End of function --------------------------------------------------------*/ | |
204 | |
205 static complexf_t equalizer_get(v22bis_state_t *s) | |
206 { | |
207 int i; | |
208 int p; | |
209 complexf_t z; | |
210 complexf_t z1; | |
211 | |
212 /* Get the next equalized value. */ | |
213 z = complex_setf(0.0f, 0.0f); | |
214 p = s->rx.eq_step - 1; | |
215 for (i = 0; i < 2*V22BIS_EQUALIZER_LEN + 1; i++) | |
216 { | |
217 p = (p - 1) & V22BIS_EQUALIZER_MASK; | |
218 z1 = complex_mulf(&s->rx.eq_coeff[i], &s->rx.eq_buf[p]); | |
219 z = complex_addf(&z, &z1); | |
220 } | |
221 return z; | |
222 } | |
223 /*- End of function --------------------------------------------------------*/ | |
224 | |
225 static void tune_equalizer(v22bis_state_t *s, const complexf_t *z, const complexf_t *target) | |
226 { | |
227 int i; | |
228 int p; | |
229 complexf_t ez; | |
230 complexf_t z1; | |
231 | |
232 /* Find the x and y mismatch from the exact constellation position. */ | |
233 ez = complex_subf(target, z); | |
234 ez.re *= s->rx.eq_delta; | |
235 ez.im *= s->rx.eq_delta; | |
236 | |
237 p = s->rx.eq_step - 1; | |
238 for (i = 0; i < 2*V22BIS_EQUALIZER_LEN + 1; i++) | |
239 { | |
240 p = (p - 1) & V22BIS_EQUALIZER_MASK; | |
241 z1 = complex_conjf(&s->rx.eq_buf[p]); | |
242 z1 = complex_mulf(&ez, &z1); | |
243 s->rx.eq_coeff[i] = complex_addf(&s->rx.eq_coeff[i], &z1); | |
244 /* If we don't leak a little bit we seem to get some wandering adaption */ | |
245 s->rx.eq_coeff[i].re *= 0.9999f; | |
246 s->rx.eq_coeff[i].im *= 0.9999f; | |
247 } | |
248 } | |
249 /*- End of function --------------------------------------------------------*/ | |
250 | |
251 static __inline__ void track_carrier(v22bis_state_t *s, const complexf_t *z, const complexf_t *target) | |
252 { | |
253 float error; | |
254 | |
255 /* For small errors the imaginary part of the difference between the actual and the target | |
256 positions is proportional to the phase error, for any particular target. However, the | |
257 different amplitudes of the various target positions scale things. */ | |
258 error = z->im*target->re - z->re*target->im; | |
259 | |
260 s->rx.carrier_phase_rate += (int32_t) (s->rx.carrier_track_i*error); | |
261 s->rx.carrier_phase += (int32_t) (s->rx.carrier_track_p*error); | |
262 //span_log(&s->logging, SPAN_LOG_FLOW, "Im = %15.5f f = %15.5f\n", error, dds_frequencyf(s->rx.carrier_phase_rate)); | |
263 } | |
264 /*- End of function --------------------------------------------------------*/ | |
265 | |
266 static __inline__ int descramble(v22bis_state_t *s, int bit) | |
267 { | |
268 int out_bit; | |
269 | |
270 bit &= 1; | |
271 | |
272 /* Descramble the bit */ | |
273 out_bit = (bit ^ (s->rx.scramble_reg >> 13) ^ (s->rx.scramble_reg >> 16)) & 1; | |
274 s->rx.scramble_reg = (s->rx.scramble_reg << 1) | bit; | |
275 | |
276 if (s->rx.scrambler_pattern_count >= 64) | |
277 { | |
278 out_bit ^= 1; | |
279 s->rx.scrambler_pattern_count = 0; | |
280 } | |
281 if (bit) | |
282 s->rx.scrambler_pattern_count++; | |
283 else | |
284 s->rx.scrambler_pattern_count = 0; | |
285 return out_bit; | |
286 } | |
287 /*- End of function --------------------------------------------------------*/ | |
288 | |
289 static __inline__ void put_bit(v22bis_state_t *s, int bit) | |
290 { | |
291 int out_bit; | |
292 | |
293 /* Descramble the bit */ | |
294 out_bit = descramble(s, bit); | |
295 s->put_bit(s->put_bit_user_data, out_bit); | |
296 } | |
297 /*- End of function --------------------------------------------------------*/ | |
298 | |
299 static void decode_baud(v22bis_state_t *s, int nearest) | |
300 { | |
301 int raw_bits; | |
302 | |
303 raw_bits = phase_steps[((nearest >> 2) - (s->rx.constellation_state >> 2)) & 3]; | |
304 s->rx.constellation_state = nearest; | |
305 /* The first two bits are the quadrant */ | |
306 put_bit(s, raw_bits >> 1); | |
307 put_bit(s, raw_bits); | |
308 if (s->rx.sixteen_way_decisions) | |
309 { | |
310 /* The other two bits are the position within the quadrant */ | |
311 put_bit(s, nearest >> 1); | |
312 put_bit(s, nearest); | |
313 } | |
314 } | |
315 /*- End of function --------------------------------------------------------*/ | |
316 | |
317 static int decode_baudx(v22bis_state_t *s, int nearest) | |
318 { | |
319 int raw_bits; | |
320 int out_bits; | |
321 | |
322 raw_bits = phase_steps[((nearest >> 2) - (s->rx.constellation_state >> 2)) & 3]; | |
323 s->rx.constellation_state = nearest; | |
324 /* The first two bits are the quadrant */ | |
325 out_bits = descramble(s, raw_bits >> 1); | |
326 out_bits = (out_bits << 1) | descramble(s, raw_bits); | |
327 if (s->rx.sixteen_way_decisions) | |
328 { | |
329 /* The other two bits are the position within the quadrant */ | |
330 out_bits = (out_bits << 1) | descramble(s, nearest >> 1); | |
331 out_bits = (out_bits << 1) | descramble(s, nearest); | |
332 } | |
333 return out_bits; | |
334 } | |
335 /*- End of function --------------------------------------------------------*/ | |
336 | |
337 static __inline__ void symbol_sync(v22bis_state_t *s) | |
338 { | |
339 float p; | |
340 float q; | |
341 complexf_t zz; | |
342 complexf_t a; | |
343 complexf_t b; | |
344 complexf_t c; | |
345 | |
346 /* This routine adapts the position of the half baud samples entering the equalizer. */ | |
347 | |
348 /* Perform a Gardner test for baud alignment on the three most recent samples. */ | |
349 if (s->rx.sixteen_way_decisions) | |
350 { | |
351 p = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].re | |
352 - s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].re; | |
353 p *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].re; | |
354 | |
355 q = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].im | |
356 - s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].im; | |
357 q *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].im; | |
358 } | |
359 else | |
360 { | |
361 /* Rotate the points to the 45 degree positions, to maximise the effectiveness of | |
362 the Gardner algorithm. This is particularly significant at the start of operation | |
363 to pull things in quickly. */ | |
364 zz = complex_setf(0.894427, 0.44721f); | |
365 a = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK], &zz); | |
366 b = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK], &zz); | |
367 c = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK], &zz); | |
368 p = (a.re - c.re)*b.re; | |
369 q = (a.im - c.im)*b.im; | |
370 } | |
371 | |
372 s->rx.gardner_integrate += (p + q > 0.0f) ? s->rx.gardner_step : -s->rx.gardner_step; | |
373 | |
374 if (abs(s->rx.gardner_integrate) >= 16) | |
375 { | |
376 /* This integrate and dump approach avoids rapid changes of the equalizer put step. | |
377 Rapid changes, without hysteresis, are bad. They degrade the equalizer performance | |
378 when the true symbol boundary is close to a sample boundary. */ | |
379 s->rx.eq_put_step += (s->rx.gardner_integrate/16); | |
380 s->rx.total_baud_timing_correction += (s->rx.gardner_integrate/16); | |
381 //span_log(&s->logging, SPAN_LOG_FLOW, "Gardner kick %d [total %d]\n", s->rx.gardner_integrate, s->rx.total_baud_timing_correction); | |
382 if (s->rx.qam_report) | |
383 s->rx.qam_report(s->rx.qam_user_data, NULL, NULL, s->rx.gardner_integrate); | |
384 s->rx.gardner_integrate = 0; | |
385 } | |
386 } | |
387 /*- End of function --------------------------------------------------------*/ | |
388 | |
389 static void process_half_baud(v22bis_state_t *s, const complexf_t *sample) | |
390 { | |
391 complexf_t z; | |
392 complexf_t zz; | |
393 const complexf_t *target; | |
394 int re; | |
395 int im; | |
396 int nearest; | |
397 int bitstream; | |
398 int raw_bits; | |
399 | |
400 z.re = sample->re; | |
401 z.im = sample->im; | |
402 | |
403 /* Add a sample to the equalizer's circular buffer, but don't calculate anything | |
404 at this time. */ | |
405 s->rx.eq_buf[s->rx.eq_step] = z; | |
406 s->rx.eq_step = (s->rx.eq_step + 1) & V22BIS_EQUALIZER_MASK; | |
407 | |
408 /* On alternate insertions we have a whole baud and must process it. */ | |
409 if ((s->rx.baud_phase ^= 1)) | |
410 return; | |
411 | |
412 symbol_sync(s); | |
413 | |
414 z = equalizer_get(s); | |
415 | |
416 /* Find the constellation point */ | |
417 if (s->rx.sixteen_way_decisions) | |
418 { | |
419 re = (int) (z.re + 3.0f); | |
420 if (re > 5) | |
421 re = 5; | |
422 else if (re < 0) | |
423 re = 0; | |
424 im = (int) (z.im + 3.0f); | |
425 if (im > 5) | |
426 im = 5; | |
427 else if (im < 0) | |
428 im = 0; | |
429 nearest = space_map_v22bis[re][im]; | |
430 } | |
431 else | |
432 { | |
433 /* Rotate to 45 degrees, to make the slicing trivial */ | |
434 zz = complex_setf(0.894427, 0.44721f); | |
435 zz = complex_mulf(&z, &zz); | |
436 nearest = 0x01; | |
437 if (zz.re < 0.0f) | |
438 nearest |= 0x04; | |
439 if (zz.im < 0.0f) | |
440 { | |
441 nearest ^= 0x04; | |
442 nearest |= 0x08; | |
443 } | |
444 } | |
445 raw_bits = 0; | |
446 | |
447 switch (s->rx.training) | |
448 { | |
449 case V22BIS_RX_TRAINING_STAGE_NORMAL_OPERATION: | |
450 /* Normal operation. */ | |
451 target = &v22bis_constellation[nearest]; | |
452 track_carrier(s, &z, target); | |
453 tune_equalizer(s, &z, target); | |
454 raw_bits = phase_steps[((nearest >> 2) - (s->rx.constellation_state >> 2)) & 3]; | |
455 /* TODO: detect unscrambled ones indicating a loopback request */ | |
456 | |
457 /* Search for the S1 signal that might be requesting a retrain */ | |
458 if ((s->rx.last_raw_bits ^ raw_bits) == 0x3) | |
459 { | |
460 s->rx.pattern_repeats++; | |
461 } | |
462 else | |
463 { | |
464 if (s->rx.pattern_repeats >= 50 && (s->rx.last_raw_bits == 0x3 || s->rx.last_raw_bits == 0x0)) | |
465 { | |
466 /* We should get a full run of 00 11 (about 60 bauds) at either modem. */ | |
467 span_log(&s->logging, SPAN_LOG_FLOW, "+++ S1 detected (%d long)\n", s->rx.pattern_repeats); | |
468 span_log(&s->logging, SPAN_LOG_FLOW, "+++ Accepting a retrain request\n"); | |
469 s->rx.pattern_repeats = 0; | |
470 s->rx.training_count = 0; | |
471 s->rx.training = V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200; | |
472 s->tx.training_count = 0; | |
473 s->tx.training = V22BIS_TX_TRAINING_STAGE_U0011; | |
474 v22bis_equalizer_coefficient_reset(s); | |
475 v22bis_report_status_change(s, SIG_STATUS_MODEM_RETRAIN_OCCURRED); | |
476 } | |
477 s->rx.pattern_repeats = 0; | |
478 } | |
479 decode_baud(s, nearest); | |
480 break; | |
481 case V22BIS_RX_TRAINING_STAGE_SYMBOL_ACQUISITION: | |
482 /* Allow time for the Gardner algorithm to settle the symbol timing. */ | |
483 target = &z; | |
484 if (++s->rx.training_count >= 40) | |
485 { | |
486 /* QAM and Gardner only play nicely with heavy damping, so we need to change to | |
487 a slow rate of symbol timing adaption. However, it must not be so slow that it | |
488 cannot track the worst case timing error specified in V.22bis. This should be 0.01%, | |
489 but since we might be off in the opposite direction from the source, the total | |
490 error could be higher. */ | |
491 s->rx.gardner_step = 4; | |
492 s->rx.pattern_repeats = 0; | |
493 if (s->calling_party) | |
494 s->rx.training = V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES; | |
495 else | |
496 s->rx.training = V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200; | |
497 /* Be pessimistic and see what the handshake brings */ | |
498 s->negotiated_bit_rate = 1200; | |
499 break; | |
500 } | |
501 /* Once we have pulled in the symbol timing in a coarse way, use finer | |
502 steps to fine tune the timing. */ | |
503 if (s->rx.training_count == 30) | |
504 s->rx.gardner_step = 32; | |
505 break; | |
506 case V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES: | |
507 /* Calling modem only */ | |
508 /* The calling modem should initially receive unscrambled ones at 1200bps */ | |
509 target = &v22bis_constellation[nearest]; | |
510 track_carrier(s, &z, target); | |
511 raw_bits = phase_steps[((nearest >> 2) - (s->rx.constellation_state >> 2)) & 3]; | |
512 s->rx.constellation_state = nearest; | |
513 if (raw_bits != s->rx.last_raw_bits) | |
514 s->rx.pattern_repeats = 0; | |
515 else | |
516 s->rx.pattern_repeats++; | |
517 if (++s->rx.training_count == ms_to_symbols(155 + 456)) | |
518 { | |
519 /* After the first 155ms things should have been steady, so check if the last 456ms was | |
520 steady at 11 or 00. */ | |
521 if (raw_bits == s->rx.last_raw_bits | |
522 && | |
523 (raw_bits == 0x3 || raw_bits == 0x0) | |
524 && | |
525 s->rx.pattern_repeats >= ms_to_symbols(456)) | |
526 { | |
527 /* It looks like the answering machine is sending us a clean unscrambled 11 or 00 */ | |
528 if (s->bit_rate == 2400) | |
529 { | |
530 /* Try to establish at 2400bps */ | |
531 span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting U0011 (S1) (Caller)\n"); | |
532 s->tx.training = V22BIS_TX_TRAINING_STAGE_U0011; | |
533 s->tx.training_count = 0; | |
534 } | |
535 else | |
536 { | |
537 /* Only try to establish at 1200bps */ | |
538 span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting S11 (1200) (Caller)\n"); | |
539 s->tx.training = V22BIS_TX_TRAINING_STAGE_S11; | |
540 s->tx.training_count = 0; | |
541 } | |
542 } | |
543 s->rx.pattern_repeats = 0; | |
544 s->rx.training_count = 0; | |
545 s->rx.training = V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES_SUSTAINING; | |
546 } | |
547 break; | |
548 case V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES_SUSTAINING: | |
549 /* Calling modem only */ | |
550 /* Wait for the end of the unscrambled ones at 1200bps */ | |
551 target = &v22bis_constellation[nearest]; | |
552 track_carrier(s, &z, target); | |
553 raw_bits = phase_steps[((nearest >> 2) - (s->rx.constellation_state >> 2)) & 3]; | |
554 s->rx.constellation_state = nearest; | |
555 if (raw_bits != s->rx.last_raw_bits) | |
556 { | |
557 /* This looks like the end of the sustained initial unscrambled 11 or 00 */ | |
558 s->tx.training_count = 0; | |
559 s->tx.training = V22BIS_TX_TRAINING_STAGE_TIMED_S11; | |
560 s->rx.training_count = 0; | |
561 s->rx.training = V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200; | |
562 s->rx.pattern_repeats = 0; | |
563 } | |
564 break; | |
565 case V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200: | |
566 target = &v22bis_constellation[nearest]; | |
567 track_carrier(s, &z, target); | |
568 tune_equalizer(s, &z, target); | |
569 raw_bits = phase_steps[((nearest >> 2) - (s->rx.constellation_state >> 2)) & 3]; | |
570 bitstream = decode_baudx(s, nearest); | |
571 s->rx.training_count++; | |
572 //span_log(&s->logging, SPAN_LOG_FLOW, "S11 0x%02x 0x%02x 0x%X %d %d %d %d %d\n", raw_bits, nearest, bitstream, s->rx.scrambled_ones_to_date, 0, 0, 0, s->rx.training_count); | |
573 if (s->negotiated_bit_rate == 1200) | |
574 { | |
575 /* Search for the S1 signal */ | |
576 if ((s->rx.last_raw_bits ^ raw_bits) == 0x3) | |
577 { | |
578 s->rx.pattern_repeats++; | |
579 } | |
580 else | |
581 { | |
582 if (s->rx.pattern_repeats >= 15 && (s->rx.last_raw_bits == 0x3 || s->rx.last_raw_bits == 0x0)) | |
583 { | |
584 /* We should get a full run of 00 11 (about 60 bauds) at the calling modem, but only about 20 | |
585 at the answering modem, as the first 40 are TED settling time. */ | |
586 span_log(&s->logging, SPAN_LOG_FLOW, "+++ S1 detected (%d long)\n", s->rx.pattern_repeats); | |
587 if (s->bit_rate == 2400) | |
588 { | |
589 if (!s->calling_party) | |
590 { | |
591 /* Accept establishment at 2400bps */ | |
592 span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting U0011 (S1) (Answerer)\n"); | |
593 s->tx.training = V22BIS_TX_TRAINING_STAGE_U0011; | |
594 s->tx.training_count = 0; | |
595 } | |
596 s->negotiated_bit_rate = 2400; | |
597 } | |
598 } | |
599 s->rx.pattern_repeats = 0; | |
600 } | |
601 if (s->rx.training_count >= ms_to_symbols(270)) | |
602 { | |
603 /* If we haven't seen the S1 signal by now, we are committed to be in 1200bps mode */ | |
604 if (s->calling_party) | |
605 { | |
606 span_log(&s->logging, SPAN_LOG_FLOW, "+++ Rx normal operation (1200)\n"); | |
607 /* The transmit side needs to sustain the scrambled ones for a timed period */ | |
608 s->tx.training_count = 0; | |
609 s->tx.training = V22BIS_TX_TRAINING_STAGE_TIMED_S11; | |
610 /* Normal reception starts immediately */ | |
611 s->rx.training = V22BIS_RX_TRAINING_STAGE_NORMAL_OPERATION; | |
612 s->rx.carrier_track_i = 8000.0f; | |
613 } | |
614 else | |
615 { | |
616 span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting S11 (1200) (Answerer)\n"); | |
617 /* The transmit side needs to sustain the scrambled ones for a timed period */ | |
618 s->tx.training_count = 0; | |
619 s->tx.training = V22BIS_TX_TRAINING_STAGE_TIMED_S11; | |
620 /* The receive side needs to wait a timed period, receiving scrambled ones, | |
621 before entering normal operation. */ | |
622 s->rx.training = V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200_SUSTAINING; | |
623 } | |
624 } | |
625 } | |
626 else | |
627 { | |
628 if (s->calling_party) | |
629 { | |
630 if (s->rx.training_count >= ms_to_symbols(100 + 450)) | |
631 { | |
632 span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting 16 way decisions (caller)\n"); | |
633 s->rx.sixteen_way_decisions = TRUE; | |
634 s->rx.training = V22BIS_RX_TRAINING_STAGE_WAIT_FOR_SCRAMBLED_ONES_AT_2400; | |
635 s->rx.pattern_repeats = 0; | |
636 s->rx.carrier_track_i = 8000.0f; | |
637 } | |
638 } | |
639 else | |
640 { | |
641 if (s->rx.training_count >= ms_to_symbols(450)) | |
642 { | |
643 span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting 16 way decisions (answerer)\n"); | |
644 s->rx.sixteen_way_decisions = TRUE; | |
645 s->rx.training = V22BIS_RX_TRAINING_STAGE_WAIT_FOR_SCRAMBLED_ONES_AT_2400; | |
646 s->rx.pattern_repeats = 0; | |
647 } | |
648 } | |
649 } | |
650 break; | |
651 case V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200_SUSTAINING: | |
652 target = &v22bis_constellation[nearest]; | |
653 track_carrier(s, &z, target); | |
654 tune_equalizer(s, &z, target); | |
655 bitstream = decode_baudx(s, nearest); | |
656 if (++s->rx.training_count > ms_to_symbols(270 + 765)) | |
657 { | |
658 span_log(&s->logging, SPAN_LOG_FLOW, "+++ Rx normal operation (1200)\n"); | |
659 s->rx.training = V22BIS_RX_TRAINING_STAGE_NORMAL_OPERATION; | |
660 } | |
661 break; | |
662 case V22BIS_RX_TRAINING_STAGE_WAIT_FOR_SCRAMBLED_ONES_AT_2400: | |
663 target = &v22bis_constellation[nearest]; | |
664 track_carrier(s, &z, target); | |
665 tune_equalizer(s, &z, target); | |
666 bitstream = decode_baudx(s, nearest); | |
667 /* We need 32 sustained 1's to switch into normal operation. */ | |
668 if (bitstream == 0xF) | |
669 { | |
670 if (++s->rx.pattern_repeats >= 9) | |
671 { | |
672 span_log(&s->logging, SPAN_LOG_FLOW, "+++ Rx normal operation (2400)\n"); | |
673 s->rx.training = V22BIS_RX_TRAINING_STAGE_NORMAL_OPERATION; | |
674 } | |
675 } | |
676 else | |
677 { | |
678 s->rx.pattern_repeats = 0; | |
679 } | |
680 break; | |
681 case V22BIS_RX_TRAINING_STAGE_PARKED: | |
682 default: | |
683 /* We failed to train! */ | |
684 /* Park here until the carrier drops. */ | |
685 target = &z; | |
686 break; | |
687 } | |
688 s->rx.last_raw_bits = raw_bits; | |
689 if (s->rx.qam_report) | |
690 s->rx.qam_report(s->rx.qam_user_data, &z, target, s->rx.constellation_state); | |
691 } | |
692 /*- End of function --------------------------------------------------------*/ | |
693 | |
694 SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int len) | |
695 { | |
696 int i; | |
697 int j; | |
698 int step; | |
699 complexf_t z; | |
700 complexf_t zz; | |
701 int32_t power; | |
702 complexf_t sample; | |
703 float ii; | |
704 float qq; | |
705 | |
706 for (i = 0; i < len; i++) | |
707 { | |
708 /* Complex bandpass filter the signal, using a pair of FIRs, and RRC coeffs shifted | |
709 to centre at 1200Hz or 2400Hz. The filters support 12 fractional phase shifts, to | |
710 permit signal extraction very close to the middle of a symbol. */ | |
711 s->rx.rrc_filter[s->rx.rrc_filter_step] = | |
712 s->rx.rrc_filter[s->rx.rrc_filter_step + V22BIS_RX_FILTER_STEPS] = amp[i]; | |
713 if (++s->rx.rrc_filter_step >= V22BIS_RX_FILTER_STEPS) | |
714 s->rx.rrc_filter_step = 0; | |
715 | |
716 /* Calculate the I filter, with an arbitrary phase step, just so we can calculate | |
717 the signal power of the required carrier, with any guard tone or spillback of our | |
718 own transmitted signal suppressed. */ | |
719 if (s->calling_party) | |
720 { | |
721 ii = rx_pulseshaper_2400_re[6][0]*s->rx.rrc_filter[s->rx.rrc_filter_step]; | |
722 for (j = 1; j < V22BIS_RX_FILTER_STEPS; j++) | |
723 ii += rx_pulseshaper_2400_re[6][j]*s->rx.rrc_filter[j + s->rx.rrc_filter_step]; | |
724 } | |
725 else | |
726 { | |
727 ii = rx_pulseshaper_1200_re[6][0]*s->rx.rrc_filter[s->rx.rrc_filter_step]; | |
728 for (j = 1; j < V22BIS_RX_FILTER_STEPS; j++) | |
729 ii += rx_pulseshaper_1200_re[6][j]*s->rx.rrc_filter[j + s->rx.rrc_filter_step]; | |
730 } | |
731 power = power_meter_update(&(s->rx.rx_power), (int16_t) ii); | |
732 if (s->rx.signal_present) | |
733 { | |
734 /* Look for power below the carrier off point */ | |
735 if (power < s->rx.carrier_off_power) | |
736 { | |
737 v22bis_restart(s, s->bit_rate); | |
738 v22bis_report_status_change(s, SIG_STATUS_CARRIER_DOWN); | |
739 continue; | |
740 } | |
741 } | |
742 else | |
743 { | |
744 /* Look for power exceeding the carrier on point */ | |
745 if (power < s->rx.carrier_on_power) | |
746 continue; | |
747 s->rx.signal_present = TRUE; | |
748 v22bis_report_status_change(s, SIG_STATUS_CARRIER_UP); | |
749 } | |
750 if (s->rx.training != V22BIS_RX_TRAINING_STAGE_PARKED) | |
751 { | |
752 /* Only spend effort processing this data if the modem is not | |
753 parked, after a training failure. */ | |
754 z = dds_complexf(&s->rx.carrier_phase, s->rx.carrier_phase_rate); | |
755 if (s->rx.training == V22BIS_RX_TRAINING_STAGE_SYMBOL_ACQUISITION) | |
756 { | |
757 /* Only AGC during the initial symbol acquisition, and then lock the gain. */ | |
758 s->rx.agc_scaling = 0.18f*3.60f/sqrtf(power); | |
759 } | |
760 /* Put things into the equalization buffer at T/2 rate. The Gardner algorithm | |
761 will fiddle the step to align this with the symbols. */ | |
762 if ((s->rx.eq_put_step -= PULSESHAPER_COEFF_SETS) <= 0) | |
763 { | |
764 /* Pulse shape while still at the carrier frequency, using a quadrature | |
765 pair of filters. This results in a properly bandpass filtered complex | |
766 signal, which can be brought directly to bandband by complex mixing. | |
767 No further filtering, to remove mixer harmonics, is needed. */ | |
768 step = -s->rx.eq_put_step; | |
769 if (step > PULSESHAPER_COEFF_SETS - 1) | |
770 step = PULSESHAPER_COEFF_SETS - 1; | |
771 s->rx.eq_put_step += PULSESHAPER_COEFF_SETS*40/(3*2); | |
772 if (s->calling_party) | |
773 { | |
774 ii = rx_pulseshaper_2400_re[step][0]*s->rx.rrc_filter[s->rx.rrc_filter_step]; | |
775 qq = rx_pulseshaper_2400_im[step][0]*s->rx.rrc_filter[s->rx.rrc_filter_step]; | |
776 for (j = 1; j < V22BIS_RX_FILTER_STEPS; j++) | |
777 { | |
778 ii += rx_pulseshaper_2400_re[step][j]*s->rx.rrc_filter[j + s->rx.rrc_filter_step]; | |
779 qq += rx_pulseshaper_2400_im[step][j]*s->rx.rrc_filter[j + s->rx.rrc_filter_step]; | |
780 } | |
781 } | |
782 else | |
783 { | |
784 ii = rx_pulseshaper_1200_re[step][0]*s->rx.rrc_filter[s->rx.rrc_filter_step]; | |
785 qq = rx_pulseshaper_1200_im[step][0]*s->rx.rrc_filter[s->rx.rrc_filter_step]; | |
786 for (j = 1; j < V22BIS_RX_FILTER_STEPS; j++) | |
787 { | |
788 ii += rx_pulseshaper_1200_re[step][j]*s->rx.rrc_filter[j + s->rx.rrc_filter_step]; | |
789 qq += rx_pulseshaper_1200_im[step][j]*s->rx.rrc_filter[j + s->rx.rrc_filter_step]; | |
790 } | |
791 } | |
792 sample.re = ii*s->rx.agc_scaling; | |
793 sample.im = qq*s->rx.agc_scaling; | |
794 /* Shift to baseband - since this is done in a full complex form, the | |
795 result is clean, and requires no further filtering apart from the | |
796 equalizer. */ | |
797 zz.re = sample.re*z.re - sample.im*z.im; | |
798 zz.im = -sample.re*z.im - sample.im*z.re; | |
799 process_half_baud(s, &zz); | |
800 } | |
801 } | |
802 } | |
803 return 0; | |
804 } | |
805 /*- End of function --------------------------------------------------------*/ | |
806 | |
807 SPAN_DECLARE(int) v22bis_rx_fillin(v22bis_state_t *s, int len) | |
808 { | |
809 int i; | |
810 | |
811 /* We want to sustain the current state (i.e carrier on<->carrier off), and | |
812 try to sustain the carrier phase. We should probably push the filters, as well */ | |
813 span_log(&s->logging, SPAN_LOG_FLOW, "Fill-in %d samples\n", len); | |
814 if (!s->rx.signal_present) | |
815 return 0; | |
816 for (i = 0; i < len; i++) | |
817 { | |
818 #if defined(SPANDSP_USE_FIXED_POINTx) | |
819 dds_advance(&s->rx.carrier_phase, s->rx.carrier_phase_rate); | |
820 #else | |
821 dds_advancef(&s->rx.carrier_phase, s->rx.carrier_phase_rate); | |
822 #endif | |
823 } | |
824 /* TODO: Advance the symbol phase the appropriate amount */ | |
825 return 0; | |
826 } | |
827 /*- End of function --------------------------------------------------------*/ | |
828 | |
829 int v22bis_rx_restart(v22bis_state_t *s) | |
830 { | |
831 vec_zerof(s->rx.rrc_filter, sizeof(s->rx.rrc_filter)/sizeof(s->rx.rrc_filter[0])); | |
832 s->rx.rrc_filter_step = 0; | |
833 s->rx.scramble_reg = 0; | |
834 s->rx.scrambler_pattern_count = 0; | |
835 s->rx.training = V22BIS_RX_TRAINING_STAGE_SYMBOL_ACQUISITION; | |
836 s->rx.training_count = 0; | |
837 s->rx.signal_present = FALSE; | |
838 | |
839 s->rx.carrier_phase_rate = dds_phase_ratef((s->calling_party) ? 2400.0f : 1200.0f); | |
840 s->rx.carrier_phase = 0; | |
841 power_meter_init(&(s->rx.rx_power), 5); | |
842 v22bis_rx_signal_cutoff(s, -45.5f); | |
843 s->rx.agc_scaling = 0.0005f*0.025f; | |
844 | |
845 s->rx.constellation_state = 0; | |
846 s->rx.sixteen_way_decisions = FALSE; | |
847 | |
848 equalizer_reset(s); | |
849 | |
850 s->rx.pattern_repeats = 0; | |
851 s->rx.last_raw_bits = 0; | |
852 s->rx.gardner_integrate = 0; | |
853 s->rx.gardner_step = 256; | |
854 s->rx.baud_phase = 0; | |
855 s->rx.training_error = 0.0f; | |
856 s->rx.total_baud_timing_correction = 0; | |
857 /* We want the carrier to pull in faster on the answerer side, as it has very little time to adapt. */ | |
858 s->rx.carrier_track_i = (s->calling_party) ? 8000.0f : 40000.0f; | |
859 s->rx.carrier_track_p = 8000000.0f; | |
860 | |
861 s->negotiated_bit_rate = 1200; | |
862 | |
863 return 0; | |
864 } | |
865 /*- End of function --------------------------------------------------------*/ | |
866 | |
867 SPAN_DECLARE(void) v22bis_rx_set_qam_report_handler(v22bis_state_t *s, qam_report_handler_t handler, void *user_data) | |
868 { | |
869 s->rx.qam_report = handler; | |
870 s->rx.qam_user_data = user_data; | |
871 } | |
872 /*- End of function --------------------------------------------------------*/ | |
873 /*- End of file ------------------------------------------------------------*/ |