view 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 source

/*
 * 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.