comparison spandsp-0.0.3/spandsp-0.0.3/src/v27ter_rx.c @ 5:f762bf195c4b

import spandsp-0.0.3
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 16:00:21 +0200
parents
children
comparison
equal deleted inserted replaced
4:26cd8f1ef0b1 5:f762bf195c4b
1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * v27ter_rx.c - ITU V.27ter modem receive part
5 *
6 * Written by Steve Underwood <steveu@coppice.org>
7 *
8 * Copyright (C) 2003 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 General Public License version 2, as
14 * 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 General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 * $Id: v27ter_rx.c,v 1.75 2006/11/28 16:59:57 steveu Exp $
26 */
27
28 /*! \file */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <inttypes.h>
35 #include <string.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #if defined(HAVE_TGMATH_H)
39 #include <tgmath.h>
40 #endif
41 #if defined(HAVE_MATH_H)
42 #include <math.h>
43 #endif
44
45 #include "spandsp/telephony.h"
46 #include "spandsp/logging.h"
47 #include "spandsp/complex.h"
48 #include "spandsp/vector_float.h"
49 #include "spandsp/complex_vector_float.h"
50 #include "spandsp/async.h"
51 #include "spandsp/power_meter.h"
52 #include "spandsp/arctan2.h"
53 #include "spandsp/dds.h"
54 #include "spandsp/complex_filters.h"
55
56 #include "spandsp/v29rx.h"
57 #include "spandsp/v27ter_rx.h"
58
59 /* V.27ter is a DPSK modem, but this code treats it like QAM. It nails down the
60 signal to a static constellation, even though dealing with differences is all
61 that is necessary. */
62
63 #define CARRIER_NOMINAL_FREQ 1800.0f
64 #define EQUALIZER_DELTA 0.25f
65
66 /* Segments of the training sequence */
67 /* V.27ter defines a long and a short sequence. FAX doesn't use the
68 short sequence, so it is not implemented here. */
69 #define V27TER_TRAINING_SEG_3_LEN 50
70 #define V27TER_TRAINING_SEG_5_LEN 1074
71 #define V27TER_TRAINING_SEG_6_LEN 8
72
73 enum
74 {
75 TRAINING_STAGE_NORMAL_OPERATION = 0,
76 TRAINING_STAGE_SYMBOL_ACQUISITION,
77 TRAINING_STAGE_LOG_PHASE,
78 TRAINING_STAGE_WAIT_FOR_HOP,
79 TRAINING_STAGE_TRAIN_ON_ABAB,
80 TRAINING_STAGE_TEST_ONES,
81 TRAINING_STAGE_PARKED
82 };
83
84 static const complexf_t v27ter_constellation[8] =
85 {
86 { 1.414f, 0.0f}, /* 0deg */
87 { 1.0f, 1.0f}, /* 45deg */
88 { 0.0f, 1.414f}, /* 90deg */
89 {-1.0f, 1.0f}, /* 135deg */
90 {-1.414f, 0.0f}, /* 180deg */
91 {-1.0f, -1.0f}, /* 225deg */
92 { 0.0f, -1.414f}, /* 270deg */
93 { 1.0f, -1.0f} /* 315deg */
94 };
95
96 /* Raised root cosine pulse shaping filter set; beta 0.5; sample rate 8000; 8 phase steps;
97 baud rate 1600; shifted to centre at 1800Hz; complex. */
98 #define PULSESHAPER_4800_GAIN (2.4975f*2.0f)
99 #define PULSESHAPER_4800_COEFF_SETS 8
100 static const complexf_t pulseshaper_4800[PULSESHAPER_4800_COEFF_SETS][V27TER_RX_4800_FILTER_STEPS] =
101 {
102 {
103 {-0.0050334423f, -0.0025646669f}, /* Filter 0 */
104 { 0.0001996320f, -0.0006144041f},
105 {-0.0064914716f, -0.0010281481f},
106 {-0.0000000000f, 0.0057152766f},
107 {-0.0060638961f, 0.0009604268f},
108 { 0.0046534477f, 0.0143218395f},
109 {-0.0027026909f, 0.0013770898f},
110 { 0.0114324470f, 0.0157354133f},
111 { 0.0161335660f, -0.0161335660f},
112 { 0.0216170550f, 0.0157057098f},
113 { 0.0523957134f, -0.1028323775f},
114 { 0.1009107956f, 0.0327879051f},
115 { 0.0626681574f, -0.3956711736f},
116 { 1.0309650898f, 0.0000000000f},
117 { 0.1612784723f, 1.0182721987f},
118 {-0.3809963452f, 0.1237932168f},
119 { 0.0481701579f, 0.0945392579f},
120 {-0.0933698449f, 0.0678371631f},
121 { 0.0188939989f, 0.0188939989f},
122 {-0.0134110893f, 0.0184587808f},
123 { 0.0173301130f, 0.0088301336f},
124 { 0.0009373415f, -0.0028848406f},
125 { 0.0148734735f, 0.0023557268f},
126 {-0.0000000000f, -0.0061394833f},
127 { 0.0056449120f, -0.0008940662f},
128 {-0.0020309798f, -0.0062507130f},
129 {-0.0005756104f, 0.0002932882f},
130 },
131 {
132 {-0.0018682578f, -0.0009519249f}, /* Filter 1 */
133 {-0.0002684621f, 0.0008262413f},
134 {-0.0059141931f, -0.0009367162f},
135 {-0.0000000000f, 0.0073941285f},
136 {-0.0037772132f, 0.0005982518f},
137 { 0.0050394423f, 0.0155098087f},
138 { 0.0010806327f, -0.0005506098f},
139 { 0.0105277637f, 0.0144902237f},
140 { 0.0209691082f, -0.0209691082f},
141 { 0.0125153543f, 0.0090929371f},
142 { 0.0603186345f, -0.1183819857f},
143 { 0.0675630592f, 0.0219525687f},
144 { 0.0765237582f, -0.4831519944f},
145 { 1.0763458014f, 0.0000000000f},
146 { 0.1524445751f, 0.9624971666f},
147 {-0.2992580667f, 0.0972348401f},
148 { 0.0600222537f, 0.1178003057f},
149 {-0.0774892752f, 0.0562992540f},
150 { 0.0247376160f, 0.0247376159f},
151 {-0.0090916622f, 0.0125135995f},
152 { 0.0175076452f, 0.0089205908f},
153 { 0.0021568809f, -0.0066381970f},
154 { 0.0129897446f, 0.0020573734f},
155 {-0.0000000000f, -0.0079766726f},
156 { 0.0037729191f, -0.0005975717f},
157 {-0.0020837980f, -0.0064132707f},
158 {-0.0018682578f, 0.0009519249f},
159 },
160 {
161 {-0.0030355143f, -0.0015466718f}, /* Filter 2 */
162 {-0.0007306011f, 0.0022485590f},
163 {-0.0049435003f, -0.0007829735f},
164 {-0.0000000000f, 0.0087472824f},
165 {-0.0011144870f, 0.0001765174f},
166 { 0.0051901643f, 0.0159736834f},
167 { 0.0049297142f, -0.0025118148f},
168 { 0.0088213528f, 0.0121415505f},
169 { 0.0251126307f, -0.0251126307f},
170 { 0.0011182680f, 0.0008124692f},
171 { 0.0667555589f, -0.1310151612f},
172 { 0.0256033627f, 0.0083190368f},
173 { 0.0905183226f, -0.5715101964f},
174 { 1.1095595360f, 0.0000000000f},
175 { 0.1420835849f, 0.8970804494f},
176 {-0.2215345589f, 0.0719809416f},
177 { 0.0679608493f, 0.1333806768f},
178 {-0.0606982839f, 0.0440998847f},
179 { 0.0284660210f, 0.0284660210f},
180 {-0.0047348689f, 0.0065169880f},
181 { 0.0165731197f, 0.0084444263f},
182 { 0.0032233168f, -0.0099203492f},
183 { 0.0105861265f, 0.0016766777f},
184 {-0.0000000000f, -0.0092685623f},
185 { 0.0018009090f, -0.0002852360f},
186 {-0.0020112222f, -0.0061899056f},
187 {-0.0030355143f, 0.0015466718f},
188 },
189 {
190 {-0.0040182937f, -0.0020474229f}, /* Filter 3 */
191 {-0.0011603659f, 0.0035712391f},
192 {-0.0036173562f, -0.0005729329f},
193 {-0.0000000000f, 0.0096778115f},
194 { 0.0018022529f, -0.0002854488f},
195 { 0.0050847711f, 0.0156493164f},
196 { 0.0086257291f, -0.0043950285f},
197 { 0.0063429899f, 0.0087303766f},
198 { 0.0282322904f, -0.0282322904f},
199 {-0.0123306868f, -0.0089587683f},
200 { 0.0712060603f, -0.1397497620f},
201 {-0.0248170325f, -0.0080635427f},
202 { 0.1043647251f, -0.6589329411f},
203 { 1.1298123598f, 0.0000000000f},
204 { 0.1304361227f, 0.8235412673f},
205 {-0.1491678531f, 0.0484675735f},
206 { 0.0722366382f, 0.1417723850f},
207 {-0.0437871917f, 0.0318132570f},
208 { 0.0301678844f, 0.0301678844f},
209 {-0.0005794433f, 0.0007975353f},
210 { 0.0146599874f, 0.0074696367f},
211 { 0.0040878789f, -0.0125811975f},
212 { 0.0078126085f, 0.0012373956f},
213 {-0.0000000000f, -0.0099797659f},
214 {-0.0001576582f, 0.0000249706f},
215 {-0.0018223262f, -0.0056085432f},
216 {-0.0040182937f, 0.0020474229f},
217 },
218 {
219 {-0.0047695783f, -0.0024302215f}, /* Filter 4 */
220 {-0.0015320920f, 0.0047152944f},
221 {-0.0019955989f, -0.0003160718f},
222 {-0.0000000000f, 0.0101070339f},
223 { 0.0048302421f, -0.0007650352f},
224 { 0.0047152968f, 0.0145121913f},
225 { 0.0119428503f, -0.0060851862f},
226 { 0.0031686377f, 0.0043612557f},
227 { 0.0300119095f, -0.0300119094f},
228 {-0.0274628457f, -0.0199529254f},
229 { 0.0731841827f, -0.1436320457f},
230 {-0.0832936387f, -0.0270637438f},
231 { 0.1177684882f, -0.7435609707f},
232 { 1.1366178989f, 0.0000000000f},
233 { 0.1177684882f, 0.7435609707f},
234 {-0.0832936387f, 0.0270637438f},
235 { 0.0731841827f, 0.1436320457f},
236 {-0.0274628457f, 0.0199529254f},
237 { 0.0300119095f, 0.0300119094f},
238 { 0.0031686377f, -0.0043612557f},
239 { 0.0119428503f, 0.0060851862f},
240 { 0.0047152968f, -0.0145121913f},
241 { 0.0048302421f, 0.0007650352f},
242 {-0.0000000000f, -0.0101070339f},
243 {-0.0019955989f, 0.0003160718f},
244 {-0.0015320920f, -0.0047152944f},
245 {-0.0047695783f, 0.0024302215f},
246 },
247 {
248 {-0.0052564711f, -0.0026783058f}, /* Filter 5 */
249 {-0.0018223262f, 0.0056085432f},
250 {-0.0001576582f, -0.0000249706f},
251 {-0.0000000000f, 0.0099797659f},
252 { 0.0078126085f, -0.0012373956f},
253 { 0.0040878789f, 0.0125811975f},
254 { 0.0146599874f, -0.0074696367f},
255 {-0.0005794433f, -0.0007975353f},
256 { 0.0301678844f, -0.0301678844f},
257 {-0.0437871917f, -0.0318132570f},
258 { 0.0722366382f, -0.1417723850f},
259 {-0.1491678531f, -0.0484675735f},
260 { 0.1304361227f, -0.8235412673f},
261 { 1.1298123598f, 0.0000000000f},
262 { 0.1043647251f, 0.6589329411f},
263 {-0.0248170325f, 0.0080635427f},
264 { 0.0712060603f, 0.1397497620f},
265 {-0.0123306868f, 0.0089587683f},
266 { 0.0282322904f, 0.0282322904f},
267 { 0.0063429899f, -0.0087303766f},
268 { 0.0086257291f, 0.0043950285f},
269 { 0.0050847711f, -0.0156493164f},
270 { 0.0018022529f, 0.0002854488f},
271 {-0.0000000000f, -0.0096778115f},
272 {-0.0036173562f, 0.0005729329f},
273 {-0.0011603659f, -0.0035712391f},
274 {-0.0052564711f, 0.0026783058f},
275 },
276 {
277 {-0.0054614245f, -0.0027827348f}, /* Filter 6 */
278 {-0.0020112222f, 0.0061899056f},
279 { 0.0018009090f, 0.0002852360f},
280 {-0.0000000000f, 0.0092685623f},
281 { 0.0105861265f, -0.0016766777f},
282 { 0.0032233168f, 0.0099203492f},
283 { 0.0165731197f, -0.0084444263f},
284 {-0.0047348689f, -0.0065169880f},
285 { 0.0284660210f, -0.0284660210f},
286 {-0.0606982839f, -0.0440998847f},
287 { 0.0679608493f, -0.1333806768f},
288 {-0.2215345589f, -0.0719809416f},
289 { 0.1420835849f, -0.8970804494f},
290 { 1.1095595360f, 0.0000000000f},
291 { 0.0905183226f, 0.5715101964f},
292 { 0.0256033627f, -0.0083190368f},
293 { 0.0667555589f, 0.1310151612f},
294 { 0.0011182680f, -0.0008124692f},
295 { 0.0251126307f, 0.0251126307f},
296 { 0.0088213528f, -0.0121415505f},
297 { 0.0049297142f, 0.0025118148f},
298 { 0.0051901643f, -0.0159736834f},
299 {-0.0011144870f, -0.0001765174f},
300 {-0.0000000000f, -0.0087472824f},
301 {-0.0049435003f, 0.0007829735f},
302 {-0.0007306011f, -0.0022485590f},
303 {-0.0054614245f, 0.0027827348f},
304 },
305 {
306 {-0.0053826099f, -0.0027425768f}, /* Filter 7 */
307 {-0.0020837980f, 0.0064132707f},
308 { 0.0037729191f, 0.0005975717f},
309 {-0.0000000000f, 0.0079766726f},
310 { 0.0129897446f, -0.0020573734f},
311 { 0.0021568809f, 0.0066381970f},
312 { 0.0175076452f, -0.0089205908f},
313 {-0.0090916622f, -0.0125135995f},
314 { 0.0247376160f, -0.0247376159f},
315 {-0.0774892752f, -0.0562992540f},
316 { 0.0600222537f, -0.1178003057f},
317 {-0.2992580667f, -0.0972348401f},
318 { 0.1524445751f, -0.9624971666f},
319 { 1.0763458014f, 0.0000000000f},
320 { 0.0765237582f, 0.4831519944f},
321 { 0.0675630592f, -0.0219525687f},
322 { 0.0603186345f, 0.1183819857f},
323 { 0.0125153543f, -0.0090929371f},
324 { 0.0209691082f, 0.0209691082f},
325 { 0.0105277637f, -0.0144902237f},
326 { 0.0010806327f, 0.0005506098f},
327 { 0.0050394423f, -0.0155098087f},
328 {-0.0037772132f, -0.0005982518f},
329 {-0.0000000000f, -0.0073941285f},
330 {-0.0059141931f, 0.0009367162f},
331 {-0.0002684621f, -0.0008262413f},
332 {-0.0053826099f, 0.0027425768f},
333 },
334 };
335
336 /* Raised root cosine pulse shaping filter set; beta 0.5; sample rate 8000; 12 phase steps;
337 baud rate 1200; shifted to centre at 1800Hz; complex. */
338 #define PULSESHAPER_2400_GAIN 2.223f
339 #define PULSESHAPER_2400_COEFF_SETS 12
340 static const complexf_t pulseshaper_2400[PULSESHAPER_2400_COEFF_SETS][V27TER_RX_2400_FILTER_STEPS] =
341 {
342 {
343 { 0.0036326320f, 0.0018509185f}, /* Filter 0 */
344 { 0.0003793370f, -0.0011674794f},
345 { 0.0048754563f, 0.0007721964f},
346 { 0.0000000000f, -0.0062069190f},
347 {-0.0027810383f, 0.0004404732f},
348 {-0.0021925965f, -0.0067481182f},
349 {-0.0140173459f, 0.0071421944f},
350 { 0.0019772880f, 0.0027215034f},
351 {-0.0092149554f, 0.0092149553f},
352 { 0.0334995425f, 0.0243388423f},
353 { 0.0199195813f, -0.0390943796f},
354 { 0.1477459776f, 0.0480055782f},
355 { 0.0427277333f, -0.2697722907f},
356 { 1.0040582418f, 0.0000000000f},
357 { 0.1570693140f, 0.9916966187f},
358 {-0.2597668560f, 0.0844033680f},
359 { 0.0705271128f, 0.1384172525f},
360 {-0.0354969538f, 0.0257900466f},
361 { 0.0292796738f, 0.0292796738f},
362 { 0.0076599673f, -0.0105430406f},
363 { 0.0029973132f, 0.0015272073f},
364 { 0.0048614662f, -0.0149620544f},
365 {-0.0070080354f, -0.0011099638f},
366 {-0.0000000000f, -0.0028157043f},
367 {-0.0061305015f, 0.0009709761f},
368 { 0.0015253788f, 0.0046946332f},
369 {-0.0010937644f, 0.0005573008f},
370 },
371 {
372 {-0.0002819961f, -0.0001436842f}, /* Filter 1 */
373 { 0.0006588563f, -0.0020277512f},
374 { 0.0041797109f, 0.0006620012f},
375 { 0.0000000000f, -0.0065623410f},
376 {-0.0042368606f, 0.0006710528f},
377 {-0.0017245111f, -0.0053074995f},
378 {-0.0146686673f, 0.0074740593f},
379 { 0.0038644283f, 0.0053189292f},
380 {-0.0067358415f, 0.0067358415f},
381 { 0.0345347757f, 0.0250909833f},
382 { 0.0269170677f, -0.0528277198f},
383 { 0.1389398473f, 0.0451442930f},
384 { 0.0525256151f, -0.3316336818f},
385 { 1.0434222221f, 0.0000000000f},
386 { 0.1499906453f, 0.9470036639f},
387 {-0.2028542371f, 0.0659113371f},
388 { 0.0727579142f, 0.1427954467f},
389 {-0.0235379981f, 0.0171013566f},
390 { 0.0275384769f, 0.0275384769f},
391 { 0.0093041035f, -0.0128059998f},
392 { 0.0001136455f, 0.0000579053f},
393 { 0.0045116911f, -0.0138855574f},
394 {-0.0082179267f, -0.0013015917f},
395 {-0.0000000000f, -0.0013177606f},
396 {-0.0055834514f, 0.0008843318f},
397 { 0.0016945064f, 0.0052151545f},
398 {-0.0002819961f, 0.0001436842f},
399 },
400 {
401 { 0.0005112062f, 0.0002604726f}, /* Filter 2 */
402 { 0.0009277352f, -0.0028552754f},
403 { 0.0033466091f, 0.0005300508f},
404 { 0.0000000000f, -0.0067017064f},
405 {-0.0056208495f, 0.0008902551f},
406 {-0.0011771217f, -0.0036228080f},
407 {-0.0149285967f, 0.0076064999f},
408 { 0.0056743444f, 0.0078100650f},
409 {-0.0038028170f, 0.0038028170f},
410 { 0.0344837758f, 0.0250539297f},
411 { 0.0340482504f, -0.0668234539f},
412 { 0.1257304818f, 0.0408523100f},
413 { 0.0626620127f, -0.3956323778f},
414 { 1.0763765574f, 0.0000000000f},
415 { 0.1420845360f, 0.8970864542f},
416 {-0.1491315874f, 0.0484557901f},
417 { 0.0731690629f, 0.1436023716f},
418 {-0.0123276338f, 0.0089565502f},
419 { 0.0250869159f, 0.0250869159f},
420 { 0.0105070407f, -0.0144617008f},
421 {-0.0027029676f, -0.0013772308f},
422 { 0.0040530413f, -0.0124739786f},
423 {-0.0091186142f, -0.0014442466f},
424 { 0.0000000000f, 0.0001565015f},
425 {-0.0048632493f, 0.0007702630f},
426 { 0.0018111360f, 0.0055741034f},
427 { 0.0005112062f, -0.0002604726f},
428 },
429 {
430 { 0.0012626700f, 0.0006433625f}, /* Filter 3 */
431 { 0.0011774450f, -0.0036238031f},
432 { 0.0023987660f, 0.0003799272f},
433 { 0.0000000000f, -0.0066136620f},
434 {-0.0068852097f, 0.0010905101f},
435 {-0.0005635325f, -0.0017343746f},
436 {-0.0147735044f, 0.0075274764f},
437 { 0.0073440948f, 0.0101082792f},
438 {-0.0004807357f, 0.0004807357f},
439 { 0.0332365955f, 0.0241478001f},
440 { 0.0411429005f, -0.0807474888f},
441 { 0.1079056364f, 0.0350606666f},
442 { 0.0730301872f, -0.4610944549f},
443 { 1.1024802923f, 0.0000000000f},
444 { 0.1334542238f, 0.8425968074f},
445 {-0.0990668329f, 0.0321887653f},
446 { 0.0719339457f, 0.1411783175f},
447 {-0.0020666801f, 0.0015015310f},
448 { 0.0220599213f, 0.0220599213f},
449 { 0.0112587393f, -0.0154963252f},
450 {-0.0053688554f, -0.0027355684f},
451 { 0.0035031104f, -0.0107814651f},
452 {-0.0096971580f, -0.0015358789f},
453 { 0.0000000000f, 0.0015619067f},
454 {-0.0039973434f, 0.0006331170f},
455 { 0.0018730190f, 0.0057645597f},
456 { 0.0012626700f, -0.0006433625f},
457 },
458 {
459 { 0.0019511001f, 0.0009941351f}, /* Filter 4 */
460 { 0.0013998188f, -0.0043081992f},
461 { 0.0013630316f, 0.0002158830f},
462 { 0.0000000000f, -0.0062936717f},
463 {-0.0079839961f, 0.0012645408f},
464 { 0.0001005750f, 0.0003095379f},
465 {-0.0141915007f, 0.0072309308f},
466 { 0.0088117029f, 0.0121282686f},
467 { 0.0031486956f, -0.0031486956f},
468 { 0.0307069943f, 0.0223099373f},
469 { 0.0480159027f, -0.0942365150f},
470 { 0.0853178815f, 0.0277214601f},
471 { 0.0835162618f, -0.5273009241f},
472 { 1.1213819981f, 0.0000000000f},
473 { 0.1242110958f, 0.7842379942f},
474 {-0.0530547129f, 0.0172385212f},
475 { 0.0692420091f, 0.1358950944f},
476 { 0.0070828755f, -0.0051460103f},
477 { 0.0185973391f, 0.0185973390f},
478 { 0.0115630893f, -0.0159152271f},
479 {-0.0078088406f, -0.0039788030f},
480 { 0.0028814530f, -0.0088682003f},
481 {-0.0099506630f, -0.0015760302f},
482 { 0.0000000000f, 0.0028570218f},
483 {-0.0030168074f, 0.0004778154f},
484 { 0.0018796273f, 0.0057848981f},
485 { 0.0019511001f, -0.0009941351f},
486 },
487 {
488 { 0.0025576725f, 0.0013031992f}, /* Filter 5 */
489 { 0.0015873149f, -0.0048852529f},
490 { 0.0002697804f, 0.0000427290f},
491 { 0.0000000000f, -0.0057443897f},
492 {-0.0088745956f, 0.0014055979f},
493 { 0.0007973152f, 0.0024538838f},
494 {-0.0131833524f, 0.0067172536f},
495 { 0.0100180850f, 0.0137887111f},
496 { 0.0069881240f, -0.0069881240f},
497 { 0.0268364889f, 0.0194978505f},
498 { 0.0544701590f, -0.1069037062f},
499 { 0.0578903347f, 0.0188097100f},
500 { 0.0940009470f, -0.5934986215f},
501 { 1.1328263283f, 0.0000000000f},
502 { 0.1144728051f, 0.7227528464f},
503 {-0.0114127678f, 0.0037082330f},
504 { 0.0652944573f, 0.1281475878f},
505 { 0.0149986858f, -0.0108971831f},
506 { 0.0148400450f, 0.0148400450f},
507 { 0.0114369676f, -0.0157416354f},
508 {-0.0099579979f, -0.0050738534f},
509 { 0.0022089316f, -0.0067983924f},
510 {-0.0098859888f, -0.0015657868f},
511 { 0.0000000000f, 0.0040052626f},
512 {-0.0019552917f, 0.0003096878f},
513 { 0.0018321212f, 0.0056386894f},
514 { 0.0025576725f, -0.0013031992f},
515 },
516 {
517 { 0.0030665390f, 0.0015624797f}, /* Filter 6 */
518 { 0.0017332683f, -0.0053344513f},
519 {-0.0008479269f, -0.0001342984f},
520 { 0.0000000000f, -0.0049758209f},
521 {-0.0095191676f, 0.0015076880f},
522 { 0.0015070535f, 0.0046382337f},
523 {-0.0117630486f, 0.0059935726f},
524 { 0.0109089996f, 0.0150149499f},
525 { 0.0109262557f, -0.0109262557f},
526 { 0.0215979519f, 0.0156918306f},
527 { 0.0602999226f, -0.1183452615f},
528 { 0.0256212387f, 0.0083248451f},
529 { 0.1043613218f, -0.6589114533f},
530 { 1.1366584301f, 0.0000000000f},
531 { 0.1043613218f, 0.6589114533f},
532 { 0.0256212387f, -0.0083248451f},
533 { 0.0602999226f, 0.1183452615f},
534 { 0.0215979519f, -0.0156918306f},
535 { 0.0109262557f, 0.0109262557f},
536 { 0.0109089996f, -0.0150149499f},
537 {-0.0117630486f, -0.0059935726f},
538 { 0.0015070535f, -0.0046382337f},
539 {-0.0095191676f, -0.0015076880f},
540 { 0.0000000000f, 0.0049758209f},
541 {-0.0008479269f, 0.0001342984f},
542 { 0.0017332683f, 0.0053344513f},
543 { 0.0030665390f, -0.0015624797f},
544 },
545 {
546 { 0.0034652296f, 0.0017656227f}, /* Filter 7 */
547 { 0.0018321212f, -0.0056386894f},
548 {-0.0019552917f, -0.0003096878f},
549 { 0.0000000000f, -0.0040052626f},
550 {-0.0098859888f, 0.0015657868f},
551 { 0.0022089316f, 0.0067983924f},
552 {-0.0099579979f, 0.0050738534f},
553 { 0.0114369676f, 0.0157416354f},
554 { 0.0148400450f, -0.0148400450f},
555 { 0.0149986858f, 0.0108971831f},
556 { 0.0652944573f, -0.1281475878f},
557 {-0.0114127678f, -0.0037082330f},
558 { 0.1144728051f, -0.7227528464f},
559 { 1.1328263283f, 0.0000000000f},
560 { 0.0940009470f, 0.5934986215f},
561 { 0.0578903347f, -0.0188097100f},
562 { 0.0544701590f, 0.1069037062f},
563 { 0.0268364889f, -0.0194978505f},
564 { 0.0069881240f, 0.0069881240f},
565 { 0.0100180850f, -0.0137887111f},
566 {-0.0131833524f, -0.0067172536f},
567 { 0.0007973152f, -0.0024538838f},
568 {-0.0088745956f, -0.0014055979f},
569 { 0.0000000000f, 0.0057443897f},
570 { 0.0002697804f, -0.0000427290f},
571 { 0.0015873149f, 0.0048852529f},
572 { 0.0034652296f, -0.0017656227f},
573 },
574 {
575 { 0.0037449420f, 0.0019081433f}, /* Filter 8 */
576 { 0.0018796273f, -0.0057848981f},
577 {-0.0030168074f, -0.0004778154f},
578 { 0.0000000000f, -0.0028570218f},
579 {-0.0099506630f, 0.0015760302f},
580 { 0.0028814530f, 0.0088682003f},
581 {-0.0078088406f, 0.0039788030f},
582 { 0.0115630893f, 0.0159152271f},
583 { 0.0185973391f, -0.0185973390f},
584 { 0.0070828755f, 0.0051460103f},
585 { 0.0692420091f, -0.1358950944f},
586 {-0.0530547129f, -0.0172385212f},
587 { 0.1242110958f, -0.7842379942f},
588 { 1.1213819981f, 0.0000000000f},
589 { 0.0835162618f, 0.5273009241f},
590 { 0.0853178815f, -0.0277214601f},
591 { 0.0480159027f, 0.0942365150f},
592 { 0.0307069943f, -0.0223099373f},
593 { 0.0031486956f, 0.0031486956f},
594 { 0.0088117029f, -0.0121282686f},
595 {-0.0141915007f, -0.0072309308f},
596 { 0.0001005750f, -0.0003095379f},
597 {-0.0079839961f, -0.0012645408f},
598 { 0.0000000000f, 0.0062936717f},
599 { 0.0013630316f, -0.0002158830f},
600 { 0.0013998188f, 0.0043081992f},
601 { 0.0037449420f, -0.0019081433f},
602 },
603 {
604 { 0.0039007144f, 0.0019875132f}, /* Filter 9 */
605 { 0.0018730190f, -0.0057645597f},
606 {-0.0039973434f, -0.0006331170f},
607 { 0.0000000000f, -0.0015619067f},
608 {-0.0096971580f, 0.0015358789f},
609 { 0.0035031104f, 0.0107814651f},
610 {-0.0053688554f, 0.0027355684f},
611 { 0.0112587393f, 0.0154963252f},
612 { 0.0220599213f, -0.0220599213f},
613 {-0.0020666801f, -0.0015015310f},
614 { 0.0719339457f, -0.1411783175f},
615 {-0.0990668329f, -0.0321887653f},
616 { 0.1334542238f, -0.8425968074f},
617 { 1.1024802923f, 0.0000000000f},
618 { 0.0730301872f, 0.4610944549f},
619 { 0.1079056364f, -0.0350606666f},
620 { 0.0411429005f, 0.0807474888f},
621 { 0.0332365955f, -0.0241478001f},
622 {-0.0004807357f, -0.0004807357f},
623 { 0.0073440948f, -0.0101082792f},
624 {-0.0147735044f, -0.0075274764f},
625 {-0.0005635325f, 0.0017343746f},
626 {-0.0068852097f, -0.0010905101f},
627 { 0.0000000000f, 0.0066136620f},
628 { 0.0023987660f, -0.0003799272f},
629 { 0.0011774450f, 0.0036238031f},
630 { 0.0039007144f, -0.0019875132f},
631 },
632 {
633 { 0.0039314768f, 0.0020031875f}, /* Filter 10 */
634 { 0.0018111360f, -0.0055741034f},
635 {-0.0048632493f, -0.0007702630f},
636 { 0.0000000000f, -0.0001565015f},
637 {-0.0091186142f, 0.0014442466f},
638 { 0.0040530413f, 0.0124739786f},
639 {-0.0027029676f, 0.0013772308f},
640 { 0.0105070407f, 0.0144617008f},
641 { 0.0250869159f, -0.0250869159f},
642 {-0.0123276338f, -0.0089565502f},
643 { 0.0731690629f, -0.1436023716f},
644 {-0.1491315874f, -0.0484557901f},
645 { 0.1420845360f, -0.8970864542f},
646 { 1.0763765574f, 0.0000000000f},
647 { 0.0626620127f, 0.3956323778f},
648 { 0.1257304818f, -0.0408523100f},
649 { 0.0340482504f, 0.0668234539f},
650 { 0.0344837758f, -0.0250539297f},
651 {-0.0038028170f, -0.0038028170f},
652 { 0.0056743444f, -0.0078100650f},
653 {-0.0149285967f, -0.0076064999f},
654 {-0.0011771217f, 0.0036228080f},
655 {-0.0056208495f, -0.0008902551f},
656 { 0.0000000000f, 0.0067017064f},
657 { 0.0033466091f, -0.0005300508f},
658 { 0.0009277352f, 0.0028552754f},
659 { 0.0039314768f, -0.0020031875f},
660 },
661 {
662 { 0.0038399827f, 0.0019565689f}, /* Filter 11 */
663 { 0.0016945064f, -0.0052151545f},
664 {-0.0055834514f, -0.0008843318f},
665 {-0.0000000000f, 0.0013177606f},
666 {-0.0082179267f, 0.0013015917f},
667 { 0.0045116911f, 0.0138855574f},
668 { 0.0001136455f, -0.0000579053f},
669 { 0.0093041035f, 0.0128059998f},
670 { 0.0275384769f, -0.0275384769f},
671 {-0.0235379981f, -0.0171013566f},
672 { 0.0727579142f, -0.1427954467f},
673 {-0.2028542371f, -0.0659113371f},
674 { 0.1499906453f, -0.9470036639f},
675 { 1.0434222221f, 0.0000000000f},
676 { 0.0525256151f, 0.3316336818f},
677 { 0.1389398473f, -0.0451442930f},
678 { 0.0269170677f, 0.0528277198f},
679 { 0.0345347757f, -0.0250909833f},
680 {-0.0067358415f, -0.0067358415f},
681 { 0.0038644283f, -0.0053189292f},
682 {-0.0146686673f, -0.0074740593f},
683 {-0.0017245111f, 0.0053074995f},
684 {-0.0042368606f, -0.0006710528f},
685 { 0.0000000000f, 0.0065623410f},
686 { 0.0041797109f, -0.0006620012f},
687 { 0.0006588563f, 0.0020277512f},
688 { 0.0038399827f, -0.0019565689f},
689 },
690 };
691
692 float v27ter_rx_carrier_frequency(v27ter_rx_state_t *s)
693 {
694 return dds_frequencyf(s->carrier_phase_rate);
695 }
696 /*- End of function --------------------------------------------------------*/
697
698 float v27ter_rx_symbol_timing_correction(v27ter_rx_state_t *s)
699 {
700 int steps_per_symbol;
701
702 steps_per_symbol = (s->bit_rate == 4800) ? PULSESHAPER_4800_COEFF_SETS*5 : PULSESHAPER_2400_COEFF_SETS*20/3;
703 return (float) s->total_baud_timing_correction/(float) steps_per_symbol;
704 }
705 /*- End of function --------------------------------------------------------*/
706
707 float v27ter_rx_signal_power(v27ter_rx_state_t *s)
708 {
709 return power_meter_dbm0(&s->power);
710 }
711 /*- End of function --------------------------------------------------------*/
712
713 void v27ter_rx_signal_cutoff(v27ter_rx_state_t *s, float cutoff)
714 {
715 /* The 0.4 factor allows for the gain of the DC blocker */
716 s->carrier_on_power = (int32_t) (power_meter_level_dbm0(cutoff + 2.5f)*0.4f);
717 s->carrier_off_power = (int32_t) (power_meter_level_dbm0(cutoff - 2.5f)*0.4f);
718 }
719 /*- End of function --------------------------------------------------------*/
720
721 int v27ter_rx_equalizer_state(v27ter_rx_state_t *s, complexf_t **coeffs)
722 {
723 *coeffs = s->eq_coeff;
724 return V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN;
725 }
726 /*- End of function --------------------------------------------------------*/
727
728 static void equalizer_save(v27ter_rx_state_t *s)
729 {
730 cvec_copyf(s->eq_coeff_save, s->eq_coeff, V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN);
731 }
732 /*- End of function --------------------------------------------------------*/
733
734 static void equalizer_restore(v27ter_rx_state_t *s)
735 {
736 cvec_copyf(s->eq_coeff, s->eq_coeff_save, V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN);
737 cvec_zerof(s->eq_buf, V27TER_EQUALIZER_MASK);
738
739 s->eq_put_step = (s->bit_rate == 4800) ? PULSESHAPER_4800_COEFF_SETS*5/2 : PULSESHAPER_2400_COEFF_SETS*20/(3*2);
740 s->eq_step = 0;
741 s->eq_delta = EQUALIZER_DELTA/(V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN);
742 }
743 /*- End of function --------------------------------------------------------*/
744
745 static void equalizer_reset(v27ter_rx_state_t *s)
746 {
747 /* Start with an equalizer based on everything being perfect */
748 cvec_zerof(s->eq_coeff, V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN);
749 s->eq_coeff[V27TER_EQUALIZER_PRE_LEN] = complex_setf(1.414f, 0.0f);
750 cvec_zerof(s->eq_buf, V27TER_EQUALIZER_MASK);
751
752 s->eq_put_step = (s->bit_rate == 4800) ? PULSESHAPER_4800_COEFF_SETS*5/2 : PULSESHAPER_2400_COEFF_SETS*20/(3*2);
753 s->eq_step = 0;
754 s->eq_delta = EQUALIZER_DELTA/(V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN);
755 }
756 /*- End of function --------------------------------------------------------*/
757
758 static __inline__ complexf_t equalizer_get(v27ter_rx_state_t *s)
759 {
760 int i;
761 int p;
762 complexf_t z;
763 complexf_t z1;
764
765 /* Get the next equalized value. */
766 z = complex_setf(0.0, 0.0);
767 p = s->eq_step - 1;
768 for (i = 0; i < V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN; i++)
769 {
770 p = (p - 1) & V27TER_EQUALIZER_MASK;
771 z1 = complex_mulf(&s->eq_coeff[i], &s->eq_buf[p]);
772 z = complex_addf(&z, &z1);
773 }
774 return z;
775 }
776 /*- End of function --------------------------------------------------------*/
777
778 static void tune_equalizer(v27ter_rx_state_t *s, const complexf_t *z, const complexf_t *target)
779 {
780 int i;
781 int p;
782 complexf_t ez;
783 complexf_t z1;
784
785 /* Find the x and y mismatch from the exact constellation position. */
786 ez = complex_subf(target, z);
787 ez.re *= s->eq_delta;
788 ez.im *= s->eq_delta;
789
790 p = s->eq_step - 1;
791 for (i = 0; i < V27TER_EQUALIZER_PRE_LEN + 1 + V27TER_EQUALIZER_POST_LEN; i++)
792 {
793 p = (p - 1) & V27TER_EQUALIZER_MASK;
794 z1 = complex_conjf(&s->eq_buf[p]);
795 z1 = complex_mulf(&ez, &z1);
796 s->eq_coeff[i] = complex_addf(&s->eq_coeff[i], &z1);
797 /* Leak a little to tame uncontrolled wandering */
798 s->eq_coeff[i].re *= 0.9999f;
799 s->eq_coeff[i].im *= 0.9999f;
800 }
801 }
802 /*- End of function --------------------------------------------------------*/
803
804 static __inline__ void track_carrier(v27ter_rx_state_t *s, const complexf_t *z, const complexf_t *target)
805 {
806 float error;
807
808 /* For small errors the imaginary part of the difference between the actual and the target
809 positions is proportional to the phase error, for any particular target. However, the
810 different amplitudes of the various target positions scale things. */
811 error = z->im*target->re - z->re*target->im;
812
813 s->carrier_phase_rate += (int32_t) (s->carrier_track_i*error);
814 s->carrier_phase += (int32_t) (s->carrier_track_p*error);
815 //span_log(&s->logging, SPAN_LOG_FLOW, "Im = %15.5f f = %15.5f\n", error, dds_frequencyf(s->carrier_phase_rate));
816 }
817 /*- End of function --------------------------------------------------------*/
818
819 static __inline__ int descramble(v27ter_rx_state_t *s, int in_bit)
820 {
821 int out_bit;
822
823 out_bit = (in_bit ^ (s->scramble_reg >> 5) ^ (s->scramble_reg >> 6)) & 1;
824 if (s->scrambler_pattern_count >= 33)
825 {
826 out_bit ^= 1;
827 s->scrambler_pattern_count = 0;
828 }
829 else
830 {
831 if (s->in_training > TRAINING_STAGE_NORMAL_OPERATION && s->in_training < TRAINING_STAGE_TEST_ONES)
832 {
833 s->scrambler_pattern_count = 0;
834 }
835 else
836 {
837 if ((((s->scramble_reg >> 7) ^ in_bit) & ((s->scramble_reg >> 8) ^ in_bit) & ((s->scramble_reg >> 11) ^ in_bit) & 1))
838 s->scrambler_pattern_count = 0;
839 else
840 s->scrambler_pattern_count++;
841 }
842 }
843 s->scramble_reg <<= 1;
844 if (s->in_training > TRAINING_STAGE_NORMAL_OPERATION && s->in_training < TRAINING_STAGE_TEST_ONES)
845 s->scramble_reg |= out_bit;
846 else
847 s->scramble_reg |= in_bit;
848 return out_bit;
849 }
850 /*- End of function --------------------------------------------------------*/
851
852 static __inline__ void put_bit(v27ter_rx_state_t *s, int bit)
853 {
854 int out_bit;
855
856 bit &= 1;
857
858 out_bit = descramble(s, bit);
859
860 /* We need to strip the last part of the training before we let data
861 go to the application. */
862 if (s->in_training == TRAINING_STAGE_NORMAL_OPERATION)
863 {
864 s->put_bit(s->user_data, out_bit);
865 }
866 else
867 {
868 //span_log(&s->logging, SPAN_LOG_FLOW, "Test bit %d\n", out_bit);
869 /* The bits during the final stage of training should be all ones. However,
870 buggy modems mean you cannot rely on this. Therefore we don't bother
871 testing for ones, but just rely on a constellation mismatch measurement. */
872 }
873 }
874 /*- End of function --------------------------------------------------------*/
875
876 static __inline__ int find_quadrant(const complexf_t *z)
877 {
878 int b1;
879 int b2;
880
881 /* Split the space along the two diagonals. */
882 b1 = (z->im > z->re);
883 b2 = (z->im < -z->re);
884 return (b2 << 1) | (b1 ^ b2);
885 }
886 /*- End of function --------------------------------------------------------*/
887
888 static __inline__ int find_octant(complexf_t *z)
889 {
890 float abs_re;
891 float abs_im;
892 int b1;
893 int b2;
894 int bits;
895
896 /* Are we near an axis or a diagonal? */
897 abs_re = fabsf(z->re);
898 abs_im = fabsf(z->im);
899 if (abs_im > abs_re*0.4142136 && abs_im < abs_re*2.4142136)
900 {
901 /* Split the space along the two axes. */
902 b1 = (z->re < 0.0);
903 b2 = (z->im < 0.0);
904 bits = (b2 << 2) | ((b1 ^ b2) << 1) | 1;
905 }
906 else
907 {
908 /* Split the space along the two diagonals. */
909 b1 = (z->im > z->re);
910 b2 = (z->im < -z->re);
911 bits = (b2 << 2) | ((b1 ^ b2) << 1);
912 }
913 return bits;
914 }
915 /*- End of function --------------------------------------------------------*/
916
917 static void decode_baud(v27ter_rx_state_t *s, complexf_t *z)
918 {
919 static const uint8_t phase_steps_4800[8] =
920 {
921 4, 0, 2, 6, 7, 3, 1, 5
922 };
923 static const uint8_t phase_steps_2400[4] =
924 {
925 0, 2, 3, 1
926 };
927 int nearest;
928 int raw_bits;
929
930 switch (s->bit_rate)
931 {
932 case 4800:
933 default:
934 nearest = find_octant(z);
935 raw_bits = phase_steps_4800[(nearest - s->constellation_state) & 7];
936 put_bit(s, raw_bits);
937 put_bit(s, raw_bits >> 1);
938 put_bit(s, raw_bits >> 2);
939 s->constellation_state = nearest;
940 break;
941 case 2400:
942 nearest = find_quadrant(z);
943 raw_bits = phase_steps_2400[(nearest - s->constellation_state) & 3];
944 put_bit(s, raw_bits);
945 put_bit(s, raw_bits >> 1);
946 s->constellation_state = nearest;
947 nearest <<= 1;
948 break;
949 }
950 track_carrier(s, z, &v27ter_constellation[nearest]);
951 if (--s->eq_skip <= 0)
952 {
953 /* Once we are in the data the equalization should not need updating.
954 However, the line characteristics may slowly drift. We, therefore,
955 tune up on the occassional sample, keeping the compute down. */
956 s->eq_skip = 100;
957 tune_equalizer(s, z, &v27ter_constellation[nearest]);
958 }
959 }
960 /*- End of function --------------------------------------------------------*/
961
962 static __inline__ void process_half_baud(v27ter_rx_state_t *s, const complexf_t *sample)
963 {
964 static const int abab_pos[2] =
965 {
966 0, 4
967 };
968 complexf_t z;
969 complexf_t zz;
970 float p;
971 float q;
972 int i;
973 int j;
974 int32_t angle;
975 int32_t ang;
976
977 /* Add a sample to the equalizer's circular buffer, but don't calculate anything
978 at this time. */
979 s->eq_buf[s->eq_step] = *sample;
980 s->eq_step = (s->eq_step + 1) & V27TER_EQUALIZER_MASK;
981
982 /* On alternate insertions we have a whole baud, and must process it. */
983 if ((s->baud_phase ^= 1))
984 {
985 //span_log(&s->logging, SPAN_LOG_FLOW, "Samp, %f, %f, %f, -1, 0x%X\n", z.re, z.im, sqrt(z.re*z.re + z.im*z.im), s->eq_put_step);
986 return;
987 }
988 //span_log(&s->logging, SPAN_LOG_FLOW, "Samp, %f, %f, %f, 1, 0x%X\n", z.re, z.im, sqrt(z.re*z.re + z.im*z.im), s->eq_put_step);
989
990 /* Perform a Gardner test for baud alignment */
991 p = s->eq_buf[(s->eq_step - 3) & V27TER_EQUALIZER_MASK].re
992 - s->eq_buf[(s->eq_step - 1) & V27TER_EQUALIZER_MASK].re;
993 p *= s->eq_buf[(s->eq_step - 2) & V27TER_EQUALIZER_MASK].re;
994
995 q = s->eq_buf[(s->eq_step - 3) & V27TER_EQUALIZER_MASK].im
996 - s->eq_buf[(s->eq_step - 1) & V27TER_EQUALIZER_MASK].im;
997 q *= s->eq_buf[(s->eq_step - 2) & V27TER_EQUALIZER_MASK].im;
998
999 s->gardner_integrate += (p + q > 0.0) ? s->gardner_step : -s->gardner_step;
1000
1001 if (abs(s->gardner_integrate) >= 256)
1002 {
1003 /* This integrate and dump approach avoids rapid changes of the equalizer put step.
1004 Rapid changes, without hysteresis, are bad. They degrade the equalizer performance
1005 when the true symbol boundary is close to a sample boundary. */
1006 //span_log(&s->logging, SPAN_LOG_FLOW, "Hop %d\n", s->gardner_integrate);
1007 s->eq_put_step += (s->gardner_integrate/256);
1008 s->total_baud_timing_correction += (s->gardner_integrate/256);
1009 if (s->qam_report)
1010 s->qam_report(s->qam_user_data, NULL, NULL, s->gardner_integrate);
1011 s->gardner_integrate = 0;
1012 }
1013 //span_log(&s->logging, SPAN_LOG_FLOW, "Gardner=%10.5f 0x%X\n", p, s->eq_put_step);
1014
1015 z = equalizer_get(s);
1016
1017 //span_log(&s->logging, SPAN_LOG_FLOW, "Equalized symbol - %15.5f %15.5f\n", z.re, z.im);
1018 switch (s->in_training)
1019 {
1020 case TRAINING_STAGE_NORMAL_OPERATION:
1021 decode_baud(s, &z);
1022 break;
1023 case TRAINING_STAGE_SYMBOL_ACQUISITION:
1024 /* Allow time for the Gardner algorithm to settle the baud timing */
1025 /* Don't start narrowing the bandwidth of the Gardner algorithm too early.
1026 Some modems are a bit wobbly when they start sending the signal. Also, we start
1027 this analysis before our filter buffers have completely filled. */
1028 if (++s->training_count >= 30)
1029 {
1030 s->gardner_step = 32;
1031 s->in_training = TRAINING_STAGE_LOG_PHASE;
1032 s->angles[0] =
1033 s->start_angles[0] = arctan2(z.im, z.re);
1034 }
1035 break;
1036 case TRAINING_STAGE_LOG_PHASE:
1037 /* Record the current alternate phase angle */
1038 angle = arctan2(z.im, z.re);
1039 s->angles[1] =
1040 s->start_angles[1] = angle;
1041 s->training_count = 1;
1042 s->in_training = TRAINING_STAGE_WAIT_FOR_HOP;
1043 break;
1044 case TRAINING_STAGE_WAIT_FOR_HOP:
1045 angle = arctan2(z.im, z.re);
1046 /* Look for the initial ABAB sequence to display a phase reversal, which will
1047 signal the start of the scrambled ABAB segment */
1048 ang = angle - s->angles[(s->training_count - 1) & 0xF];
1049 s->angles[(s->training_count + 1) & 0xF] = angle;
1050 if ((ang > 0x20000000 || ang < -0x20000000) && s->training_count >= 3)
1051 {
1052 /* We seem to have a phase reversal */
1053 /* Slam the carrier frequency into line, based on the total phase drift over the last
1054 section. Use the shift from the odd bits and the shift from the even bits to get
1055 better jitter suppression. We need to scale here, or at the maximum specified
1056 frequency deviation we could overflow, and get a silly answer. */
1057 /* Step back a few symbols so we don't get ISI distorting things. */
1058 i = (s->training_count - 8) & ~1;
1059 /* Avoid the possibility of a divide by zero */
1060 if (i)
1061 {
1062 j = i & 0xF;
1063 ang = (s->angles[j] - s->start_angles[0])/i
1064 + (s->angles[j | 0x1] - s->start_angles[1])/i;
1065 if (s->bit_rate == 4800)
1066 s->carrier_phase_rate += ang/10;
1067 else
1068 s->carrier_phase_rate += 3*(ang/40);
1069 }
1070 span_log(&s->logging, SPAN_LOG_FLOW, "Coarse carrier frequency %7.2f (%d)\n", dds_frequencyf(s->carrier_phase_rate), s->training_count);
1071 /* Check if the carrier frequency is plausible */
1072 if (s->carrier_phase_rate < dds_phase_ratef(CARRIER_NOMINAL_FREQ - 20.0f)
1073 ||
1074 s->carrier_phase_rate > dds_phase_ratef(CARRIER_NOMINAL_FREQ + 20.0f))
1075 {
1076 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n");
1077 /* Park this modem */
1078 s->in_training = TRAINING_STAGE_PARKED;
1079 s->put_bit(s->user_data, PUTBIT_TRAINING_FAILED);
1080 break;
1081 }
1082
1083 /* Make a step shift in the phase, to pull it into line. We need to rotate the equalizer
1084 buffer, as well as the carrier phase, for this to play out nicely. */
1085 angle += 0x80000000;
1086 p = angle*2.0f*3.14159f/(65536.0f*65536.0f);
1087 zz = complex_setf(cosf(p), -sinf(p));
1088 for (i = 0; i <= V27TER_EQUALIZER_MASK; i++)
1089 s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz);
1090 s->carrier_phase += angle;
1091
1092 s->gardner_step = 1;
1093 /* We have just seen the first element of the scrambled sequence so skip it. */
1094 s->training_bc = 1;
1095 s->training_bc ^= descramble(s, 1);
1096 descramble(s, 1);
1097 descramble(s, 1);
1098 s->training_count = 1;
1099 s->in_training = TRAINING_STAGE_TRAIN_ON_ABAB;
1100 }
1101 else if (++s->training_count > V27TER_TRAINING_SEG_3_LEN)
1102 {
1103 /* This is bogus. There are not this many bits in this section
1104 of a real training sequence. */
1105 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n");
1106 /* Park this modem */
1107 s->in_training = TRAINING_STAGE_PARKED;
1108 s->put_bit(s->user_data, PUTBIT_TRAINING_FAILED);
1109 }
1110 break;
1111 case TRAINING_STAGE_TRAIN_ON_ABAB:
1112 /* Train on the scrambled ABAB section */
1113 s->training_bc ^= descramble(s, 1);
1114 descramble(s, 1);
1115 descramble(s, 1);
1116 s->constellation_state = abab_pos[s->training_bc];
1117 track_carrier(s, &z, &v27ter_constellation[s->constellation_state]);
1118 tune_equalizer(s, &z, &v27ter_constellation[s->constellation_state]);
1119
1120 if (++s->training_count >= V27TER_TRAINING_SEG_5_LEN)
1121 {
1122 s->constellation_state = (s->bit_rate == 4800) ? 4 : 2;
1123 s->training_count = 0;
1124 s->in_training = TRAINING_STAGE_TEST_ONES;
1125 s->carrier_track_i = 400.0;
1126 s->carrier_track_p = 1000000.0;
1127 }
1128 break;
1129 case TRAINING_STAGE_TEST_ONES:
1130 decode_baud(s, &z);
1131 /* Measure the training error */
1132 if (s->bit_rate == 4800)
1133 zz = complex_subf(&z, &v27ter_constellation[s->constellation_state]);
1134 else
1135 zz = complex_subf(&z, &v27ter_constellation[s->constellation_state << 1]);
1136 s->training_error += powerf(&zz);
1137 if (++s->training_count >= V27TER_TRAINING_SEG_6_LEN)
1138 {
1139 if ((s->bit_rate == 4800 && s->training_error < 1.0f)
1140 ||
1141 (s->bit_rate == 2400 && s->training_error < 1.0f))
1142 {
1143 /* We are up and running */
1144 span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded (constellation mismatch %f)\n", s->training_error);
1145 s->put_bit(s->user_data, PUTBIT_TRAINING_SUCCEEDED);
1146 /* Apply some lag to the carrier off condition, to ensure the last few bits get pushed through
1147 the processing. */
1148 s->carrier_present = (s->bit_rate == 4800) ? 90 : 120;
1149 s->in_training = TRAINING_STAGE_NORMAL_OPERATION;
1150 equalizer_save(s);
1151 s->carrier_phase_rate_save = s->carrier_phase_rate;
1152 s->agc_scaling_save = s->agc_scaling;
1153 }
1154 else
1155 {
1156 /* Training has failed */
1157 span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error);
1158 /* Park this modem */
1159 s->in_training = TRAINING_STAGE_PARKED;
1160 s->put_bit(s->user_data, PUTBIT_TRAINING_FAILED);
1161 }
1162 }
1163 break;
1164 case TRAINING_STAGE_PARKED:
1165 /* We failed to train! */
1166 /* Park here until the carrier drops. */
1167 break;
1168 }
1169 if (s->qam_report)
1170 {
1171 s->qam_report(s->qam_user_data,
1172 &z,
1173 &v27ter_constellation[s->constellation_state],
1174 s->constellation_state);
1175 }
1176 }
1177 /*- End of function --------------------------------------------------------*/
1178
1179 int v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], int len)
1180 {
1181 int i;
1182 int j;
1183 int step;
1184 complexf_t z;
1185 complexf_t zz;
1186 complexf_t samplex;
1187 int32_t power;
1188
1189 if (s->bit_rate == 4800)
1190 {
1191 for (i = 0; i < len; i++)
1192 {
1193 s->rrc_filter[s->rrc_filter_step] =
1194 s->rrc_filter[s->rrc_filter_step + V27TER_RX_4800_FILTER_STEPS] = amp[i];
1195 if (++s->rrc_filter_step >= V27TER_RX_4800_FILTER_STEPS)
1196 s->rrc_filter_step = 0;
1197
1198 /* There should be no DC in the signal, but sometimes there is.
1199 We need to measure the power with the DC blocked, but not using
1200 a slow to respond DC blocker. Use the most elementary HPF. */
1201 power = power_meter_update(&(s->power), (amp[i] - s->last_sample) >> 1);
1202 s->last_sample = amp[i];
1203 //span_log(&s->logging, SPAN_LOG_FLOW, "Power = %f\n", power_meter_dbm0(&(s->power)));
1204 if (s->carrier_present)
1205 {
1206 /* Look for power below turnoff threshold to turn the carrier off */
1207 if (power < s->carrier_off_power)
1208 {
1209 if (--s->carrier_present <= 0)
1210 {
1211 /* Count down a short delay, to ensure we push the last
1212 few bits through the filters before stopping. */
1213 v27ter_rx_restart(s, s->bit_rate, FALSE);
1214 s->put_bit(s->user_data, PUTBIT_CARRIER_DOWN);
1215 continue;
1216 }
1217 }
1218 }
1219 else
1220 {
1221 /* Look for power exceeding turnon threshold to turn the carrier on */
1222 if (power < s->carrier_on_power)
1223 continue;
1224 s->carrier_present = 1;
1225 s->put_bit(s->user_data, PUTBIT_CARRIER_UP);
1226 }
1227 if (s->in_training != TRAINING_STAGE_PARKED)
1228 {
1229 /* Only spend effort processing this data if the modem is not
1230 parked, after training failure. */
1231 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate);
1232
1233 /* Put things into the equalization buffer at T/2 rate. The Gardner algorithm
1234 will fiddle the step to align this with the symbols. */
1235 if ((s->eq_put_step -= PULSESHAPER_4800_COEFF_SETS) <= 0)
1236 {
1237 if (s->in_training == TRAINING_STAGE_SYMBOL_ACQUISITION)
1238 {
1239 /* Only AGC during the initial training */
1240 s->agc_scaling = (1.0f/PULSESHAPER_4800_GAIN)*1.414f/sqrtf(power);
1241 }
1242 /* Pulse shape while still at the carrier frequency, using a quadrature
1243 pair of filters. This results in a properly bandpass filtered complex
1244 signal, which can be brought directly to bandband by complex mixing.
1245 No further filtering, to remove mixer harmonics, is needed. */
1246 step = -s->eq_put_step;
1247 if (step > PULSESHAPER_4800_COEFF_SETS - 1)
1248 step = PULSESHAPER_4800_COEFF_SETS - 1;
1249 s->eq_put_step += PULSESHAPER_4800_COEFF_SETS*5/2;
1250 zz.re = pulseshaper_4800[step][0].re*s->rrc_filter[s->rrc_filter_step];
1251 zz.im = pulseshaper_4800[step][0].im*s->rrc_filter[s->rrc_filter_step];
1252 for (j = 1; j < V27TER_RX_4800_FILTER_STEPS; j++)
1253 {
1254 zz.re += pulseshaper_4800[step][j].re*s->rrc_filter[j + s->rrc_filter_step];
1255 zz.im += pulseshaper_4800[step][j].im*s->rrc_filter[j + s->rrc_filter_step];
1256 }
1257 samplex.re = zz.re*s->agc_scaling;
1258 samplex.im = zz.im*s->agc_scaling;
1259 /* Shift to baseband - since this is done in a full complex form, the
1260 result is clean, and requires no further filtering, apart from the
1261 equalizer. */
1262 zz.re = samplex.re*z.re - samplex.im*z.im;
1263 zz.im = -samplex.re*z.im - samplex.im*z.re;
1264 process_half_baud(s, &zz);
1265 }
1266 }
1267 }
1268 }
1269 else
1270 {
1271 for (i = 0; i < len; i++)
1272 {
1273 s->rrc_filter[s->rrc_filter_step] =
1274 s->rrc_filter[s->rrc_filter_step + V27TER_RX_2400_FILTER_STEPS] = amp[i];
1275 if (++s->rrc_filter_step >= V27TER_RX_2400_FILTER_STEPS)
1276 s->rrc_filter_step = 0;
1277
1278 /* There should be no DC in the signal, but sometimes there is.
1279 We need to measure the power with the DC blocked, but not using
1280 a slow to respond DC blocker. Use the most elementary HPF. */
1281 power = power_meter_update(&(s->power), (amp[i] - s->last_sample) >> 1);
1282 s->last_sample = amp[i];
1283 //span_log(&s->logging, SPAN_LOG_FLOW, "Power = %f\n", power_meter_dbm0(&(s->power)));
1284 if (s->carrier_present)
1285 {
1286 /* Look for power below turnoff threshold to turn the carrier off */
1287 if (power < s->carrier_off_power)
1288 {
1289 if (--s->carrier_present <= 0)
1290 {
1291 /* Count down a short delay, to ensure we push the last
1292 few bits through the filters before stopping. */
1293 v27ter_rx_restart(s, s->bit_rate, FALSE);
1294 s->put_bit(s->user_data, PUTBIT_CARRIER_DOWN);
1295 continue;
1296 }
1297 }
1298 }
1299 else
1300 {
1301 /* Look for power exceeding turnon threshold to turn the carrier on */
1302 if (power < s->carrier_on_power)
1303 continue;
1304 s->carrier_present = 1;
1305 s->put_bit(s->user_data, PUTBIT_CARRIER_UP);
1306 }
1307 if (s->in_training != TRAINING_STAGE_PARKED)
1308 {
1309 /* Only spend effort processing this data if the modem is not
1310 parked, after training failure. */
1311 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate);
1312
1313 /* Put things into the equalization buffer at T/2 rate. The Gardner algorithm
1314 will fiddle the step to align this with the symbols. */
1315 if ((s->eq_put_step -= PULSESHAPER_2400_COEFF_SETS) <= 0)
1316 {
1317 if (s->in_training == TRAINING_STAGE_SYMBOL_ACQUISITION)
1318 {
1319 /* Only AGC during the initial training */
1320 s->agc_scaling = (1.0f/PULSESHAPER_2400_GAIN)*1.414f/sqrtf(power);
1321 }
1322 /* Pulse shape while still at the carrier frequency, using a quadrature
1323 pair of filters. This results in a properly bandpass filtered complex
1324 signal, which can be brought directly to bandband by complex mixing.
1325 No further filtering, to remove mixer harmonics, is needed. */
1326 step = -s->eq_put_step;
1327 if (step > PULSESHAPER_2400_COEFF_SETS - 1)
1328 step = PULSESHAPER_2400_COEFF_SETS - 1;
1329 s->eq_put_step += PULSESHAPER_2400_COEFF_SETS*20/(3*2);
1330 zz.re = pulseshaper_2400[step][0].re*s->rrc_filter[s->rrc_filter_step];
1331 zz.im = pulseshaper_2400[step][0].im*s->rrc_filter[s->rrc_filter_step];
1332 for (j = 1; j < V27TER_RX_2400_FILTER_STEPS; j++)
1333 {
1334 zz.re += pulseshaper_2400[step][j].re*s->rrc_filter[j + s->rrc_filter_step];
1335 zz.im += pulseshaper_2400[step][j].im*s->rrc_filter[j + s->rrc_filter_step];
1336 }
1337 samplex.re = zz.re*s->agc_scaling;
1338 samplex.im = zz.im*s->agc_scaling;
1339 /* Shift to baseband - since this is done in a full complex form, the
1340 result is clean, and requires no further filtering apart from the
1341 equalizer. */
1342 zz.re = samplex.re*z.re - samplex.im*z.im;
1343 zz.im = -samplex.re*z.im - samplex.im*z.re;
1344 process_half_baud(s, &zz);
1345 }
1346 }
1347 }
1348 }
1349 return 0;
1350 }
1351 /*- End of function --------------------------------------------------------*/
1352
1353 void v27ter_rx_set_put_bit(v27ter_rx_state_t *s, put_bit_func_t put_bit, void *user_data)
1354 {
1355 s->put_bit = put_bit;
1356 s->user_data = user_data;
1357 }
1358 /*- End of function --------------------------------------------------------*/
1359
1360 int v27ter_rx_restart(v27ter_rx_state_t *s, int rate, int old_train)
1361 {
1362 span_log(&s->logging, SPAN_LOG_FLOW, "Restarting V.27ter\n");
1363 if (rate != 4800 && rate != 2400)
1364 return -1;
1365 s->bit_rate = rate;
1366
1367 vec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
1368 s->rrc_filter_step = 0;
1369
1370 s->scramble_reg = 0x3C;
1371 s->scrambler_pattern_count = 0;
1372 s->in_training = TRAINING_STAGE_SYMBOL_ACQUISITION;
1373 s->training_bc = 0;
1374 s->training_count = 0;
1375 s->training_error = 0.0f;
1376 s->carrier_present = 0;
1377
1378 s->carrier_phase = 0;
1379 //s->carrier_track_i = 100000.0f;
1380 //s->carrier_track_p = 20000000.0f;
1381 s->carrier_track_i = 200000.0f;
1382 s->carrier_track_p = 10000000.0f;
1383 power_meter_init(&(s->power), 4);
1384
1385 s->constellation_state = 0;
1386
1387 if (s->old_train)
1388 {
1389 s->carrier_phase_rate = s->carrier_phase_rate_save;
1390 s->agc_scaling = s->agc_scaling_save;
1391 equalizer_restore(s);
1392 }
1393 else
1394 {
1395 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ);
1396 s->agc_scaling = 0.0005f;
1397 equalizer_reset(s);
1398 }
1399 s->eq_skip = 0;
1400 s->last_sample = 0;
1401
1402 s->gardner_integrate = 0;
1403 s->total_baud_timing_correction = 0;
1404 s->gardner_step = 512;
1405 s->baud_phase = 0;
1406
1407 return 0;
1408 }
1409 /*- End of function --------------------------------------------------------*/
1410
1411 v27ter_rx_state_t *v27ter_rx_init(v27ter_rx_state_t *s, int rate, put_bit_func_t put_bit, void *user_data)
1412 {
1413 if (s == NULL)
1414 {
1415 if ((s = (v27ter_rx_state_t *) malloc(sizeof(*s))) == NULL)
1416 return NULL;
1417 }
1418 memset(s, 0, sizeof(*s));
1419 v27ter_rx_signal_cutoff(s, -45.5f);
1420 s->put_bit = put_bit;
1421 s->user_data = user_data;
1422 span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
1423 span_log_set_protocol(&s->logging, "V.27ter");
1424
1425 v27ter_rx_restart(s, rate, FALSE);
1426 return s;
1427 }
1428 /*- End of function --------------------------------------------------------*/
1429
1430 int v27ter_rx_release(v27ter_rx_state_t *s)
1431 {
1432 free(s);
1433 return 0;
1434 }
1435 /*- End of function --------------------------------------------------------*/
1436
1437 void v27ter_rx_set_qam_report_handler(v27ter_rx_state_t *s, qam_report_handler_t *handler, void *user_data)
1438 {
1439 s->qam_report = handler;
1440 s->qam_user_data = user_data;
1441 }
1442 /*- End of function --------------------------------------------------------*/
1443 /*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.