diff spandsp-0.0.6pre17/src/v18.c @ 4:26cd8f1ef0b1

import spandsp-0.0.6pre17
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 15:50:58 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spandsp-0.0.6pre17/src/v18.c	Fri Jun 25 15:50:58 2010 +0200
@@ -0,0 +1,838 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * v18.c - V.18 text telephony for the deaf.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2004-2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: v18.c,v 1.12 2009/11/04 15:52:06 steveu Exp $
+ */
+ 
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include "floating_fudge.h"
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/queue.h"
+#include "spandsp/async.h"
+#include "spandsp/complex.h"
+#include "spandsp/dds.h"
+#include "spandsp/tone_detect.h"
+#include "spandsp/tone_generate.h"
+#include "spandsp/super_tone_rx.h"
+#include "spandsp/power_meter.h"
+#include "spandsp/fsk.h"
+#include "spandsp/dtmf.h"
+#include "spandsp/modem_connect_tones.h"
+#include "spandsp/v8.h"
+#include "spandsp/v18.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/queue.h"
+#include "spandsp/private/tone_generate.h"
+#include "spandsp/private/async.h"
+#include "spandsp/private/fsk.h"
+#include "spandsp/private/dtmf.h"
+#include "spandsp/private/modem_connect_tones.h"
+#include "spandsp/private/v18.h"
+
+#include <stdlib.h>
+
+/*! The baudot code to shift from alpha to digits and symbols */
+#define BAUDOT_FIGURE_SHIFT     0x1B
+/*! The baudot code to shift from digits and symbols to alpha */
+#define BAUDOT_LETTER_SHIFT     0x1F
+
+struct dtmf_to_ascii_s
+{
+    const char *dtmf;
+    char ascii;
+};
+
+static const struct dtmf_to_ascii_s dtmf_to_ascii[] =
+{
+    {"###1", 'C'},
+    {"###2", 'F'},
+    {"###3", 'I'},
+    {"###4", 'L'},
+    {"###5", 'O'},
+    {"###6", 'R'},
+    {"###7", 'U'},
+    {"###8", 'X'},
+    {"###9", ';'},
+    {"###0", '!'},
+    {"##*1", 'A'},
+    {"##*2", 'D'},
+    {"##*3", 'G'},
+    {"##*4", 'J'},
+    {"##*5", 'M'},
+    {"##*6", 'P'},
+    {"##*7", 'S'},
+    {"##*8", 'V'},
+    {"##*9", 'Y'},
+    {"##1", 'B'},
+    {"##2", 'E'},
+    {"##3", 'H'},
+    {"##4", 'K'},
+    {"##5", 'N'},
+    {"##6", 'Q'},
+    {"##7", 'T'},
+    {"##8", 'W'},
+    {"##9", 'Z'},
+    {"##0", ' '},
+#if defined(WIN32)
+    {"#*1", 'X'}, // (Note 1) 111 1011
+    {"#*2", 'X'}, // (Note 1) 111 1100
+    {"#*3", 'X'}, // (Note 1) 111 1101
+    {"#*4", 'X'}, // (Note 1) 101 1011
+    {"#*5", 'X'}, // (Note 1) 101 1100
+    {"#*6", 'X'}, // (Note 1) 101 1101
+#else
+    {"#*1", 'æ'}, // (Note 1) 111 1011
+    {"#*2", 'ø'}, // (Note 1) 111 1100
+    {"#*3", 'å'}, // (Note 1) 111 1101
+    {"#*4", 'Æ'}, // (Note 1) 101 1011
+    {"#*5", 'Ø'}, // (Note 1) 101 1100
+    {"#*6", 'Å'}, // (Note 1) 101 1101
+#endif
+    {"#0", '?'},
+    {"#1", 'c'},
+    {"#2", 'f'},
+    {"#3", 'i'},
+    {"#4", 'l'},
+    {"#5", 'o'},
+    {"#6", 'r'},
+    {"#7", 'u'},
+    {"#8", 'x'},
+    {"#9", '.'},
+    {"*#0", '0'},
+    {"*#1", '1'},
+    {"*#2", '2'},
+    {"*#3", '3'},
+    {"*#4", '4'},
+    {"*#5", '5'},
+    {"*#6", '6'},
+    {"*#7", '7'},
+    {"*#8", '8'},
+    {"*#9", '9'},
+    {"**1", '+'},
+    {"**2", '-'},
+    {"**3", '='},
+    {"**4", ':'},
+    {"**5", '%'},
+    {"**6", '('},
+    {"**7", ')'},
+    {"**8", ','},
+    {"**9", '\n'},
+    {"*0", '\b'},
+    {"*1", 'a'},
+    {"*2", 'd'},
+    {"*3", 'g'},
+    {"*4", 'j'},
+    {"*5", 'm'},
+    {"*6", 'p'},
+    {"*7", 's'},
+    {"*8", 'v'},
+    {"*9", 'y'},
+    {"0", ' '},
+    {"1", 'b'},
+    {"2", 'e'},
+    {"3", 'h'},
+    {"4", 'k'},
+    {"5", 'n'},
+    {"6", 'q'},
+    {"7", 't'},
+    {"8", 'w'},
+    {"9", 'z'},
+    {"", '\0'}
+};
+
+static const char *ascii_to_dtmf[128] =
+{
+    "",         /* NULL */
+    "",         /* SOH */
+    "",         /* STX */
+    "",         /* ETX */
+    "",         /* EOT */
+    "",         /* ENQ */
+    "",         /* ACK */
+    "",         /* BEL */
+    "*0",       /* BACK SPACE */
+    "0",        /* HT >> SPACE */
+    "**9",      /* LF */
+    "**9",      /* VT >> LF */
+    "**9",      /* FF >> LF */
+    "",         /* CR */
+    "",         /* SO */
+    "",         /* SI */
+    "",         /* DLE */
+    "",         /* DC1 */
+    "",         /* DC2 */
+    "",         /* DC3 */
+    "",         /* DC4 */
+    "",         /* NAK */
+    "",         /* SYN */
+    "",         /* ETB */
+    "",         /* CAN */
+    "",         /* EM */
+    "#0",       /* SUB >> ? */
+    "",         /* ESC */
+    "**9",      /* IS4 >> LF */
+    "**9",      /* IS3 >> LF */
+    "**9",      /* IS2 >> LF */
+    "0",        /* IS1 >> SPACE */
+    "0",        /* SPACE */
+    "###0",     /* ! */
+    "",         /* " */
+    "",         /* # */
+    "",         /* $ */
+    "**5",      /* % */
+    "**1",      /* & >> + */
+    "",         /* ’ */
+    "**6",      /* ( */
+    "**7",      /* ) */
+    "#9",       /* _ >> . */
+    "**1",      /* + */
+    "**8",      /* , */
+    "**2",      /* - */
+    "#9",       /* . */
+    "",         /* / */
+    "*#0",      /* 0 */
+    "*#1",      /* 1 */
+    "*#2",      /* 2 */
+    "*#3",      /* 3 */
+    "*#4",      /* 4 */
+    "*#5",      /* 5 */
+    "*#6",      /* 6 */
+    "*#7",      /* 7 */
+    "*#8",      /* 8 */
+    "*#9",      /* 9 */
+    "**4",      /* : */
+    "###9",     /* ; */
+    "**6",      /* < >> ( */
+    "**3",      /* = */
+    "**7",      /* > >> ) */
+    "#0",       /* ? */
+    "###8",     /* @ >> X */
+    "##*1",     /* A */
+    "##1",      /* B */
+    "###1",     /* C */
+    "##*2",     /* D */
+    "##2",      /* E */
+    "###2",     /* F */
+    "##*3",     /* G */
+    "##3",      /* H */
+    "###3",     /* I */
+    "##*4",     /* J */
+    "##4",      /* K */
+    "###4",     /* L */
+    "##*5",     /* M */
+    "##5",      /* N */
+    "###5",     /* O */
+    "##*6",     /* P */
+    "##6",      /* Q */
+    "###6",     /* R */
+    "##*7",     /* S */
+    "##7",      /* T */
+    "###7",     /* U */
+    "##*8",     /* V */
+    "##8",      /* W */
+    "###8",     /* X */
+    "##*9",     /* Y */
+    "##9",      /* Z */
+    "#*4",      /* Æ (National code) */
+    "#*5",      /* Ø (National code) */
+    "#*6",      /* Å (National code) */
+    "",         /* ^ */
+    "0",        /* _ >> SPACE */
+    "",         /* ’ */
+    "*1",       /* a */
+    "1",        /* b */
+    "#1",       /* c */
+    "*2",       /* d */
+    "2",        /* e */
+    "#2",       /* f */
+    "*3",       /* g */
+    "3",        /* h */
+    "#3",       /* i */
+    "*4",       /* j */
+    "4",        /* k */
+    "#4",       /* l */
+    "*5",       /* m */
+    "5",        /* n */
+    "#5",       /* o */
+    "*6",       /* p */
+    "6",        /* q */
+    "#6",       /* r */
+    "*7",       /* s */
+    "7",        /* t */
+    "#7",       /* u */
+    "*8",       /* v */
+    "8",        /* w */
+    "#8",       /* x */
+    "*9",       /* y */
+    "9",        /* z */
+    "#*1",      /* æ (National code) */
+    "#*2",      /* ø (National code) */
+    "#*3",      /* å (National code) */
+    "0",        /* ~ >> SPACE */
+    "*0"        /* DEL >> BACK SPACE */
+};
+
+static int cmp(const void *s, const void *t)
+{
+    const char *ss;
+    struct dtmf_to_ascii_s *tt;
+
+    ss = (const char *) s;
+    tt = (struct dtmf_to_ascii_s *) t;
+    return strncmp(ss, tt->dtmf, strlen(tt->dtmf));
+}
+
+SPAN_DECLARE(int) v18_encode_dtmf(v18_state_t *s, char dtmf[], const char msg[])
+{
+    const char *t;
+    char *u;
+    const char *v;
+    
+    t = msg;
+    u = dtmf;
+    while (*t)
+    {
+        v = ascii_to_dtmf[*t & 0x7F];
+        while (*v)
+            *u++ = *v++;
+        t++;
+    }
+    *u = '\0';
+
+    return u - dtmf;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v18_decode_dtmf(v18_state_t *s, char msg[], const char dtmf[])
+{
+    int entries;
+    const char *t;
+    char *u;
+    struct dtmf_to_ascii_s *ss;
+
+    entries = sizeof(dtmf_to_ascii)/sizeof(dtmf_to_ascii[0]) - 1;
+    t = dtmf;
+    u = msg;
+    while (*t)
+    {
+        ss = bsearch(t, dtmf_to_ascii, entries, sizeof(dtmf_to_ascii[0]), cmp);
+        if (ss)
+        {
+            t += strlen(ss->dtmf);
+            *u++ = ss->ascii;
+        }
+        else
+        {
+            /* Can't match the code. Let's assume this is a code we just don't know, and skip over it */
+            while (*t == '#'  ||  *t == '*')
+                t++;
+            if (*t)
+                t++;
+        }
+    }
+    *u = '\0';
+    return u - msg;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint16_t) v18_encode_baudot(v18_state_t *s, uint8_t ch)
+{
+    static const uint8_t conv[128] =
+    {
+        0xFF, /* NUL */
+        0xFF, /* SOH */
+        0xFF, /* STX */
+        0xFF, /* ETX */
+        0xFF, /* EOT */
+        0xFF, /* ENQ */
+        0xFF, /* ACK */
+        0xFF, /* BEL */
+        0x00, /* BS */
+        0x44, /* HT >> SPACE */
+        0x42, /* LF */
+        0x42, /* VT >> LF */
+        0x42, /* FF >> LF */
+        0x48, /* CR */
+        0xFF, /* SO */
+        0xFF, /* SI */
+        0xFF, /* DLE */
+        0xFF, /* DC1 */
+        0xFF, /* DC2 */
+        0xFF, /* DC3 */
+        0xFF, /* DC4 */
+        0xFF, /* NAK */
+        0xFF, /* SYN */
+        0xFF, /* ETB */
+        0xFF, /* CAN */
+        0xFF, /* EM */
+        0x99, /* SUB >> ? */
+        0xFF, /* ESC */
+        0x42, /* IS4 >> LF */
+        0x42, /* IS3 >> LF */
+        0x42, /* IS2 >> LF */
+        0x44, /* IS1 >> SPACE */
+        0x44, /*   */
+        0x8D, /* ! */
+        0x91, /* " */
+        0x89, /* # >> $ */
+        0x89, /* $ */
+        0x9D, /* % >> / */
+        0x9A, /* & >> + */
+        0x8B, /* ' */
+        0x8F, /* ( */
+        0x92, /* ) */
+        0x9C, /* * >> . */
+        0x9A, /* + */
+        0x8C, /* , */
+        0x83, /* - */
+        0x9C, /* . */
+        0x9D, /* / */
+        0x96, /* 0 */
+        0x97, /* 1 */
+        0x93, /* 2 */
+        0x81, /* 3 */
+        0x8A, /* 4 */
+        0x90, /* 5 */
+        0x95, /* 6 */
+        0x87, /* 7 */
+        0x86, /* 8 */
+        0x98, /* 9 */
+        0x8E, /* : */
+        0x9E, /* ; */
+        0x8F, /* < >> )*/
+        0x94, /* = */
+        0x92, /* > >> ( */
+        0x99, /* ? */
+        0x1D, /* @ >> X */
+        0x03, /* A */
+        0x19, /* B */
+        0x0E, /* C */
+        0x09, /* D */
+        0x01, /* E */
+        0x0D, /* F */
+        0x1A, /* G */
+        0x14, /* H */
+        0x06, /* I */
+        0x0B, /* J */
+        0x0F, /* K */
+        0x12, /* L */
+        0x1C, /* M */
+        0x0C, /* N */
+        0x18, /* O */
+        0x16, /* P */
+        0x17, /* Q */
+        0x0A, /* R */
+        0x05, /* S */
+        0x10, /* T */
+        0x07, /* U */
+        0x1E, /* V */
+        0x13, /* W */
+        0x1D, /* X */
+        0x15, /* Y */
+        0x11, /* Z */
+        0x8F, /* [ >> (*/
+        0x9D, /* \ >> / */
+        0x92, /* ] >> ) */
+        0x8B, /* ^ >> ' */
+        0x44, /* _ >> Space */
+        0x8B, /* ` >> ' */
+        0x03, /* a */
+        0x19, /* b */
+        0x0E, /* c */
+        0x09, /* d */
+        0x01, /* e */
+        0x0D, /* f */
+        0x1A, /* g */
+        0x14, /* h */
+        0x06, /* i */
+        0x0B, /* j */
+        0x0F, /* k */
+        0x12, /* l */
+        0x1C, /* m */
+        0x0C, /* n */
+        0x18, /* o */
+        0x16, /* p */
+        0x17, /* q */
+        0x0A, /* r */
+        0x05, /* s */
+        0x10, /* t */
+        0x07, /* u */
+        0x1E, /* v */
+        0x13, /* w */
+        0x1D, /* x */
+        0x15, /* y */
+        0x11, /* z */
+        0x8F, /* { >> ( */
+        0x8D, /* | >> ! */
+        0x92, /* } >> ) */
+        0x44, /* ~ >> Space */
+        0xFF, /* DEL */
+    };
+    uint16_t shift;
+
+    if (ch == 0x7F)
+    {
+        /* DLE is a special character meaning "force a
+           change to the letter character set */
+        shift = BAUDOT_LETTER_SHIFT;
+        return 0;
+    }
+    ch = conv[ch];
+    /* Is it a non-existant code? */
+    if (ch == 0xFF)
+        return 0;
+    /* Is it a code present in both character sets? */
+    if ((ch & 0x40))
+        return 0x8000 | (ch & 0x1F);
+    /* Need to allow for a possible character set change. */
+    if ((ch & 0x80))
+    {
+        if (s->baudot_tx_shift == 1)
+            return ch & 0x1F;
+        s->baudot_tx_shift = 1;
+        shift = BAUDOT_FIGURE_SHIFT;
+    }
+    else
+    {
+        if (s->baudot_tx_shift == 0)
+            return ch & 0x1F;
+        s->baudot_tx_shift = 0;
+        shift = BAUDOT_LETTER_SHIFT;
+    }
+    return 0x8000 | (shift << 5) | (ch & 0x1F);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint8_t) v18_decode_baudot(v18_state_t *s, uint8_t ch)
+{
+    static const uint8_t conv[2][32] =
+    {
+        {"\bE\nA SIU\rDRJNFCKTZLWHYPQOBG^MXV^"},
+        {"\b3\n- -87\r$4',!:(5\")2=6019?+^./;^"}
+    };
+
+    switch (ch)
+    {
+    case BAUDOT_FIGURE_SHIFT:
+        s->baudot_rx_shift = 1;
+        break;
+    case BAUDOT_LETTER_SHIFT:
+        s->baudot_rx_shift = 0;
+        break;
+    default:
+        return conv[s->baudot_rx_shift][ch];
+    }
+    /* return 0 if we did not produce a character */
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void v18_rx_dtmf(void *user_data, const char digits[], int len)
+{
+    v18_state_t *s;
+
+    s = (v18_state_t *) user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v18_tdd_get_async_byte(void *user_data)
+{
+    v18_state_t *s;
+    int ch;
+    
+    s = (v18_state_t *) user_data;
+    if ((ch = queue_read_byte(&s->queue.queue)) >= 0)
+    {
+        int space;
+        int cont;
+        space = queue_free_space(&s->queue.queue);
+        cont = queue_contents(&s->queue.queue);
+        return ch;
+    }
+    if (s->tx_signal_on)
+    {
+        /* The FSK should now be switched off. */
+        s->tx_signal_on = FALSE;
+    }
+    return 0x1F;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void v18_tdd_put_async_byte(void *user_data, int byte)
+{
+    v18_state_t *s;
+    uint8_t octet;
+    
+    s = (v18_state_t *) user_data;
+    //printf("Rx byte %x\n", byte);
+    if (byte < 0)
+    {
+        /* Special conditions */
+        span_log(&s->logging, SPAN_LOG_FLOW, "V.18 signal status is %s (%d)\n", signal_status_to_str(byte), byte);
+        switch (byte)
+        {
+        case SIG_STATUS_CARRIER_UP:
+            s->consecutive_ones = 0;
+            s->bit_pos = 0;
+            s->in_progress = 0;
+            s->rx_msg_len = 0;
+            break;
+        case SIG_STATUS_CARRIER_DOWN:
+            if (s->rx_msg_len > 0)
+            {
+                /* Whatever we have to date constitutes the message */
+                s->rx_msg[s->rx_msg_len] = '\0';
+                s->put_msg(s->user_data, s->rx_msg, s->rx_msg_len);
+                s->rx_msg_len = 0;
+            }
+            break;
+        default:
+            span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected special put byte value - %d!\n", byte);
+            break;
+        }
+        return;
+    }
+    if ((octet = v18_decode_baudot(s, (uint8_t) (byte & 0x1F))))
+        s->rx_msg[s->rx_msg_len++] = octet;
+    if (s->rx_msg_len >= 256)
+    {
+        s->rx_msg[s->rx_msg_len] = '\0';
+        s->put_msg(s->user_data, s->rx_msg, s->rx_msg_len);
+        s->rx_msg_len = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE_NONSTD(int) v18_tx(v18_state_t *s, int16_t *amp, int max_len)
+{
+    int len;
+    int lenx;
+
+    len = tone_gen(&(s->alert_tone_gen), amp, max_len);
+    if (s->tx_signal_on)
+    {
+        switch (s->mode)
+        {
+        case V18_MODE_DTMF:
+            if (len < max_len)
+                len += dtmf_tx(&(s->dtmftx), amp, max_len - len);
+            break;
+        default:
+            if (len < max_len)
+            {
+                if ((lenx = fsk_tx(&(s->fsktx), amp + len, max_len - len)) <= 0)
+                    s->tx_signal_on = FALSE;
+                len += lenx;
+            }
+            break;
+        }
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE_NONSTD(int) v18_rx(v18_state_t *s, const int16_t amp[], int len)
+{
+    switch (s->mode)
+    {
+    case V18_MODE_DTMF:
+        /* Apply a message timeout. */
+        s->in_progress -= len;
+        if (s->in_progress <= 0)
+            s->rx_msg_len = 0;
+        dtmf_rx(&(s->dtmfrx), amp, len);
+        break;
+    default:
+        fsk_rx(&(s->fskrx), amp, len);
+        break;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v18_put(v18_state_t *s, const char msg[], int len)
+{
+    char buf[256 + 1];
+    int x;
+    int n;
+    int i;
+
+    /* This returns the number of characters that would not fit in the buffer.
+       The buffer will only be loaded if the whole string of digits will fit,
+       in which case zero is returned. */
+    if (len < 0)
+    {
+        if ((len = strlen(msg)) == 0)
+            return 0;
+    }
+    switch (s->mode)
+    {
+    case V18_MODE_5BIT_45:
+    case V18_MODE_5BIT_50:
+        for (i = 0;  i < len;  i++)
+        {
+            n = 0;
+            if ((x = v18_encode_baudot(s, msg[i])))
+            {
+                if ((x & 0x3E0))
+                    buf[n++] = (uint8_t) ((x >> 5) & 0x1F);
+                buf[n++] = (uint8_t) (x & 0x1F);
+                /* TODO: Deal with out of space condition */
+                if (queue_write(&s->queue.queue, (const uint8_t *) buf, n) < 0)
+                    return i;
+                s->tx_signal_on = TRUE;
+            }
+        }
+        return len;
+    case V18_MODE_DTMF:
+        break;
+    }
+    return -1;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(logging_state_t *) v18_get_logging_state(v18_state_t *s)
+{
+    return &s->logging;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(v18_state_t *) v18_init(v18_state_t *s,
+                                     int calling_party,
+                                     int mode,
+                                     put_msg_func_t put_msg,
+                                     void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (v18_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    s->calling_party = calling_party;
+    s->mode = mode;
+    s->put_msg = put_msg;
+    s->user_data = user_data;
+
+    switch (s->mode)
+    {
+    case V18_MODE_5BIT_45:
+        fsk_tx_init(&(s->fsktx), &preset_fsk_specs[FSK_WEITBRECHT], async_tx_get_bit, &(s->asynctx));
+        async_tx_init(&(s->asynctx), 5, ASYNC_PARITY_NONE, 2, FALSE, v18_tdd_get_async_byte, s);
+        /* Schedule an explicit shift at the start of baudot transmission */
+        s->baudot_tx_shift = 2;
+        /* TDD uses 5 bit data, no parity and 1.5 stop bits. We scan for the first stop bit, and
+           ride over the fraction. */
+        fsk_rx_init(&(s->fskrx), &preset_fsk_specs[FSK_WEITBRECHT], FSK_FRAME_MODE_5N1_FRAMES, v18_tdd_put_async_byte, s);
+        s->baudot_rx_shift = 0;
+        break;
+    case V18_MODE_5BIT_50:
+        fsk_tx_init(&(s->fsktx), &preset_fsk_specs[FSK_WEITBRECHT50], async_tx_get_bit, &(s->asynctx));
+        async_tx_init(&(s->asynctx), 5, ASYNC_PARITY_NONE, 2, FALSE, v18_tdd_get_async_byte, s);
+        /* Schedule an explicit shift at the start of baudot transmission */
+        s->baudot_tx_shift = 2;
+        /* TDD uses 5 bit data, no parity and 1.5 stop bits. We scan for the first stop bit, and
+           ride over the fraction. */
+        fsk_rx_init(&(s->fskrx), &preset_fsk_specs[FSK_WEITBRECHT50], FSK_FRAME_MODE_5N1_FRAMES, v18_tdd_put_async_byte, s);
+        s->baudot_rx_shift = 0;
+        break;
+    case V18_MODE_DTMF:
+        dtmf_tx_init(&(s->dtmftx));
+        dtmf_rx_init(&(s->dtmfrx), v18_rx_dtmf, s);
+        break;
+    case V18_MODE_EDT:
+        break;
+    case V18_MODE_BELL103:
+        break;
+    case V18_MODE_V23VIDEOTEX:
+        break;
+    case V18_MODE_V21TEXTPHONE:
+        break;
+    case V18_MODE_V18TEXTPHONE:
+        break;
+    }
+    queue_init(&s->queue.queue, 128, QUEUE_READ_ATOMIC | QUEUE_WRITE_ATOMIC);
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v18_release(v18_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v18_free(v18_state_t *s)
+{
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(const char *) v18_mode_to_str(int mode)
+{
+    switch (mode)
+    {
+    case V18_MODE_NONE:
+        return "None";
+    case V18_MODE_5BIT_45:
+        return "Weitbrecht TDD (45.45bps)";
+    case V18_MODE_5BIT_50:
+        return "Weitbrecht TDD (50bps)";
+    case V18_MODE_DTMF:
+        return "DTMF";
+    case V18_MODE_EDT:
+        return "EDT";
+    case V18_MODE_BELL103:
+        return "Bell 103";
+    case V18_MODE_V23VIDEOTEX:
+        return "Videotex";
+    case V18_MODE_V21TEXTPHONE:
+        return "V.21";
+    case V18_MODE_V18TEXTPHONE:
+        return "V.18 text telephone";
+    }
+    return "???";
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

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