diff spandsp-0.0.3/spandsp-0.0.3/src/v29rx.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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spandsp-0.0.3/spandsp-0.0.3/src/v29rx.c	Fri Jun 25 16:00:21 2010 +0200
@@ -0,0 +1,2265 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * v29rx.c - ITU V.29 modem receive part
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2003 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: v29rx.c,v 1.100 2006/11/28 16:59:57 steveu Exp $
+ */
+
+/*! \file */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/complex.h"
+#include "spandsp/vector_float.h"
+#include "spandsp/complex_vector_float.h"
+#include "spandsp/async.h"
+#include "spandsp/power_meter.h"
+#include "spandsp/arctan2.h"
+#include "spandsp/dds.h"
+#include "spandsp/complex_filters.h"
+
+#include "spandsp/v29rx.h"
+
+#define CARRIER_NOMINAL_FREQ        1700.0f
+#define BAUD_RATE                   2400
+#define EQUALIZER_DELTA             0.21f
+
+/* Segments of the training sequence */
+#define V29_TRAINING_SEG_2_LEN      128
+#define V29_TRAINING_SEG_3_LEN      384
+#define V29_TRAINING_SEG_4_LEN      48
+
+enum
+{
+    TRAINING_STAGE_NORMAL_OPERATION = 0,
+    TRAINING_STAGE_SYMBOL_ACQUISITION,
+    TRAINING_STAGE_LOG_PHASE,
+    TRAINING_STAGE_WAIT_FOR_CDCD,
+    TRAINING_STAGE_TRAIN_ON_CDCD,
+    TRAINING_STAGE_TRAIN_ON_CDCD_AND_TEST,
+    TRAINING_STAGE_TEST_ONES,
+    TRAINING_STAGE_PARKED
+};
+
+static const complexf_t v29_constellation[16] =
+{
+    { 3.0f,  0.0f},     /*   0deg low  */
+    { 1.0f,  1.0f},     /*  45deg low  */
+    { 0.0f,  3.0f},     /*  90deg low  */
+    {-1.0f,  1.0f},     /* 135deg low  */
+    {-3.0f,  0.0f},     /* 180deg low  */
+    {-1.0f, -1.0f},     /* 225deg low  */
+    { 0.0f, -3.0f},     /* 270deg low  */
+    { 1.0f, -1.0f},     /* 315deg low  */
+    { 5.0f,  0.0f},     /*   0deg high */
+    { 3.0f,  3.0f},     /*  45deg high */
+    { 0.0f,  5.0f},     /*  90deg high */
+    {-3.0f,  3.0f},     /* 135deg high */
+    {-5.0f,  0.0f},     /* 180deg high */
+    {-3.0f, -3.0f},     /* 225deg high */
+    { 0.0f, -5.0f},     /* 270deg high */
+    { 3.0f, -3.0f}      /* 315deg high */
+};
+
+static const uint8_t space_map_9600[20][20] =
+{
+    {13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11},
+    {13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11},
+    {13, 13, 13, 13, 13, 13, 13,  4,  4,  4,  4,  4,  4, 11, 11, 11, 11, 11, 11, 11},
+    {13, 13, 13, 13, 13, 13, 13,  4,  4,  4,  4,  4,  4, 11, 11, 11, 11, 11, 11, 11},
+    {13, 13, 13, 13, 13, 13, 13,  4,  4,  4,  4,  4,  4, 11, 11, 11, 11, 11, 11, 11},
+    {13, 13, 13, 13, 13, 13, 13,  5,  4,  4,  4,  4,  3, 11, 11, 11, 11, 11, 11, 11},
+    {14, 13, 13, 13, 13, 13,  5,  5,  5,  5,  3,  3,  3,  3, 11, 11, 11, 11, 11, 10},
+    {14, 14,  6,  6,  6,  5,  5,  5,  5,  5,  3,  3,  3,  3,  3,  2,  2,  2, 10, 10},
+    {14, 14,  6,  6,  6,  6,  5,  5,  5,  5,  3,  3,  3,  3,  2,  2,  2,  2, 10, 10},
+    {14, 14,  6,  6,  6,  6,  5,  5,  5,  5,  3,  3,  3,  3,  2,  2,  2,  2, 10, 10},
+    {14, 14,  6,  6,  6,  6,  7,  7,  7,  7,  1,  1,  1,  1,  2,  2,  2,  2, 10, 10},
+    {14, 14,  6,  6,  6,  6,  7,  7,  7,  7,  1,  1,  1,  1,  2,  2,  2,  2, 10, 10},
+    {14, 14,  6,  6,  6,  7,  7,  7,  7,  7,  1,  1,  1,  1,  1,  2,  2,  2, 10, 10},
+    {14, 15, 15, 15, 15, 15,  7,  7,  7,  7,  1,  1,  1,  1,  9,  9,  9,  9,  9, 10},
+    {15, 15, 15, 15, 15, 15, 15,  7,  0,  0,  0,  0,  1,  9,  9,  9,  9,  9,  9,  9},
+    {15, 15, 15, 15, 15, 15, 15,  0,  0,  0,  0,  0,  0,  9,  9,  9,  9,  9,  9,  9},
+    {15, 15, 15, 15, 15, 15, 15,  0,  0,  0,  0,  0,  0,  9,  9,  9,  9,  9,  9,  9},
+    {15, 15, 15, 15, 15, 15, 15,  0,  0,  0,  0,  0,  0,  9,  9,  9,  9,  9,  9,  9},
+    {15, 15, 15, 15, 15, 15, 15,  8,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9},
+    {15, 15, 15, 15, 15, 15,  8,  8,  8,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9}
+};
+
+/* Raised root cosine pulse shaping; Beta = 0.5; 4 symbols either
+   side of the centre. We cannot simplify this by using only half
+   the filter, as each variant are each skewed by n/PULSESHAPER_COEFF_SETS of a
+   sample. Only one is symmetric. */
+#define PULSESHAPER_GAIN        10.00736638f
+#define PULSESHAPER_COEFF_SETS  48
+static const complexf_t pulseshaper[PULSESHAPER_COEFF_SETS][V29_RX_FILTER_STEPS] =
+{
+    {
+        {-0.0006054906f, -0.0076934875f},       /* Filter 0 */
+        { 0.0036800486f, -0.0011957203f},
+        {-0.0058580399f, -0.0095594523f},
+        { 0.0088272477f, -0.0088272477f},
+        {-0.0070156625f, -0.0042992035f},
+        { 0.0051634535f, -0.0158914757f},
+        {-0.0145681539f, -0.0011465386f},
+        {-0.0064674675f, -0.0408339830f},
+        {-0.0136664375f,  0.0056608238f},
+        {-0.0772003348f, -0.1062571451f},
+        { 0.0857393910f, -0.1003879288f},
+        {-0.1385128868f, -0.0705758409f},
+        { 0.1555698727f, -0.6479951549f},
+        { 1.0773601675f,  0.0000000000f},
+        { 0.2515047363f,  1.0475926203f},
+        {-0.5937739316f,  0.3025429295f},
+        {-0.1009610161f, -0.1182101618f},
+        {-0.0775987390f,  0.1068055014f},
+        {-0.1213433119f, -0.0502620455f},
+        { 0.0023140483f, -0.0146103259f},
+        {-0.0412155371f,  0.0032437331f},
+        {-0.0045157276f, -0.0138979805f},
+        {-0.0142470087f,  0.0087305782f},
+        {-0.0058181901f, -0.0058181901f},
+        {-0.0065226701f,  0.0106440302f},
+        {-0.0106628561f, -0.0034645720f},
+        {-0.0003035921f,  0.0038575039f},
+    },
+    {
+        {-0.0003235976f, -0.0041116977f},       /* Filter 1 */
+        { 0.0034317849f, -0.0011150545f},
+        {-0.0059948336f, -0.0097826794f},
+        { 0.0086437458f, -0.0086437458f},
+        {-0.0074140419f, -0.0045433307f},
+        { 0.0050851588f, -0.0156505094f},
+        {-0.0154601145f, -0.0012167374f},
+        {-0.0064469783f, -0.0407046190f},
+        {-0.0156933045f,  0.0065003796f},
+        {-0.0783633460f, -0.1078578927f},
+        { 0.0839596331f, -0.0983041000f},
+        {-0.1466635902f, -0.0747288317f},
+        { 0.1581236518f, -0.6586324102f},
+        { 1.0822215889f,  0.0000000000f},
+        { 0.2503251863f,  1.0426794408f},
+        {-0.5839878512f,  0.2975566724f},
+        {-0.0950902426f, -0.1113363693f},
+        {-0.0791302548f,  0.1089134521f},
+        {-0.1194691818f, -0.0494857553f},
+        { 0.0019760247f, -0.0124761291f},
+        {-0.0413061980f,  0.0032508683f},
+        {-0.0042392071f, -0.0130469379f},
+        {-0.0144455612f,  0.0088522513f},
+        {-0.0054839934f, -0.0054839934f},
+        {-0.0066522258f,  0.0108554460f},
+        {-0.0104075179f, -0.0033816075f},
+        {-0.0003235976f,  0.0041116977f},
+    },
+    {
+        {-0.0003431146f, -0.0043596842f},       /* Filter 2 */
+        { 0.0031779221f, -0.0010325695f},
+        {-0.0061279979f, -0.0099999837f},
+        { 0.0084521727f, -0.0084521727f},
+        {-0.0078074148f, -0.0047843899f},
+        { 0.0050005298f, -0.0153900482f},
+        {-0.0163511406f, -0.0012868627f},
+        {-0.0064201364f, -0.0405351459f},
+        {-0.0177497724f,  0.0073521965f},
+        {-0.0794957450f, -0.1094165063f},
+        { 0.0820917217f, -0.0961170567f},
+        {-0.1549087286f, -0.0789299396f},
+        { 0.1606663689f, -0.6692235895f},
+        { 1.0868898157f,  0.0000000000f},
+        { 0.2491014054f,  1.0375820265f},
+        {-0.5741661901f,  0.2925522861f},
+        {-0.0892910248f, -0.1045463576f},
+        {-0.0805830641f,  0.1109130726f},
+        {-0.1175509420f, -0.0486911944f},
+        { 0.0016433282f, -0.0103755662f},
+        {-0.0413575411f,  0.0032549091f},
+        {-0.0039628985f, -0.0121965475f},
+        {-0.0146266493f,  0.0089632222f},
+        {-0.0051463192f, -0.0051463192f},
+        {-0.0067756751f,  0.0110568969f},
+        {-0.0101460989f, -0.0032966674f},
+        {-0.0003431146f,  0.0043596842f},
+    },
+    {
+        {-0.0003621305f, -0.0046013044f},       /* Filter 3 */
+        { 0.0029186307f, -0.0009483206f},
+        {-0.0062573914f, -0.0102111348f},
+        { 0.0082526365f, -0.0082526365f},
+        {-0.0081953754f, -0.0050221325f},
+        { 0.0049095725f, -0.0151101104f},
+        {-0.0172404079f, -0.0013568495f},
+        {-0.0063868556f, -0.0403250190f},
+        {-0.0198348595f,  0.0082158678f},
+        {-0.0805962556f, -0.1109312291f},
+        { 0.0801351139f, -0.0938261634f},
+        {-0.1632463544f, -0.0831781721f},
+        { 0.1631971552f, -0.6797650728f},
+        { 1.0913630919f,  0.0000000000f},
+        { 0.2478338529f,  1.0323022902f},
+        {-0.5643122679f,  0.2875314620f},
+        {-0.0835646644f, -0.0978416511f},
+        {-0.0819577515f,  0.1128051674f},
+        {-0.1155905906f, -0.0478791903f},
+        { 0.0013161074f, -0.0083095754f},
+        {-0.0413701808f,  0.0032559039f},
+        {-0.0036870475f, -0.0113475654f},
+        {-0.0147903020f,  0.0090635087f},
+        {-0.0048055043f, -0.0048055043f},
+        {-0.0068929555f,  0.0112482810f},
+        {-0.0098788669f, -0.0032098384f},
+        {-0.0003621305f,  0.0046013044f},
+    },
+    {
+        {-0.0003806334f, -0.0048364061f},       /* Filter 4 */
+        { 0.0026540877f, -0.0008623654f},
+        {-0.0063828750f, -0.0104159053f},
+        { 0.0080452535f, -0.0080452535f},
+        {-0.0085775184f, -0.0052563101f},
+        { 0.0048122983f, -0.0148107313f},
+        {-0.0181270820f, -0.0014266323f},
+        {-0.0063470532f, -0.0400737166f},
+        {-0.0219475497f,  0.0090909727f},
+        {-0.0816636012f, -0.1124003042f},
+        { 0.0780893017f, -0.0914308250f},
+        {-0.1716744668f, -0.0874725099f},
+        { 0.1657151424f, -0.6902532446f},
+        { 1.0956397332f,  0.0000000000f},
+        { 0.2465230038f,  1.0268422107f},
+        {-0.5544294025f,  0.2824958906f},
+        {-0.0779124236f, -0.0912237273f},
+        {-0.0832549311f,  0.1145905820f},
+        {-0.1135901216f, -0.0470505689f},
+        { 0.0009945051f, -0.0062790582f},
+        {-0.0413447525f,  0.0032539026f},
+        {-0.0034118961f, -0.0105007365f},
+        {-0.0149365628f,  0.0091531374f},
+        {-0.0044618854f, -0.0044618854f},
+        {-0.0070040103f,  0.0114295060f},
+        {-0.0096060925f, -0.0031212087f},
+        {-0.0003806334f,  0.0048364061f},
+    },
+    {
+        {-0.0003986119f, -0.0050648440f},       /* Filter 5 */
+        { 0.0023844759f, -0.0007747632f},
+        {-0.0065043116f, -0.0106140718f},
+        { 0.0078301476f, -0.0078301476f},
+        {-0.0089534403f, -0.0054866753f},
+        { 0.0047087245f, -0.0144919637f},
+        {-0.0190103194f, -0.0014961446f},
+        {-0.0063006503f, -0.0397807405f},
+        {-0.0240867927f,  0.0099770762f},
+        {-0.0826965057f, -0.1138219754f},
+        { 0.0759538132f, -0.0889304892f},
+        {-0.1801910135f, -0.0918119071f},
+        { 0.1682194641f, -0.7006844950f},
+        { 1.0997181284f,  0.0000000000f},
+        { 0.2451693490f,  1.0212038328f},
+        {-0.5445209082f,  0.2774472605f},
+        {-0.0723355236f, -0.0846940164f},
+        {-0.0844752467f,  0.1162702023f},
+        {-0.1115515226f, -0.0462061535f},
+        { 0.0006786580f, -0.0042848780f},
+        {-0.0412819110f,  0.0032489569f},
+        {-0.0031376826f, -0.0096567942f},
+        {-0.0150654901f,  0.0092321442f},
+        {-0.0041157984f, -0.0041157984f},
+        {-0.0071087889f,  0.0116004892f},
+        {-0.0093280490f, -0.0030308668f},
+        {-0.0003986119f,  0.0050648440f},
+    },
+    {
+        {-0.0004160550f, -0.0052864799f},       /* Filter 6 */
+        { 0.0021099840f, -0.0006855754f},
+        {-0.0066215668f, -0.0108054149f},
+        { 0.0076074506f, -0.0076074506f},
+        {-0.0093227386f, -0.0057129815f},
+        { 0.0045988736f, -0.0141538775f},
+        {-0.0198892682f, -0.0015653194f},
+        {-0.0062475719f, -0.0394456164f},
+        {-0.0262515049f,  0.0108737293f},
+        {-0.0836936943f, -0.1151944878f},
+        { 0.0737282126f, -0.0863246457f},
+        {-0.1887938905f, -0.0961952919f},
+        { 0.1707092554f, -0.7110552220f},
+        { 1.1035967397f,  0.0000000000f},
+        { 0.2437733949f,  1.0153892656f},
+        {-0.5345900940f,  0.2723872580f},
+        {-0.0668351454f, -0.0782539010f},
+        {-0.0856193708f,  0.1178449541f},
+        {-0.1094767749f, -0.0453467649f},
+        { 0.0003686970f, -0.0023278610f},
+        {-0.0411823301f,  0.0032411197f},
+        {-0.0028646414f, -0.0088164597f},
+        {-0.0151771562f,  0.0093005733f},
+        {-0.0037675783f, -0.0037675783f},
+        {-0.0072072465f,  0.0117611574f},
+        {-0.0090450120f, -0.0029389025f},
+        {-0.0004160550f,  0.0052864799f},
+    },
+    {
+        {-0.0004329524f, -0.0055011823f},       /* Filter 7 */
+        { 0.0018308065f, -0.0005948651f},
+        {-0.0067345088f, -0.0109897195f},
+        { 0.0073773020f, -0.0073773020f},
+        {-0.0096850130f, -0.0059349836f},
+        { 0.0044827743f, -0.0137965606f},
+        {-0.0207630684f, -0.0016340889f},
+        {-0.0061877467f, -0.0390678953f},
+        {-0.0284405684f,  0.0117804691f},
+        {-0.0846538939f, -0.1165160891f},
+        { 0.0714121011f, -0.0836128276f},
+        {-0.1974809435f, -0.1006215665f},
+        { 0.1731836536f, -0.7213618325f},
+        { 1.1072741035f,  0.0000000000f},
+        { 0.2423356631f,  1.0094006816f},
+        {-0.5246402627f,  0.2673175657f},
+        {-0.0614124285f, -0.0719047153f},
+        {-0.0866880040f,  0.1193158015f},
+        {-0.1073678519f, -0.0444732204f},
+        { 0.0000647469f, -0.0004087959f},
+        {-0.0410467015f,  0.0032304455f},
+        {-0.0025930028f, -0.0079804420f},
+        {-0.0152716480f,  0.0093584780f},
+        {-0.0034175587f, -0.0034175587f},
+        {-0.0072993441f,  0.0119114470f},
+        {-0.0087572594f, -0.0028454060f},
+        {-0.0004329524f,  0.0055011823f},
+    },
+    {
+        {-0.0004492944f, -0.0057088271f},       /* Filter 8 */
+        { 0.0015471436f, -0.0005026974f},
+        {-0.0068430082f, -0.0111667745f},
+        { 0.0071398493f, -0.0071398493f},
+        {-0.0100398656f, -0.0061524375f},
+        { 0.0043604608f, -0.0134201184f},
+        {-0.0216308523f, -0.0017023850f},
+        {-0.0061211077f, -0.0386471532f},
+        {-0.0306528326f,  0.0126968190f},
+        {-0.0855758340f, -0.1177850307f},
+        { 0.0690051173f, -0.0807946117f},
+        {-0.2062499680f, -0.1050896077f},
+        { 0.1756417989f, -0.7316007446f},
+        { 1.1107488316f,  0.0000000000f},
+        { 0.2408566902f,  1.0032403162f},
+        {-0.5146747091f,  0.2622398626f},
+        {-0.0560684714f, -0.0656477454f},
+        {-0.0876818748f,  0.1206837473f},
+        {-0.1052267179f, -0.0435863337f},
+        {-0.0002330732f,  0.0014715664f},
+        {-0.0408757346f,  0.0032169901f},
+        {-0.0023229930f, -0.0071494373f},
+        {-0.0153490660f,  0.0094059197f},
+        {-0.0030660718f, -0.0030660718f},
+        {-0.0073850485f,  0.0120513039f},
+        {-0.0084650709f, -0.0027504683f},
+        {-0.0004492944f,  0.0057088271f},
+    },
+    {
+        {-0.0004650718f, -0.0059092972f},       /* Filter 9 */
+        { 0.0012592008f, -0.0004091391f},
+        {-0.0069469389f, -0.0113363740f},
+        { 0.0068952472f, -0.0068952472f},
+        {-0.0103869007f, -0.0063651009f},
+        { 0.0042319732f, -0.0130246742f},
+        {-0.0224917457f, -0.0017701388f},
+        {-0.0060475918f, -0.0381829922f},
+        {-0.0328871136f,  0.0136222885f},
+        {-0.0864582472f, -0.1189995684f},
+        { 0.0665069376f, -0.0778696190f},
+        {-0.2150987105f, -0.1095982672f},
+        { 0.1780828343f, -0.7417683886f},
+        { 1.1140196110f,  0.0000000000f},
+        { 0.2393370276f,  0.9969104659f},
+        {-0.5046967186f,  0.2571558224f},
+        {-0.0508043306f, -0.0594842284f},
+        {-0.0886017384f,  0.1219498309f},
+        {-0.1030553278f, -0.0426869144f},
+        {-0.0005246505f,  0.0033125128f},
+        {-0.0406701557f,  0.0032008107f},
+        {-0.0020548339f, -0.0063241284f},
+        {-0.0154095242f,  0.0094429686f},
+        {-0.0027134480f, -0.0027134480f},
+        {-0.0074643325f,  0.0121806836f},
+        {-0.0081687281f, -0.0026541806f},
+        {-0.0004650718f,  0.0059092972f},
+    },
+    {
+        {-0.0004802758f, -0.0061024829f},       /* Filter 10 */
+        { 0.0009671895f, -0.0003142589f},
+        {-0.0070461774f, -0.0114983165f},
+        { 0.0066436583f, -0.0066436583f},
+        {-0.0107257260f, -0.0065727333f},
+        { 0.0040973574f, -0.0126103693f},
+        {-0.0233448674f, -0.0018372809f},
+        {-0.0059671403f, -0.0376750413f},
+        {-0.0351421949f,  0.0145563737f},
+        {-0.0872998705f, -0.1201579635f},
+        { 0.0639172763f, -0.0748375152f},
+        {-0.2240248692f, -0.1141463722f},
+        { 0.1805059061f, -0.7518612089f},
+        { 1.1170852055f,  0.0000000000f},
+        { 0.2377772412f,  0.9904134878f},
+        {-0.4947095655f,  0.2520671138f},
+        {-0.0456210210f, -0.0534153526f},
+        {-0.0894483767f,  0.1231151286f},
+        {-0.1008556255f, -0.0417757679f},
+        {-0.0008098779f,  0.0051133675f},
+        {-0.0404307068f,  0.0031819656f},
+        {-0.0017887430f, -0.0055051847f},
+        {-0.0154531504f,  0.0094697028f},
+        {-0.0023600156f, -0.0023600156f},
+        {-0.0075371745f,  0.0122995509f},
+        {-0.0078685137f, -0.0025566351f},
+        {-0.0004802758f,  0.0061024829f},
+    },
+    {
+        {-0.0004948985f, -0.0062882817f},       /* Filter 11 */
+        { 0.0006713257f, -0.0002181269f},
+        {-0.0071406035f, -0.0116524059f},
+        { 0.0063852527f, -0.0063852526f},
+        {-0.0110559525f, -0.0067750964f},
+        { 0.0039566651f, -0.0121773631f},
+        {-0.0241893313f, -0.0019037417f},
+        {-0.0058796987f, -0.0371229566f},
+        {-0.0374168281f,  0.0154985577f},
+        {-0.0880994453f, -0.1212584838f},
+        { 0.0612358868f, -0.0716980115f},
+        {-0.2330260947f, -0.1187327256f},
+        { 0.1829101645f, -0.7618756659f},
+        { 1.1199444556f,  0.0000000000f},
+        { 0.2361779109f,  0.9837517978f},
+        {-0.4847165122f,  0.2469753988f},
+        {-0.0405195152f, -0.0474422567f},
+        {-0.0902225974f,  0.1241807520f},
+        {-0.0986295432f, -0.0408536944f},
+        {-0.0010886543f,  0.0068734926f},
+        {-0.0401581456f,  0.0031605146f},
+        {-0.0015249332f, -0.0046932617f},
+        {-0.0154800853f,  0.0094862085f},
+        {-0.0020061005f, -0.0020061005f},
+        {-0.0076035587f,  0.0124078801f},
+        {-0.0075647115f, -0.0024579238f},
+        {-0.0004948985f,  0.0062882817f},
+    },
+    {
+        {-0.0005089323f, -0.0064665981f},       /* Filter 12 */
+        { 0.0003718310f, -0.0001208152f},
+        {-0.0072301003f, -0.0117984513f},
+        { 0.0061202077f, -0.0061202077f},
+        {-0.0113771948f, -0.0069719540f},
+        { 0.0038099540f, -0.0117258328f},
+        {-0.0250242456f, -0.0019694508f},
+        {-0.0057852169f, -0.0365264219f},
+        {-0.0397097323f,  0.0164483097f},
+        {-0.0888557186f, -0.1222994047f},
+        { 0.0584625613f, -0.0684508645f},
+        {-0.2420999907f, -0.1233561066f},
+        { 0.1852947639f, -0.7718082372f},
+        { 1.1225962793f,  0.0000000000f},
+        { 0.2345396308f,  0.9769278702f},
+        {-0.4747208072f,  0.2418823327f},
+        {-0.0355007435f, -0.0415660302f},
+        {-0.0909252334f,  0.1251478474f},
+        {-0.0963790005f, -0.0399214891f},
+        {-0.0013608847f,  0.0085922877f},
+        {-0.0398532442f,  0.0031365183f},
+        {-0.0012636128f, -0.0038890004f},
+        {-0.0154904827f,  0.0094925800f},
+        {-0.0016520262f, -0.0016520262f},
+        {-0.0076634750f,  0.0125056546f},
+        {-0.0072576065f, -0.0023581393f},
+        {-0.0005089323f,  0.0064665981f},
+    },
+    {
+        {-0.0005223703f, -0.0066373443f},       /* Filter 13 */
+        { 0.0000689315f, -0.0000223972f},
+        {-0.0073145539f, -0.0119362671f},
+        { 0.0058487082f, -0.0058487082f},
+        {-0.0116890717f, -0.0071630723f},
+        { 0.0036572876f, -0.0112559738f},
+        {-0.0258487141f, -0.0020343379f},
+        {-0.0056836493f, -0.0358851497f},
+        {-0.0420195954f,  0.0174050863f},
+        {-0.0895674434f, -0.1232790098f},
+        { 0.0555971318f, -0.0650958776f},
+        {-0.2512441154f, -0.1280152709f},
+        { 0.1876588631f, -0.7816554194f},
+        { 1.1250396727f,  0.0000000000f},
+        { 0.2328630086f,  0.9699442360f},
+        {-0.4647256838f,  0.2367895630f},
+        {-0.0305655940f, -0.0357877124f},
+        {-0.0915571420f,  0.1260175949f},
+        {-0.0941059032f, -0.0389799414f},
+        {-0.0016264798f,  0.0102691896f},
+        {-0.0395167881f,  0.0031100387f},
+        {-0.0010049855f, -0.0030930272f},
+        {-0.0154845091f,  0.0094889194f},
+        {-0.0012981133f, -0.0012981133f},
+        {-0.0077169192f,  0.0125928676f},
+        {-0.0069474841f, -0.0022573744f},
+        {-0.0005223703f,  0.0066373443f},
+    },
+    {
+        {-0.0005352062f, -0.0068004397f},       /* Filter 14 */
+        {-0.0002371416f,  0.0000770520f},
+        {-0.0073938544f, -0.0120656738f},
+        { 0.0055709463f, -0.0055709463f},
+        {-0.0119912063f, -0.0073482207f},
+        { 0.0034987353f, -0.0107680002f},
+        {-0.0266618369f, -0.0020983321f},
+        {-0.0055749551f, -0.0351988812f},
+        {-0.0443450744f,  0.0183683312f},
+        {-0.0902333798f, -0.1241955926f},
+        { 0.0526394697f, -0.0616329003f},
+        {-0.2604559819f, -0.1327089513f},
+        { 0.1900016262f, -0.7914137299f},
+        { 1.1272737104f,  0.0000000000f},
+        { 0.2311486652f,  0.9628034819f},
+        {-0.4547343588f,  0.2316987286f},
+        {-0.0257149117f, -0.0301082932f},
+        {-0.0921192046f,  0.1267912077f},
+        {-0.0918121426f, -0.0380298346f},
+        {-0.0018853566f,  0.0119036728f},
+        {-0.0391495767f,  0.0030811385f},
+        {-0.0007492498f, -0.0023059539f},
+        {-0.0154623431f,  0.0094753361f},
+        {-0.0009446795f, -0.0009446795f},
+        {-0.0077638924f,  0.0126695210f},
+        {-0.0066346301f, -0.0021557220f},
+        {-0.0005352062f,  0.0068004397f},
+    },
+    {
+        {-0.0005474342f, -0.0069558110f},       /* Filter 15 */
+        {-0.0005461525f,  0.0001774557f},
+        {-0.0074678951f, -0.0121864973f},
+        { 0.0052871211f, -0.0052871211f},
+        {-0.0122832268f, -0.0075271711f},
+        { 0.0033343726f, -0.0102621437f},
+        {-0.0274627107f, -0.0021613622f},
+        {-0.0054590979f, -0.0344673874f},
+        {-0.0466847952f,  0.0193374753f},
+        {-0.0908522954f, -0.1250474569f},
+        { 0.0495894870f, -0.0580618293f},
+        {-0.2697330596f, -0.1374358585f},
+        { 0.1923222223f, -0.8010797080f},
+        { 1.1292975460f,  0.0000000000f},
+        { 0.2293972347f,  0.9555082489f},
+        {-0.4447500311f,  0.2266114595f},
+        {-0.0209494991f, -0.0245287119f},
+        {-0.0926123259f,  0.1274699310f},
+        {-0.0894995944f, -0.0370719458f},
+        {-0.0021374375f,  0.0134952491f},
+        {-0.0387524213f,  0.0030498817f},
+        {-0.0004965997f, -0.0015283766f},
+        {-0.0154241758f,  0.0094519471f},
+        {-0.0005920390f, -0.0005920390f},
+        {-0.0078044017f,  0.0127356261f},
+        {-0.0063193305f, -0.0020532749f},
+        {-0.0005474342f,  0.0069558110f},
+    },
+    {
+        {-0.0005590491f, -0.0071033921f},       /* Filter 16 */
+        {-0.0008578611f,  0.0002787360f},
+        {-0.0075365733f, -0.0122985698f},
+        { 0.0049974389f, -0.0049974388f},
+        {-0.0125647661f, -0.0076996986f},
+        { 0.0031642806f, -0.0097386544f},
+        {-0.0282504297f, -0.0022233570f},
+        {-0.0053360461f, -0.0336904691f},
+        {-0.0490373539f,  0.0203119370f},
+        {-0.0914229663f, -0.1258329180f},
+        { 0.0464471360f, -0.0543826090f},
+        {-0.2790727745f, -0.1421946809f},
+        { 0.1946198263f, -0.8106499175f},
+        { 1.1311104123f,  0.0000000000f},
+        { 0.2276093640f,  0.9480612314f},
+        {-0.4347758804f,  0.2215293759f},
+        {-0.0162701156f, -0.0190498578f},
+        {-0.0930374331f,  0.1280550409f},
+        {-0.0871701184f, -0.0361070452f},
+        {-0.0023826511f,  0.0150434671f},
+        {-0.0383261449f,  0.0030163330f},
+        {-0.0002472237f, -0.0007608763f},
+        {-0.0153702098f,  0.0094188767f},
+        {-0.0002405024f, -0.0002405024f},
+        {-0.0078384594f,  0.0127912031f},
+        {-0.0060018711f, -0.0019501261f},
+        {-0.0005590491f,  0.0071033921f},
+    },
+    {
+        {-0.0005700463f, -0.0072431248f},       /* Filter 17 */
+        {-0.0011720231f,  0.0003808134f},
+        {-0.0075997900f, -0.0124017301f},
+        { 0.0047021127f, -0.0047021127f},
+        {-0.0128354632f, -0.0078655819f},
+        { 0.0029885467f, -0.0091978010f},
+        {-0.0290240862f, -0.0022842451f},
+        {-0.0052057731f, -0.0328679578f},
+        {-0.0514013169f,  0.0212911226f},
+        {-0.0919441775f, -0.1265503037f},
+        { 0.0432124100f, -0.0505952315f},
+        {-0.2884725105f, -0.1469840856f},
+        { 0.1968936193f, -0.8201209469f},
+        { 1.1327116219f,  0.0000000000f},
+        { 0.2257857123f,  0.9404651753f},
+        {-0.4248150656f,  0.2164540873f},
+        {-0.0116774779f, -0.0136725700f},
+        {-0.0933954755f,  0.1285478440f},
+        {-0.0848255565f, -0.0351358959f},
+        {-0.0026209319f,  0.0165479125f},
+        {-0.0378715811f,  0.0029805581f},
+        {-0.0000013054f, -0.0000040177f},
+        {-0.0153006596f,  0.0093762563f},
+        { 0.0001096232f,  0.0001096232f},
+        {-0.0078660833f,  0.0128362814f},
+        {-0.0056825374f, -0.0018463683f},
+        {-0.0005700463f,  0.0072431248f},
+    },
+    {
+        {-0.0005804218f, -0.0073749578f},       /* Filter 18 */
+        {-0.0014883899f,  0.0004836072f},
+        {-0.0076574502f, -0.0124958231f},
+        { 0.0044013628f, -0.0044013628f},
+        {-0.0130949624f, -0.0080246033f},
+        { 0.0028072639f, -0.0086398699f},
+        {-0.0297827710f, -0.0023439549f},
+        {-0.0050682571f, -0.0319997159f},
+        {-0.0537752213f,  0.0222744260f},
+        {-0.0924147241f, -0.1271979554f},
+        { 0.0398853435f, -0.0466997371f},
+        {-0.2979296109f, -0.1518027189f},
+        { 0.1991427888f, -0.8294894121f},
+        { 1.1341005677f,  0.0000000000f},
+        { 0.2239269510f,  0.9327228772f},
+        {-0.4148707237f,  0.2113871920f},
+        {-0.0071722592f, -0.0083976366f},
+        {-0.0936874236f,  0.1289496760f},
+        {-0.0824677330f, -0.0341592535f},
+        {-0.0028522199f,  0.0180082079f},
+        {-0.0373895741f,  0.0029426233f},
+        { 0.0002409767f,  0.0007416500f},
+        {-0.0152157506f,  0.0093242240f},
+        { 0.0004580348f,  0.0004580348f},
+        {-0.0078872970f,  0.0128708989f},
+        {-0.0053616143f, -0.0017420941f},
+        {-0.0005804218f,  0.0073749578f},
+    },
+    {
+        {-0.0005901721f, -0.0074988473f},       /* Filter 19 */
+        {-0.0018067093f,  0.0005870354f},
+        {-0.0077094631f, -0.0125807004f},
+        { 0.0040954155f, -0.0040954155f},
+        {-0.0133429149f, -0.0081765487f},
+        { 0.0026205314f, -0.0080651662f},
+        {-0.0305255746f, -0.0024024148f},
+        {-0.0049234813f, -0.0310856377f},
+        {-0.0561575756f,  0.0232612294f},
+        {-0.0928334114f, -0.1277742292f},
+        { 0.0364660122f, -0.0426962145f},
+        {-0.3074413786f, -0.1566492066f},
+        { 0.2013665292f, -0.8387519577f},
+        { 1.1352767227f,  0.0000000000f},
+        { 0.2220337633f,  0.9248371830f},
+        {-0.4049459681f,  0.2063302764f},
+        {-0.0027550900f, -0.0032257959f},
+        {-0.0939142683f,  0.1292619011f},
+        {-0.0800984531f, -0.0331778656f},
+        {-0.0030764613f,  0.0194240122f},
+        {-0.0368809773f,  0.0029025959f},
+        { 0.0004794497f,  0.0014755944f},
+        {-0.0151157193f,  0.0092629247f},
+        { 0.0008044337f,  0.0008044337f},
+        {-0.0079021290f,  0.0128951026f},
+        {-0.0050393858f, -0.0016373957f},
+        {-0.0005901721f,  0.0074988473f},
+    },
+    {
+        {-0.0005992944f, -0.0076147568f},       /* Filter 20 */
+        {-0.0021267251f,  0.0006910149f},
+        {-0.0077557420f, -0.0126562207f},
+        { 0.0037845040f, -0.0037845040f},
+        {-0.0135789777f, -0.0083212083f},
+        { 0.0024284540f, -0.0074740128f},
+        {-0.0312515874f, -0.0024595533f},
+        {-0.0047714341f, -0.0301256492f},
+        {-0.0585468603f,  0.0242509035f},
+        {-0.0931990565f, -0.1282774964f},
+        { 0.0329545338f, -0.0385848015f},
+        {-0.3170050775f, -0.1615221547f},
+        { 0.2035640423f, -0.8479052583f},
+        { 1.1362396407f,  0.0000000000f},
+        { 0.2201068440f,  0.9168109865f},
+        {-0.3950438879f,  0.2012849146f},
+        { 0.0015734425f,  0.0018422645f},
+        {-0.0940770209f,  0.1294859108f},
+        {-0.0777195022f, -0.0321924719f},
+        {-0.0032936077f,  0.0207950209f},
+        {-0.0363466528f,  0.0028605436f},
+        { 0.0007139459f,  0.0021972995f},
+        {-0.0150008130f,  0.0091925101f},
+        { 0.0011485255f,  0.0011485255f},
+        {-0.0079106134f,  0.0129089479f},
+        {-0.0047161348f, -0.0015323651f},
+        {-0.0005992944f,  0.0076147568f},
+    },
+    {
+        {-0.0006077863f, -0.0077226571f},       /* Filter 21 */
+        {-0.0024481778f,  0.0007954612f},
+        {-0.0077962045f, -0.0127222494f},
+        { 0.0034688681f, -0.0034688681f},
+        {-0.0138028157f, -0.0084583763f},
+        { 0.0022311426f, -0.0068667508f},
+        {-0.0319599006f, -0.0025152987f},
+        {-0.0046121088f, -0.0291197091f},
+        {-0.0609415283f,  0.0252428075f},
+        {-0.0935104881f, -0.1287061452f},
+        { 0.0293510677f, -0.0343656849f},
+        {-0.3266179339f, -0.1664201496f},
+        { 0.2057345372f, -0.8569460203f},
+        { 1.1369889562f,  0.0000000000f},
+        { 0.2181468989f,  0.9086472276f},
+        {-0.3851675459f,  0.1962526670f},
+        { 0.0058127944f,  0.0068059078f},
+        {-0.0941767115f,  0.1296231230f},
+        {-0.0753326453f, -0.0312038034f},
+        {-0.0035036167f,  0.0221209652f},
+        {-0.0357874706f,  0.0028165350f},
+        { 0.0009443030f,  0.0029062658f},
+        {-0.0148712891f,  0.0091131377f},
+        { 0.0014900206f,  0.0014900206f},
+        {-0.0079127895f,  0.0129124989f},
+        {-0.0043921429f, -0.0014270937f},
+        {-0.0006077863f,  0.0077226571f},
+    },
+    {
+        {-0.0006156462f, -0.0078225266f},       /* Filter 22 */
+        {-0.0027708045f,  0.0009002890f},
+        {-0.0078307725f, -0.0127786593f},
+        { 0.0031487534f, -0.0031487534f},
+        {-0.0140141002f, -0.0085878517f},
+        { 0.0020287139f, -0.0062437395f},
+        {-0.0326496069f, -0.0025695798f},
+        {-0.0044455042f, -0.0280678087f},
+        {-0.0633400059f,  0.0262362895f},
+        {-0.0937665479f, -0.1290585813f},
+        { 0.0256558155f, -0.0300391005f},
+        {-0.3362771370f, -0.1713417594f},
+        { 0.2078772313f, -0.8658709834f},
+        { 1.1375243848f,  0.0000000000f},
+        { 0.2161546447f,  0.9003488919f},
+        {-0.3753199780f,  0.1912350805f},
+        { 0.0099624646f,  0.0116645474f},
+        {-0.0942143887f,  0.1296749813f},
+        {-0.0729396259f, -0.0302125823f},
+        {-0.0037064513f,  0.0234016123f},
+        {-0.0352043080f,  0.0027706391f},
+        { 0.0011703644f,  0.0036020111f},
+        {-0.0147274152f,  0.0090249716f},
+        { 0.0018286340f,  0.0018286340f},
+        {-0.0079087015f,  0.0129058279f},
+        {-0.0040676902f, -0.0013216727f},
+        {-0.0006156462f,  0.0078225266f},
+    },
+    {
+        {-0.0006228729f, -0.0079143505f},       /* Filter 23 */
+        {-0.0030943393f,  0.0010054118f},
+        {-0.0078593727f, -0.0128253306f},
+        { 0.0028244120f, -0.0028244120f},
+        {-0.0142125109f, -0.0087094379f},
+        { 0.0018212906f, -0.0056053560f},
+        {-0.0333198014f, -0.0026223252f},
+        {-0.0042716241f, -0.0269699732f},
+        {-0.0657406925f,  0.0272306864f},
+        {-0.0939660909f, -0.1293332287f},
+        { 0.0218690212f, -0.0256053340f},
+        {-0.3459798404f, -0.1762855337f},
+        { 0.2099913501f, -0.8746769223f},
+        { 1.1378457233f,  0.0000000000f},
+        { 0.2141308087f,  0.8919190081f},
+        {-0.3655041913f,  0.1862336874f},
+        { 0.0140219952f,  0.0164176470f},
+        {-0.0941911192f,  0.1296429536f},
+        {-0.0705421655f, -0.0292195216f},
+        {-0.0039020802f,  0.0246367649f},
+        {-0.0345980484f,  0.0027229255f},
+        { 0.0013919788f,  0.0042840703f},
+        {-0.0145694683f,  0.0089281816f},
+        { 0.0021640858f,  0.0021640858f},
+        {-0.0078983989f,  0.0128890156f},
+        {-0.0037430549f, -0.0012161922f},
+        {-0.0006228729f,  0.0079143505f},
+    },
+    {
+        {-0.0006294658f, -0.0079981214f},       /* Filter 24 */
+        {-0.0034185133f,  0.0011107423f},
+        {-0.0078819362f, -0.0128621510f},
+        { 0.0024961016f, -0.0024961016f},
+        {-0.0143977349f, -0.0088229433f},
+        { 0.0016090008f, -0.0049519954f},
+        {-0.0339695814f, -0.0026734640f},
+        {-0.0040904779f, -0.0258262610f},
+        {-0.0681419627f,  0.0282253251f},
+        {-0.0941079866f, -0.1295285313f},
+        { 0.0179909715f, -0.0210647211f},
+        {-0.3557231633f, -0.1812500047f},
+        { 0.2120761281f, -0.8833606476f},
+        { 1.1379528497f,  0.0000000000f},
+        { 0.2120761281f,  0.8833606476f},
+        {-0.3557231633f,  0.1812500047f},
+        { 0.0179909715f,  0.0210647211f},
+        {-0.0941079866f,  0.1295285313f},
+        {-0.0681419627f, -0.0282253251f},
+        {-0.0040904779f,  0.0258262610f},
+        {-0.0339695814f,  0.0026734640f},
+        { 0.0016090008f,  0.0049519954f},
+        {-0.0143977349f,  0.0088229433f},
+        { 0.0024961016f,  0.0024961016f},
+        {-0.0078819362f,  0.0128621510f},
+        {-0.0034185133f, -0.0011107423f},
+        {-0.0006294658f,  0.0079981214f},
+    },
+    {
+        {-0.0006354249f, -0.0080738393f},       /* Filter 25 */
+        {-0.0037430549f,  0.0012161922f},
+        {-0.0078983989f, -0.0128890156f},
+        { 0.0021640858f, -0.0021640858f},
+        {-0.0145694683f, -0.0089281816f},
+        { 0.0013919788f, -0.0042840703f},
+        {-0.0345980484f, -0.0027229255f},
+        {-0.0039020802f, -0.0246367649f},
+        {-0.0705421655f,  0.0292195216f},
+        {-0.0941911192f, -0.1296429536f},
+        { 0.0140219952f, -0.0164176470f},
+        {-0.3655041913f, -0.1862336874f},
+        { 0.2141308087f, -0.8919190081f},
+        { 1.1378457233f,  0.0000000000f},
+        { 0.2099913501f,  0.8746769223f},
+        {-0.3459798404f,  0.1762855337f},
+        { 0.0218690212f,  0.0256053340f},
+        {-0.0939660909f,  0.1293332287f},
+        {-0.0657406925f, -0.0272306864f},
+        {-0.0042716241f,  0.0269699732f},
+        {-0.0333198014f,  0.0026223252f},
+        { 0.0018212906f,  0.0056053560f},
+        {-0.0142125109f,  0.0087094379f},
+        { 0.0028244120f,  0.0028244120f},
+        {-0.0078593727f,  0.0128253306f},
+        {-0.0030943393f, -0.0010054118f},
+        {-0.0006354249f,  0.0080738393f},
+    },
+    {
+        {-0.0006407508f, -0.0081415109f},       /* Filter 26 */
+        {-0.0040676902f,  0.0013216727f},
+        {-0.0079087015f, -0.0129058279f},
+        { 0.0018286340f, -0.0018286340f},
+        {-0.0147274152f, -0.0090249716f},
+        { 0.0011703644f, -0.0036020111f},
+        {-0.0352043080f, -0.0027706391f},
+        {-0.0037064513f, -0.0234016123f},
+        {-0.0729396259f,  0.0302125823f},
+        {-0.0942143887f, -0.1296749813f},
+        { 0.0099624646f, -0.0116645474f},
+        {-0.3753199780f, -0.1912350805f},
+        { 0.2161546447f, -0.9003488919f},
+        { 1.1375243848f,  0.0000000000f},
+        { 0.2078772313f,  0.8658709834f},
+        {-0.3362771370f,  0.1713417594f},
+        { 0.0256558155f,  0.0300391005f},
+        {-0.0937665479f,  0.1290585813f},
+        {-0.0633400059f, -0.0262362895f},
+        {-0.0044455042f,  0.0280678087f},
+        {-0.0326496069f,  0.0025695798f},
+        { 0.0020287139f,  0.0062437395f},
+        {-0.0140141002f,  0.0085878517f},
+        { 0.0031487534f,  0.0031487534f},
+        {-0.0078307725f,  0.0127786593f},
+        {-0.0027708045f, -0.0009002890f},
+        {-0.0006407508f,  0.0081415109f},
+    },
+    {
+        {-0.0006454445f, -0.0082011502f},       /* Filter 27 */
+        {-0.0043921429f,  0.0014270937f},
+        {-0.0079127895f, -0.0129124989f},
+        { 0.0014900206f, -0.0014900206f},
+        {-0.0148712891f, -0.0091131377f},
+        { 0.0009443030f, -0.0029062658f},
+        {-0.0357874706f, -0.0028165350f},
+        {-0.0035036167f, -0.0221209652f},
+        {-0.0753326453f,  0.0312038034f},
+        {-0.0941767115f, -0.1296231230f},
+        { 0.0058127944f, -0.0068059078f},
+        {-0.3851675459f, -0.1962526670f},
+        { 0.2181468989f, -0.9086472276f},
+        { 1.1369889562f,  0.0000000000f},
+        { 0.2057345372f,  0.8569460203f},
+        {-0.3266179339f,  0.1664201496f},
+        { 0.0293510677f,  0.0343656849f},
+        {-0.0935104881f,  0.1287061452f},
+        {-0.0609415283f, -0.0252428075f},
+        {-0.0046121088f,  0.0291197091f},
+        {-0.0319599006f,  0.0025152987f},
+        { 0.0022311426f,  0.0068667508f},
+        {-0.0138028157f,  0.0084583763f},
+        { 0.0034688681f,  0.0034688681f},
+        {-0.0077962045f,  0.0127222494f},
+        {-0.0024481778f, -0.0007954612f},
+        {-0.0006454445f,  0.0082011502f},
+    },
+    {
+        {-0.0006495077f, -0.0082527783f},       /* Filter 28 */
+        {-0.0047161348f,  0.0015323651f},
+        {-0.0079106134f, -0.0129089479f},
+        { 0.0011485255f, -0.0011485255f},
+        {-0.0150008130f, -0.0091925101f},
+        { 0.0007139459f, -0.0021972995f},
+        {-0.0363466528f, -0.0028605436f},
+        {-0.0032936077f, -0.0207950209f},
+        {-0.0777195022f,  0.0321924719f},
+        {-0.0940770209f, -0.1294859108f},
+        { 0.0015734425f, -0.0018422645f},
+        {-0.3950438879f, -0.2012849146f},
+        { 0.2201068440f, -0.9168109865f},
+        { 1.1362396407f,  0.0000000000f},
+        { 0.2035640423f,  0.8479052583f},
+        {-0.3170050775f,  0.1615221547f},
+        { 0.0329545338f,  0.0385848015f},
+        {-0.0931990565f,  0.1282774964f},
+        {-0.0585468603f, -0.0242509035f},
+        {-0.0047714341f,  0.0301256492f},
+        {-0.0312515874f,  0.0024595533f},
+        { 0.0024284540f,  0.0074740128f},
+        {-0.0135789777f,  0.0083212083f},
+        { 0.0037845040f,  0.0037845040f},
+        {-0.0077557420f,  0.0126562207f},
+        {-0.0021267251f, -0.0006910149f},
+        {-0.0006495077f,  0.0082527783f},
+    },
+    {
+        {-0.0006529426f, -0.0082964229f},       /* Filter 29 */
+        {-0.0050393858f,  0.0016373957f},
+        {-0.0079021290f, -0.0128951026f},
+        { 0.0008044337f, -0.0008044337f},
+        {-0.0151157193f, -0.0092629247f},
+        { 0.0004794497f, -0.0014755944f},
+        {-0.0368809773f, -0.0029025959f},
+        {-0.0030764613f, -0.0194240122f},
+        {-0.0800984531f,  0.0331778656f},
+        {-0.0939142683f, -0.1292619011f},
+        {-0.0027550900f,  0.0032257959f},
+        {-0.4049459681f, -0.2063302764f},
+        { 0.2220337633f, -0.9248371830f},
+        { 1.1352767227f,  0.0000000000f},
+        { 0.2013665292f,  0.8387519577f},
+        {-0.3074413786f,  0.1566492066f},
+        { 0.0364660122f,  0.0426962145f},
+        {-0.0928334114f,  0.1277742292f},
+        {-0.0561575756f, -0.0232612294f},
+        {-0.0049234813f,  0.0310856377f},
+        {-0.0305255746f,  0.0024024148f},
+        { 0.0026205314f,  0.0080651662f},
+        {-0.0133429149f,  0.0081765487f},
+        { 0.0040954155f,  0.0040954155f},
+        {-0.0077094631f,  0.0125807004f},
+        {-0.0018067093f, -0.0005870354f},
+        {-0.0006529426f,  0.0082964229f},
+    },
+    {
+        {-0.0006557520f, -0.0083321188f},       /* Filter 30 */
+        {-0.0053616143f,  0.0017420941f},
+        {-0.0078872970f, -0.0128708989f},
+        { 0.0004580348f, -0.0004580348f},
+        {-0.0152157506f, -0.0093242240f},
+        { 0.0002409767f, -0.0007416500f},
+        {-0.0373895741f, -0.0029426233f},
+        {-0.0028522199f, -0.0180082079f},
+        {-0.0824677330f,  0.0341592535f},
+        {-0.0936874236f, -0.1289496760f},
+        {-0.0071722592f,  0.0083976366f},
+        {-0.4148707237f, -0.2113871920f},
+        { 0.2239269510f, -0.9327228772f},
+        { 1.1341005677f,  0.0000000000f},
+        { 0.1991427888f,  0.8294894121f},
+        {-0.2979296109f,  0.1518027189f},
+        { 0.0398853435f,  0.0466997371f},
+        {-0.0924147241f,  0.1271979554f},
+        {-0.0537752213f, -0.0222744260f},
+        {-0.0050682571f,  0.0319997159f},
+        {-0.0297827710f,  0.0023439549f},
+        { 0.0028072639f,  0.0086398699f},
+        {-0.0130949624f,  0.0080246033f},
+        { 0.0044013628f,  0.0044013628f},
+        {-0.0076574502f,  0.0124958231f},
+        {-0.0014883899f, -0.0004836072f},
+        {-0.0006557520f,  0.0083321188f},
+    },
+    {
+        {-0.0006579390f, -0.0083599075f},       /* Filter 31 */
+        {-0.0056825374f,  0.0018463683f},
+        {-0.0078660833f, -0.0128362814f},
+        { 0.0001096232f, -0.0001096232f},
+        {-0.0153006596f, -0.0093762563f},
+        {-0.0000013054f,  0.0000040177f},
+        {-0.0378715811f, -0.0029805581f},
+        {-0.0026209319f, -0.0165479125f},
+        {-0.0848255565f,  0.0351358959f},
+        {-0.0933954755f, -0.1285478440f},
+        {-0.0116774779f,  0.0136725700f},
+        {-0.4248150656f, -0.2164540873f},
+        { 0.2257857123f, -0.9404651753f},
+        { 1.1327116219f,  0.0000000000f},
+        { 0.1968936193f,  0.8201209469f},
+        {-0.2884725105f,  0.1469840856f},
+        { 0.0432124100f,  0.0505952315f},
+        {-0.0919441775f,  0.1265503037f},
+        {-0.0514013169f, -0.0212911226f},
+        {-0.0052057731f,  0.0328679578f},
+        {-0.0290240862f,  0.0022842451f},
+        { 0.0029885467f,  0.0091978010f},
+        {-0.0128354632f,  0.0078655819f},
+        { 0.0047021127f,  0.0047021127f},
+        {-0.0075997900f,  0.0124017301f},
+        {-0.0011720231f, -0.0003808134f},
+        {-0.0006579390f,  0.0083599075f},
+    },
+    {
+        {-0.0006595075f, -0.0083798372f},       /* Filter 32 */
+        {-0.0060018711f,  0.0019501261f},
+        {-0.0078384594f, -0.0127912031f},
+        {-0.0002405024f,  0.0002405024f},
+        {-0.0153702098f, -0.0094188767f},
+        {-0.0002472237f,  0.0007608763f},
+        {-0.0383261449f, -0.0030163330f},
+        {-0.0023826511f, -0.0150434671f},
+        {-0.0871701184f,  0.0361070452f},
+        {-0.0930374331f, -0.1280550409f},
+        {-0.0162701156f,  0.0190498578f},
+        {-0.4347758804f, -0.2215293759f},
+        { 0.2276093640f, -0.9480612314f},
+        { 1.1311104123f,  0.0000000000f},
+        { 0.1946198263f,  0.8106499175f},
+        {-0.2790727745f,  0.1421946809f},
+        { 0.0464471360f,  0.0543826090f},
+        {-0.0914229663f,  0.1258329180f},
+        {-0.0490373539f, -0.0203119370f},
+        {-0.0053360461f,  0.0336904691f},
+        {-0.0282504297f,  0.0022233570f},
+        { 0.0031642806f,  0.0097386544f},
+        {-0.0125647661f,  0.0076996986f},
+        { 0.0049974389f,  0.0049974388f},
+        {-0.0075365733f,  0.0122985698f},
+        {-0.0008578611f, -0.0002787360f},
+        {-0.0006595075f,  0.0083798372f},
+    },
+    {
+        {-0.0006604618f, -0.0083919627f},       /* Filter 33 */
+        {-0.0063193305f,  0.0020532749f},
+        {-0.0078044017f, -0.0127356261f},
+        {-0.0005920390f,  0.0005920390f},
+        {-0.0154241758f, -0.0094519471f},
+        {-0.0004965997f,  0.0015283766f},
+        {-0.0387524213f, -0.0030498817f},
+        {-0.0021374375f, -0.0134952491f},
+        {-0.0894995944f,  0.0370719458f},
+        {-0.0926123259f, -0.1274699310f},
+        {-0.0209494991f,  0.0245287119f},
+        {-0.4447500311f, -0.2266114595f},
+        { 0.2293972347f, -0.9555082489f},
+        { 1.1292975460f,  0.0000000000f},
+        { 0.1923222223f,  0.8010797080f},
+        {-0.2697330596f,  0.1374358585f},
+        { 0.0495894870f,  0.0580618293f},
+        {-0.0908522954f,  0.1250474569f},
+        {-0.0466847952f, -0.0193374753f},
+        {-0.0054590979f,  0.0344673874f},
+        {-0.0274627107f,  0.0021613622f},
+        { 0.0033343726f,  0.0102621437f},
+        {-0.0122832268f,  0.0075271711f},
+        { 0.0052871211f,  0.0052871211f},
+        {-0.0074678951f,  0.0121864973f},
+        {-0.0005461525f, -0.0001774557f},
+        {-0.0006604618f,  0.0083919627f},
+    },
+    {
+        {-0.0006608067f, -0.0083963451f},       /* Filter 34 */
+        {-0.0066346301f,  0.0021557220f},
+        {-0.0077638924f, -0.0126695210f},
+        {-0.0009446795f,  0.0009446795f},
+        {-0.0154623431f, -0.0094753361f},
+        {-0.0007492498f,  0.0023059539f},
+        {-0.0391495767f, -0.0030811385f},
+        {-0.0018853566f, -0.0119036728f},
+        {-0.0918121426f,  0.0380298346f},
+        {-0.0921192046f, -0.1267912077f},
+        {-0.0257149117f,  0.0301082932f},
+        {-0.4547343588f, -0.2316987286f},
+        { 0.2311486652f, -0.9628034819f},
+        { 1.1272737104f,  0.0000000000f},
+        { 0.1900016262f,  0.7914137299f},
+        {-0.2604559819f,  0.1327089513f},
+        { 0.0526394697f,  0.0616329003f},
+        {-0.0902333798f,  0.1241955926f},
+        {-0.0443450744f, -0.0183683312f},
+        {-0.0055749551f,  0.0351988812f},
+        {-0.0266618369f,  0.0020983321f},
+        { 0.0034987353f,  0.0107680002f},
+        {-0.0119912063f,  0.0073482207f},
+        { 0.0055709463f,  0.0055709463f},
+        {-0.0073938544f,  0.0120656738f},
+        {-0.0002371416f, -0.0000770520f},
+        {-0.0006608067f,  0.0083963451f},
+    },
+    {
+        {-0.0006605475f, -0.0083930522f},       /* Filter 35 */
+        {-0.0069474841f,  0.0022573744f},
+        {-0.0077169192f, -0.0125928676f},
+        {-0.0012981133f,  0.0012981133f},
+        {-0.0154845091f, -0.0094889194f},
+        {-0.0010049855f,  0.0030930272f},
+        {-0.0395167881f, -0.0031100387f},
+        {-0.0016264798f, -0.0102691896f},
+        {-0.0941059032f,  0.0389799414f},
+        {-0.0915571420f, -0.1260175949f},
+        {-0.0305655940f,  0.0357877124f},
+        {-0.4647256838f, -0.2367895630f},
+        { 0.2328630086f, -0.9699442360f},
+        { 1.1250396727f,  0.0000000000f},
+        { 0.1876588631f,  0.7816554194f},
+        {-0.2512441154f,  0.1280152709f},
+        { 0.0555971318f,  0.0650958776f},
+        {-0.0895674434f,  0.1232790098f},
+        {-0.0420195954f, -0.0174050863f},
+        {-0.0056836493f,  0.0358851497f},
+        {-0.0258487141f,  0.0020343379f},
+        { 0.0036572876f,  0.0112559738f},
+        {-0.0116890717f,  0.0071630723f},
+        { 0.0058487082f,  0.0058487082f},
+        {-0.0073145539f,  0.0119362671f},
+        { 0.0000689315f,  0.0000223972f},
+        {-0.0006605475f,  0.0083930522f},
+    },
+    {
+        {-0.0006596901f, -0.0083821579f},       /* Filter 36 */
+        {-0.0072576065f,  0.0023581393f},
+        {-0.0076634750f, -0.0125056546f},
+        {-0.0016520262f,  0.0016520262f},
+        {-0.0154904827f, -0.0094925800f},
+        {-0.0012636128f,  0.0038890004f},
+        {-0.0398532442f, -0.0031365183f},
+        {-0.0013608847f, -0.0085922877f},
+        {-0.0963790005f,  0.0399214891f},
+        {-0.0909252334f, -0.1251478474f},
+        {-0.0355007435f,  0.0415660302f},
+        {-0.4747208072f, -0.2418823327f},
+        { 0.2345396308f, -0.9769278702f},
+        { 1.1225962793f,  0.0000000000f},
+        { 0.1852947639f,  0.7718082372f},
+        {-0.2420999907f,  0.1233561066f},
+        { 0.0584625613f,  0.0684508645f},
+        {-0.0888557186f,  0.1222994047f},
+        {-0.0397097323f, -0.0164483097f},
+        {-0.0057852169f,  0.0365264219f},
+        {-0.0250242456f,  0.0019694508f},
+        { 0.0038099540f,  0.0117258328f},
+        {-0.0113771948f,  0.0069719540f},
+        { 0.0061202077f,  0.0061202077f},
+        {-0.0072301003f,  0.0117984513f},
+        { 0.0003718310f,  0.0001208152f},
+        {-0.0006596901f,  0.0083821579f},
+    },
+    {
+        {-0.0006582408f, -0.0083637423f},       /* Filter 37 */
+        {-0.0075647115f,  0.0024579238f},
+        {-0.0076035587f, -0.0124078801f},
+        {-0.0020061005f,  0.0020061005f},
+        {-0.0154800853f, -0.0094862085f},
+        {-0.0015249332f,  0.0046932617f},
+        {-0.0401581456f, -0.0031605146f},
+        {-0.0010886543f, -0.0068734926f},
+        {-0.0986295432f,  0.0408536944f},
+        {-0.0902225974f, -0.1241807520f},
+        {-0.0405195152f,  0.0474422567f},
+        {-0.4847165122f, -0.2469753988f},
+        { 0.2361779109f, -0.9837517978f},
+        { 1.1199444556f,  0.0000000000f},
+        { 0.1829101645f,  0.7618756659f},
+        {-0.2330260947f,  0.1187327256f},
+        { 0.0612358868f,  0.0716980115f},
+        {-0.0880994453f,  0.1212584838f},
+        {-0.0374168281f, -0.0154985577f},
+        {-0.0058796987f,  0.0371229566f},
+        {-0.0241893313f,  0.0019037417f},
+        { 0.0039566651f,  0.0121773631f},
+        {-0.0110559525f,  0.0067750964f},
+        { 0.0063852527f,  0.0063852526f},
+        {-0.0071406035f,  0.0116524059f},
+        { 0.0006713257f,  0.0002181269f},
+        {-0.0006582408f,  0.0083637423f},
+    },
+    {
+        {-0.0006562063f, -0.0083378917f},       /* Filter 38 */
+        {-0.0078685137f,  0.0025566351f},
+        {-0.0075371745f, -0.0122995509f},
+        {-0.0023600156f,  0.0023600156f},
+        {-0.0154531504f, -0.0094697028f},
+        {-0.0017887430f,  0.0055051847f},
+        {-0.0404307068f, -0.0031819656f},
+        {-0.0008098779f, -0.0051133675f},
+        {-0.1008556255f,  0.0417757679f},
+        {-0.0894483767f, -0.1231151286f},
+        {-0.0456210210f,  0.0534153526f},
+        {-0.4947095655f, -0.2520671138f},
+        { 0.2377772412f, -0.9904134878f},
+        { 1.1170852055f,  0.0000000000f},
+        { 0.1805059061f,  0.7518612089f},
+        {-0.2240248692f,  0.1141463722f},
+        { 0.0639172763f,  0.0748375152f},
+        {-0.0872998705f,  0.1201579635f},
+        {-0.0351421949f, -0.0145563737f},
+        {-0.0059671403f,  0.0376750413f},
+        {-0.0233448674f,  0.0018372809f},
+        { 0.0040973574f,  0.0126103693f},
+        {-0.0107257260f,  0.0065727333f},
+        { 0.0066436583f,  0.0066436583f},
+        {-0.0070461774f,  0.0114983165f},
+        { 0.0009671895f,  0.0003142589f},
+        {-0.0006562063f,  0.0083378917f},
+    },
+    {
+        {-0.0006535939f, -0.0083046980f},       /* Filter 39 */
+        {-0.0081687281f,  0.0026541806f},
+        {-0.0074643325f, -0.0121806836f},
+        {-0.0027134480f,  0.0027134480f},
+        {-0.0154095242f, -0.0094429686f},
+        {-0.0020548339f,  0.0063241284f},
+        {-0.0406701557f, -0.0032008107f},
+        {-0.0005246505f, -0.0033125128f},
+        {-0.1030553278f,  0.0426869144f},
+        {-0.0886017384f, -0.1219498309f},
+        {-0.0508043306f,  0.0594842284f},
+        {-0.5046967186f, -0.2571558224f},
+        { 0.2393370276f, -0.9969104659f},
+        { 1.1140196110f,  0.0000000000f},
+        { 0.1780828343f,  0.7417683886f},
+        {-0.2150987105f,  0.1095982672f},
+        { 0.0665069376f,  0.0778696190f},
+        {-0.0864582472f,  0.1189995684f},
+        {-0.0328871136f, -0.0136222885f},
+        {-0.0060475918f,  0.0381829922f},
+        {-0.0224917457f,  0.0017701388f},
+        { 0.0042319732f,  0.0130246742f},
+        {-0.0103869007f,  0.0063651009f},
+        { 0.0068952472f,  0.0068952472f},
+        {-0.0069469389f,  0.0113363740f},
+        { 0.0012592008f,  0.0004091391f},
+        {-0.0006535939f,  0.0083046980f},
+    },
+    {
+        {-0.0006504113f, -0.0082642593f},       /* Filter 40 */
+        {-0.0084650709f,  0.0027504683f},
+        {-0.0073850485f, -0.0120513039f},
+        {-0.0030660718f,  0.0030660718f},
+        {-0.0153490660f, -0.0094059197f},
+        {-0.0023229930f,  0.0071494373f},
+        {-0.0408757346f, -0.0032169901f},
+        {-0.0002330732f, -0.0014715664f},
+        {-0.1052267179f,  0.0435863337f},
+        {-0.0876818748f, -0.1206837473f},
+        {-0.0560684714f,  0.0656477454f},
+        {-0.5146747091f, -0.2622398626f},
+        { 0.2408566902f, -1.0032403162f},
+        { 1.1107488316f,  0.0000000000f},
+        { 0.1756417989f,  0.7316007446f},
+        {-0.2062499680f,  0.1050896077f},
+        { 0.0690051173f,  0.0807946117f},
+        {-0.0855758340f,  0.1177850307f},
+        {-0.0306528326f, -0.0126968190f},
+        {-0.0061211077f,  0.0386471532f},
+        {-0.0216308523f,  0.0017023850f},
+        { 0.0043604608f,  0.0134201184f},
+        {-0.0100398656f,  0.0061524375f},
+        { 0.0071398493f,  0.0071398493f},
+        {-0.0068430082f,  0.0111667745f},
+        { 0.0015471436f,  0.0005026974f},
+        {-0.0006504113f,  0.0082642593f},
+    },
+    {
+        {-0.0006466667f, -0.0082166790f},       /* Filter 41 */
+        {-0.0087572594f,  0.0028454060f},
+        {-0.0072993441f, -0.0119114470f},
+        {-0.0034175587f,  0.0034175587f},
+        {-0.0152716480f, -0.0093584780f},
+        {-0.0025930028f,  0.0079804420f},
+        {-0.0410467015f, -0.0032304455f},
+        { 0.0000647469f,  0.0004087959f},
+        {-0.1073678519f,  0.0444732204f},
+        {-0.0866880040f, -0.1193158015f},
+        {-0.0614124285f,  0.0719047153f},
+        {-0.5246402627f, -0.2673175657f},
+        { 0.2423356631f, -1.0094006816f},
+        { 1.1072741035f,  0.0000000000f},
+        { 0.1731836536f,  0.7213618325f},
+        {-0.1974809435f,  0.1006215665f},
+        { 0.0714121011f,  0.0836128276f},
+        {-0.0846538939f,  0.1165160891f},
+        {-0.0284405684f, -0.0117804691f},
+        {-0.0061877467f,  0.0390678953f},
+        {-0.0207630684f,  0.0016340889f},
+        { 0.0044827743f,  0.0137965606f},
+        {-0.0096850130f,  0.0059349836f},
+        { 0.0073773020f,  0.0073773020f},
+        {-0.0067345088f,  0.0109897195f},
+        { 0.0018308065f,  0.0005948651f},
+        {-0.0006466667f,  0.0082166790f},
+    },
+    {
+        {-0.0006423686f, -0.0081620664f},       /* Filter 42 */
+        {-0.0090450120f,  0.0029389025f},
+        {-0.0072072465f, -0.0117611574f},
+        {-0.0037675783f,  0.0037675783f},
+        {-0.0151771562f, -0.0093005733f},
+        {-0.0028646414f,  0.0088164597f},
+        {-0.0411823301f, -0.0032411197f},
+        { 0.0003686970f,  0.0023278610f},
+        {-0.1094767749f,  0.0453467649f},
+        {-0.0856193708f, -0.1178449541f},
+        {-0.0668351454f,  0.0782539010f},
+        {-0.5345900940f, -0.2723872580f},
+        { 0.2437733949f, -1.0153892656f},
+        { 1.1035967397f,  0.0000000000f},
+        { 0.1707092554f,  0.7110552220f},
+        {-0.1887938905f,  0.0961952919f},
+        { 0.0737282126f,  0.0863246457f},
+        {-0.0836936943f,  0.1151944878f},
+        {-0.0262515049f, -0.0108737293f},
+        {-0.0062475719f,  0.0394456164f},
+        {-0.0198892682f,  0.0015653194f},
+        { 0.0045988736f,  0.0141538775f},
+        {-0.0093227386f,  0.0057129815f},
+        { 0.0076074506f,  0.0076074506f},
+        {-0.0066215668f,  0.0108054149f},
+        { 0.0021099840f,  0.0006855754f},
+        {-0.0006423686f,  0.0081620664f},
+    },
+    {
+        {-0.0006375260f, -0.0081005359f},       /* Filter 43 */
+        {-0.0093280490f,  0.0030308668f},
+        {-0.0071087889f, -0.0116004892f},
+        {-0.0041157984f,  0.0041157984f},
+        {-0.0150654901f, -0.0092321442f},
+        {-0.0031376826f,  0.0096567942f},
+        {-0.0412819110f, -0.0032489569f},
+        { 0.0006786580f,  0.0042848780f},
+        {-0.1115515226f,  0.0462061535f},
+        {-0.0844752467f, -0.1162702023f},
+        {-0.0723355236f,  0.0846940164f},
+        {-0.5445209082f, -0.2774472605f},
+        { 0.2451693490f, -1.0212038328f},
+        { 1.0997181284f,  0.0000000000f},
+        { 0.1682194641f,  0.7006844950f},
+        {-0.1801910135f,  0.0918119071f},
+        { 0.0759538132f,  0.0889304892f},
+        {-0.0826965057f,  0.1138219754f},
+        {-0.0240867927f, -0.0099770762f},
+        {-0.0063006503f,  0.0397807405f},
+        {-0.0190103194f,  0.0014961446f},
+        { 0.0047087245f,  0.0144919637f},
+        {-0.0089534403f,  0.0054866753f},
+        { 0.0078301476f,  0.0078301476f},
+        {-0.0065043116f,  0.0106140718f},
+        { 0.0023844759f,  0.0007747632f},
+        {-0.0006375260f,  0.0081005359f},
+    },
+    {
+        {-0.0006321484f, -0.0080322075f},       /* Filter 44 */
+        {-0.0096060925f,  0.0031212087f},
+        {-0.0070040103f, -0.0114295060f},
+        {-0.0044618854f,  0.0044618854f},
+        {-0.0149365628f, -0.0091531374f},
+        {-0.0034118961f,  0.0105007365f},
+        {-0.0413447525f, -0.0032539026f},
+        { 0.0009945051f,  0.0062790582f},
+        {-0.1135901216f,  0.0470505689f},
+        {-0.0832549311f, -0.1145905820f},
+        {-0.0779124236f,  0.0912237273f},
+        {-0.5544294025f, -0.2824958906f},
+        { 0.2465230038f, -1.0268422107f},
+        { 1.0956397332f,  0.0000000000f},
+        { 0.1657151424f,  0.6902532446f},
+        {-0.1716744668f,  0.0874725099f},
+        { 0.0780893017f,  0.0914308250f},
+        {-0.0816636012f,  0.1124003042f},
+        {-0.0219475497f, -0.0090909727f},
+        {-0.0063470532f,  0.0400737166f},
+        {-0.0181270820f,  0.0014266323f},
+        { 0.0048122983f,  0.0148107313f},
+        {-0.0085775184f,  0.0052563101f},
+        { 0.0080452535f,  0.0080452535f},
+        {-0.0063828750f,  0.0104159053f},
+        { 0.0026540877f,  0.0008623654f},
+        {-0.0006321484f,  0.0080322075f},
+    },
+    {
+        {-0.0006262457f, -0.0079572062f},       /* Filter 45 */
+        {-0.0098788669f,  0.0032098384f},
+        {-0.0068929555f, -0.0112482810f},
+        {-0.0048055043f,  0.0048055043f},
+        {-0.0147903020f, -0.0090635087f},
+        {-0.0036870475f,  0.0113475654f},
+        {-0.0413701808f, -0.0032559039f},
+        { 0.0013161074f,  0.0083095754f},
+        {-0.1155905906f,  0.0478791903f},
+        {-0.0819577515f, -0.1128051674f},
+        {-0.0835646644f,  0.0978416511f},
+        {-0.5643122679f, -0.2875314620f},
+        { 0.2478338529f, -1.0323022902f},
+        { 1.0913630919f,  0.0000000000f},
+        { 0.1631971552f,  0.6797650728f},
+        {-0.1632463544f,  0.0831781721f},
+        { 0.0801351139f,  0.0938261634f},
+        {-0.0805962556f,  0.1109312291f},
+        {-0.0198348595f, -0.0082158678f},
+        {-0.0063868556f,  0.0403250190f},
+        {-0.0172404079f,  0.0013568495f},
+        { 0.0049095725f,  0.0151101104f},
+        {-0.0081953754f,  0.0050221325f},
+        { 0.0082526365f,  0.0082526365f},
+        {-0.0062573914f,  0.0102111348f},
+        { 0.0029186307f,  0.0009483206f},
+        {-0.0006262457f,  0.0079572062f},
+    },
+    {
+        {-0.0006198280f, -0.0078756618f},       /* Filter 46 */
+        {-0.0101460989f,  0.0032966674f},
+        {-0.0067756751f, -0.0110568969f},
+        {-0.0051463192f,  0.0051463192f},
+        {-0.0146266493f, -0.0089632222f},
+        {-0.0039628985f,  0.0121965475f},
+        {-0.0413575411f, -0.0032549091f},
+        { 0.0016433282f,  0.0103755662f},
+        {-0.1175509420f,  0.0486911944f},
+        {-0.0805830641f, -0.1109130726f},
+        {-0.0892910248f,  0.1045463576f},
+        {-0.5741661901f, -0.2925522861f},
+        { 0.2491014054f, -1.0375820265f},
+        { 1.0868898157f,  0.0000000000f},
+        { 0.1606663689f,  0.6692235895f},
+        {-0.1549087286f,  0.0789299396f},
+        { 0.0820917217f,  0.0961170567f},
+        {-0.0794957450f,  0.1094165063f},
+        {-0.0177497724f, -0.0073521965f},
+        {-0.0064201364f,  0.0405351459f},
+        {-0.0163511406f,  0.0012868627f},
+        { 0.0050005298f,  0.0153900482f},
+        {-0.0078074148f,  0.0047843899f},
+        { 0.0084521727f,  0.0084521727f},
+        {-0.0061279979f,  0.0099999837f},
+        { 0.0031779221f,  0.0010325695f},
+        {-0.0006198280f,  0.0078756618f},
+    },
+    {
+        {-0.0006129060f, -0.0077877091f},       /* Filter 47 */
+        {-0.0104075179f,  0.0033816075f},
+        {-0.0066522258f, -0.0108554460f},
+        {-0.0054839934f,  0.0054839934f},
+        {-0.0144455612f, -0.0088522513f},
+        {-0.0042392071f,  0.0130469379f},
+        {-0.0413061980f, -0.0032508683f},
+        { 0.0019760247f,  0.0124761291f},
+        {-0.1194691818f,  0.0494857553f},
+        {-0.0791302548f, -0.1089134521f},
+        {-0.0950902426f,  0.1113363693f},
+        {-0.5839878512f, -0.2975566724f},
+        { 0.2503251863f, -1.0426794408f},
+        { 1.0822215889f,  0.0000000000f},
+        { 0.1581236518f,  0.6586324102f},
+        {-0.1466635902f,  0.0747288317f},
+        { 0.0839596331f,  0.0983041000f},
+        {-0.0783633460f,  0.1078578927f},
+        {-0.0156933045f, -0.0065003796f},
+        {-0.0064469783f,  0.0407046190f},
+        {-0.0154601145f,  0.0012167374f},
+        { 0.0050851588f,  0.0156505094f},
+        {-0.0074140419f,  0.0045433307f},
+        { 0.0086437458f,  0.0086437458f},
+        {-0.0059948336f,  0.0097826794f},
+        { 0.0034317849f,  0.0011150545f},
+        {-0.0006129060f,  0.0077877091f},
+    },
+};
+
+/* Coefficients for the band edge symbol timing synchroniser */
+#define SYNC_LOW_BAND_EDGE_COEFF_0       1.829281f    /* 2*alpha*cos(low_edge) */
+#define SYNC_LOW_BAND_EDGE_COEFF_1      -0.980100f    /* -alpha^2 */
+#define SYNC_HIGH_BAND_EDGE_COEFF_0     -1.285907f    /* 2*alpha*cos(high_edge) */
+#define SYNC_HIGH_BAND_EDGE_COEFF_1     -0.980100f    /* -alpha^2 */
+#define SYNC_CROSS_CORR_COEFF_A         -0.932131f    /* -alpha^2*sin(freq_diff) */
+#define SYNC_CROSS_CORR_COEFF_B          0.752802f    /* alpha*sin(high_edge) */
+#define SYNC_CROSS_CORR_COEFF_C         -0.378857f    /* -alpha*sin(low_edge) */
+
+float v29_rx_carrier_frequency(v29_rx_state_t *s)
+{
+    return dds_frequencyf(s->carrier_phase_rate);
+}
+/*- End of function --------------------------------------------------------*/
+
+float v29_rx_symbol_timing_correction(v29_rx_state_t *s)
+{
+    return (float) s->total_baud_timing_correction/((float) PULSESHAPER_COEFF_SETS*10.0f/3.0f);
+}
+/*- End of function --------------------------------------------------------*/
+
+void v29_rx_signal_cutoff(v29_rx_state_t *s, float cutoff)
+{
+    /* The 0.4 factor allows for the gain of the DC blocker */
+    s->carrier_on_power = (int32_t) (power_meter_level_dbm0(cutoff + 2.5f)*0.4f);
+    s->carrier_off_power = (int32_t) (power_meter_level_dbm0(cutoff - 2.5f)*0.4f);
+}
+/*- End of function --------------------------------------------------------*/
+
+float v29_rx_signal_power(v29_rx_state_t *s)
+{
+    return power_meter_dbm0(&s->power);
+}
+/*- End of function --------------------------------------------------------*/
+
+int v29_rx_equalizer_state(v29_rx_state_t *s, complexf_t **coeffs)
+{
+    *coeffs = s->eq_coeff;
+    return V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void equalizer_save(v29_rx_state_t *s)
+{
+    cvec_copyf(s->eq_coeff_save, s->eq_coeff, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void equalizer_restore(v29_rx_state_t *s)
+{
+    cvec_copyf(s->eq_coeff, s->eq_coeff_save, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
+    cvec_zerof(s->eq_buf, V29_EQUALIZER_MASK);
+
+    s->eq_put_step = PULSESHAPER_COEFF_SETS*10/(3*2) - 1;
+    s->eq_step = 0;
+    s->eq_delta = EQUALIZER_DELTA/(V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void equalizer_reset(v29_rx_state_t *s)
+{
+    /* Start with an equalizer based on everything being perfect */
+    cvec_zerof(s->eq_coeff, V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
+    s->eq_coeff[V29_EQUALIZER_PRE_LEN] = complex_setf(3.0f, 0.0f);
+    cvec_zerof(s->eq_buf, V29_EQUALIZER_MASK);
+
+    s->eq_put_step = PULSESHAPER_COEFF_SETS*10/(3*2) - 1;
+    s->eq_step = 0;
+    s->eq_delta = EQUALIZER_DELTA/(V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN);
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ complexf_t equalizer_get(v29_rx_state_t *s)
+{
+    int i;
+    int p;
+    complexf_t z;
+    complexf_t z1;
+
+    /* Get the next equalized value. */
+    z = complex_setf(0.0f, 0.0f);
+    p = s->eq_step - 1;
+    for (i = 0;  i < V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN;  i++)
+    {
+        p = (p - 1) & V29_EQUALIZER_MASK;
+        z1 = complex_mulf(&s->eq_coeff[i], &s->eq_buf[p]);
+        z = complex_addf(&z, &z1);
+    }
+    return z;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void tune_equalizer(v29_rx_state_t *s, const complexf_t *z, const complexf_t *target)
+{
+    int i;
+    int p;
+    complexf_t ez;
+    complexf_t z1;
+
+    /* Find the x and y mismatch from the exact constellation position. */
+    ez = complex_subf(target, z);
+    ez.re *= s->eq_delta;
+    ez.im *= s->eq_delta;
+
+    p = s->eq_step - 1;
+    for (i = 0;  i < V29_EQUALIZER_PRE_LEN + 1 + V29_EQUALIZER_POST_LEN;  i++)
+    {
+        p = (p - 1) & V29_EQUALIZER_MASK;
+        z1 = complex_conjf(&s->eq_buf[p]);
+        z1 = complex_mulf(&ez, &z1);
+        s->eq_coeff[i] = complex_addf(&s->eq_coeff[i], &z1);
+        /* Leak a little to tame uncontrolled wandering */
+        s->eq_coeff[i].re *= 0.9999f;
+        s->eq_coeff[i].im *= 0.9999f;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int scrambled_training_bit(v29_rx_state_t *s)
+{
+    int bit;
+
+    /* Segment 3 of the training sequence - the scrambled CDCD part. */
+    /* Apply the 1 + x^-6 + x^-7 scrambler */
+    bit = s->training_scramble_reg & 1;
+    s->training_scramble_reg >>= 1;
+    if (bit ^ (s->training_scramble_reg & 1))
+        s->training_scramble_reg |= 0x40;
+    return bit;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ int find_quadrant(const complexf_t *z)
+{
+    int b1;
+    int b2;
+
+    /* Split the space along the two diagonals. */
+    b1 = (z->im > z->re);
+    b2 = (z->im < -z->re);
+    return (b2 << 1) | (b1 ^ b2);
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void track_carrier(v29_rx_state_t *s, const complexf_t *z, const complexf_t *target)
+{
+    float error;
+
+    /* The initial coarse carrier frequency and phase estimation should have
+       got us in the right ballpark. Now we need to fine tune fairly quickly,
+       to get the receovered carrier more precisely on target. Then we need to
+       fine tune in a more damped way to keep us on target. The goal is to have
+       things running really well by the time the training is complete. 
+       We assume the frequency of the oscillators at the two ends drift only
+       very slowly. The PSTN has rather limited doppler problems. :-) Any
+       remaining FDM in the network should also drift slowly. */
+    /* For small errors the imaginary part of the difference between the actual and the target
+       positions is proportional to the phase error, for any particular target. However, the
+       different amplitudes of the various target positions scale things. This isn't all bad,
+       as the angular error for the larger amplitude constellation points is probably
+       a more reliable indicator, and we are weighting it as such. */
+    error = z->im*target->re - z->re*target->im;
+
+    /* Use a proportional-integral approach to tracking the carrier. The PI
+       parameters are coarser at first, until we get precisely on target. Then,
+       the filter will be damped more to keep us on target. */
+    s->carrier_phase_rate += (int32_t) (s->carrier_track_i*error);
+    s->carrier_phase += (int32_t) (s->carrier_track_p*error);
+    //span_log(&s->logging, SPAN_LOG_FLOW, "Im = %15.5f   f = %15.5f\n", error, dds_frequencyf(s->carrier_phase_rate));
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void put_bit(v29_rx_state_t *s, int bit)
+{
+    int out_bit;
+
+    bit &= 1;
+
+    /* Descramble the bit */
+    out_bit = (bit ^ (s->scramble_reg >> 17) ^ (s->scramble_reg >> 22)) & 1;
+    s->scramble_reg = (s->scramble_reg << 1) | bit;
+
+    /* We need to strip the last part of the training - the test period of all 1s -
+       before we let data go to the application. */
+    if (s->in_training == TRAINING_STAGE_NORMAL_OPERATION)
+    {
+        s->put_bit(s->user_data, out_bit);
+    }
+    else
+    {
+        //span_log(&s->logging, SPAN_LOG_FLOW, "bit %5d %d\n", s->training_cd, out_bit);
+        /* The bits during the final stage of training should be all ones. However,
+           buggy modems mean you cannot rely on this. Therefore we don't bother
+           testing for ones, but just rely on a constellation mismatch measurement. */
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void decode_baud(v29_rx_state_t *s, complexf_t *z)
+{
+    static const uint8_t phase_steps_9600[8] =
+    {
+        4, 0, 2, 6, 7, 3, 1, 5
+    };
+    static const uint8_t phase_steps_4800[4] =
+    {
+        0, 2, 3, 1
+    };
+    int nearest;
+    int raw_bits;
+    int i;
+    int re;
+    int im;
+
+    switch (s->bit_rate)
+    {
+    case 9600:
+    default:
+        re = (int) ((z->re + 5.0f)*2.0f);
+        if (re > 19)
+            re = 19;
+        else if (re < 0)
+            re = 0;
+        im = (int) ((z->im + 5.0f)*2.0f);
+        if (im > 19)
+            im = 19;
+        else if (im < 0)
+            im = 0;
+        nearest = space_map_9600[re][im];
+        /* Deal with the amplitude bit */
+        put_bit(s, nearest >> 3);
+        raw_bits = phase_steps_9600[(nearest - s->constellation_state) & 7];
+        for (i = 0;  i < 3;  i++)
+        {
+            put_bit(s, raw_bits);
+            raw_bits >>= 1;
+        }
+        break;
+    case 7200:
+        /* We can reuse the space map for 9600, but drop the top bit */
+        re = (int) ((z->re + 5.0f)*2.0f);
+        if (re > 19)
+            re = 19;
+        else if (re < 0)
+            re = 0;
+        im = (int) ((z->im + 5.0f)*2.0f);
+        if (im > 19)
+            im = 19;
+        else if (im < 0)
+            im = 0;
+        nearest = space_map_9600[re][im] & 7;
+        raw_bits = phase_steps_9600[(nearest - s->constellation_state) & 7];
+        for (i = 0;  i < 3;  i++)
+        {
+            put_bit(s, raw_bits);
+            raw_bits >>= 1;
+        }
+        break;
+    case 4800:
+        nearest = find_quadrant(z) << 1;
+        raw_bits = phase_steps_4800[((nearest - s->constellation_state) >> 1) & 3];
+        put_bit(s, raw_bits);
+        put_bit(s, raw_bits >> 1);
+        break;
+    }
+    track_carrier(s, z, &v29_constellation[nearest]);
+    if (--s->eq_skip <= 0)
+    {
+        /* Once we are in the data the equalization should not need updating.
+           However, the line characteristics may slowly drift. We, therefore,
+           tune up on the occassional sample, keeping the compute down. */
+        s->eq_skip = 10;
+        tune_equalizer(s, z, &v29_constellation[nearest]);
+    }
+    s->constellation_state = nearest;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void process_half_baud(v29_rx_state_t *s, complexf_t *sample)
+{
+    static const int cdcd_pos[6] =
+    {
+        0, 11,
+        0,  3,
+        0,  2
+    };
+    complexf_t z;
+    complexf_t zz;
+    const complexf_t *target;
+    float v;
+    float p;
+    int bit;
+    int i;
+    int j;
+    int32_t angle;
+    int32_t ang;
+
+    /* This routine processes every half a baud, as we put things into the equalizer at the T/2 rate.
+       This routine adapts the position of the half baud samples, which the caller takes. */
+
+    /* Add a sample to the equalizer's circular buffer, but don't calculate anything
+       at this time. */
+    s->eq_buf[s->eq_step] = *sample;
+    s->eq_step = (s->eq_step + 1) & V29_EQUALIZER_MASK;
+
+    /* On alternate insertions we have a whole baud, and must process it. */
+    if ((s->baud_half ^= 1))
+        return;
+
+    /* Symbol timing synchronisation */
+    /* Cross correlate */
+    v = s->symbol_sync_low[1]*s->symbol_sync_high[1]*SYNC_CROSS_CORR_COEFF_A
+      + s->symbol_sync_low[0]*s->symbol_sync_high[1]*SYNC_CROSS_CORR_COEFF_B
+      + s->symbol_sync_low[1]*s->symbol_sync_high[0]*SYNC_CROSS_CORR_COEFF_C;
+
+    /* Filter away any DC component  */
+    p = v - s->symbol_sync_dc_filter[1];
+    s->symbol_sync_dc_filter[1] = s->symbol_sync_dc_filter[0];
+    s->symbol_sync_dc_filter[0] = v;
+    /* A little integration will now filter away much of the noise */
+    s->baud_phase -= p;
+
+    i = 0;
+    if (s->baud_phase > 1000.0f)
+        i = 5;
+    else if (s->baud_phase < -1000.0f)
+        i = -5;
+    else if (s->baud_phase > 100.0f)
+        i = 1;
+    else if (s->baud_phase < -100.0f)
+        i = -1;
+
+    //printf("v = %10.5f %5d - %f %f %d %d\n", v, i, p, s->baud_phase, s->total_baud_timing_correction);
+
+    if (i)
+    {
+        s->eq_put_step += i;
+        s->total_baud_timing_correction += i;
+    }
+
+    z = equalizer_get(s);
+
+    switch (s->in_training)
+    {
+    case TRAINING_STAGE_NORMAL_OPERATION:
+        /* Normal operation. */
+        decode_baud(s, &z);
+        target = &v29_constellation[s->constellation_state];
+        break;
+    case TRAINING_STAGE_SYMBOL_ACQUISITION:
+        /* Allow time for symbol synchronisation to settle the symbol timing. */
+        target = &z;
+        if (++s->training_count >= 60)
+        {
+            /* Record the current phase angle */
+            s->in_training = TRAINING_STAGE_LOG_PHASE;
+            s->angles[0] =
+            s->start_angles[0] = arctan2(z.im, z.re);
+        }
+        break;
+    case TRAINING_STAGE_LOG_PHASE:
+        /* Record the current alternate phase angle */
+        target = &z;
+        s->angles[1] =
+        s->start_angles[1] = arctan2(z.im, z.re);
+        s->training_count = 1;
+        s->in_training = TRAINING_STAGE_WAIT_FOR_CDCD;
+        break;
+    case TRAINING_STAGE_WAIT_FOR_CDCD:
+        target = &z;
+        angle = arctan2(z.im, z.re);
+        /* Look for the initial ABAB sequence to display a phase reversal, which will
+           signal the start of the scrambled CDCD segment */
+        ang = angle - s->angles[(s->training_count - 1) & 0xF];
+        s->angles[(s->training_count + 1) & 0xF] = angle;
+        if ((ang > 0x20000000  ||  ang < -0x20000000)  &&  s->training_count >= 13)
+        {
+            /* We seem to have a phase reversal */
+            /* Slam the carrier frequency into line, based on the total phase drift over the last
+               section. Use the shift from the odd bits and the shift from the even bits to get
+               better jitter suppression. We need to scale here, or at the maximum specified
+               frequency deviation we could overflow, and get a silly answer. */
+            /* Step back a few symbols so we don't get ISI distorting things. */
+            i = (s->training_count - 8) & ~1;
+            /* Avoid the possibility of a divide by zero */
+            if (i)
+            {
+                j = i & 0xF;
+                ang = (s->angles[j] - s->start_angles[0])/i
+                    + (s->angles[j | 0x1] - s->start_angles[1])/i;
+                s->carrier_phase_rate += 3*(ang/20);
+            }
+            span_log(&s->logging, SPAN_LOG_FLOW, "Coarse carrier frequency %7.2f\n", dds_frequencyf(s->carrier_phase_rate));
+            /* Check if the carrier frequency is plausible */
+            if (s->carrier_phase_rate < dds_phase_ratef(CARRIER_NOMINAL_FREQ - 20.0f)
+                ||
+                s->carrier_phase_rate > dds_phase_ratef(CARRIER_NOMINAL_FREQ + 20.0f))
+            {
+               span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n");
+               /* Park this modem */
+               s->in_training = TRAINING_STAGE_PARKED;
+               s->put_bit(s->user_data, PUTBIT_TRAINING_FAILED);
+               break;
+            }
+            /* Make a step shift in the phase, to pull it into line. We need to rotate the equalizer
+               buffer, as well as the carrier phase, for this to play out nicely. */
+            p = angle*2.0f*3.14159f/(65536.0f*65536.0f);
+            zz = complex_setf(cosf(p), -sinf(p));
+            for (i = 0;  i <= V29_EQUALIZER_MASK;  i++)
+                s->eq_buf[i] = complex_mulf(&s->eq_buf[i], &zz);
+            s->carrier_phase += angle;
+            /* We have just seen the first bit of the scrambled sequence, so skip it. */
+            bit = scrambled_training_bit(s);
+            s->training_count = 1;
+            s->in_training = TRAINING_STAGE_TRAIN_ON_CDCD;
+            break;
+        }
+        if (++s->training_count > V29_TRAINING_SEG_2_LEN)
+        {
+            /* This is bogus. There are not this many bauds in this section
+               of a real training sequence. */
+            span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (sequence failed)\n");
+            /* Park this modem */
+            s->in_training = TRAINING_STAGE_PARKED;
+            s->put_bit(s->user_data, PUTBIT_TRAINING_FAILED);
+        }
+        break;
+    case TRAINING_STAGE_TRAIN_ON_CDCD:
+        /* Train on the scrambled CDCD section. */
+        bit = scrambled_training_bit(s);
+        //span_log(&s->logging, SPAN_LOG_FLOW, "%5d %15.5f, %15.5f     %15.5f, %15.5f\n", s->training_count, z.re, z.im, v29_constellation[cdcd_pos[s->training_cd + bit]].re, v29_constellation[cdcd_pos[s->training_cd + bit]].im);
+        s->constellation_state = cdcd_pos[s->training_cd + bit];
+        target = &v29_constellation[s->constellation_state];
+        track_carrier(s, &z, target);
+        tune_equalizer(s, &z, target);
+        if (++s->training_count >= V29_TRAINING_SEG_3_LEN - 48)
+        {
+            s->in_training = TRAINING_STAGE_TRAIN_ON_CDCD_AND_TEST;
+            s->training_error = 0.0f;
+            s->carrier_track_i = 200.0f;
+            s->carrier_track_p = 1000000.0f;
+        }
+        break;
+    case TRAINING_STAGE_TRAIN_ON_CDCD_AND_TEST:
+        /* Continue training on the scrambled CDCD section, but measure the quality of training too. */
+        bit = scrambled_training_bit(s);
+        //span_log(&s->logging, SPAN_LOG_FLOW, "%5d %15.5f, %15.5f     %15.5f, %15.5f\n", s->training_count, z.re, z.im, v29_constellation[cdcd_pos[s->training_cd + bit]].re, v29_constellation[cdcd_pos[s->training_cd + bit]].im);
+        s->constellation_state = cdcd_pos[s->training_cd + bit];
+        target = &v29_constellation[s->constellation_state];
+        track_carrier(s, &z, target);
+        tune_equalizer(s, &z, target);
+        /* Measure the training error */
+        zz = complex_subf(&z, target);
+        s->training_error += powerf(&zz);
+        if (++s->training_count >= V29_TRAINING_SEG_3_LEN)
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "Constellation mismatch %f\n", s->training_error);
+            if (s->training_error < 100.0f)
+            {
+                s->training_count = 0;
+                s->training_cd = 0;
+                s->training_error = 0.0f;
+                s->constellation_state = 0;
+                s->in_training = TRAINING_STAGE_TEST_ONES;
+            }
+            else
+            {
+                span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (convergence failed)\n");
+                /* Park this modem */
+                s->in_training = TRAINING_STAGE_PARKED;
+                s->put_bit(s->user_data, PUTBIT_TRAINING_FAILED);
+            }
+        }
+        break;
+    case TRAINING_STAGE_TEST_ONES:
+        /* We are in the test phase, where we check that we can receive reliably.
+           We should get a run of 1's, 48 symbols (192 bits at 9600bps) long. */
+        //span_log(&s->logging, SPAN_LOG_FLOW, "%5d %15.5f, %15.5f\n", s->training_count, z.re, z.im);
+        decode_baud(s, &z);
+        target = &v29_constellation[s->constellation_state];
+        /* Measure the training error */
+        zz = complex_subf(&z, target);
+        s->training_error += powerf(&zz);
+        if (++s->training_count >= V29_TRAINING_SEG_4_LEN)
+        {
+            if (s->training_error < 50.0f)
+            {
+                /* We are up and running */
+                span_log(&s->logging, SPAN_LOG_FLOW, "Training succeeded (constellation mismatch %f)\n", s->training_error);
+                s->put_bit(s->user_data, PUTBIT_TRAINING_SUCCEEDED);
+                /* Apply some lag to the carrier off condition, to ensure the last few bits get pushed through
+                   the processing. */
+                s->carrier_present = 60;
+                s->in_training = TRAINING_STAGE_NORMAL_OPERATION;
+                equalizer_save(s);
+                s->carrier_phase_rate_save = s->carrier_phase_rate;
+                s->agc_scaling_save = s->agc_scaling;
+            }
+            else
+            {
+                /* Training has failed */
+                span_log(&s->logging, SPAN_LOG_FLOW, "Training failed (constellation mismatch %f)\n", s->training_error);
+                /* Park this modem */
+                s->put_bit(s->user_data, PUTBIT_TRAINING_FAILED);
+                s->in_training = TRAINING_STAGE_PARKED;
+            }
+        }
+        break;
+    case TRAINING_STAGE_PARKED:
+    default:
+        /* We failed to train! */
+        /* Park here until the carrier drops. */
+        target = &z;
+        break;
+    }
+    if (s->qam_report)
+        s->qam_report(s->qam_user_data, &z, target, s->constellation_state);
+}
+/*- End of function --------------------------------------------------------*/
+
+int v29_rx(v29_rx_state_t *s, const int16_t amp[], int len)
+{
+    int i;
+    int j;
+    int step;
+    complexf_t z;
+    complexf_t zz;
+    complexf_t sample;
+    int32_t power;
+    float v;
+
+    for (i = 0;  i < len;  i++)
+    {
+        s->rrc_filter[s->rrc_filter_step] =
+        s->rrc_filter[s->rrc_filter_step + V29_RX_FILTER_STEPS] = amp[i];
+        if (++s->rrc_filter_step >= V29_RX_FILTER_STEPS)
+            s->rrc_filter_step = 0;
+
+        /* There should be no DC in the signal, but sometimes there is.
+           We need to measure the power with the DC blocked, but not using
+           a slow to respond DC blocker. Use the most elementary HPF. */
+        power = power_meter_update(&(s->power), (amp[i] - s->last_sample) >> 1);
+        s->last_sample = amp[i];
+        if (s->carrier_present)
+        {
+            /* Look for power below turn-off threshold to turn the carrier off */
+            if (power < s->carrier_off_power)
+            {
+                if (--s->carrier_present <= 0)
+                {
+                    /* Count down a short delay, to ensure we push the last
+                       few bits through the filters before stopping. */
+                    v29_rx_restart(s, s->bit_rate, FALSE);
+                    s->put_bit(s->user_data, PUTBIT_CARRIER_DOWN);
+                    continue;
+                }
+            }
+        }
+        else
+        {
+            /* Look for power exceeding turn-on threshold to turn the carrier on */
+            if (power < s->carrier_on_power)
+                continue;
+            s->carrier_present = 1;
+            s->put_bit(s->user_data, PUTBIT_CARRIER_UP);
+        }
+        if (s->in_training != TRAINING_STAGE_PARKED)
+        {
+            /* Only spend effort processing this data if the modem is not
+               parked, after training failure. */
+            s->eq_put_step -= PULSESHAPER_COEFF_SETS;
+            step = -s->eq_put_step;
+            if (step > PULSESHAPER_COEFF_SETS - 1)
+                step = PULSESHAPER_COEFF_SETS - 1;
+            if (step < 0)
+                step += PULSESHAPER_COEFF_SETS;
+            zz.re = pulseshaper[step][0].re*s->rrc_filter[s->rrc_filter_step];
+            for (j = 1;  j < V29_RX_FILTER_STEPS;  j++)
+                zz.re += pulseshaper[step][j].re*s->rrc_filter[j + s->rrc_filter_step];
+            sample.re = zz.re*s->agc_scaling;
+
+            /* Symbol timing synchronisation band edge filters */
+            /* Low Nyquist band edge filter */
+            v = s->symbol_sync_low[0]*SYNC_LOW_BAND_EDGE_COEFF_0 + s->symbol_sync_low[1]*SYNC_LOW_BAND_EDGE_COEFF_1 + sample.re;
+            s->symbol_sync_low[1] = s->symbol_sync_low[0];
+            s->symbol_sync_low[0] = v;
+            /* High Nyquist band edge filter */
+            v = s->symbol_sync_high[0]*SYNC_HIGH_BAND_EDGE_COEFF_0 + s->symbol_sync_high[1]*SYNC_HIGH_BAND_EDGE_COEFF_1 + sample.re;
+            s->symbol_sync_high[1] = s->symbol_sync_high[0];
+            s->symbol_sync_high[0] = v;
+
+            z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate);
+
+            /* Put things into the equalization buffer at T/2 rate. The symbol synchronisation
+               will fiddle the step to align this with the symbols. */
+            if (s->eq_put_step <= 0)
+            {
+                if (s->in_training == TRAINING_STAGE_SYMBOL_ACQUISITION)
+                {
+                    /* Only AGC during the initial training */
+                    s->agc_scaling = (1.0f/PULSESHAPER_GAIN)*5.0f*0.72f/sqrtf(power);
+                }
+                /* Pulse shape while still at the carrier frequency, using a quadrature
+                   pair of filters. This results in a properly bandpass filtered complex
+                   signal, which can be brought directly to bandband by complex mixing.
+                   No further filtering, to remove mixer harmonics, is needed. */
+                step = -s->eq_put_step;
+                if (step > PULSESHAPER_COEFF_SETS - 1)
+                    step = PULSESHAPER_COEFF_SETS - 1;
+                zz.im = pulseshaper[step][0].im*s->rrc_filter[s->rrc_filter_step];
+                for (j = 1;  j < V29_RX_FILTER_STEPS;  j++)
+                    zz.im += pulseshaper[step][j].im*s->rrc_filter[j + s->rrc_filter_step];
+                sample.re = zz.re*s->agc_scaling;
+                sample.im = zz.im*s->agc_scaling;
+                s->eq_put_step += PULSESHAPER_COEFF_SETS*10/(3*2);
+                /* Shift to baseband - since this is done in a full complex form, the
+                   result is clean, and requires no further filtering, apart from the
+                   equalizer. */
+                zz.re = sample.re*z.re - sample.im*z.im;
+                zz.im = -sample.re*z.im - sample.im*z.re;
+                process_half_baud(s, &zz);
+            }
+        }
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+void v29_rx_set_put_bit(v29_rx_state_t *s, put_bit_func_t put_bit, void *user_data)
+{
+    s->put_bit = put_bit;
+    s->user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+int v29_rx_restart(v29_rx_state_t *s, int rate, int old_train)
+{
+    switch (rate)
+    {
+    case 9600:
+        s->training_cd = 0;
+        break;
+    case 7200:
+        s->training_cd = 2;
+        break;
+    case 4800:
+        s->training_cd = 4;
+        break;
+    default:
+        return -1;
+    }
+    s->bit_rate = rate;
+
+    vec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
+    s->rrc_filter_step = 0;
+
+    s->scramble_reg = 0;
+    s->training_scramble_reg = 0x2A;
+    s->in_training = TRAINING_STAGE_SYMBOL_ACQUISITION;
+    s->training_count = 0;
+    s->carrier_present = 0;
+    s->old_train = old_train;
+
+    s->carrier_phase = 0;
+    s->carrier_track_i = 8000.0f;
+    s->carrier_track_p = 8000000.0f;
+
+    power_meter_init(&(s->power), 4);
+
+    s->constellation_state = 0;
+
+    if (s->old_train)
+    {
+        s->carrier_phase_rate = s->carrier_phase_rate_save;
+        s->agc_scaling = s->agc_scaling_save;
+        equalizer_restore(s);
+    }
+    else
+    {
+        s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ);
+        s->agc_scaling = 0.0005f;
+        equalizer_reset(s);
+    }
+    s->eq_skip = 0;
+    s->last_sample = 0;
+
+    /* Initialise the working data for symbol timing synchronisation */
+    s->symbol_sync_low[0] = 0.0f;
+    s->symbol_sync_low[1] = 0.0f;
+    s->symbol_sync_high[0] = 0.0f;
+    s->symbol_sync_high[1] = 0.0f;
+    s->symbol_sync_dc_filter[0] = 0.0f;
+    s->symbol_sync_dc_filter[1] = 0.0f;
+    s->baud_phase = 0.0f;
+    s->baud_half = 0;
+
+    s->total_baud_timing_correction = 0;
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+v29_rx_state_t *v29_rx_init(v29_rx_state_t *s, int rate, put_bit_func_t put_bit, void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (v29_rx_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+
+    memset(s, 0, sizeof(*s));
+    s->put_bit = put_bit;
+    s->user_data = user_data;
+    /* The V.29 spec says the thresholds should be -31dBm and -26dBm, but that makes little
+       sense. V.17 uses -48dBm and -43dBm, and there seems no good reason to cut off at a
+       higher level (though at 9600bps and 7200bps, TCM should put V.17 sensitivity several
+       dB ahead of V.29). */
+    /* The thresholds should be on at -26dBm0 and off at -31dBm0 */
+    v29_rx_signal_cutoff(s, -28.5f);
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "V.29");
+
+    v29_rx_restart(s, rate, FALSE);
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+int v29_rx_release(v29_rx_state_t *s)
+{
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+void v29_rx_set_qam_report_handler(v29_rx_state_t *s, qam_report_handler_t *handler, void *user_data)
+{
+    s->qam_report = handler;
+    s->qam_user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

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