minimodem -T rtty (baudot RTTY transmit)
This commit is contained in:
parent
286879cfb3
commit
2a7f8214df
209
src/baudot.c
209
src/baudot.c
|
@ -9,10 +9,19 @@
|
||||||
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
//#define BAUDOT_DEBUG
|
||||||
|
#ifdef BAUDOT_DEBUG
|
||||||
|
# define debug_log(format, args...) fprintf(stderr, format, ## args)
|
||||||
|
#else
|
||||||
|
# define debug_log(format, args...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static char
|
static char
|
||||||
baudot_table[32][3] = {
|
baudot_decode_table[32][3] = {
|
||||||
// letter, U.S. figs, CCITT No.2 figs (Europe)
|
// letter, U.S. figs, CCITT No.2 figs (Europe)
|
||||||
{ '*', '*', '*' }, // NUL
|
{ '*', '*', '*' }, // NUL
|
||||||
{ 'E', '3', '3' },
|
{ 'E', '3', '3' },
|
||||||
|
@ -51,18 +60,137 @@ baudot_table[32][3] = {
|
||||||
{ '*', '*', '*' }, // LTRS
|
{ '*', '*', '*' }, // LTRS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static char
|
||||||
|
baudot_encode_table[0x60][2] = {
|
||||||
|
// index: ascii char; values: bits, ltrs_or_figs_or_neither_or_both
|
||||||
|
|
||||||
|
/* 0x00 */
|
||||||
|
/* NUL */ { 0x00, 3 }, // NUL
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* BEL */ { 0x05, 2 }, // BELL (or CCITT2 apostrophe)
|
||||||
|
/* BS */ { 0, 0 }, // non-encodable (FIXME???)
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* LF */ { 0x02, 3 }, // LF
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* 0xD */ { 0x08, 3 }, // CR
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
|
||||||
|
/* 0x10 */
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
/* xxx */ { 0, 0 }, // non-encodable
|
||||||
|
|
||||||
|
/* 0x20 */
|
||||||
|
/* */ { 0x04, 3 }, // SPACE
|
||||||
|
/* ! */ { 0x0d, 2 }, //
|
||||||
|
/* " */ { 0x11, 2 }, //
|
||||||
|
/* # */ { 0x14, 2 }, // '#' (or CCITT2 British pounds symbol)
|
||||||
|
/* $ */ { 0x09, 2 }, // '$' (or CCITT2 ENQ)
|
||||||
|
/* % */ { 0, 0 }, // non-encodable
|
||||||
|
/* & */ { 0x1a, 2 }, //
|
||||||
|
/* ' */ { 0x0b, 2 }, // apostrophe (or CCITT2 BELL)
|
||||||
|
/* ( */ { 0x0f, 2 }, //
|
||||||
|
/* ) */ { 0x12, 2 }, //
|
||||||
|
/* * */ { 0, 0 }, // non-encodable
|
||||||
|
/* + */ { 0x12, 2 }, //
|
||||||
|
/* , */ { 0x0c, 2 }, //
|
||||||
|
/* - */ { 0x03, 2 }, //
|
||||||
|
/* . */ { 0x1c, 2 }, //
|
||||||
|
/* / */ { 0x1d, 2 }, //
|
||||||
|
|
||||||
|
/* 0x30 */
|
||||||
|
/* 0 */ { 0x16, 2 }, //
|
||||||
|
/* 1 */ { 0x17, 2 }, //
|
||||||
|
/* 2 */ { 0x13, 2 }, //
|
||||||
|
/* 3 */ { 0x01, 2 }, //
|
||||||
|
/* 4 */ { 0x0a, 2 }, //
|
||||||
|
/* 5 */ { 0x10, 2 }, //
|
||||||
|
/* 6 */ { 0x15, 2 }, //
|
||||||
|
/* 7 */ { 0x07, 2 }, //
|
||||||
|
/* 8 */ { 0x06, 2 }, //
|
||||||
|
/* 9 */ { 0x18, 2 }, //
|
||||||
|
/* : */ { 0x0e, 2 }, //
|
||||||
|
/* ; */ { 0x1e, 2 }, //
|
||||||
|
/* < */ { 0, 0 }, // non-encodable
|
||||||
|
/* = */ { 0, 0 }, // non-encodable
|
||||||
|
/* > */ { 0, 0 }, // non-encodable
|
||||||
|
/* ? */ { 0x19, 2 }, //
|
||||||
|
|
||||||
|
/* 0x40 */
|
||||||
|
/* @ */ { 0, 0 }, // non-encodable
|
||||||
|
/* A */ { 0x03, 1 }, //
|
||||||
|
/* B */ { 0x19, 1 }, //
|
||||||
|
/* C */ { 0x0e, 1 }, //
|
||||||
|
/* D */ { 0x09, 1 }, //
|
||||||
|
/* E */ { 0x01, 1 }, //
|
||||||
|
/* F */ { 0x0d, 1 }, //
|
||||||
|
/* G */ { 0x1a, 1 }, //
|
||||||
|
/* H */ { 0x14, 1 }, //
|
||||||
|
/* I */ { 0x06, 1 }, //
|
||||||
|
/* J */ { 0x0b, 1 }, //
|
||||||
|
/* K */ { 0x0f, 1 }, //
|
||||||
|
/* L */ { 0x12, 1 }, //
|
||||||
|
/* M */ { 0x1c, 1 }, //
|
||||||
|
/* N */ { 0x0c, 1 }, //
|
||||||
|
/* O */ { 0x18, 1 }, //
|
||||||
|
|
||||||
|
/* 0x50 */
|
||||||
|
/* P */ { 0x16, 1 }, //
|
||||||
|
/* Q */ { 0x17, 1 }, //
|
||||||
|
/* R */ { 0x0a, 1 }, //
|
||||||
|
/* S */ { 0x05, 1 }, //
|
||||||
|
/* T */ { 0x10, 1 }, //
|
||||||
|
/* U */ { 0x07, 1 }, //
|
||||||
|
/* V */ { 0x1e, 1 }, //
|
||||||
|
/* W */ { 0x13, 1 }, //
|
||||||
|
/* X */ { 0x1d, 1 }, //
|
||||||
|
/* Y */ { 0x15, 1 }, //
|
||||||
|
/* Z */ { 0x11, 1 }, //
|
||||||
|
/* [ */ { 0, 0 }, // non-encodable
|
||||||
|
/* \\ */ { 0, 0 }, // non-encodable
|
||||||
|
/* ] */ { 0, 0 }, // non-encodable
|
||||||
|
/* ^ */ { 0, 0 }, // non-encodable
|
||||||
|
/* _ */ { 0, 0 }, // non-encodable
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
#define BAUDOT_LTRS 0x1F
|
#define BAUDOT_LTRS 0x1F
|
||||||
#define BAUDOT_FIGS 0x1B
|
#define BAUDOT_FIGS 0x1B
|
||||||
#define BAUDOT_SPACE 0x04
|
#define BAUDOT_SPACE 0x04
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0 unknown state
|
||||||
|
* 1 LTRS state
|
||||||
|
* 2 FIGS state
|
||||||
|
*/
|
||||||
static int baudot_charset = 0; // FIXME
|
static int baudot_charset = 0; // FIXME
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
baudot_reset()
|
baudot_reset()
|
||||||
{
|
{
|
||||||
baudot_charset = 0;
|
baudot_charset = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,16 +207,83 @@ baudot_decode( char *char_outp, unsigned char databits )
|
||||||
|
|
||||||
int stuff_char = 1;
|
int stuff_char = 1;
|
||||||
if ( databits == BAUDOT_FIGS ) {
|
if ( databits == BAUDOT_FIGS ) {
|
||||||
baudot_charset = 1;
|
baudot_charset = 2;
|
||||||
stuff_char = 0;
|
stuff_char = 0;
|
||||||
} else if ( databits == BAUDOT_LTRS ) {
|
} else if ( databits == BAUDOT_LTRS ) {
|
||||||
baudot_charset = 0;
|
baudot_charset = 1;
|
||||||
stuff_char = 0;
|
stuff_char = 0;
|
||||||
} else if ( databits == BAUDOT_SPACE ) {
|
} else if ( databits == BAUDOT_SPACE ) { /* RX un-shift on space */
|
||||||
baudot_charset = 0;
|
baudot_charset = 1;
|
||||||
}
|
}
|
||||||
if ( stuff_char )
|
if ( stuff_char )
|
||||||
*char_outp = baudot_table[databits][baudot_charset];
|
*char_outp = baudot_decode_table[databits][baudot_charset-1];
|
||||||
return stuff_char;
|
return stuff_char;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
baudot_skip_warning( char char_out )
|
||||||
|
{
|
||||||
|
unsigned char byte = char_out;
|
||||||
|
fprintf(stderr, "W: baudot skipping non-encodable character '%c' 0x%02x\n",
|
||||||
|
char_out, byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of 5-bit data words stuffed into *databits_outp (1 or 2)
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
baudot_encode( unsigned int *databits_outp, char char_out )
|
||||||
|
{
|
||||||
|
|
||||||
|
char_out = toupper(char_out);
|
||||||
|
if( char_out >= 0x60 || char_out < 0 ) {
|
||||||
|
baudot_skip_warning(char_out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char ind = char_out;
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
unsigned char charset_mask = baudot_encode_table[ind][1];
|
||||||
|
|
||||||
|
debug_log("I: (baudot_charset==%u) input character '%c' 0x%02x charset_mask=%u\n", baudot_charset, char_out, char_out, charset_mask);
|
||||||
|
|
||||||
|
if ( (baudot_charset & charset_mask ) == 0 ) {
|
||||||
|
if ( charset_mask == 0 ) {
|
||||||
|
baudot_skip_warning(char_out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( baudot_charset == 0 )
|
||||||
|
baudot_charset = 1;
|
||||||
|
|
||||||
|
if ( charset_mask != 3 )
|
||||||
|
baudot_charset = charset_mask;
|
||||||
|
|
||||||
|
if ( baudot_charset == 1 )
|
||||||
|
databits_outp[n++] = BAUDOT_LTRS;
|
||||||
|
else if ( baudot_charset == 2 )
|
||||||
|
databits_outp[n++] = BAUDOT_FIGS;
|
||||||
|
else
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
debug_log("I: emit charset select 0x%02X\n", databits_outp[n-1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !( baudot_charset == 1 || baudot_charset == 2 ) ) {
|
||||||
|
fprintf(stderr, "E: baudot input character failed '%c' 0x%02x\n",
|
||||||
|
char_out, char_out);
|
||||||
|
fprintf(stderr, "E: baudot_charset==%u\n", baudot_charset);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
databits_outp[n++] = baudot_encode_table[ind][0];
|
||||||
|
|
||||||
|
/* TX un-shift on space */
|
||||||
|
if ( char_out == ' ' )
|
||||||
|
baudot_charset = 1;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
11
src/baudot.h
11
src/baudot.h
|
@ -11,9 +11,16 @@
|
||||||
void
|
void
|
||||||
baudot_reset();
|
baudot_reset();
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* returns nonzero if *char_outp was stuffed with an output character
|
* Returns 1 if *char_outp was stuffed with an output character
|
||||||
|
* or 0 if no output character was stuffed (in other words, returns
|
||||||
|
* the count of characters decoded and stuffed).
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
baudot_decode( char *char_outp, unsigned char databits );
|
baudot_decode( char *char_outp, unsigned char databits );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of 5-bit datawords stuffed into *databits_outp (1 or 2)
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
baudot_encode( unsigned int *databits_outp, char char_out );
|
||||||
|
|
|
@ -21,8 +21,17 @@
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ASCII 8-bit data framebits decoder (passthrough)
|
* ASCII 8-bit data framebits decoder/encoder (passthrough)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* returns the number of datawords stuffed into *databits_outp */
|
||||||
|
int
|
||||||
|
framebits_encode_ascii8( unsigned int *databits_outp, char char_out )
|
||||||
|
{
|
||||||
|
*databits_outp = char_out;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* returns nbytes decoded */
|
/* returns nbytes decoded */
|
||||||
static unsigned int
|
static unsigned int
|
||||||
framebits_decode_ascii8( char *dataout_p, unsigned int dataout_size,
|
framebits_decode_ascii8( char *dataout_p, unsigned int dataout_size,
|
||||||
|
@ -38,8 +47,11 @@ framebits_decode_ascii8( char *dataout_p, unsigned int dataout_size,
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Baudot 5-bit data framebits decoder
|
* Baudot 5-bit data framebits decoder/encoder
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define framebits_encode_baudot baudot_encode
|
||||||
|
|
||||||
/* returns nbytes decoded */
|
/* returns nbytes decoded */
|
||||||
static unsigned int
|
static unsigned int
|
||||||
framebits_decode_baudot( char *dataout_p, unsigned int dataout_size,
|
framebits_decode_baudot( char *dataout_p, unsigned int dataout_size,
|
||||||
|
@ -63,7 +75,8 @@ static void fsk_transmit_stdin(
|
||||||
float data_rate,
|
float data_rate,
|
||||||
float bfsk_mark_f,
|
float bfsk_mark_f,
|
||||||
float bfsk_space_f,
|
float bfsk_space_f,
|
||||||
int n_data_bits
|
int n_data_bits,
|
||||||
|
int (*framebits_encoder)( unsigned int *databits_outp, char char_out )
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
size_t sample_rate = simpleaudio_get_rate(sa_out);
|
size_t sample_rate = simpleaudio_get_rate(sa_out);
|
||||||
|
@ -73,14 +86,23 @@ static void fsk_transmit_stdin(
|
||||||
simpleaudio_tone(sa_out, bfsk_mark_f, sample_rate/2); // 0.5 sec leader
|
simpleaudio_tone(sa_out, bfsk_mark_f, sample_rate/2); // 0.5 sec leader
|
||||||
while ( (c = getchar()) != EOF )
|
while ( (c = getchar()) != EOF )
|
||||||
{
|
{
|
||||||
simpleaudio_tone(sa_out, bfsk_space_f, bit_nsamples); // start
|
|
||||||
int i;
|
// HACK - baudot
|
||||||
for ( i=0; i<n_data_bits; i++ ) { // data
|
unsigned int nwords;
|
||||||
unsigned int bit = ( c >> i ) & 1;
|
unsigned int bits[2];
|
||||||
float tone_freq = bit == 1 ? bfsk_mark_f : bfsk_space_f;
|
nwords = framebits_encoder(bits, c);
|
||||||
simpleaudio_tone(sa_out, tone_freq, bit_nsamples);
|
|
||||||
|
unsigned int j;
|
||||||
|
for ( j=0; j<nwords; j++ ) {
|
||||||
|
simpleaudio_tone(sa_out, bfsk_space_f, bit_nsamples); // start
|
||||||
|
int i;
|
||||||
|
for ( i=0; i<n_data_bits; i++ ) { // data
|
||||||
|
unsigned int bit = ( bits[j] >> i ) & 1;
|
||||||
|
float tone_freq = bit == 1 ? bfsk_mark_f : bfsk_space_f;
|
||||||
|
simpleaudio_tone(sa_out, tone_freq, bit_nsamples);
|
||||||
|
}
|
||||||
|
simpleaudio_tone(sa_out, bfsk_mark_f, bit_nsamples); // stop
|
||||||
}
|
}
|
||||||
simpleaudio_tone(sa_out, bfsk_mark_f, bit_nsamples); // stop
|
|
||||||
}
|
}
|
||||||
simpleaudio_tone(sa_out, bfsk_mark_f, sample_rate/2); // 0.5 sec tail
|
simpleaudio_tone(sa_out, bfsk_mark_f, sample_rate/2); // 0.5 sec tail
|
||||||
|
|
||||||
|
@ -113,6 +135,7 @@ main( int argc, char*argv[] )
|
||||||
|
|
||||||
float bfsk_data_rate;
|
float bfsk_data_rate;
|
||||||
int bfsk_n_data_bits;
|
int bfsk_n_data_bits;
|
||||||
|
int (*bfsk_framebits_encode)( unsigned int *databits_outp, char char_out );
|
||||||
|
|
||||||
unsigned int (*bfsk_framebits_decode)( char *dataout_p, unsigned int dataout_size,
|
unsigned int (*bfsk_framebits_decode)( char *dataout_p, unsigned int dataout_size,
|
||||||
unsigned int bits );
|
unsigned int bits );
|
||||||
|
@ -121,10 +144,12 @@ main( int argc, char*argv[] )
|
||||||
bfsk_data_rate = 45.45;
|
bfsk_data_rate = 45.45;
|
||||||
bfsk_n_data_bits = 5;
|
bfsk_n_data_bits = 5;
|
||||||
bfsk_framebits_decode = framebits_decode_baudot;
|
bfsk_framebits_decode = framebits_decode_baudot;
|
||||||
|
bfsk_framebits_encode = framebits_encode_baudot;
|
||||||
} else {
|
} else {
|
||||||
bfsk_data_rate = atof(argv[argi]);
|
bfsk_data_rate = atof(argv[argi]);
|
||||||
bfsk_n_data_bits = 8;
|
bfsk_n_data_bits = 8;
|
||||||
bfsk_framebits_decode = framebits_decode_ascii8;
|
bfsk_framebits_decode = framebits_decode_ascii8;
|
||||||
|
bfsk_framebits_encode = framebits_encode_ascii8;
|
||||||
}
|
}
|
||||||
argi++;
|
argi++;
|
||||||
|
|
||||||
|
@ -171,7 +196,8 @@ main( int argc, char*argv[] )
|
||||||
fsk_transmit_stdin(sa_out,
|
fsk_transmit_stdin(sa_out,
|
||||||
bfsk_data_rate,
|
bfsk_data_rate,
|
||||||
bfsk_mark_f, bfsk_space_f,
|
bfsk_mark_f, bfsk_space_f,
|
||||||
bfsk_n_data_bits
|
bfsk_n_data_bits,
|
||||||
|
bfsk_framebits_encode
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue