Prosody application note: implementing V.18 compatibility

The V.18 standard gives specifications for the implementation of text telephones, more commonly called TDDs (Telecommunications Devices for the Deaf). The annexes each specify an alternative communications method. This is because the standards brings together existing practice from different regions, each of which developed different types of terminal. Here we consider the implementation of each of these. The initial probing sequence which would automatically detect which one to use is rather more complex and is not considered.

Allocate a full duplex channel, even if the mode you are using is half-duplex. This allows you to configure it once, rather than having to re-configure every time you switch between sending and receiving.

Annex A

This describes an FSK modem which uses a 5-bit code very similar to Baudot. To implement this on Prosody, configure the channel to use FSK as its protocol as follows:

speed50
mark_frequency1400
space_frequency1800
rx_carrier_on_mS1
rx_carrier_off_mS80

But note that in the USA a speed of 45.45 bits per second is typical. To specify this, use 45 as the speed.

In principle, the correct value for rx_carrier_on_mS is 150, but some existing devices send data with no carrier before the first data bit, so the receiver must react instantly to a signal to avoid missing the first character of each transmission.

Configure it to use Async as its encoding as follows:

databits5

Note that V.18 requires 150mS of carrier to be sent before any data every time the carrier is turned on. To achieve this, set a prefix '11111111' (eight bits of binary 1). This generates carrier for 160 mS. Similarly, a suffix of '111111111111111' (fifteen bits of binary 1) can be used to measure the required 300 mS after transmission in which the receiver is to be disabled.

When sending or receiving data, you will normally need to convert into and out of the 5-bit code. Some useful functions are provided in libutil/v18a.h and libutil/v18a.c which can do this for you. They are:

void v18astate(V18ASTATE *ts);
Prepare a V18ASTATE structure for use by the other functions.
void v18astate_dtor(V18ASTATE *ts);
Destroy a V18ASTATE structure which was previously prepared by v18astate().
char *v18a_fromT50(char *os, char *oe, V18ASTATE *ts, unsigned char **ibuf, unsigned char *ie);
Convert into the V.18 Annex A code from T.50 (or ASCII). The parameters are:
osThe address of the first character of the output buffer.
oeThe address of the first character after the end of the output buffer.
vsA pointer to a V18ASTATE structure that has been initialised with v18astate().
*ibufThe address of the first character of the output buffer. On return, this has been updated to indicate how much data has been successfully processed.
ieThe address of the first character after the end of the input buffer.
It returns the address of the first unused location in the output buffer. If there is an unconvertible character in the input, the conversion stops at it, and will not pass it. You can tell that the input starts with an unconvertible character because no input has been consumed. Note that it would be incorrect to check for output being generated, since some characters are specified to generate no output. Conversion proceeds character by character until the input buffer has no more data, the next character in the input buffer is not convertible, or there is insufficient space in the output buffer.
char *v18a_toT50(char *os, char *oe, V18ASTATE *ts, unsigned char **ibuf, unsigned char *ie);
Convert from the V.18 Annex A code into T.50. The parameters are the same as for v18a_fromT50().
void v18a_show(void (*ofn)(void *p, char oc), void *p, V18ASTATE *ts, unsigned char ic);
Convert data from the code in V.18 Annex A into a form suitable for debugging. This is how the test program, gdcrx prints V.18 data. The function, ofn is invoked to output each character, with the parameter p being passed in each time, along with the character to be printed. The character ic is the next in the sequence of V.18 Annex A characters. Since this is used for debugging, every invocation generates some output, showing what character was processed. In addition, the characters LTRS and FIGS modify the state to indicate the new mode, so that future characters are interpreted correctly.

The V.18 (02/1998) appears to have a few errors which are handled as follows:

Obviously, since the source code is provided, if you want some variation based on the V.18 specification you can use the source code provided as a starting point for modifications.

Annex B

Since this uses ordinary DTMF tones, you can use the speech API functions sm_listen_for() and sm_play_digits() to receive and transmit the data. You will also need to translate characters into and out of the sequences of tones used.

Annex C

This uses standard V.21 FSK modulation (half-duplex, using only channel 1), see the FSK rx and FSK tx documentation for configuration details, but note that the speed used is only 110 bits per second, even though V.21 is capable of 300 bits per second.

You will also need to add even parity bits to the data being sent. This is trivially done with a small array which maps a character into the same character with the correct parity bit. Here is an example:

static char even_parity[256];

void init_parity(void)
{
 unsigned i;
 even_parity[0] = 0;
 for (i=0; i<8; i++) {		// for each bit
	unsigned m = 1 << i;
	unsigned j;
	for (j=0; j < m; j++) {
		even_parity[j + m] = even_parity[j] ^ 0x80 ^ m;
	}
 }
}

After initialisation, even_parity[x] is the character x with bit 7 (the parity bit) set to even parity.

Annex D

This uses standard Bell 103 FSK modulation, so see the FSK rx and FSK tx documentation for configuration details.

Annex E

This uses standard V.23 FSK modulation, so see the FSK rx and FSK tx documentation for configuration details.

Annex F

This uses standard V.21 FSK modulation, see the FSK rx and FSK tx documentation for configuration details.

Annex G

This is essentially the same as for Annex F.


Document reference: AN 1399