freeswitch/libs/spandsp/src/v18.c

1236 lines
33 KiB
C

/*
* 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-2015 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.
*/
/*! \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
#if defined(HAVE_STDBOOL_H)
#include <stdbool.h>
#else
#include "spandsp/stdbool.h"
#endif
#include "floating_fudge.h"
#include "spandsp/telephony.h"
#include "spandsp/alloc.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/power_meter.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>
/*
Ways in which a V.18 call may start
-----------------------------------
Originate:
ANS
Silence for 0.5s then send TXP
DTMF
Proceed as Annex B
1650Hz (V21 ch 2 low) [1650Hz +-12Hz]
Proceed as Annex F in call mode
1300Hz (Calling tone) [1300Hz +-16Hz]
Proceed as Annex E in call mode
1400Hz/1800Hz (Weitbrecht) [1400Hz +-5% and 1800Hz +-5%]
Detect rate and proceed as Annex A
980Hz/1180Hz (V21 ch 1) [980Hz +-12Hz, 1180Hz +-12Hz]
Start timer Tr
2225Hz (Bell ANS)
Proceed as Annex D call mode
1270Hz (Bell103 ch 2 high)
Proceed as Annex D answer mode
390Hz (V23 ch 2 low)
Proceed as Annex E answer mode
Answer:
ANS
Monitor as caller for 980Hz or 1300Hz
CI/XCI
Respond with ANSam
1300Hz [1300Hz +-16Hz]
Probe
Timer Ta (3s)
Probe
1400Hz/1800Hz (Weitbrecht) [1400Hz +-5% and 1800Hz +-5%]
Detect rate and proceed as Annex A
DTMF
Proceed as Annex B
980Hz (V21 ch 1 low) [980Hz +-12Hz]
Start timer Te
1270Hz (Bell103 ch 2 high)
Proceed as Annex D answer mode
2225Hz (Bell ANS)
Proceed as Annex D call mode
1650Hz (V21 ch 2 low) [1650Hz +-12Hz]
Proceed as Annex F answer mode
ANSam
Proceed as V.8 caller Annex G
*/
/*! 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[] =
{
{"###0", '!'},
{"###1", 'C'},
{"###2", 'F'},
{"###3", 'I'},
{"###4", 'L'},
{"###5", 'O'},
{"###6", 'R'},
{"###7", 'U'},
{"###8", 'X'},
{"###9", ';'},
{"##*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) || (defined(__SVR4) && defined (__sun))
{"#*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", 0xE6}, // (Note 1) 111 1011
{"#*2", 0xF8}, // (Note 1) 111 1100
{"#*3", 0xE5}, // (Note 1) 111 1101
{"#*4", 0xC6}, // (Note 1) 101 1011
{"#*5", 0xD8}, // (Note 1) 101 1100
{"#*6", 0xC5}, // (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", /* & >> + */
"", /* 0x92 */
"**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", /* 0xC6 (National code) */
"#*5", /* 0xD8 (National code) */
"#*6", /* 0xC5 (National code) */
"", /* ^ */
"0", /* _ >> SPACE */
"", /* 0x92 */
"*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", /* 0xE6 (National code) */
"#*2", /* 0xF8 (National code) */
"#*3", /* 0xE5 (National code) */
"0", /* ~ >> SPACE */
"*0" /* DEL >> BACK SPACE */
};
#if 0
static const uint8_t txp[] = "1111111111000101011100001101110000010101";
/* XCI is:
400 ms mark;
XCI marker;
800 ms mark;
XCI marker;
800 ms mark;
XCI marker;
800 ms mark;
XCI marker;
100 ms mark. */
static const uint8_t xci[] = "01111111110111111111";
/* The entries here must match the order in which the related names are defined in v18.h */
static const int automoding_sequences[][6] =
{
{
/* Dummy entry 0 */
V18_MODE_5BIT_4545,
V18_MODE_BELL103,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_EDT,
V18_MODE_DTMF
},
{
/* Australia */
V18_MODE_5BIT_50,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_EDT,
V18_MODE_DTMF,
V18_MODE_BELL103
},
{
/* Ireland */
V18_MODE_5BIT_50,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_EDT,
V18_MODE_DTMF,
V18_MODE_BELL103
},
{
/* Germany */
V18_MODE_EDT,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_5BIT_50,
V18_MODE_DTMF,
V18_MODE_BELL103
},
{
/* Switzerland */
V18_MODE_EDT,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_5BIT_50,
V18_MODE_DTMF,
V18_MODE_BELL103
},
{
/* Italy */
V18_MODE_EDT,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_5BIT_50,
V18_MODE_DTMF,
V18_MODE_BELL103
},
{
/* Spain */
V18_MODE_EDT,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_5BIT_50,
V18_MODE_DTMF,
V18_MODE_BELL103
},
{
/* Austria */
V18_MODE_EDT,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_5BIT_50,
V18_MODE_DTMF,
V18_MODE_BELL103
},
{
/* Netherlands */
V18_MODE_DTMF,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_5BIT_50,
V18_MODE_EDT,
V18_MODE_BELL103
},
{
/* Iceland */
V18_MODE_V21TEXTPHONE,
V18_MODE_DTMF,
V18_MODE_5BIT_50,
V18_MODE_EDT,
V18_MODE_V23VIDEOTEX,
V18_MODE_BELL103
},
{
/* Norway */
V18_MODE_V21TEXTPHONE,
V18_MODE_DTMF,
V18_MODE_5BIT_50,
V18_MODE_EDT,
V18_MODE_V23VIDEOTEX,
V18_MODE_BELL103
},
{
/* Sweden */
V18_MODE_V21TEXTPHONE,
V18_MODE_DTMF,
V18_MODE_5BIT_50,
V18_MODE_EDT,
V18_MODE_V23VIDEOTEX,
V18_MODE_BELL103
},
{
/* Finland */
V18_MODE_V21TEXTPHONE,
V18_MODE_DTMF,
V18_MODE_5BIT_50,
V18_MODE_EDT,
V18_MODE_V23VIDEOTEX,
V18_MODE_BELL103
},
{
/* Denmark */
V18_MODE_V21TEXTPHONE,
V18_MODE_DTMF,
V18_MODE_5BIT_50,
V18_MODE_EDT,
V18_MODE_V23VIDEOTEX,
V18_MODE_BELL103
},
{
/* UK */
V18_MODE_V21TEXTPHONE,
V18_MODE_5BIT_50,
V18_MODE_V23VIDEOTEX,
V18_MODE_EDT,
V18_MODE_DTMF,
V18_MODE_BELL103
},
{
/* USA */
V18_MODE_5BIT_4545,
V18_MODE_BELL103,
V18_MODE_V21TEXTPHONE,
V18_MODE_V23VIDEOTEX,
V18_MODE_EDT,
V18_MODE_DTMF
},
{
/* France */
V18_MODE_V23VIDEOTEX,
V18_MODE_EDT,
V18_MODE_DTMF,
V18_MODE_5BIT_50,
V18_MODE_V21TEXTPHONE,
V18_MODE_BELL103
},
{
/* Belgium */
V18_MODE_V23VIDEOTEX,
V18_MODE_EDT,
V18_MODE_DTMF,
V18_MODE_5BIT_50,
V18_MODE_V21TEXTPHONE,
V18_MODE_BELL103
}
};
#endif
static uint16_t 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 */
0x40, /* 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;
ch = conv[ch & 0x7F];
/* 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->repeat_shifts && s->baudot_tx_shift == 1)
return ch & 0x1F;
s->baudot_tx_shift = 1;
shift = BAUDOT_FIGURE_SHIFT;
}
else
{
if (!s->repeat_shifts && 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 --------------------------------------------------------*/
static uint8_t 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 0xFF if we did not produce a character */
return 0xFF;
}
/*- End of function --------------------------------------------------------*/
static int v18_tdd_get_async_byte(void *user_data)
{
v18_state_t *s;
int ch;
unsigned int x;
s = (v18_state_t *) user_data;
if (s->next_byte != 0xFF)
{
s->rx_suppression = (300*SAMPLE_RATE)/1000;
x = s->next_byte;
s->next_byte = (uint8_t) 0xFF;
return x;
}
for (;;)
{
if ((ch = queue_read_byte(&s->queue.queue)) < 0)
{
if (s->tx_signal_on)
{
/* The FSK should now be switched off. */
s->tx_signal_on = 0;
}
async_tx_presend_bits(&s->async_tx, 42);
return SIG_STATUS_LINK_IDLE;
}
if ((x = encode_baudot(s, ch)) != 0)
break;
}
s->rx_suppression = (300*SAMPLE_RATE)/1000;
if (s->tx_signal_on == 1)
{
async_tx_presend_bits(&s->async_tx, 7);
s->tx_signal_on = 2;
}
if ((x & 0x3E0))
{
s->next_byte = (uint8_t) (x & 0x1F);
return (uint8_t) ((x >> 5) & 0x1F);
}
s->next_byte = (uint8_t) 0xFF;
return (uint8_t) (x & 0x1F);
}
/*- End of function --------------------------------------------------------*/
static void v18_dtmf_get(void *user_data)
{
v18_state_t *s;
int ch;
int len;
const char *t;
s = (v18_state_t *) user_data;
if ((ch = queue_read_byte(&s->queue.queue)) >= 0)
{
t = ascii_to_dtmf[ch & 0x7F];
len = strlen(t);
dtmf_tx_put(&s->dtmf_tx, t, len);
s->rx_suppression = ((300 + 100*len)*SAMPLE_RATE)/1000;
}
}
/*- End of function --------------------------------------------------------*/
static int v18_edt_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)
{
s->rx_suppression = (300*SAMPLE_RATE)/1000;
return ch;
}
if (s->tx_signal_on)
{
/* The FSK should now be switched off. */
s->tx_signal_on = 0;
}
return 0;
}
/*- 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;
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';
if (s->put_msg)
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 (s->rx_suppression > 0)
return;
span_log(&s->logging, SPAN_LOG_FLOW, "Rx byte %x\n", byte);
if ((octet = decode_baudot(s, byte)) != 0xFF)
{
s->rx_msg[s->rx_msg_len++] = octet;
span_log(&s->logging, SPAN_LOG_FLOW, "Rx byte 0x%x '%c'\n", octet, octet);
}
if (s->rx_msg_len > 0) //= 256)
{
s->rx_msg[s->rx_msg_len] = '\0';
if (s->put_msg)
s->put_msg(s->user_data, s->rx_msg, s->rx_msg_len);
s->rx_msg_len = 0;
}
}
/*- End of function --------------------------------------------------------*/
static int decode_dtmf_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));
}
/*- End of function --------------------------------------------------------*/
static int decode_dtmf(v18_state_t *s, char msg[], const char dtmf[])
{
int entries;
int len;
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]), decode_dtmf_cmp);
if (ss)
{
len = strlen(ss->dtmf);
*u++ = ss->ascii;
return len;
}
/* 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 --------------------------------------------------------*/
static void v18_dtmf_put(void *user_data, const char dtmf[], int len)
{
v18_state_t *s;
char buf[128];
int i;
int matched;
s = (v18_state_t *) user_data;
if (s->rx_suppression > 0)
return;
for (i = 0; i < len; i++)
{
s->rx_msg[s->rx_msg_len++] = dtmf[i];
if (dtmf[i] >= '0' && dtmf[i] <= '9')
{
s->rx_msg[s->rx_msg_len] = '\0';
if ((matched = decode_dtmf(s, buf, (const char *) s->rx_msg)) > 0)
{
buf[1] = '\0';
s->put_msg(s->user_data, (const uint8_t *) buf, 1);
}
if (s->rx_msg_len > matched)
memcpy(&s->rx_msg[0], &s->rx_msg[matched], s->rx_msg_len - matched);
s->rx_msg_len -= matched;
}
}
s->in_progress = 5*SAMPLE_RATE;
}
/*- End of function --------------------------------------------------------*/
static void v18_edt_put_async_byte(void *user_data, int byte)
{
v18_state_t *s;
s = (v18_state_t *) user_data;
if (s->rx_suppression > 0)
return;
}
/*- End of function --------------------------------------------------------*/
static void v18_bell103_put_async_byte(void *user_data, int byte)
{
v18_state_t *s;
s = (v18_state_t *) user_data;
if (s->rx_suppression > 0)
return;
}
/*- End of function --------------------------------------------------------*/
static void v18_videotex_put_async_byte(void *user_data, int byte)
{
v18_state_t *s;
s = (v18_state_t *) user_data;
if (s->rx_suppression > 0)
return;
}
/*- End of function --------------------------------------------------------*/
static void v18_textphone_put_async_byte(void *user_data, int byte)
{
v18_state_t *s;
s = (v18_state_t *) user_data;
if (s->rx_suppression > 0)
return;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(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->dtmf_tx, amp, max_len - len);
break;
default:
if (len < max_len)
{
if ((lenx = fsk_tx(&s->fsk_tx, amp + len, max_len - len)) <= 0)
s->tx_signal_on = 0;
len += lenx;
}
break;
}
}
return len;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v18_rx(v18_state_t *s, const int16_t amp[], int len)
{
if (s->rx_suppression > 0)
{
if (s->rx_suppression > len)
s->rx_suppression -= len;
else
s->rx_suppression = 0;
}
if ((s->mode & V18_MODE_DTMF))
{
/* Apply a message timeout. */
if (s->in_progress)
{
s->in_progress -= len;
if (s->in_progress <= 0)
{
s->in_progress = 0;
s->rx_msg_len = 0;
}
}
dtmf_rx(&s->dtmf_rx, amp, len);
}
if ((s->mode & (V18_MODE_5BIT_4545 | V18_MODE_5BIT_476 | V18_MODE_5BIT_50)))
{
fsk_rx(&s->fsk_rx, amp, len);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v18_rx_fillin(v18_state_t *s, int len)
{
if (s->rx_suppression > 0)
{
if (s->rx_suppression > len)
s->rx_suppression -= len;
else
s->rx_suppression = 0;
}
if ((s->mode & V18_MODE_DTMF))
{
/* Apply a message timeout. */
if (s->in_progress)
{
s->in_progress -= len;
if (s->in_progress <= 0)
{
s->in_progress = 0;
s->rx_msg_len = 0;
}
}
dtmf_rx_fillin(&s->dtmf_rx, len);
}
if ((s->mode & (V18_MODE_5BIT_4545 | V18_MODE_5BIT_476 | V18_MODE_5BIT_50)))
{
fsk_rx_fillin(&s->fsk_rx, len);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v18_put(v18_state_t *s, const char msg[], int len)
{
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;
}
/* TODO: Deal with out of space condition */
if ((i = queue_write(&s->queue.queue, (const uint8_t *) msg, len)) < 0)
return i;
s->tx_signal_on = 1;
return i;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(const char *) v18_mode_to_str(int mode)
{
switch ((mode & 0xFFF))
{
case V18_MODE_NONE:
return "None";
case V18_MODE_5BIT_4545:
return "Weitbrecht TDD (45.45bps)";
case V18_MODE_5BIT_476:
return "Weitbrecht TDD (47.6bps)";
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 --------------------------------------------------------*/
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,
bool calling_party,
int mode,
int nation,
put_msg_func_t put_msg,
void *user_data)
{
if (nation < 0 || nation >= V18_AUTOMODING_END)
return NULL;
if (s == NULL)
{
if ((s = (v18_state_t *) span_alloc(sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
s->calling_party = calling_party;
s->mode = mode & ~V18_MODE_REPETITIVE_SHIFTS_OPTION;
s->put_msg = put_msg;
s->user_data = user_data;
switch (s->mode)
{
case V18_MODE_5BIT_4545:
s->repeat_shifts = mode & V18_MODE_REPETITIVE_SHIFTS_OPTION;
fsk_tx_init(&s->fsk_tx, &preset_fsk_specs[FSK_WEITBRECHT_4545], async_tx_get_bit, &s->async_tx);
async_tx_init(&s->async_tx, 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->fsk_rx, &preset_fsk_specs[FSK_WEITBRECHT_4545], FSK_FRAME_MODE_5N1_FRAMES, v18_tdd_put_async_byte, s);
s->baudot_rx_shift = 0;
s->next_byte = (uint8_t) 0xFF;
break;
case V18_MODE_5BIT_476:
s->repeat_shifts = mode & V18_MODE_REPETITIVE_SHIFTS_OPTION;
fsk_tx_init(&s->fsk_tx, &preset_fsk_specs[FSK_WEITBRECHT_476], async_tx_get_bit, &s->async_tx);
async_tx_init(&s->async_tx, 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->fsk_rx, &preset_fsk_specs[FSK_WEITBRECHT_476], FSK_FRAME_MODE_5N1_FRAMES, v18_tdd_put_async_byte, s);
s->baudot_rx_shift = 0;
s->next_byte = (uint8_t) 0xFF;
break;
case V18_MODE_5BIT_50:
s->repeat_shifts = mode & V18_MODE_REPETITIVE_SHIFTS_OPTION;
fsk_tx_init(&s->fsk_tx, &preset_fsk_specs[FSK_WEITBRECHT_50], async_tx_get_bit, &s->async_tx);
async_tx_init(&s->async_tx, 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->fsk_rx, &preset_fsk_specs[FSK_WEITBRECHT_50], FSK_FRAME_MODE_5N1_FRAMES, v18_tdd_put_async_byte, s);
s->baudot_rx_shift = 0;
s->next_byte = (uint8_t) 0xFF;
break;
case V18_MODE_DTMF:
dtmf_tx_init(&s->dtmf_tx, v18_dtmf_get, s);
dtmf_rx_init(&s->dtmf_rx, v18_dtmf_put, s);
break;
case V18_MODE_EDT:
fsk_tx_init(&s->fsk_tx, &preset_fsk_specs[FSK_V21CH1_110], async_tx_get_bit, &s->async_tx);
async_tx_init(&s->async_tx, 7, ASYNC_PARITY_EVEN, 2, false, v18_edt_get_async_byte, s);
fsk_rx_init(&s->fsk_rx, &preset_fsk_specs[FSK_V21CH1_110], FSK_FRAME_MODE_7E2_FRAMES, v18_edt_put_async_byte, s);
break;
case V18_MODE_BELL103:
fsk_tx_init(&s->fsk_tx, &preset_fsk_specs[FSK_BELL103CH1], async_tx_get_bit, &s->async_tx);
async_tx_init(&s->async_tx, 7, ASYNC_PARITY_EVEN, 1, false, v18_edt_get_async_byte, s);
fsk_rx_init(&s->fsk_rx, &preset_fsk_specs[FSK_BELL103CH2], FSK_FRAME_MODE_7E1_FRAMES, v18_bell103_put_async_byte, s);
break;
case V18_MODE_V23VIDEOTEX:
fsk_tx_init(&s->fsk_tx, &preset_fsk_specs[FSK_V23CH1], async_tx_get_bit, &s->async_tx);
async_tx_init(&s->async_tx, 7, ASYNC_PARITY_EVEN, 1, false, v18_edt_get_async_byte, s);
fsk_rx_init(&s->fsk_rx, &preset_fsk_specs[FSK_V23CH2], FSK_FRAME_MODE_7E1_FRAMES, v18_videotex_put_async_byte, s);
break;
case V18_MODE_V21TEXTPHONE:
fsk_tx_init(&s->fsk_tx, &preset_fsk_specs[FSK_V21CH1], async_tx_get_bit, &s->async_tx);
async_tx_init(&s->async_tx, 7, ASYNC_PARITY_EVEN, 1, false, v18_edt_get_async_byte, s);
fsk_rx_init(&s->fsk_rx, &preset_fsk_specs[FSK_V21CH1], FSK_FRAME_MODE_7E1_FRAMES, v18_textphone_put_async_byte, s);
break;
case V18_MODE_V18TEXTPHONE:
fsk_tx_init(&s->fsk_tx, &preset_fsk_specs[FSK_V21CH1], async_tx_get_bit, &s->async_tx);
async_tx_init(&s->async_tx, 7, ASYNC_PARITY_EVEN, 1, false, v18_edt_get_async_byte, s);
fsk_rx_init(&s->fsk_rx, &preset_fsk_specs[FSK_V21CH1], FSK_FRAME_MODE_7E1_FRAMES, v18_textphone_put_async_byte, s);
break;
}
s->nation = nation;
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)
{
queue_release(&s->queue.queue);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v18_free(v18_state_t *s)
{
queue_release(&s->queue.queue);
span_free(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/