diff spandsp-0.0.3/spandsp-0.0.3/src/v17tx.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/v17tx.c	Fri Jun 25 16:00:21 2010 +0200
@@ -0,0 +1,717 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * v17tx.c - ITU V.17 modem transmit part
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2004 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: v17tx.c,v 1.46 2006/11/28 16:59:57 steveu Exp $
+ */
+
+/*! \file */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.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/dds.h"
+#include "spandsp/power_meter.h"
+
+#include "spandsp/v17tx.h"
+
+#define CARRIER_NOMINAL_FREQ        1800.0f
+
+/* Segments of the training sequence */
+#define V17_TRAINING_SEG_TEP_A      0
+#define V17_TRAINING_SEG_TEP_B      (V17_TRAINING_SEG_TEP_A + 480)
+#define V17_TRAINING_SEG_1          (V17_TRAINING_SEG_TEP_B + 48)
+#define V17_TRAINING_SEG_2          (V17_TRAINING_SEG_1 + 256)
+#define V17_TRAINING_SEG_3          (V17_TRAINING_SEG_2 + 2976)
+#define V17_TRAINING_SEG_4          (V17_TRAINING_SEG_3 + 64)
+#define V17_TRAINING_END            (V17_TRAINING_SEG_4 + 48)
+#define V17_TRAINING_SHUTDOWN_A     (V17_TRAINING_END + 32)
+#define V17_TRAINING_SHUTDOWN_END   (V17_TRAINING_SHUTDOWN_A + 48)
+
+#define V17_TRAINING_SHORT_SEG_4    (V17_TRAINING_SEG_2 + 38)
+
+#define V17_BRIDGE_WORD             0x8880
+
+const complexf_t v17_14400_constellation[128] =
+{
+    {-8, -3},       /* 0x00 */
+    { 9,  2},       /* 0x01 */
+    { 2, -9},       /* 0x02 */
+    {-3,  8},       /* 0x03 */
+    { 8,  3},       /* 0x04 */
+    {-9, -2},       /* 0x05 */
+    {-2,  9},       /* 0x06 */
+    { 3, -8},       /* 0x07 */
+    {-8,  1},       /* 0x08 */
+    { 9, -2},       /* 0x09 */
+    {-2, -9},       /* 0x0A */
+    { 1,  8},       /* 0x0B */
+    { 8, -1},       /* 0x0C */
+    {-9,  2},       /* 0x0D */
+    { 2,  9},       /* 0x0E */
+    {-1, -8},       /* 0x0F */
+    {-4, -3},       /* 0x10 */
+    { 5,  2},       /* 0x11 */
+    { 2, -5},       /* 0x12 */
+    {-3,  4},       /* 0x13 */
+    { 4,  3},       /* 0x14 */
+    {-5, -2},       /* 0x15 */
+    {-2,  5},       /* 0x16 */
+    { 3, -4},       /* 0x17 */
+    {-4,  1},       /* 0x18 */
+    { 5, -2},       /* 0x19 */
+    {-2, -5},       /* 0x1A */
+    { 1,  4},       /* 0x1B */
+    { 4, -1},       /* 0x1C */
+    {-5,  2},       /* 0x1D */
+    { 2,  5},       /* 0x1E */
+    {-1, -4},       /* 0x1F */
+    { 4, -3},       /* 0x20 */
+    {-3,  2},       /* 0x21 */
+    { 2,  3},       /* 0x22 */
+    {-3, -4},       /* 0x23 */
+    {-4,  3},       /* 0x24 */
+    { 3, -2},       /* 0x25 */
+    {-2, -3},       /* 0x26 */
+    { 3,  4},       /* 0x27 */
+    { 4,  1},       /* 0x28 */
+    {-3, -2},       /* 0x29 */
+    {-2,  3},       /* 0x2A */
+    { 1, -4},       /* 0x2B */
+    {-4, -1},       /* 0x2C */
+    { 3,  2},       /* 0x2D */
+    { 2, -3},       /* 0x2E */
+    {-1,  4},       /* 0x2F */
+    { 0, -3},       /* 0x30 */
+    { 1,  2},       /* 0x31 */
+    { 2, -1},       /* 0x32 */
+    {-3,  0},       /* 0x33 */
+    { 0,  3},       /* 0x34 */
+    {-1, -2},       /* 0x35 */
+    {-2,  1},       /* 0x36 */
+    { 3,  0},       /* 0x37 */
+    { 0,  1},       /* 0x38 */
+    { 1, -2},       /* 0x39 */
+    {-2, -1},       /* 0x3A */
+    { 1,  0},       /* 0x3B */
+    { 0, -1},       /* 0x3C */
+    {-1,  2},       /* 0x3D */
+    { 2,  1},       /* 0x3E */
+    {-1,  0},       /* 0x3F */
+    { 8, -3},       /* 0x40 */
+    {-7,  2},       /* 0x41 */
+    { 2,  7},       /* 0x42 */
+    {-3, -8},       /* 0x43 */
+    {-8,  3},       /* 0x44 */
+    { 7, -2},       /* 0x45 */
+    {-2, -7},       /* 0x46 */
+    { 3,  8},       /* 0x47 */
+    { 8,  1},       /* 0x48 */
+    {-7, -2},       /* 0x49 */
+    {-2,  7},       /* 0x4A */
+    { 1, -8},       /* 0x4B */
+    {-8, -1},       /* 0x4C */
+    { 7,  2},       /* 0x4D */
+    { 2, -7},       /* 0x4E */
+    {-1,  8},       /* 0x4F */
+    {-4, -7},       /* 0x50 */
+    { 5,  6},       /* 0x51 */
+    { 6, -5},       /* 0x52 */
+    {-7,  4},       /* 0x53 */
+    { 4,  7},       /* 0x54 */
+    {-5, -6},       /* 0x55 */
+    {-6,  5},       /* 0x56 */
+    { 7, -4},       /* 0x57 */
+    {-4,  5},       /* 0x58 */
+    { 5, -6},       /* 0x59 */
+    {-6, -5},       /* 0x5A */
+    { 5,  4},       /* 0x5B */
+    { 4, -5},       /* 0x5C */
+    {-5,  6},       /* 0x5D */
+    { 6,  5},       /* 0x5E */
+    {-5, -4},       /* 0x5F */
+    { 4, -7},       /* 0x60 */
+    {-3,  6},       /* 0x61 */
+    { 6,  3},       /* 0x62 */
+    {-7, -4},       /* 0x63 */
+    {-4,  7},       /* 0x64 */
+    { 3, -6},       /* 0x65 */
+    {-6, -3},       /* 0x66 */
+    { 7,  4},       /* 0x67 */
+    { 4,  5},       /* 0x68 */
+    {-3, -6},       /* 0x69 */
+    {-6,  3},       /* 0x6A */
+    { 5, -4},       /* 0x6B */
+    {-4, -5},       /* 0x6C */
+    { 3,  6},       /* 0x6D */
+    { 6, -3},       /* 0x6E */
+    {-5,  4},       /* 0x6F */
+    { 0, -7},       /* 0x70 */
+    { 1,  6},       /* 0x71 */
+    { 6, -1},       /* 0x72 */
+    {-7,  0},       /* 0x73 */
+    { 0,  7},       /* 0x74 */
+    {-1, -6},       /* 0x75 */
+    {-6,  1},       /* 0x76 */
+    { 7,  0},       /* 0x77 */
+    { 0,  5},       /* 0x78 */
+    { 1, -6},       /* 0x79 */
+    {-6, -1},       /* 0x7A */
+    { 5,  0},       /* 0x7B */
+    { 0, -5},       /* 0x7C */
+    {-1,  6},       /* 0x7D */
+    { 6,  1},       /* 0x7E */
+    {-5,  0}        /* 0x7F */
+};
+
+const complexf_t v17_12000_constellation[64] =
+{
+    { 7,  1},       /* 0x00 */
+    {-5, -1},       /* 0x01 */
+    {-1,  5},       /* 0x02 */
+    { 1, -7},       /* 0x03 */
+    {-7, -1},       /* 0x04 */
+    { 5,  1},       /* 0x05 */
+    { 1, -5},       /* 0x06 */
+    {-1,  7},       /* 0x07 */
+    { 3, -3},       /* 0x08 */
+    {-1,  3},       /* 0x09 */
+    { 3,  1},       /* 0x0A */
+    {-3, -3},       /* 0x0B */
+    {-3,  3},       /* 0x0C */
+    { 1, -3},       /* 0x0D */
+    {-3, -1},       /* 0x0E */
+    { 3,  3},       /* 0x0F */
+    { 7, -7},       /* 0x10 */
+    {-5,  7},       /* 0x11 */
+    { 7,  5},       /* 0x12 */
+    {-7, -7},       /* 0x13 */
+    {-7,  7},       /* 0x14 */
+    { 5, -7},       /* 0x15 */
+    {-7, -5},       /* 0x16 */
+    { 7,  7},       /* 0x17 */
+    {-1, -7},       /* 0x18 */
+    { 3,  7},       /* 0x19 */
+    { 7, -3},       /* 0x1A */
+    {-7,  1},       /* 0x1B */
+    { 1,  7},       /* 0x1C */
+    {-3, -7},       /* 0x1D */
+    {-7,  3},       /* 0x1E */
+    { 7, -1},       /* 0x1F */
+    { 3,  5},       /* 0x20 */
+    {-1, -5},       /* 0x21 */
+    {-5,  1},       /* 0x22 */
+    { 5, -3},       /* 0x23 */
+    {-3, -5},       /* 0x24 */
+    { 1,  5},       /* 0x25 */
+    { 5, -1},       /* 0x26 */
+    {-5,  3},       /* 0x27 */
+    {-1,  1},       /* 0x28 */
+    { 3, -1},       /* 0x29 */
+    {-1, -3},       /* 0x2A */
+    { 1,  1},       /* 0x2B */
+    { 1, -1},       /* 0x2C */
+    {-3,  1},       /* 0x2D */
+    { 1,  3},       /* 0x2E */
+    {-1, -1},       /* 0x2F */
+    {-5,  5},       /* 0x30 */
+    { 7, -5},       /* 0x31 */
+    {-5, -7},       /* 0x32 */
+    { 5,  5},       /* 0x33 */
+    { 5, -5},       /* 0x34 */
+    {-7,  5},       /* 0x35 */
+    { 5,  7},       /* 0x36 */
+    {-5, -5},       /* 0x37 */
+    {-5, -3},       /* 0x38 */
+    { 7,  3},       /* 0x39 */
+    { 3, -7},       /* 0x3A */
+    {-3,  5},       /* 0x3B */
+    { 5,  3},       /* 0x3C */
+    {-7, -3},       /* 0x3D */
+    {-3,  7},       /* 0x3E */
+    { 3, -5}        /* 0x3F */
+};
+
+const complexf_t v17_9600_constellation[32] =
+{
+    {-8,  2},       /* 0x00 */
+    {-6, -4},       /* 0x01 */
+    {-4,  6},       /* 0x02 */
+    { 2,  8},       /* 0x03 */
+    { 8, -2},       /* 0x04 */
+    { 6,  4},       /* 0x05 */
+    { 4, -6},       /* 0x06 */
+    {-2, -8},       /* 0x07 */
+    { 0,  2},       /* 0x08 */
+    {-6,  4},       /* 0x09 */
+    { 4,  6},       /* 0x0A */
+    { 2,  0},       /* 0x0B */
+    { 0, -2},       /* 0x0C */
+    { 6, -4},       /* 0x0D */
+    {-4, -6},       /* 0x0E */
+    {-2,  0},       /* 0x0F */
+    { 0, -6},       /* 0x10 */
+    { 2, -4},       /* 0x11 */
+    {-4, -2},       /* 0x12 */
+    {-6,  0},       /* 0x13 */
+    { 0,  6},       /* 0x14 */
+    {-2,  4},       /* 0x15 */
+    { 4,  2},       /* 0x16 */
+    { 6,  0},       /* 0x17 */
+    { 8,  2},       /* 0x18 */
+    { 2,  4},       /* 0x19 */
+    { 4, -2},       /* 0x1A */
+    { 2, -8},       /* 0x1B */
+    {-8, -2},       /* 0x1C */
+    {-2, -4},       /* 0x1D */
+    {-4,  2},       /* 0x1E */
+    {-2,  8}        /* 0x1F */
+};
+
+const complexf_t v17_7200_constellation[16] =
+{
+    { 6, -6},       /* 0x00 */
+    {-2,  6},       /* 0x01 */
+    { 6,  2},       /* 0x02 */
+    {-6, -6},       /* 0x03 */
+    {-6,  6},       /* 0x04 */
+    { 2, -6},       /* 0x05 */
+    {-6, -2},       /* 0x06 */
+    { 6,  6},       /* 0x07 */
+    {-2,  2},       /* 0x08 */
+    { 6, -2},       /* 0x09 */
+    {-2, -6},       /* 0x0A */
+    { 2,  2},       /* 0x0B */
+    { 2, -2},       /* 0x0C */
+    {-6,  2},       /* 0x0D */
+    { 2,  6},       /* 0x0E */
+    {-2, -2}        /* 0x0F */
+};
+
+/* Raised root cosine pulse shaping; Beta = 0.25; 4 symbols either
+   side of the centre. */
+/* Created with mkshape -r 0.05 0.25 91 -l and then split up */
+#define PULSESHAPER_GAIN            (9.9888356312f/10.0f)
+#define PULSESHAPER_COEFF_SETS      10
+
+static const float pulseshaper[PULSESHAPER_COEFF_SETS][V17_TX_FILTER_STEPS] =
+{
+    {
+        -0.0029426223f,         /* Filter 0 */
+        -0.0183060118f,
+         0.0653192857f,
+        -0.1703207714f,
+         0.6218069936f,
+         0.6218069936f,
+        -0.1703207714f,
+         0.0653192857f,
+        -0.0183060118f
+    },
+    {
+         0.0031876922f,         /* Filter 1 */
+        -0.0300884145f,
+         0.0832744718f,
+        -0.1974255221f,
+         0.7664229820f,
+         0.4670580725f,
+        -0.1291107519f,
+         0.0424189243f,
+        -0.0059810465f
+    },
+    {
+         0.0097229236f,         /* Filter 2 */
+        -0.0394811291f,
+         0.0931039664f,
+        -0.2043906784f,
+         0.8910868760f,
+         0.3122713836f,
+        -0.0802880559f,
+         0.0179050490f,
+         0.0052057308f
+    },
+    {
+         0.0156117223f,         /* Filter 3 */
+        -0.0447125347f,
+         0.0922040267f,
+        -0.1862939416f,
+         0.9870942864f,
+         0.1669790517f,
+        -0.0301581072f,
+        -0.0051358510f,
+         0.0139350286f
+    },
+    {
+         0.0197702545f,         /* Filter 4 */
+        -0.0443470335f,
+         0.0789538534f,
+        -0.1399184160f,
+         1.0476130256f,
+         0.0393903028f,
+         0.0157339854f,
+        -0.0241879599f,
+         0.0193774571f
+    },
+    {
+         0.0212455717f,         /* Filter 5 */
+        -0.0375307894f,
+         0.0530516472f,
+        -0.0642195521f,
+         1.0682849922f,
+        -0.0642195521f,
+         0.0530516472f,
+        -0.0375307894f,
+         0.0212455717f
+    },
+    {
+         0.0193774571f,         /* Filter 6 */
+        -0.0241879599f,
+         0.0157339854f,
+         0.0393903028f,
+         1.0476130256f,
+        -0.1399184160f,
+         0.0789538534f,
+        -0.0443470335f,
+         0.0197702545f
+    },
+    {
+         0.0139350286f,         /* Filter 7 */
+        -0.0051358510f,
+        -0.0301581072f,
+         0.1669790517f,
+         0.9870942864f,
+        -0.1862939416f,
+         0.0922040267f,
+        -0.0447125347f,
+         0.0156117223f
+    },
+    {
+         0.0052057308f,         /* Filter 8 */
+         0.0179050490f,
+        -0.0802880559f,
+         0.3122713836f,
+         0.8910868760f,
+        -0.2043906784f,
+         0.0931039664f,
+        -0.0394811291f,
+         0.0097229236f
+    },
+    {
+        -0.0059810465f,         /* Filter 9 */
+         0.0424189243f,
+        -0.1291107519f,
+         0.4670580725f,
+         0.7664229820f,
+        -0.1974255221f,
+         0.0832744718f,
+        -0.0300884145f,
+         0.0031876922f
+    },
+};
+
+static __inline__ int scramble(v17_tx_state_t *s, int in_bit)
+{
+    int out_bit;
+
+    out_bit = (in_bit ^ (s->scramble_reg >> 17) ^ (s->scramble_reg >> 22)) & 1;
+    s->scramble_reg = (s->scramble_reg << 1) | out_bit;
+    return out_bit;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ complexf_t training_get(v17_tx_state_t *s)
+{
+    static const complexf_t abcd[4] =
+    {
+        {-6.0f, -2.0f},
+        { 2.0f, -6.0f},
+        { 6.0f,  2.0f},
+        {-2.0f,  6.0f}
+    };
+    static const int cdba_to_abcd[4] =
+    {
+        2, 3, 1, 0
+    };
+    static const int dibit_to_step[4] =
+    {
+        1, 0, 2, 3
+    };
+    int bits;
+    int shift;
+
+    if (++s->training_step <= V17_TRAINING_SEG_3)
+    {
+        if (s->training_step <= V17_TRAINING_SEG_2)
+        {
+            if (s->training_step <= V17_TRAINING_SEG_TEP_B)
+            {
+                /* Optional segment: Unmodulated carrier (talker echo protection) */
+                return abcd[0];
+            }
+            if (s->training_step <= V17_TRAINING_SEG_1)
+            {
+                /* Optional segment: silence (talker echo protection) */
+                return complex_setf(0.0f, 0.0f);
+            }
+            /* Segment 1: ABAB... */
+            return abcd[(s->training_step & 1) ^ 1];
+        }
+        /* Segment 2: CDBA... */
+        /* Apply the scrambler */
+        bits = scramble(s, 1);
+        bits = (bits << 1) | scramble(s, 1);
+        s->constellation_state = cdba_to_abcd[bits];
+        if (s->short_train  &&  s->training_step == V17_TRAINING_SHORT_SEG_4)
+        {
+            /* Go straight to the ones test. */
+            s->training_step = V17_TRAINING_SEG_4;
+        }
+        return abcd[s->constellation_state];
+    }
+    /* Segment 3: Bridge... */
+    shift = ((s->training_step - V17_TRAINING_SEG_3 - 1) & 0x7) << 1;
+    span_log(&s->logging, SPAN_LOG_FLOW, "Seg 3 shift %d\n", shift);
+    bits = scramble(s, V17_BRIDGE_WORD >> shift);
+    bits = (bits << 1) | scramble(s, V17_BRIDGE_WORD >> (shift + 1));
+    s->constellation_state = (s->constellation_state + dibit_to_step[bits]) & 3;
+    return abcd[s->constellation_state];
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ int diff_and_convolutional_encode(v17_tx_state_t *s, int q)
+{
+    static const int diff_code[16] =
+    {
+        0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2
+    };
+    int y1;
+    int y2;
+    int this1;
+    int this2;
+
+    /* Differentially encode */
+    s->diff = diff_code[((q & 0x03) << 2) | s->diff];
+
+    /* Convolutionally encode the redundant bit */
+    y2 = s->diff >> 1;
+    y1 = s->diff;
+    this2 = y2 ^ y1 ^ (s->convolution >> 2) ^ ((y2 ^ (s->convolution >> 1)) & s->convolution);
+    this1 = y2 ^ (s->convolution >> 1) ^ (y1 & s->convolution);
+    s->convolution = ((s->convolution & 1) << 2) | ((this2 & 1) << 1) | (this1 & 1);
+    return ((q << 1) & 0x78) | (s->diff << 1) | ((s->convolution >> 2) & 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+static int fake_get_bit(void *user_data)
+{
+    return 1;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ complexf_t getbaud(v17_tx_state_t *s)
+{
+    int i;
+    int bit;
+    int bits;
+
+    if (s->in_training)
+    {
+        if (s->training_step <= V17_TRAINING_END)
+        {
+            /* Send the training sequence */
+            if (s->training_step < V17_TRAINING_SEG_4)
+                return training_get(s);
+            /* The last step in training is to send some 1's */
+            if (++s->training_step > V17_TRAINING_END)
+            {
+                /* Training finished - commence normal operation. */
+                s->current_get_bit = s->get_bit;
+                s->in_training = FALSE;
+            }
+        }
+        else
+        {
+            if (++s->training_step > V17_TRAINING_SHUTDOWN_A)
+            {
+                /* The shutdown sequence is 32 bauds of all 1's, then 48 bauds
+                   of silence */
+                return complex_setf(0.0f, 0.0f);
+            }
+        }
+    }
+    bits = 0;
+    for (i = 0;  i < s->bits_per_symbol;  i++)
+    {
+        if ((bit = s->current_get_bit(s->user_data)) == PUTBIT_END_OF_DATA)
+        {
+printf("End of real data\n");
+            /* End of real data. Switch to the fake get_bit routine, until we
+               have shut down completely. */
+            s->current_get_bit = fake_get_bit;
+            s->in_training = TRUE;
+            bit = 1;
+        }
+        bits |= (scramble(s, bit) << i);
+    }
+    return s->constellation[diff_and_convolutional_encode(s, bits)];
+}
+/*- End of function --------------------------------------------------------*/
+
+int v17_tx(v17_tx_state_t *s, int16_t amp[], int len)
+{
+    complexf_t x;
+    complexf_t z;
+    int i;
+    int sample;
+
+    if (s->training_step >= V17_TRAINING_SHUTDOWN_END)
+    {
+        /* Once we have sent the shutdown sequence, we stop sending completely. */
+        return 0;
+    }
+    for (sample = 0;  sample < len;  sample++)
+    {
+        if ((s->baud_phase += 3) >= 10)
+        {
+            s->baud_phase -= 10;
+            s->rrc_filter[s->rrc_filter_step] =
+            s->rrc_filter[s->rrc_filter_step + V17_TX_FILTER_STEPS] = getbaud(s);
+            if (++s->rrc_filter_step >= V17_TX_FILTER_STEPS)
+                s->rrc_filter_step = 0;
+        }
+        /* Root raised cosine pulse shaping at baseband */
+        x.re = 0.0f;
+        x.im = 0.0f;
+        for (i = 0;  i < V17_TX_FILTER_STEPS;  i++)
+        {
+            x.re += pulseshaper[9 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].re;
+            x.im += pulseshaper[9 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].im;
+        }
+        /* Now create and modulate the carrier */
+        z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate);
+        /* Don't bother saturating. We should never clip. */
+        amp[sample] = (int16_t) lrintf((x.re*z.re - x.im*z.im)*s->gain);
+    }
+    return sample;
+}
+/*- End of function --------------------------------------------------------*/
+
+void v17_tx_power(v17_tx_state_t *s, float power)
+{
+    /* The constellation design seems to keep the average power the same, regardless
+       of which bit rate is in use. */
+    s->gain = 0.223f*powf(10.0f, (power - DBM0_MAX_POWER)/20.0f)*32768.0f/PULSESHAPER_GAIN;
+}
+/*- End of function --------------------------------------------------------*/
+
+void v17_tx_set_get_bit(v17_tx_state_t *s, get_bit_func_t get_bit, void *user_data)
+{
+    if (s->get_bit == s->current_get_bit)
+        s->current_get_bit = get_bit;
+    s->get_bit = get_bit;
+    s->user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+int v17_tx_restart(v17_tx_state_t *s, int rate, int tep, int short_train)
+{
+    switch (rate)
+    {
+    case 14400:
+        s->bits_per_symbol = 6;
+        s->constellation = v17_14400_constellation;
+        break;
+    case 12000:
+        s->bits_per_symbol = 5;
+        s->constellation = v17_12000_constellation;
+        break;
+    case 9600:
+        s->bits_per_symbol = 4;
+        s->constellation = v17_9600_constellation;
+        break;
+    case 7200:
+        s->bits_per_symbol = 3;
+        s->constellation = v17_7200_constellation;
+        break;
+    default:
+        return -1;
+    }
+    /* NB: some modems seem to use 3 instead of 1 for long training */
+    s->diff = (short_train)  ?  0  :  1;
+    s->bit_rate = rate;
+    cvec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
+    s->rrc_filter_step = 0;
+    s->convolution = 0;
+    s->scramble_reg = 0x2ECDD5;
+    s->in_training = TRUE;
+    s->short_train = short_train;
+    s->training_step =  (tep)  ?  V17_TRAINING_SEG_TEP_A  :  V17_TRAINING_SEG_1;
+    s->carrier_phase = 0;
+    s->baud_phase = 0;
+    s->constellation_state = 0;
+    s->current_get_bit = fake_get_bit;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+v17_tx_state_t *v17_tx_init(v17_tx_state_t *s, int rate, int tep, get_bit_func_t get_bit, void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (v17_tx_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    s->get_bit = get_bit;
+    s->user_data = user_data;
+    s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ);
+    v17_tx_power(s, -14.0f);
+    v17_tx_restart(s, rate, tep, FALSE);
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+int v17_tx_release(v17_tx_state_t *s)
+{
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

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