Bug Summary

File:libs/spandsp/src/v22bis_rx.c
Location:line 645, column 9
Description:Value stored to 'bitstream' is never read

Annotated Source Code

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