mirror of https://github.com/wb2osz/direwolf.git
Add IL2P.
This commit is contained in:
parent
17b9336ce0
commit
53e9ff7908
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
### New Features: ###
|
### New Features: ###
|
||||||
|
|
||||||
|
- Improved Layer 2 Protocol [(IL2P)](https://en.wikipedia.org/wiki/FX.25_Forward_Error_Correction). Use "-I 1" to enable transmit for first channel.
|
||||||
|
|
||||||
- Limited support for CM109/CM119 GPIO PTT on Windows.
|
- Limited support for CM109/CM119 GPIO PTT on Windows.
|
||||||
|
|
||||||
- Dire Wolf now advertises itself using DNS Service Discovery. This allows suitable APRS / Packet Radio applications to find a network KISS TNC without knowing the IP address or TCP port. Thanks to Hessu for providing this. Currently available only for Linux and Mac OSX. [Read all about it here.](https://github.com/hessu/aprs-specs/blob/master/TCP-KISS-DNS-SD.md)
|
- Dire Wolf now advertises itself using DNS Service Discovery. This allows suitable APRS / Packet Radio applications to find a network KISS TNC without knowing the IP address or TCP port. Thanks to Hessu for providing this. Currently available only for Linux and Mac OSX. [Read all about it here.](https://github.com/hessu/aprs-specs/blob/master/TCP-KISS-DNS-SD.md)
|
||||||
|
|
|
@ -13,7 +13,11 @@ Dire Wolf now includes [FX.25](https://en.wikipedia.org/wiki/FX.25_Forward_Error
|
||||||
|
|
||||||
![](fx25.png)
|
![](fx25.png)
|
||||||
|
|
||||||
Dire Wolf is a modern software replacement for the old 1980's style TNC built with special hardware.
|
Version 1.7 adds [IL2P](https://en.wikipedia.org/wiki/Improved_Layer_2_Protocol), a different method of FEC with less overhead.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Dire Wolf is a modern software replacement for the old 1980's style TNC built with special hardware. ###
|
||||||
|
|
||||||
Without any additional software, it can perform as:
|
Without any additional software, it can perform as:
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,15 @@ Divide audio sample by n for first channel.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "-X " "n"
|
.BI "-X " "n"
|
||||||
1 to enable FX.25 transmit.
|
1 to enable FX.25 transmit. 16, 32, 64 for specific number of check bytes.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "-I " "n"
|
||||||
|
Enable IL2P transmit. n=1 is recommended. 0 uses weaker FEC.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "-i " "n"
|
||||||
|
Enable IL2P transmit, inverted polarity. n=1 is recommended. 0 uses weaker FEC.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "-d " "x"
|
.BI "-d " "x"
|
||||||
|
|
|
@ -62,6 +62,18 @@ Force G3RUH modem regardless of data rate.
|
||||||
.BI "-J "
|
.BI "-J "
|
||||||
2400 bps QPSK compatible with MFJ-2400.
|
2400 bps QPSK compatible with MFJ-2400.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "-X " "n"
|
||||||
|
1 to enable FX.25 transmit. 16, 32, 64 for specific number of check bytes.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "-I " "n"
|
||||||
|
Enable IL2P transmit. n=1 is recommended. 0 uses weaker FEC.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "-i " "n"
|
||||||
|
Enable IL2P transmit, inverted polarity. n=1 is recommended. 0 uses weaker FEC.
|
||||||
|
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "-m " "n"
|
.BI "-m " "n"
|
||||||
|
|
|
@ -60,6 +60,13 @@ list(APPEND direwolf_SOURCES
|
||||||
hdlc_rec2.c
|
hdlc_rec2.c
|
||||||
hdlc_send.c
|
hdlc_send.c
|
||||||
igate.c
|
igate.c
|
||||||
|
il2p_codec.c
|
||||||
|
il2p_scramble.c
|
||||||
|
il2p_rec.c
|
||||||
|
il2p_payload.c
|
||||||
|
il2p_init.c
|
||||||
|
il2p_header.c
|
||||||
|
il2p_send.c
|
||||||
kiss_frame.c
|
kiss_frame.c
|
||||||
kiss.c
|
kiss.c
|
||||||
kissserial.c
|
kissserial.c
|
||||||
|
@ -289,12 +296,20 @@ target_link_libraries(log2gpx
|
||||||
list(APPEND gen_packets_SOURCES
|
list(APPEND gen_packets_SOURCES
|
||||||
gen_packets.c
|
gen_packets.c
|
||||||
ax25_pad.c
|
ax25_pad.c
|
||||||
|
ax25_pad2.c
|
||||||
fx25_encode.c
|
fx25_encode.c
|
||||||
|
fx25_extract.c
|
||||||
fx25_init.c
|
fx25_init.c
|
||||||
fx25_send.c
|
fx25_send.c
|
||||||
hdlc_send.c
|
hdlc_send.c
|
||||||
fcs_calc.c
|
fcs_calc.c
|
||||||
gen_tone.c
|
gen_tone.c
|
||||||
|
il2p_codec.c
|
||||||
|
il2p_scramble.c
|
||||||
|
il2p_payload.c
|
||||||
|
il2p_init.c
|
||||||
|
il2p_header.c
|
||||||
|
il2p_send.c
|
||||||
morse.c
|
morse.c
|
||||||
dtmf.c
|
dtmf.c
|
||||||
textcolor.c
|
textcolor.c
|
||||||
|
@ -321,14 +336,22 @@ list(APPEND atest_SOURCES
|
||||||
demod_9600.c
|
demod_9600.c
|
||||||
dsp.c
|
dsp.c
|
||||||
fx25_extract.c
|
fx25_extract.c
|
||||||
|
fx25_encode.c
|
||||||
fx25_init.c
|
fx25_init.c
|
||||||
fx25_rec.c
|
fx25_rec.c
|
||||||
hdlc_rec.c
|
hdlc_rec.c
|
||||||
hdlc_rec2.c
|
hdlc_rec2.c
|
||||||
|
il2p_codec.c
|
||||||
|
il2p_scramble.c
|
||||||
|
il2p_rec.c
|
||||||
|
il2p_payload.c
|
||||||
|
il2p_init.c
|
||||||
|
il2p_header.c
|
||||||
multi_modem.c
|
multi_modem.c
|
||||||
rrbb.c
|
rrbb.c
|
||||||
fcs_calc.c
|
fcs_calc.c
|
||||||
ax25_pad.c
|
ax25_pad.c
|
||||||
|
ax25_pad2.c
|
||||||
decode_aprs.c
|
decode_aprs.c
|
||||||
dwgpsnmea.c
|
dwgpsnmea.c
|
||||||
dwgps.c
|
dwgps.c
|
||||||
|
@ -421,6 +444,29 @@ if(WIN32 OR CYGWIN)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
# TNC interoperability testing for AX.25 connected mode.
|
||||||
|
# tnctest
|
||||||
|
list(APPEND tnctest_SOURCES
|
||||||
|
tnctest.c
|
||||||
|
textcolor.c
|
||||||
|
dtime_now.c
|
||||||
|
serial_port.c
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(tnctest
|
||||||
|
${tnctest_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(tnctest
|
||||||
|
${MISC_LIBRARIES}
|
||||||
|
Threads::Threads
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32 OR CYGWIN)
|
||||||
|
target_link_libraries(tnctest ws2_32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# List USB audio adapters than can use GPIO for PTT.
|
# List USB audio adapters than can use GPIO for PTT.
|
||||||
# Originally for Linux only (using udev).
|
# Originally for Linux only (using udev).
|
||||||
# Version 1.7 adds it for Windows. Needs hidapi library.
|
# Version 1.7 adds it for Windows. Needs hidapi library.
|
||||||
|
@ -520,6 +566,7 @@ install(TARGETS gen_packets DESTINATION ${INSTALL_BIN_DIR})
|
||||||
install(TARGETS atest DESTINATION ${INSTALL_BIN_DIR})
|
install(TARGETS atest DESTINATION ${INSTALL_BIN_DIR})
|
||||||
install(TARGETS ttcalc DESTINATION ${INSTALL_BIN_DIR})
|
install(TARGETS ttcalc DESTINATION ${INSTALL_BIN_DIR})
|
||||||
install(TARGETS kissutil DESTINATION ${INSTALL_BIN_DIR})
|
install(TARGETS kissutil DESTINATION ${INSTALL_BIN_DIR})
|
||||||
|
install(TARGETS tnctest DESTINATION ${INSTALL_BIN_DIR})
|
||||||
install(TARGETS appserver DESTINATION ${INSTALL_BIN_DIR})
|
install(TARGETS appserver DESTINATION ${INSTALL_BIN_DIR})
|
||||||
if(UDEV_FOUND OR WIN32 OR CYGWIN)
|
if(UDEV_FOUND OR WIN32 OR CYGWIN)
|
||||||
install(TARGETS cm108 DESTINATION ${INSTALL_BIN_DIR})
|
install(TARGETS cm108 DESTINATION ${INSTALL_BIN_DIR})
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019 John Langner, WB2OSZ
|
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019, 2021 John Langner, WB2OSZ
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -82,6 +82,7 @@
|
||||||
#include "ptt.h"
|
#include "ptt.h"
|
||||||
#include "dtime_now.h"
|
#include "dtime_now.h"
|
||||||
#include "fx25.h"
|
#include "fx25.h"
|
||||||
|
#include "il2p.h"
|
||||||
#include "hdlc_rec.h"
|
#include "hdlc_rec.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -189,6 +190,7 @@ static int h_opt = 0; // Hexadecimal display of received packet.
|
||||||
static char P_opt[16] = ""; // Demodulator profiles.
|
static char P_opt[16] = ""; // Demodulator profiles.
|
||||||
static int d_x_opt = 1; // FX.25 debug.
|
static int d_x_opt = 1; // FX.25 debug.
|
||||||
static int d_o_opt = 0; // "-d o" option for DCD output control. */
|
static int d_o_opt = 0; // "-d o" option for DCD output control. */
|
||||||
|
static int d_2_opt = 0; // "-d 2" option for IL2P details. */
|
||||||
static int dcd_count = 0;
|
static int dcd_count = 0;
|
||||||
static int dcd_missing_errors = 0;
|
static int dcd_missing_errors = 0;
|
||||||
|
|
||||||
|
@ -389,6 +391,7 @@ int main (int argc, char *argv[])
|
||||||
switch (*p) {
|
switch (*p) {
|
||||||
case 'x': d_x_opt++; break; // FX.25
|
case 'x': d_x_opt++; break; // FX.25
|
||||||
case 'o': d_o_opt++; break; // DCD output control
|
case 'o': d_o_opt++; break; // DCD output control
|
||||||
|
case '2': d_2_opt++; break; // IL2P debug out
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -539,6 +542,7 @@ int main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fx25_init (d_x_opt);
|
fx25_init (d_x_opt);
|
||||||
|
il2p_init (d_2_opt);
|
||||||
|
|
||||||
start_time = dtime_now();
|
start_time = dtime_now();
|
||||||
|
|
||||||
|
|
20
src/audio.h
20
src/audio.h
|
@ -107,10 +107,11 @@ struct audio_s {
|
||||||
float recv_ber; /* Receive Bit Error Rate (BER). */
|
float recv_ber; /* Receive Bit Error Rate (BER). */
|
||||||
/* Probability of inverting a bit coming out of the modem. */
|
/* Probability of inverting a bit coming out of the modem. */
|
||||||
|
|
||||||
int fx25_xmit_enable; /* Enable transmission of FX.25. */
|
//int fx25_xmit_enable; /* Enable transmission of FX.25. */
|
||||||
/* See fx25_init.c for explanation of values. */
|
/* See fx25_init.c for explanation of values. */
|
||||||
/* Initially this applies to all channels. */
|
/* Initially this applies to all channels. */
|
||||||
/* This should probably be per channel. One step at a time. */
|
/* This should probably be per channel. One step at a time. */
|
||||||
|
/* v1.7 - replaced by layer2_xmit==LAYER2_FX25 */
|
||||||
|
|
||||||
int fx25_auto_enable; /* Turn on FX.25 for current connected mode session */
|
int fx25_auto_enable; /* Turn on FX.25 for current connected mode session */
|
||||||
/* under poor conditions. */
|
/* under poor conditions. */
|
||||||
|
@ -156,6 +157,23 @@ struct audio_s {
|
||||||
/* Might try MFJ-2400 / CCITT v.26 / Bell 201 someday. */
|
/* Might try MFJ-2400 / CCITT v.26 / Bell 201 someday. */
|
||||||
/* No modem. Might want this for DTMF only channel. */
|
/* No modem. Might want this for DTMF only channel. */
|
||||||
|
|
||||||
|
enum layer2_t { LAYER2_AX25 = 0, LAYER2_FX25, LAYER2_IL2P } layer2_xmit;
|
||||||
|
|
||||||
|
// IL2P - New for version 1.7.
|
||||||
|
// New layer 2 with FEC. Much less overhead than FX.25 but no longer backward compatible.
|
||||||
|
// Only applies to transmit.
|
||||||
|
// Listening for FEC sync word should add negligible overhead so
|
||||||
|
// we leave reception enabled all the time as we do with FX.25.
|
||||||
|
// TODO: FX.25 should probably be put here rather than global for all channels.
|
||||||
|
|
||||||
|
int fx25_strength; // Strength of FX.25 FEC.
|
||||||
|
// 16, 23, 64 for specific number of parity symbols.
|
||||||
|
// 1 for automatic selection based on frame size.
|
||||||
|
|
||||||
|
int il2p_max_fec; // 1 for max FEC length, 0 for automatic based on size.
|
||||||
|
|
||||||
|
int il2p_invert_polarity; // 1 means invert on transmit. Receive handles either automatically.
|
||||||
|
|
||||||
enum v26_e { V26_UNSPECIFIED=0, V26_A, V26_B } v26_alternative;
|
enum v26_e { V26_UNSPECIFIED=0, V26_A, V26_B } v26_alternative;
|
||||||
|
|
||||||
// Original implementation used alternative A for 2400 bbps PSK.
|
// Original implementation used alternative A for 2400 bbps PSK.
|
||||||
|
|
|
@ -2751,6 +2751,7 @@ unsigned short ax25_m_m_crc (packet_t pp)
|
||||||
unsigned char fbuf[AX25_MAX_PACKET_LEN];
|
unsigned char fbuf[AX25_MAX_PACKET_LEN];
|
||||||
int flen;
|
int flen;
|
||||||
|
|
||||||
|
// TODO: I think this can be more efficient by getting the packet content pointer instead of copying.
|
||||||
flen = ax25_pack (pp, fbuf);
|
flen = ax25_pack (pp, fbuf);
|
||||||
|
|
||||||
crc = 0xffff;
|
crc = 0xffff;
|
||||||
|
@ -2803,7 +2804,8 @@ unsigned short ax25_m_m_crc (packet_t pp)
|
||||||
*
|
*
|
||||||
*------------------------------------------------------------------*/
|
*------------------------------------------------------------------*/
|
||||||
|
|
||||||
#define MAXSAFE 500
|
//#define MAXSAFE 500
|
||||||
|
#define MAXSAFE AX25_MAX_INFO_LEN
|
||||||
|
|
||||||
void ax25_safe_print (char *pstr, int len, int ascii_only)
|
void ax25_safe_print (char *pstr, int len, int ascii_only)
|
||||||
{
|
{
|
||||||
|
|
60
src/config.c
60
src/config.c
|
@ -585,7 +585,7 @@ static int check_via_path (char *via_path)
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
#define MAXCMDLEN 256
|
#define MAXCMDLEN 1200
|
||||||
|
|
||||||
|
|
||||||
static char *split (char *string, int rest_of_line)
|
static char *split (char *string, int rest_of_line)
|
||||||
|
@ -770,6 +770,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
p_audio_config->achan[channel].num_freq = 1;
|
p_audio_config->achan[channel].num_freq = 1;
|
||||||
p_audio_config->achan[channel].offset = 0;
|
p_audio_config->achan[channel].offset = 0;
|
||||||
|
|
||||||
|
p_audio_config->achan[channel].layer2_xmit = LAYER2_AX25;
|
||||||
|
p_audio_config->achan[channel].il2p_max_fec = 1;
|
||||||
|
p_audio_config->achan[channel].il2p_invert_polarity = 0;
|
||||||
|
|
||||||
p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
|
p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
|
||||||
p_audio_config->achan[channel].sanity_test = SANITY_APRS;
|
p_audio_config->achan[channel].sanity_test = SANITY_APRS;
|
||||||
p_audio_config->achan[channel].passall = 0;
|
p_audio_config->achan[channel].passall = 0;
|
||||||
|
@ -2252,7 +2256,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
* 0 = off, 1 = auto mode, others are suggestions for testing
|
* 0 = off, 1 = auto mode, others are suggestions for testing
|
||||||
* or special cases. 16, 32, 64 is number of parity bytes to add.
|
* or special cases. 16, 32, 64 is number of parity bytes to add.
|
||||||
* Also set by "-X n" command line option.
|
* Also set by "-X n" command line option.
|
||||||
* Current a global setting. Could be per channel someday.
|
* V1.7 changed from global to per-channel setting.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
else if (strcasecmp(t, "FX25TX") == 0) {
|
else if (strcasecmp(t, "FX25TX") == 0) {
|
||||||
|
@ -2265,13 +2269,15 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
}
|
}
|
||||||
n = atoi(t);
|
n = atoi(t);
|
||||||
if (n >= 0 && n < 200) {
|
if (n >= 0 && n < 200) {
|
||||||
p_audio_config->fx25_xmit_enable = n;
|
p_audio_config->achan[channel].fx25_strength = n;
|
||||||
|
p_audio_config->achan[channel].layer2_xmit = LAYER2_FX25;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p_audio_config->fx25_xmit_enable = 1;
|
p_audio_config->achan[channel].fx25_strength = 1;
|
||||||
|
p_audio_config->achan[channel].layer2_xmit = LAYER2_FX25;
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Line %d: Unreasonable value for FX.25 transmission mode. Using %d.\n",
|
dw_printf ("Line %d: Unreasonable value for FX.25 transmission mode. Using %d.\n",
|
||||||
line, p_audio_config->fx25_xmit_enable);
|
line, p_audio_config->achan[channel].fx25_strength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2304,6 +2310,48 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IL2PTX [ + - ] [ 0 1 ] - Enable IL2P transmission. Default off.
|
||||||
|
* "+" means normal polarity. Redundant since it is the default.
|
||||||
|
* (command line -I for first channel)
|
||||||
|
* "-" means inverted polarity. Do not use for 1200 bps.
|
||||||
|
* (command line -i for first channel)
|
||||||
|
* "0" means weak FEC. Not recommended.
|
||||||
|
* "1" means stronger FEC. "Max FEC." Default if not specified.
|
||||||
|
*/
|
||||||
|
|
||||||
|
else if (strcasecmp(t, "IL2PTX") == 0) {
|
||||||
|
|
||||||
|
p_audio_config->achan[channel].layer2_xmit = LAYER2_IL2P;
|
||||||
|
p_audio_config->achan[channel].il2p_max_fec = 1;
|
||||||
|
p_audio_config->achan[channel].il2p_invert_polarity = 0;
|
||||||
|
|
||||||
|
while ((t = split(NULL,0)) != NULL) {
|
||||||
|
for (char *c = t; *t != '\0'; c++) {
|
||||||
|
switch (*c) {
|
||||||
|
case '+':
|
||||||
|
p_audio_config->achan[channel].il2p_invert_polarity = 0;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
p_audio_config->achan[channel].il2p_invert_polarity = 1;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
p_audio_config->achan[channel].il2p_max_fec = 0;
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
p_audio_config->achan[channel].il2p_max_fec = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Line %d: Invalid parameter '%c' fol IL2PTX command.\n", line, *c);
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ==================== APRS Digipeater parameters ====================
|
* ==================== APRS Digipeater parameters ====================
|
||||||
*/
|
*/
|
||||||
|
@ -5399,7 +5447,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
|
||||||
while ((t = split(NULL,0)) != NULL) {
|
while ((t = split(NULL,0)) != NULL) {
|
||||||
|
|
||||||
char keyword[20];
|
char keyword[20];
|
||||||
char value[200];
|
char value[1000];
|
||||||
char *e;
|
char *e;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,9 @@
|
||||||
* Internet Gateway (IGate)
|
* Internet Gateway (IGate)
|
||||||
* Ham Radio of Things - IoT with Ham Radio
|
* Ham Radio of Things - IoT with Ham Radio
|
||||||
* FX.25 Forward Error Correction.
|
* FX.25 Forward Error Correction.
|
||||||
*
|
* IL2P Forward Error Correction.
|
||||||
|
* Emergency Alert System (EAS) Specific Area Message Encoding (SAME) receiver.
|
||||||
|
* AIS receiver for tracking ships.
|
||||||
*
|
*
|
||||||
*---------------------------------------------------------------*/
|
*---------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@ -123,6 +125,7 @@
|
||||||
#include "ax25_link.h"
|
#include "ax25_link.h"
|
||||||
#include "dtime_now.h"
|
#include "dtime_now.h"
|
||||||
#include "fx25.h"
|
#include "fx25.h"
|
||||||
|
#include "il2p.h"
|
||||||
#include "dwsock.h"
|
#include "dwsock.h"
|
||||||
#include "dns_sd_dw.h"
|
#include "dns_sd_dw.h"
|
||||||
|
|
||||||
|
@ -222,6 +225,8 @@ int main (int argc, char *argv[])
|
||||||
int d_h_opt = 0; /* "-d h" option for hamlib debugging. Repeat for more detail */
|
int d_h_opt = 0; /* "-d h" option for hamlib debugging. Repeat for more detail */
|
||||||
#endif
|
#endif
|
||||||
int d_x_opt = 1; /* "-d x" option for FX.25. Default minimal. Repeat for more detail. -qx to silence. */
|
int d_x_opt = 1; /* "-d x" option for FX.25. Default minimal. Repeat for more detail. -qx to silence. */
|
||||||
|
int d_2_opt = 0; /* "-d 2" option for IL2P. Default minimal. Repeat for more detail. */
|
||||||
|
|
||||||
int aprstt_debug = 0; /* "-d d" option for APRStt (think Dtmf) debug. */
|
int aprstt_debug = 0; /* "-d d" option for APRStt (think Dtmf) debug. */
|
||||||
|
|
||||||
int E_tx_opt = 0; /* "-E n" Error rate % for clobbering transmit frames. */
|
int E_tx_opt = 0; /* "-E n" Error rate % for clobbering transmit frames. */
|
||||||
|
@ -230,6 +235,9 @@ int main (int argc, char *argv[])
|
||||||
float e_recv_ber = 0.0; /* Receive Bit Error Rate (BER). */
|
float e_recv_ber = 0.0; /* Receive Bit Error Rate (BER). */
|
||||||
int X_fx25_xmit_enable = 0; /* FX.25 transmit enable. */
|
int X_fx25_xmit_enable = 0; /* FX.25 transmit enable. */
|
||||||
|
|
||||||
|
int I_opt = -1; /* IL2P transmit, normal polarity, arg is max_fec. */
|
||||||
|
int i_opt = -1; /* IL2P transmit, inverted polarity, arg is max_fec. */
|
||||||
|
|
||||||
char x_opt_mode = ' '; /* "-x N" option for transmitting calibration tones. */
|
char x_opt_mode = ' '; /* "-x N" option for transmitting calibration tones. */
|
||||||
int x_opt_chan = 0; /* Split into 2 parts. Mode e.g. m, a, and optional channel. */
|
int x_opt_chan = 0; /* Split into 2 parts. Mode e.g. m, a, and optional channel. */
|
||||||
|
|
||||||
|
@ -291,7 +299,7 @@ int main (int argc, char *argv[])
|
||||||
text_color_init(t_opt);
|
text_color_init(t_opt);
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
||||||
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "A", __DATE__);
|
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "B", __DATE__);
|
||||||
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||||
|
|
||||||
|
|
||||||
|
@ -363,7 +371,20 @@ int main (int argc, char *argv[])
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// I've seen many references to people running this as root.
|
||||||
|
// There is no reason to do that.
|
||||||
|
// There is for some privileges to access the audio system, GPIO (if needed for PTT),
|
||||||
|
// etc. but ordinary users have those abilities.
|
||||||
|
// Giving an applications permission to do things it does not need to do
|
||||||
|
// is a huge security risk.
|
||||||
|
|
||||||
|
#ifndef __WIN32__
|
||||||
|
if (getuid() == 0 || geteuid() == 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Dire Wolf requires only privileges available to ordinary users.\n");
|
||||||
|
dw_printf ("Running this as root is an unnecssary security risk.\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default location of configuration file is current directory.
|
* Default location of configuration file is current directory.
|
||||||
|
@ -393,7 +414,7 @@ int main (int argc, char *argv[])
|
||||||
|
|
||||||
/* ':' following option character means arg is required. */
|
/* ':' following option character means arg is required. */
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "hP:B:gjJD:U:c:px:r:b:n:d:q:t:ul:L:Sa:E:T:e:X:A",
|
c = getopt_long(argc, argv, "hP:B:gjJD:U:c:px:r:b:n:d:q:t:ul:L:Sa:E:T:e:X:AI:i:",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -606,6 +627,7 @@ int main (int argc, char *argv[])
|
||||||
case 'h': d_h_opt++; break; // Hamlib verbose level.
|
case 'h': d_h_opt++; break; // Hamlib verbose level.
|
||||||
#endif
|
#endif
|
||||||
case 'x': d_x_opt++; break; // FX.25
|
case 'x': d_x_opt++; break; // FX.25
|
||||||
|
case '2': d_2_opt++; break; // IL2P
|
||||||
case 'd': aprstt_debug++; break; // APRStt (mnemonic Dtmf)
|
case 'd': aprstt_debug++; break; // APRStt (mnemonic Dtmf)
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
@ -694,6 +716,16 @@ int main (int argc, char *argv[])
|
||||||
X_fx25_xmit_enable = atoi(optarg);
|
X_fx25_xmit_enable = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'I': // IL2P, normal polarity
|
||||||
|
|
||||||
|
I_opt = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'i': // IL2P, inverted polarity
|
||||||
|
|
||||||
|
i_opt = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'A': // -A convert AIS to APRS object
|
case 'A': // -A convert AIS to APRS object
|
||||||
|
|
||||||
A_opt_ais_to_obj = 1;
|
A_opt_ais_to_obj = 1;
|
||||||
|
@ -894,7 +926,45 @@ int main (int argc, char *argv[])
|
||||||
|
|
||||||
audio_config.recv_ber = e_recv_ber;
|
audio_config.recv_ber = e_recv_ber;
|
||||||
|
|
||||||
audio_config.fx25_xmit_enable = X_fx25_xmit_enable;
|
if (X_fx25_xmit_enable > 0) {
|
||||||
|
if (I_opt != -1 || i_opt != -1) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Can't mix -X with -I or -i.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
audio_config.achan[0].fx25_strength = X_fx25_xmit_enable;
|
||||||
|
audio_config.achan[0].layer2_xmit = LAYER2_FX25;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (I_opt != -1 && i_opt != -1) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Can't use both -I and -i at the same time.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (I_opt >= 0) {
|
||||||
|
audio_config.achan[0].layer2_xmit = LAYER2_IL2P;
|
||||||
|
audio_config.achan[0].il2p_max_fec = (I_opt > 0);
|
||||||
|
if (audio_config.achan[0].il2p_max_fec == 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("It is highly recommended that 1, rather than 0, is used with -I for best results.\n");
|
||||||
|
}
|
||||||
|
audio_config.achan[0].il2p_invert_polarity = 0; // normal
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i_opt >= 0) {
|
||||||
|
audio_config.achan[0].layer2_xmit = LAYER2_IL2P;
|
||||||
|
audio_config.achan[0].il2p_max_fec = (i_opt > 0);
|
||||||
|
if (audio_config.achan[0].il2p_max_fec == 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("It is highly recommended that 1, rather than 0, is used with -i for best results.\n");
|
||||||
|
}
|
||||||
|
audio_config.achan[0].il2p_invert_polarity = 1; // invert for transmit
|
||||||
|
if (audio_config.achan[0].baud == 1200) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Using -i with 1200 bps is a bad idea. Use -I instead.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -915,10 +985,11 @@ int main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the demodulator(s) and HDLC decoder.
|
* Initialize the demodulator(s) and layer 2 decoder (HDLC, IL2P).
|
||||||
*/
|
*/
|
||||||
multi_modem_init (&audio_config);
|
multi_modem_init (&audio_config);
|
||||||
fx25_init (d_x_opt);
|
fx25_init (d_x_opt);
|
||||||
|
il2p_init (d_2_opt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the touch tone decoder & APRStt gateway.
|
* Initialize the touch tone decoder & APRStt gateway.
|
||||||
|
@ -1573,7 +1644,9 @@ static void usage (char **argv)
|
||||||
dw_printf (" -P xxx Modem Profiles.\n");
|
dw_printf (" -P xxx Modem Profiles.\n");
|
||||||
dw_printf (" -A Convert AIS positions to APRS Object Reports.\n");
|
dw_printf (" -A Convert AIS positions to APRS Object Reports.\n");
|
||||||
dw_printf (" -D n Divide audio sample rate by n for channel 0.\n");
|
dw_printf (" -D n Divide audio sample rate by n for channel 0.\n");
|
||||||
dw_printf (" -X n 1 to enable FX.25 transmit.\n");
|
dw_printf (" -X n 1 to enable FX.25 transmit. 16, 32, 64 for specific number of check bytes.\n");
|
||||||
|
dw_printf (" -I n Enable IL2P transmit. n=1 is recommended. 0 uses weaker FEC.\n");
|
||||||
|
dw_printf (" -i n Enable IL2P transmit, inverted polarity. n=1 is recommended. 0 uses weaker FEC.\n");
|
||||||
dw_printf (" -d Debug options:\n");
|
dw_printf (" -d Debug options:\n");
|
||||||
dw_printf (" a a = AGWPE network protocol client.\n");
|
dw_printf (" a a = AGWPE network protocol client.\n");
|
||||||
dw_printf (" k k = KISS serial port or pseudo terminal client.\n");
|
dw_printf (" k k = KISS serial port or pseudo terminal client.\n");
|
||||||
|
@ -1591,6 +1664,7 @@ static void usage (char **argv)
|
||||||
dw_printf (" h h = hamlib increase verbose level.\n");
|
dw_printf (" h h = hamlib increase verbose level.\n");
|
||||||
#endif
|
#endif
|
||||||
dw_printf (" x x = FX.25 increase verbose level.\n");
|
dw_printf (" x x = FX.25 increase verbose level.\n");
|
||||||
|
dw_printf (" 2 2 = IL2P.\n");
|
||||||
dw_printf (" d d = APRStt (DTMF to APRS object translation).\n");
|
dw_printf (" d d = APRStt (DTMF to APRS object translation).\n");
|
||||||
dw_printf (" -q Quiet (suppress output) options:\n");
|
dw_printf (" -q Quiet (suppress output) options:\n");
|
||||||
dw_printf (" h h = Heard line with the audio level.\n");
|
dw_printf (" h h = Heard line with the audio level.\n");
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2019 John Langner, WB2OSZ
|
// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2019, 2021 John Langner, WB2OSZ
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -76,6 +76,7 @@
|
||||||
#include "morse.h"
|
#include "morse.h"
|
||||||
#include "dtmf.h"
|
#include "dtmf.h"
|
||||||
#include "fx25.h"
|
#include "fx25.h"
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
|
|
||||||
/* Own random number generator so we can get */
|
/* Own random number generator so we can get */
|
||||||
|
@ -123,6 +124,7 @@ static void send_packet (char *str)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
flen = ax25_pack (pp, fbuf);
|
flen = ax25_pack (pp, fbuf);
|
||||||
|
(void)flen;
|
||||||
for (c=0; c<modem.adev[0].num_channels; c++)
|
for (c=0; c<modem.adev[0].num_channels; c++)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -151,12 +153,10 @@ static void send_packet (char *str)
|
||||||
gen_tone_put_sample (c, 0, 0);
|
gen_tone_put_sample (c, 0, 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
hdlc_send_flags (c, 8, 0);
|
|
||||||
hdlc_send_flags (c, 8, 0);
|
layer2_preamble_postamble (c, 32, 0, &modem);
|
||||||
hdlc_send_flags (c, 8, 0);
|
layer2_send_frame (c, pp, 0, &modem);
|
||||||
hdlc_send_flags (c, 8, 0);
|
layer2_preamble_postamble (c, 2, 1, &modem);
|
||||||
hdlc_send_frame (c, fbuf, flen, 0, modem.fx25_xmit_enable);
|
|
||||||
hdlc_send_flags (c, 2, 1);
|
|
||||||
}
|
}
|
||||||
ax25_delete (pp);
|
ax25_delete (pp);
|
||||||
}
|
}
|
||||||
|
@ -176,6 +176,9 @@ int main(int argc, char **argv)
|
||||||
int g_opt = 0;
|
int g_opt = 0;
|
||||||
int j_opt = 0;
|
int j_opt = 0;
|
||||||
int J_opt = 0;
|
int J_opt = 0;
|
||||||
|
int X_opt = 0; // send FX.25
|
||||||
|
int I_opt = -1; // send IL2P rather than AX.25, normal polarity
|
||||||
|
int i_opt = -1; // send IL2P rather than AX.25, inverted polarity
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up default values for the modem.
|
* Set up default values for the modem.
|
||||||
|
@ -227,7 +230,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
/* ':' following option character means arg is required. */
|
/* ':' following option character means arg is required. */
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "gjJm:s:a:b:B:r:n:N:o:z:82M:X:",
|
c = getopt_long(argc, argv, "gjJm:s:a:b:B:r:n:N:o:z:82M:X:I:i:",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -453,7 +456,17 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
case 'X':
|
case 'X':
|
||||||
|
|
||||||
modem.fx25_xmit_enable = atoi(optarg);
|
X_opt = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'I': // IL2P, normal polarity
|
||||||
|
|
||||||
|
I_opt = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'i': // IL2P, inverted polarity
|
||||||
|
|
||||||
|
i_opt = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
|
@ -507,6 +520,43 @@ int main(int argc, char **argv)
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (X_opt > 0) {
|
||||||
|
if (I_opt != -1 || i_opt != -1) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Can't mix -X with -I or -i.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
modem.achan[0].fx25_strength = X_opt;
|
||||||
|
modem.achan[0].layer2_xmit = LAYER2_FX25;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (I_opt != -1 && i_opt != -1) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Can't use both -I and -i at the same time.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (I_opt >= 0) {
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Using IL2P normal polarity.\n");
|
||||||
|
modem.achan[0].layer2_xmit = LAYER2_IL2P;
|
||||||
|
modem.achan[0].il2p_max_fec = (I_opt > 0);
|
||||||
|
modem.achan[0].il2p_invert_polarity = 0; // normal
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i_opt >= 0) {
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Using IL2P inverted polarity.\n");
|
||||||
|
modem.achan[0].layer2_xmit = LAYER2_IL2P;
|
||||||
|
modem.achan[0].il2p_max_fec = (i_opt > 0);
|
||||||
|
modem.achan[0].il2p_invert_polarity = 1; // invert for transmit
|
||||||
|
if (modem.achan[0].baud == 1200) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Using -i with 1200 bps is a bad idea. Use -I instead.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the output file.
|
* Open the output file.
|
||||||
*/
|
*/
|
||||||
|
@ -536,6 +586,7 @@ int main(int argc, char **argv)
|
||||||
// Just use the default of minimal information.
|
// Just use the default of minimal information.
|
||||||
|
|
||||||
fx25_init (1);
|
fx25_init (1);
|
||||||
|
il2p_init (0); // There are no "-d" options so far but it could be handy here.
|
||||||
|
|
||||||
assert (modem.adev[0].bits_per_sample == 8 || modem.adev[0].bits_per_sample == 16);
|
assert (modem.adev[0].bits_per_sample == 8 || modem.adev[0].bits_per_sample == 16);
|
||||||
assert (modem.adev[0].num_channels == 1 || modem.adev[0].num_channels == 2);
|
assert (modem.adev[0].num_channels == 1 || modem.adev[0].num_channels == 2);
|
||||||
|
@ -669,7 +720,9 @@ static void usage (char **argv)
|
||||||
dw_printf (" -g Scrambled baseband rather than AFSK.\n");
|
dw_printf (" -g Scrambled baseband rather than AFSK.\n");
|
||||||
dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n");
|
dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n");
|
||||||
dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n");
|
dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n");
|
||||||
dw_printf (" -X n Generate FX.25 frames. Specify number of check bytes: 16, 32, or 64.\n");
|
dw_printf (" -X n 1 to enable FX.25 transmit. 16, 32, 64 for specific number of check bytes.\n");
|
||||||
|
dw_printf (" -I n Enable IL2P transmit. n=1 is recommended. 0 uses weaker FEC.\n");
|
||||||
|
dw_printf (" -i n Enable IL2P transmit, inverted polarity. n=1 is recommended. 0 uses weaker FEC.\n");
|
||||||
dw_printf (" -m <number> Mark frequency. Default is %d.\n", DEFAULT_MARK_FREQ);
|
dw_printf (" -m <number> Mark frequency. Default is %d.\n", DEFAULT_MARK_FREQ);
|
||||||
dw_printf (" -s <number> Space frequency. Default is %d.\n", DEFAULT_SPACE_FREQ);
|
dw_printf (" -s <number> Space frequency. Default is %d.\n", DEFAULT_SPACE_FREQ);
|
||||||
dw_printf (" -r <number> Audio sample Rate. Default is %d.\n", DEFAULT_SAMPLES_PER_SEC);
|
dw_printf (" -r <number> Audio sample Rate. Default is %d.\n", DEFAULT_SAMPLES_PER_SEC);
|
||||||
|
|
|
@ -380,7 +380,14 @@ void tone_gen_put_bit (int chan, int dat)
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("tone_gen_put_bit %d AFSK\n", __LINE__);
|
dw_printf ("tone_gen_put_bit %d AFSK\n", __LINE__);
|
||||||
#endif
|
#endif
|
||||||
tone_phase[chan] += dat ? f2_change_per_sample[chan] : f1_change_per_sample[chan];
|
|
||||||
|
// v1.7 reversed.
|
||||||
|
// Previously a data '1' selected the second (usually higher) tone.
|
||||||
|
// It never really mattered before because we were using NRZI.
|
||||||
|
// With the addition of IL2P, we need to be more careful.
|
||||||
|
// A data '1' should be the mark tone.
|
||||||
|
|
||||||
|
tone_phase[chan] += dat ? f1_change_per_sample[chan] : f2_change_per_sample[chan];
|
||||||
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
|
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
|
||||||
gen_tone_put_sample (chan, a, sam);
|
gen_tone_put_sample (chan, a, sam);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "demod_9600.h" /* for descramble() */
|
#include "demod_9600.h" /* for descramble() */
|
||||||
#include "ptt.h"
|
#include "ptt.h"
|
||||||
#include "fx25.h"
|
#include "fx25.h"
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
|
|
||||||
//#define TEST 1 /* Define for unit testing. */
|
//#define TEST 1 /* Define for unit testing. */
|
||||||
|
@ -496,6 +497,7 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
|
||||||
|
|
||||||
if (g_audio_p->achan[chan].modem_type != MODEM_AIS) {
|
if (g_audio_p->achan[chan].modem_type != MODEM_AIS) {
|
||||||
fx25_rec_bit (chan, subchan, slice, dbit);
|
fx25_rec_bit (chan, subchan, slice, dbit);
|
||||||
|
il2p_rec_bit (chan, subchan, slice, raw); // Note: skip NRZI.
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
136
src/hdlc_send.c
136
src/hdlc_send.c
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2011, 2013, 2014, 2019 John Langner, WB2OSZ
|
// Copyright (C) 2011, 2013, 2014, 2019, 2021 John Langner, WB2OSZ
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -27,37 +27,37 @@
|
||||||
#include "gen_tone.h"
|
#include "gen_tone.h"
|
||||||
#include "textcolor.h"
|
#include "textcolor.h"
|
||||||
#include "fcs_calc.h"
|
#include "fcs_calc.h"
|
||||||
|
#include "ax25_pad.h"
|
||||||
#include "fx25.h"
|
#include "fx25.h"
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
static void send_control (int, int);
|
static void send_byte_msb_first (int chan, int x, int polarity);
|
||||||
static void send_data (int, int);
|
|
||||||
static void send_bit (int, int);
|
static void send_control_nrzi (int, int);
|
||||||
|
static void send_data_nrzi (int, int);
|
||||||
|
static void send_bit_nrzi (int, int);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdlc_send_frame" or "hdlc_send_flags"
|
static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdlc_send_frame" or "hdlc_send_flags"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------
|
/*-------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* Name: hdlc_send
|
* Name: layer2_send_frame
|
||||||
*
|
*
|
||||||
* Purpose: Convert HDLC frames to a stream of bits.
|
* Purpose: Convert frames to a stream of bits.
|
||||||
|
* Originally this was for AX.25 only, hence the file name.
|
||||||
|
* Over time, FX.25 and IL2P were shoehorned in.
|
||||||
*
|
*
|
||||||
* Inputs: chan - Audio channel number, 0 = first.
|
* Inputs: chan - Audio channel number, 0 = first.
|
||||||
*
|
*
|
||||||
* fbuf - Frame buffer address.
|
* pp - Packet object.
|
||||||
*
|
|
||||||
* flen - Frame length, not including the FCS.
|
|
||||||
*
|
*
|
||||||
* bad_fcs - Append an invalid FCS for testing purposes.
|
* bad_fcs - Append an invalid FCS for testing purposes.
|
||||||
* Applies only to regular AX.25.
|
* Applies only to regular AX.25.
|
||||||
*
|
*
|
||||||
* fx25_xmit_enable - Just like the name says.
|
|
||||||
*
|
|
||||||
* Outputs: Bits are shipped out by calling tone_gen_put_bit().
|
* Outputs: Bits are shipped out by calling tone_gen_put_bit().
|
||||||
*
|
*
|
||||||
* Returns: Number of bits sent including "flags" and the
|
* Returns: Number of bits sent including "flags" and the
|
||||||
|
@ -65,12 +65,12 @@ static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdl
|
||||||
* The required time can be calculated by dividing this
|
* The required time can be calculated by dividing this
|
||||||
* number by the transmit rate of bits/sec.
|
* number by the transmit rate of bits/sec.
|
||||||
*
|
*
|
||||||
* Description: Convert to stream of bits including:
|
* Description: For AX.25, send:
|
||||||
* start flag
|
* start flag
|
||||||
* bit stuffed data
|
* bit stuffed data
|
||||||
* calculated FCS
|
* calculated FCS
|
||||||
* end flag
|
* end flag
|
||||||
* NRZI encoding
|
* NRZI encoding for all but the "flags."
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Assumptions: It is assumed that the tone_gen module has been
|
* Assumptions: It is assumed that the tone_gen module has been
|
||||||
|
@ -81,23 +81,40 @@ static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdl
|
||||||
|
|
||||||
static int ax25_only_hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs);
|
static int ax25_only_hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs);
|
||||||
|
|
||||||
// New in 1.6: Option to encapsulate in FX.25.
|
|
||||||
|
|
||||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs, int fx25_xmit_enable)
|
int layer2_send_frame (int chan, packet_t pp, int bad_fcs, struct audio_s *audio_config_p)
|
||||||
{
|
{
|
||||||
if (fx25_xmit_enable) {
|
if (audio_config_p->achan[chan].layer2_xmit == LAYER2_IL2P) {
|
||||||
int n = fx25_send_frame (chan, fbuf, flen, fx25_xmit_enable);
|
|
||||||
|
int n = il2p_send_frame (chan, pp, audio_config_p->achan[chan].il2p_max_fec,
|
||||||
|
audio_config_p->achan[chan].il2p_invert_polarity);
|
||||||
|
if (n > 0) {
|
||||||
|
return (n);
|
||||||
|
}
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Unable to send IL2p frame. Falling back to regular AX.25.\n");
|
||||||
|
// Not sure if we should fall back to AX.25 or not here.
|
||||||
|
}
|
||||||
|
else if (audio_config_p->achan[chan].layer2_xmit == LAYER2_FX25) {
|
||||||
|
unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
|
||||||
|
int flen = ax25_pack (pp, fbuf);
|
||||||
|
int n = fx25_send_frame (chan, fbuf, flen, audio_config_p->achan[chan].fx25_strength);
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
return (n);
|
return (n);
|
||||||
}
|
}
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Unable to send FX.25. Falling back to regular AX.25.\n");
|
dw_printf ("Unable to send FX.25. Falling back to regular AX.25.\n");
|
||||||
|
// Definitely need to fall back to AX.25 here because
|
||||||
|
// the FX.25 frame length is so limited.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
|
||||||
|
int flen = ax25_pack (pp, fbuf);
|
||||||
return (ax25_only_hdlc_send_frame (chan, fbuf, flen, bad_fcs));
|
return (ax25_only_hdlc_send_frame (chan, fbuf, flen, bad_fcs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int ax25_only_hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs)
|
static int ax25_only_hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs)
|
||||||
{
|
{
|
||||||
int j, fcs;
|
int j, fcs;
|
||||||
|
@ -105,33 +122,31 @@ static int ax25_only_hdlc_send_frame (int chan, unsigned char *fbuf, int flen, i
|
||||||
|
|
||||||
number_of_bits_sent[chan] = 0;
|
number_of_bits_sent[chan] = 0;
|
||||||
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("hdlc_send_frame ( chan = %d, fbuf = %p, flen = %d, bad_fcs = %d)\n", chan, fbuf, flen, bad_fcs);
|
dw_printf ("hdlc_send_frame ( chan = %d, fbuf = %p, flen = %d, bad_fcs = %d)\n", chan, fbuf, flen, bad_fcs);
|
||||||
fflush (stdout);
|
fflush (stdout);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
send_control_nrzi (chan, 0x7e); /* Start frame */
|
||||||
send_control (chan, 0x7e); /* Start frame */
|
|
||||||
|
|
||||||
for (j=0; j<flen; j++) {
|
for (j=0; j<flen; j++) {
|
||||||
send_data (chan, fbuf[j]);
|
send_data_nrzi (chan, fbuf[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fcs = fcs_calc (fbuf, flen);
|
fcs = fcs_calc (fbuf, flen);
|
||||||
|
|
||||||
if (bad_fcs) {
|
if (bad_fcs) {
|
||||||
/* For testing only - Simulate a frame getting corrupted along the way. */
|
/* For testing only - Simulate a frame getting corrupted along the way. */
|
||||||
send_data (chan, (~fcs) & 0xff);
|
send_data_nrzi (chan, (~fcs) & 0xff);
|
||||||
send_data (chan, ((~fcs) >> 8) & 0xff);
|
send_data_nrzi (chan, ((~fcs) >> 8) & 0xff);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
send_data (chan, fcs & 0xff);
|
send_data_nrzi (chan, fcs & 0xff);
|
||||||
send_data (chan, (fcs >> 8) & 0xff);
|
send_data_nrzi (chan, (fcs >> 8) & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
send_control (chan, 0x7e); /* End frame */
|
send_control_nrzi (chan, 0x7e); /* End frame */
|
||||||
|
|
||||||
return (number_of_bits_sent[chan]);
|
return (number_of_bits_sent[chan]);
|
||||||
}
|
}
|
||||||
|
@ -139,22 +154,25 @@ static int ax25_only_hdlc_send_frame (int chan, unsigned char *fbuf, int flen, i
|
||||||
|
|
||||||
/*-------------------------------------------------------------
|
/*-------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* Name: hdlc_send_flags
|
* Name: layer2_preamble_postamble
|
||||||
*
|
*
|
||||||
* Purpose: Send HDLC flags before and after the frame.
|
* Purpose: Send filler pattern before and after the frame.
|
||||||
|
* For HDLC it is 01111110, for IL2P 01010101.
|
||||||
*
|
*
|
||||||
* Inputs: chan - Audio channel number, 0 = first.
|
* Inputs: chan - Audio channel number, 0 = first.
|
||||||
*
|
*
|
||||||
* nflags - Number of flag patterns to send.
|
* nbytes - Number of bytes to send.
|
||||||
*
|
*
|
||||||
* finish - True for end of transmission.
|
* finish - True for end of transmission.
|
||||||
* This causes the last audio buffer to be flushed.
|
* This causes the last audio buffer to be flushed.
|
||||||
*
|
*
|
||||||
|
* audio_config_p - Configuration for audio and modems.
|
||||||
|
*
|
||||||
* Outputs: Bits are shipped out by calling tone_gen_put_bit().
|
* Outputs: Bits are shipped out by calling tone_gen_put_bit().
|
||||||
*
|
*
|
||||||
* Returns: Number of bits sent.
|
* Returns: Number of bits sent.
|
||||||
* There is no bit-stuffing so we would expect this to
|
* There is no bit-stuffing so we would expect this to
|
||||||
* be 8 * nflags.
|
* be 8 * nbytes.
|
||||||
* The required time can be calculated by dividing this
|
* The required time can be calculated by dividing this
|
||||||
* number by the transmit rate of bits/sec.
|
* number by the transmit rate of bits/sec.
|
||||||
*
|
*
|
||||||
|
@ -164,25 +182,30 @@ static int ax25_only_hdlc_send_frame (int chan, unsigned char *fbuf, int flen, i
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------*/
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
int hdlc_send_flags (int chan, int nflags, int finish)
|
int layer2_preamble_postamble (int chan, int nbytes, int finish, struct audio_s *audio_config_p)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
|
|
||||||
number_of_bits_sent[chan] = 0;
|
number_of_bits_sent[chan] = 0;
|
||||||
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("hdlc_send_flags ( chan = %d, nflags = %d, finish = %d )\n", chan, nflags, finish);
|
dw_printf ("hdlc_send_flags ( chan = %d, nflags = %d, finish = %d )\n", chan, nflags, finish);
|
||||||
fflush (stdout);
|
fflush (stdout);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* The AX.25 spec states that when the transmitter is on but not sending data */
|
// When the transmitter is on but not sending data, it should be sending
|
||||||
/* it should send a continuous stream of "flags." */
|
// a stream of a filler pattern.
|
||||||
|
// For AX.25, it is the 01111110 "flag" pattern with NRZI and no bit stuffing.
|
||||||
|
// For IL2P, it is 01010101 without NRZI.
|
||||||
|
|
||||||
for (j=0; j<nflags; j++) {
|
for (j=0; j<nbytes; j++) {
|
||||||
send_control (chan, 0x7e);
|
if (audio_config_p->achan[chan].layer2_xmit == LAYER2_IL2P) {
|
||||||
|
send_byte_msb_first (chan, IL2P_PREAMBLE, audio_config_p->achan[chan].il2p_invert_polarity);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
send_control_nrzi (chan, 0x7e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push out the final partial buffer! */
|
/* Push out the final partial buffer! */
|
||||||
|
@ -196,33 +219,54 @@ int hdlc_send_flags (int chan, int nflags, int finish)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// The next one is only for IL2P. No NRZI.
|
||||||
|
// MSB first, opposite of AX.25.
|
||||||
|
|
||||||
|
static void send_byte_msb_first (int chan, int x, int polarity)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i<8; i++) {
|
||||||
|
int dbit = (x & 0x80) != 0;
|
||||||
|
tone_gen_put_bit (chan, (dbit ^ polarity) & 1);
|
||||||
|
x <<= 1;
|
||||||
|
number_of_bits_sent[chan]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The following are only for HDLC.
|
||||||
|
// All bits are sent NRZI.
|
||||||
|
// Data (non flags) use bit stuffing.
|
||||||
|
|
||||||
|
|
||||||
static int stuff[MAX_CHANS]; // Count number of "1" bits to keep track of when we
|
static int stuff[MAX_CHANS]; // Count number of "1" bits to keep track of when we
|
||||||
// need to break up a long run by "bit stuffing."
|
// need to break up a long run by "bit stuffing."
|
||||||
// Needs to be array because we could be transmitting
|
// Needs to be array because we could be transmitting
|
||||||
// on multiple channels at the same time.
|
// on multiple channels at the same time.
|
||||||
|
|
||||||
static void send_control (int chan, int x)
|
static void send_control_nrzi (int chan, int x)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i=0; i<8; i++) {
|
for (i=0; i<8; i++) {
|
||||||
send_bit (chan, x & 1);
|
send_bit_nrzi (chan, x & 1);
|
||||||
x >>= 1;
|
x >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
stuff[chan] = 0;
|
stuff[chan] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_data (int chan, int x)
|
static void send_data_nrzi (int chan, int x)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i=0; i<8; i++) {
|
for (i=0; i<8; i++) {
|
||||||
send_bit (chan, x & 1);
|
send_bit_nrzi (chan, x & 1);
|
||||||
if (x & 1) {
|
if (x & 1) {
|
||||||
stuff[chan]++;
|
stuff[chan]++;
|
||||||
if (stuff[chan] == 5) {
|
if (stuff[chan] == 5) {
|
||||||
send_bit (chan, 0);
|
send_bit_nrzi (chan, 0);
|
||||||
stuff[chan] = 0;
|
stuff[chan] = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -238,7 +282,7 @@ static void send_data (int chan, int x)
|
||||||
* data 0 bit -> invert signal.
|
* data 0 bit -> invert signal.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void send_bit (int chan, int b)
|
static void send_bit_nrzi (int chan, int b)
|
||||||
{
|
{
|
||||||
static int output[MAX_CHANS];
|
static int output[MAX_CHANS];
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
|
|
||||||
/* hdlc_send.h */
|
/* hdlc_send.h */
|
||||||
|
|
||||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs, int fx25_xmit_enable);
|
// In version 1.7 an extra layer of abstraction was added here.
|
||||||
|
// Rather than calling hdlc_send_frame, we now use another function
|
||||||
|
// which sends AX.25, FX.25, or IL2P depending on
|
||||||
|
|
||||||
int hdlc_send_flags (int chan, int flags, int finish);
|
#include "ax25_pad.h"
|
||||||
|
#include "audio.h"
|
||||||
|
|
||||||
|
int layer2_send_frame (int chan, packet_t pp, int bad_fcs, struct audio_s *audio_config_p);
|
||||||
|
|
||||||
|
int layer2_preamble_postamble (int chan, int flags, int finish, struct audio_s *audio_config_p);
|
||||||
|
|
||||||
/* end hdlc_send.h */
|
/* end hdlc_send.h */
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef IL2P_H
|
||||||
|
#define IL2P_H 1
|
||||||
|
|
||||||
|
|
||||||
|
#define IL2P_PREAMBLE 0x55
|
||||||
|
|
||||||
|
#define IL2P_SYNC_WORD 0xF15E48
|
||||||
|
|
||||||
|
#define IL2P_SYNC_WORD_SIZE 3
|
||||||
|
#define IL2P_HEADER_SIZE 13 // Does not include 2 parity.
|
||||||
|
#define IL2P_HEADER_PARITY 2
|
||||||
|
|
||||||
|
#define IL2P_MAX_PAYLOAD_SIZE 1023
|
||||||
|
#define IL2P_MAX_PAYLOAD_BLOCKS 5
|
||||||
|
#define IL2P_MAX_PARITY_SYMBOLS 16 // For payload only.
|
||||||
|
#define IL2P_MAX_ENCODED_PAYLOAD_SIZE (IL2P_MAX_PAYLOAD_SIZE + IL2P_MAX_PAYLOAD_BLOCKS * IL2P_MAX_PARITY_SYMBOLS)
|
||||||
|
|
||||||
|
#define IL2P_MAX_PACKET_SIZE (IL2P_SYNC_WORD_SIZE + IL2P_HEADER_SIZE + IL2P_HEADER_PARITY + IL2P_MAX_ENCODED_PAYLOAD_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_init.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
// Init must be called at start of application.
|
||||||
|
|
||||||
|
extern void il2p_init (int debug);
|
||||||
|
|
||||||
|
#include "fx25.h" // For Reed Solomon stuff. e.g. struct rs
|
||||||
|
// Maybe rearrange someday because RS now used another place.
|
||||||
|
|
||||||
|
extern struct rs *il2p_find_rs(int nparity); // Internal later?
|
||||||
|
|
||||||
|
extern void il2p_encode_rs (unsigned char *tx_data, int data_size, int num_parity, unsigned char *parity_out);
|
||||||
|
|
||||||
|
extern int il2p_decode_rs (unsigned char *rec_block, int data_size, int num_parity, unsigned char *out);
|
||||||
|
|
||||||
|
extern int il2p_get_debug(void);
|
||||||
|
extern void il2p_set_debug(int debug);
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_rec.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Receives a bit stream from demodulator.
|
||||||
|
|
||||||
|
extern void il2p_rec_bit (int chan, int subchan, int slice, int dbit);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_send.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "ax25_pad.h" // For packet object.
|
||||||
|
|
||||||
|
// Send bit stream to modulator.
|
||||||
|
|
||||||
|
int il2p_send_frame (int chan, packet_t pp, int max_fec, int polarity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_codec.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "ax25_pad.h"
|
||||||
|
|
||||||
|
extern int il2p_encode_frame (packet_t pp, int max_fec, unsigned char *iout);
|
||||||
|
|
||||||
|
packet_t il2p_decode_frame (unsigned char *irec);
|
||||||
|
|
||||||
|
packet_t il2p_decode_header_payload (unsigned char* uhdr, unsigned char *epayload, int *symbols_corrected);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_header.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
extern int il2p_type_1_header (packet_t pp, int max_fec, unsigned char *hdr);
|
||||||
|
|
||||||
|
extern packet_t il2p_decode_header_type_1 (unsigned char *hdr, int num_sym_changed);
|
||||||
|
|
||||||
|
|
||||||
|
extern int il2p_type_0_header (packet_t pp, int max_fec, unsigned char *hdr);
|
||||||
|
|
||||||
|
extern int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descrambled_hdr);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_scramble.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
extern void il2p_scramble_block (unsigned char *in, unsigned char *out, int len);
|
||||||
|
|
||||||
|
extern void il2p_descramble_block (unsigned char *in, unsigned char *out, int len);
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_payload.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int payload_byte_count; // Total size, 0 thru 1023
|
||||||
|
int payload_block_count;
|
||||||
|
int small_block_size;
|
||||||
|
int large_block_size;
|
||||||
|
int large_block_count;
|
||||||
|
int small_block_count;
|
||||||
|
int parity_symbols_per_block; // 2, 4, 6, 8, 16
|
||||||
|
} il2p_payload_properties_t;
|
||||||
|
|
||||||
|
extern int il2p_payload_compute (il2p_payload_properties_t *p, int payload_size, int max_fec);
|
||||||
|
|
||||||
|
extern int il2p_encode_payload (unsigned char *payload, int payload_size, int max_fec, unsigned char *enc);
|
||||||
|
|
||||||
|
extern int il2p_decode_payload (unsigned char *received, int payload_size, int max_fec, unsigned char *payload_out, int *symbols_corrected);
|
||||||
|
|
||||||
|
extern int il2p_get_header_attributes (unsigned char *hdr, int *hdr_type, int *max_fec);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,150 @@
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef IL2P_H
|
||||||
|
#define IL2P_H 1
|
||||||
|
|
||||||
|
|
||||||
|
#define IL2P_PREAMBLE 0x55
|
||||||
|
|
||||||
|
#define IL2P_SYNC_WORD 0xF15E48
|
||||||
|
|
||||||
|
#define IL2P_SYNC_WORD_SIZE 3
|
||||||
|
#define IL2P_HEADER_SIZE 13 // Does not include 2 parity.
|
||||||
|
#define IL2P_HEADER_PARITY 2
|
||||||
|
|
||||||
|
#define IL2P_MAX_PAYLOAD_SIZE 1023
|
||||||
|
#define IL2P_MAX_PAYLOAD_BLOCKS 5
|
||||||
|
#define IL2P_MAX_PARITY_SYMBOLS 16 // For payload only.
|
||||||
|
#define IL2P_MAX_ENCODED_SIZE (IL2P_MAX_PAYLOAD_SIZE + IL2P_MAX_PAYLOAD_BLOCKS * IL2P_MAX_PARITY_SYMBOLS)
|
||||||
|
// FIXME: above & below IL2P_MAX_ENCODED_PAYLOAD_SIZE
|
||||||
|
|
||||||
|
#define IL2P_MAX_PACKET_SIZE (IL2P_SYNC_WORD_SIZE + IL2P_HEADER_SIZE + IL2P_HEADER_PARITY + IL2P_MAX_ENCODED_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_init.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
// Init must be called at start of application.
|
||||||
|
|
||||||
|
extern void il2p_init (int debug);
|
||||||
|
|
||||||
|
#include "fx25.h" // For Reed Solomon stuff. e.g. struct rs
|
||||||
|
// Maybe rearrange someday because RS now used another place.
|
||||||
|
|
||||||
|
extern struct rs *il2p_find_rs(int nparity); // Internal later?
|
||||||
|
|
||||||
|
extern void il2p_encode_rs (unsigned char *tx_data, int data_size, int num_parity, unsigned char *parity_out);
|
||||||
|
|
||||||
|
extern int il2p_decode_rs (unsigned char *rec_block, int data_size, int num_parity, unsigned char *out);
|
||||||
|
|
||||||
|
extern int il2p_get_debug(void);
|
||||||
|
extern void il2p_set_debug(int debug);
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_rec.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Receives a bit stream from demodulator.
|
||||||
|
|
||||||
|
extern void il2p_rec_bit (int chan, int subchan, int slice, int dbit);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_send.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "ax25_pad.h" // For packet object.
|
||||||
|
|
||||||
|
// Send bit stream to modulator.
|
||||||
|
|
||||||
|
int il2p_send_frame (int chan, packet_t pp, int max_fec, int polarity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_codec.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "ax25_pad.h"
|
||||||
|
|
||||||
|
extern int il2p_encode_frame (packet_t pp, int max_fec, unsigned char *iout);
|
||||||
|
|
||||||
|
packet_t il2p_decode_frame (unsigned char *irec);
|
||||||
|
|
||||||
|
packet_t il2p_decode_header_payload (unsigned char* uhdr, unsigned char *epayload, int *symbols_corrected);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_header.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
extern int il2p_type_1_header (packet_t pp, int max_fec, unsigned char *hdr);
|
||||||
|
|
||||||
|
extern packet_t il2p_decode_header_type_1 (unsigned char *hdr, int num_sym_changed);
|
||||||
|
|
||||||
|
|
||||||
|
extern int il2p_type_0_header (packet_t pp, int max_fec, unsigned char *hdr);
|
||||||
|
|
||||||
|
extern int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_unscrambled_hdr);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_scramble.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
extern void il2p_scramble_reset (int *lsfr_state);
|
||||||
|
|
||||||
|
extern void il2p_scramble_block (unsigned char *in, unsigned char *out, int len, int *lsfr_state);
|
||||||
|
|
||||||
|
extern void il2p_descramble_reset (int *lsfr_state);
|
||||||
|
|
||||||
|
extern void il2p_descramble_block (unsigned char *in, unsigned char *out, int len, int *lsfr_state);
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// il2p_payload.c
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int payload_byte_count; // Total size, 0 thru 1023
|
||||||
|
int payload_block_count;
|
||||||
|
int small_block_size;
|
||||||
|
int large_block_size;
|
||||||
|
int large_block_count;
|
||||||
|
int small_block_count;
|
||||||
|
int parity_symbols_per_block; // 2, 4, 6, 8, 16
|
||||||
|
} il2p_payload_properties_t;
|
||||||
|
|
||||||
|
extern int il2p_payload_compute (il2p_payload_properties_t *p, int payload_size, int max_fec);
|
||||||
|
|
||||||
|
extern int il2p_encode_payload (unsigned char *payload, int payload_size, int max_fec, unsigned char *enc);
|
||||||
|
|
||||||
|
extern int il2p_decode_payload (unsigned char *received, int payload_size, int max_fec, unsigned char *payload_out, int *symbols_corrected);
|
||||||
|
|
||||||
|
extern int il2p_get_header_attributes (unsigned char *hdr, int *hdr_type, int *max_fec);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,263 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "il2p.h"
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "demod.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* File: il2p_codec.c
|
||||||
|
*
|
||||||
|
* Purpose: Convert IL2P encoded format from and to direwolf internal packet format.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_encode_frame
|
||||||
|
*
|
||||||
|
* Purpose: Convert AX.25 frame to IL2P encoding.
|
||||||
|
*
|
||||||
|
* Inputs: chan - Audio channel number, 0 = first.
|
||||||
|
*
|
||||||
|
* pp - Packet object pointer.
|
||||||
|
*
|
||||||
|
* max_fec - 1 to send maximum FEC size rather than automatic.
|
||||||
|
*
|
||||||
|
* Outputs: iout - Encoded result, excluding the 3 byte sync word.
|
||||||
|
* Caller should provide IL2P_MAX_PACKET_SIZE bytes.
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes for transmission.
|
||||||
|
* -1 is returned for failure.
|
||||||
|
*
|
||||||
|
* Description: Encode into IL2P format.
|
||||||
|
*
|
||||||
|
* Errors: If something goes wrong, return -1.
|
||||||
|
*
|
||||||
|
* Most likely reason is that the frame is too large.
|
||||||
|
* IL2P has a max payload size of 1023 bytes.
|
||||||
|
* For a type 1 header, this is the maximum AX.25 Information part size.
|
||||||
|
* For a type 0 header, this is the entire AX.25 frame.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_encode_frame (packet_t pp, int max_fec, unsigned char *iout)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Can a type 1 header be used?
|
||||||
|
|
||||||
|
unsigned char hdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY];
|
||||||
|
int e;
|
||||||
|
int out_len = 0;
|
||||||
|
|
||||||
|
e = il2p_type_1_header (pp, max_fec, hdr);
|
||||||
|
if (e >= 0) {
|
||||||
|
il2p_scramble_block (hdr, iout, IL2P_HEADER_SIZE);
|
||||||
|
il2p_encode_rs (iout, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, iout+IL2P_HEADER_SIZE);
|
||||||
|
out_len = IL2P_HEADER_SIZE + IL2P_HEADER_PARITY;
|
||||||
|
|
||||||
|
if (e == 0) {
|
||||||
|
// Sucess. No info part.
|
||||||
|
return (out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload is AX.25 info part.
|
||||||
|
unsigned char *pinfo;
|
||||||
|
int info_len;
|
||||||
|
info_len = ax25_get_info (pp, &pinfo);
|
||||||
|
|
||||||
|
int k = il2p_encode_payload (pinfo, info_len, max_fec, iout+out_len);
|
||||||
|
if (k > 0) {
|
||||||
|
out_len += k;
|
||||||
|
// Success. Info part was <= 1023 bytes.
|
||||||
|
return (out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Something went wrong with the payload encoding.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
else if (e == -1) {
|
||||||
|
|
||||||
|
// Could not use type 1 header for some reason.
|
||||||
|
// e.g. More than 2 addresses, extended (mod 128) sequence numbers, etc.
|
||||||
|
|
||||||
|
e = il2p_type_0_header (pp, max_fec, hdr);
|
||||||
|
if (e > 0) {
|
||||||
|
|
||||||
|
il2p_scramble_block (hdr, iout, IL2P_HEADER_SIZE);
|
||||||
|
il2p_encode_rs (iout, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, iout+IL2P_HEADER_SIZE);
|
||||||
|
out_len = IL2P_HEADER_SIZE + IL2P_HEADER_PARITY;
|
||||||
|
|
||||||
|
// Payload is entire AX.25 frame.
|
||||||
|
|
||||||
|
unsigned char *frame_data_ptr = ax25_get_frame_data_ptr (pp);
|
||||||
|
int frame_len = ax25_get_frame_len (pp);
|
||||||
|
int k = il2p_encode_payload (frame_data_ptr, frame_len, max_fec, iout+out_len);
|
||||||
|
if (k > 0) {
|
||||||
|
out_len += k;
|
||||||
|
// Success. Entire AX.25 frame <= 1023 bytes.
|
||||||
|
return (out_len);
|
||||||
|
}
|
||||||
|
// Something went wrong with the payload encoding.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
else if (e == 0) {
|
||||||
|
// Impossible condition. Type 0 header must have payload.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// AX.25 frame is too large.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AX.25 Information part is too large.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_decode_frame
|
||||||
|
*
|
||||||
|
* Purpose: Convert IL2P encoding to AX.25 frame.
|
||||||
|
* This is only used during testing, with a whole encoded frame.
|
||||||
|
* During reception, the header would have FEC and descrambling
|
||||||
|
* applied first so we would know how much to collect for the payload.
|
||||||
|
*
|
||||||
|
* Inputs: irec - Received IL2P frame excluding the 3 byte sync word.
|
||||||
|
*
|
||||||
|
* Future Out: Number of symbols corrected.
|
||||||
|
*
|
||||||
|
* Returns: Packet pointer or NULL for error.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
packet_t il2p_decode_frame (unsigned char *irec)
|
||||||
|
{
|
||||||
|
unsigned char uhdr[IL2P_HEADER_SIZE]; // After FEC and descrambling.
|
||||||
|
int e = il2p_clarify_header (irec, uhdr);
|
||||||
|
|
||||||
|
// TODO?: for symmetry we might want to clarify the payload before combining.
|
||||||
|
|
||||||
|
return (il2p_decode_header_payload(uhdr, irec + IL2P_HEADER_SIZE + IL2P_HEADER_PARITY, &e));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_decode_header_payload
|
||||||
|
*
|
||||||
|
* Purpose: Convert IL2P encoding to AX.25 frame
|
||||||
|
*
|
||||||
|
* Inputs: uhdr - Received header after FEC and descrambling.
|
||||||
|
* epayload - Encoded payload.
|
||||||
|
*
|
||||||
|
* In/Out: symbols_corrected - Symbols (bytes) corrected in the header.
|
||||||
|
* Should be 0 or 1 because it has 2 parity symbols.
|
||||||
|
* Here we add number of corrections for the payload.
|
||||||
|
*
|
||||||
|
* Returns: Packet pointer or NULL for error.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
packet_t il2p_decode_header_payload (unsigned char* uhdr, unsigned char *epayload, int *symbols_corrected)
|
||||||
|
{
|
||||||
|
int hdr_type;
|
||||||
|
int max_fec;
|
||||||
|
int payload_len = il2p_get_header_attributes (uhdr, &hdr_type, &max_fec);
|
||||||
|
|
||||||
|
packet_t pp = NULL;
|
||||||
|
|
||||||
|
if (hdr_type == 1) {
|
||||||
|
|
||||||
|
// Header type 1. Any payload is the AX.25 Information part.
|
||||||
|
|
||||||
|
pp = il2p_decode_header_type_1 (uhdr, *symbols_corrected);
|
||||||
|
if (pp == NULL) {
|
||||||
|
// Failed for some reason.
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload_len > 0) {
|
||||||
|
// This is the AX.25 Information part.
|
||||||
|
|
||||||
|
unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE];
|
||||||
|
int e = il2p_decode_payload (epayload, payload_len, max_fec, extracted, symbols_corrected);
|
||||||
|
|
||||||
|
// It would be possible to have a good header but too many errors in the payload.
|
||||||
|
|
||||||
|
if (e <= 0) {
|
||||||
|
ax25_delete (pp);
|
||||||
|
pp = NULL;
|
||||||
|
return (pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e != payload_len) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P Internal Error: %s(): hdr_type=%d, max_fec=%d, payload_len=%d, e=%d.\n", __func__, hdr_type, max_fec, payload_len, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ax25_set_info (pp, extracted, payload_len);
|
||||||
|
}
|
||||||
|
return (pp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Header type 0. The payload is the entire AX.25 frame.
|
||||||
|
|
||||||
|
unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE];
|
||||||
|
int e = il2p_decode_payload (epayload, payload_len, max_fec, extracted, symbols_corrected);
|
||||||
|
|
||||||
|
if (e <= 0) { // Payload was not received correctly.
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e != payload_len) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P Internal Error: %s(): hdr_type=%d, e=%d, payload_len=%d\n", __func__, hdr_type, e, payload_len);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
alevel_t alevel;
|
||||||
|
memset (&alevel, 0, sizeof(alevel));
|
||||||
|
//alevel = demod_get_audio_level (chan, subchan); // What TODO? We don't know channel here.
|
||||||
|
// I think alevel gets filled in somewhere later making
|
||||||
|
// this redundant.
|
||||||
|
|
||||||
|
pp = ax25_from_frame (extracted, payload_len, alevel);
|
||||||
|
return (pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end il2p_decode_header_payload
|
||||||
|
|
||||||
|
// end il2p_codec.c
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "il2p.h"
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "demod.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* File: il2p_codec.c
|
||||||
|
*
|
||||||
|
* Purpose: Convert IL2P encoded format from and to direwolf internal packet format.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_encode_frame
|
||||||
|
*
|
||||||
|
* Purpose: Convert AX.25 frame to IL2P encoding.
|
||||||
|
*
|
||||||
|
* Inputs: chan - Audio channel number, 0 = first.
|
||||||
|
*
|
||||||
|
* pp - Packet object pointer.
|
||||||
|
*
|
||||||
|
* max_fec - 1 to send maximum FEC size rather than automatic.
|
||||||
|
*
|
||||||
|
* Outputs: iout - Encoded result, excluding the 3 byte sync word.
|
||||||
|
* Caller should provide IL2P_MAX_PACKET_SIZE bytes.
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes for transmission.
|
||||||
|
* -1 is returned for failure.
|
||||||
|
*
|
||||||
|
* Description: Encode into IL2P format.
|
||||||
|
*
|
||||||
|
* Errors: If something goes wrong, return -1.
|
||||||
|
*
|
||||||
|
* Most likely reason is that the frame is too large.
|
||||||
|
* IL2P has a max payload size of 1023 bytes.
|
||||||
|
* For a type 1 header, this is the maximum AX.25 Information part size.
|
||||||
|
* For a type 0 header, this is the entire AX.25 frame.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_encode_frame (packet_t pp, int max_fec, unsigned char *iout)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Can a type 1 header be used?
|
||||||
|
|
||||||
|
unsigned char hdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY];
|
||||||
|
int e;
|
||||||
|
int tx_lfsr_state;
|
||||||
|
il2p_scramble_reset(&tx_lfsr_state);
|
||||||
|
int out_len = 0;
|
||||||
|
|
||||||
|
e = il2p_type_1_header (pp, max_fec, hdr);
|
||||||
|
if (e >= 0) {
|
||||||
|
il2p_scramble_block (hdr, iout, IL2P_HEADER_SIZE, &tx_lfsr_state);
|
||||||
|
il2p_encode_rs (iout, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, iout+IL2P_HEADER_SIZE);
|
||||||
|
out_len = IL2P_HEADER_SIZE + IL2P_HEADER_PARITY;
|
||||||
|
|
||||||
|
if (e == 0) {
|
||||||
|
// Sucess. No info part.
|
||||||
|
return (out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload is AX.25 info part.
|
||||||
|
unsigned char *pinfo;
|
||||||
|
int info_len;
|
||||||
|
info_len = ax25_get_info (pp, &pinfo);
|
||||||
|
|
||||||
|
int k = il2p_encode_payload (pinfo, info_len, max_fec, iout+out_len);
|
||||||
|
if (k > 0) {
|
||||||
|
out_len += k;
|
||||||
|
// Success. Info part was <= 1023 bytes.
|
||||||
|
return (out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Something went wrong with the payload encoding.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
else if (e == -1) {
|
||||||
|
|
||||||
|
// Could not use type 1 header for some reason.
|
||||||
|
// e.g. More than 2 addresses, extended (mod 128) sequence numbers, etc.
|
||||||
|
|
||||||
|
e = il2p_type_0_header (pp, max_fec, hdr);
|
||||||
|
if (e > 0) {
|
||||||
|
|
||||||
|
il2p_scramble_block (hdr, iout, IL2P_HEADER_SIZE, &tx_lfsr_state);
|
||||||
|
il2p_encode_rs (iout, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, iout+IL2P_HEADER_SIZE);
|
||||||
|
out_len = IL2P_HEADER_SIZE + IL2P_HEADER_PARITY;
|
||||||
|
|
||||||
|
// Payload is entire AX.25 frame.
|
||||||
|
|
||||||
|
unsigned char *frame_data_ptr = ax25_get_frame_data_ptr (pp);
|
||||||
|
int frame_len = ax25_get_frame_len (pp);
|
||||||
|
int k = il2p_encode_payload (frame_data_ptr, frame_len, max_fec, iout+out_len);
|
||||||
|
if (k > 0) {
|
||||||
|
out_len += k;
|
||||||
|
// Success. Entire AX.25 frame <= 1023 bytes.
|
||||||
|
return (out_len);
|
||||||
|
}
|
||||||
|
// Something went wrong with the payload encoding.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
else if (e == 0) {
|
||||||
|
// Impossible condition. Type 0 header must have payload.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// AX.25 frame is too large.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AX.25 Information part is too large.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_decode_frame
|
||||||
|
*
|
||||||
|
* Purpose: Convert IL2P encoding to AX.25 frame.
|
||||||
|
* This is only used during testing, with a whole encoded frame.
|
||||||
|
* During reception, the header would have FEC and descrambling
|
||||||
|
* applied first so we would know how much to collect for the payload.
|
||||||
|
*
|
||||||
|
* Inputs: irec - Received IL2P frame excluding the 3 byte sync word.
|
||||||
|
*
|
||||||
|
* Future Out: Number of symbols corrected.
|
||||||
|
*
|
||||||
|
* Returns: Packet pointer or NULL for error.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
packet_t il2p_decode_frame (unsigned char *irec)
|
||||||
|
{
|
||||||
|
unsigned char uhdr[IL2P_HEADER_SIZE]; // After FEC and descrambling.
|
||||||
|
int e = il2p_clarify_header (irec, uhdr);
|
||||||
|
|
||||||
|
// FIXME: for symmetry we might want to clarify the payload before combining.
|
||||||
|
|
||||||
|
return (il2p_decode_header_payload(uhdr, irec + IL2P_HEADER_SIZE + IL2P_HEADER_PARITY, &e));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_decode_header_payload
|
||||||
|
*
|
||||||
|
* Purpose: Convert IL2P encoding to AX.25 frame
|
||||||
|
*
|
||||||
|
* Inputs: uhdr - Received header after FEC and descrambling.
|
||||||
|
* epayload - Encoded payload.
|
||||||
|
*
|
||||||
|
* In/Out: symbols_corrected - Symbols (bytes) corrected in the header.
|
||||||
|
* Should be 0 or 1 because it has 2 parity symbols.
|
||||||
|
* Here we add number of corrections for the payload.
|
||||||
|
*
|
||||||
|
* Returns: Packet pointer or NULL for error.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
packet_t il2p_decode_header_payload (unsigned char* uhdr, unsigned char *epayload, int *symbols_corrected)
|
||||||
|
{
|
||||||
|
int hdr_type;
|
||||||
|
int max_fec;
|
||||||
|
int payload_len = il2p_get_header_attributes (uhdr, &hdr_type, &max_fec);
|
||||||
|
|
||||||
|
packet_t pp = NULL;
|
||||||
|
|
||||||
|
if (hdr_type == 1) {
|
||||||
|
|
||||||
|
// Header type 1. Any payload is the AX.25 Information part.
|
||||||
|
|
||||||
|
pp = il2p_decode_header_type_1 (uhdr, *symbols_corrected);
|
||||||
|
if (pp == NULL) {
|
||||||
|
// Failed for some reason.
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload_len > 0) {
|
||||||
|
// This is the AX.25 Information part.
|
||||||
|
|
||||||
|
unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE];
|
||||||
|
int e = il2p_decode_payload (epayload, payload_len, max_fec, extracted, symbols_corrected);
|
||||||
|
|
||||||
|
// It would be possible to have a good header but too many errors in the payload.
|
||||||
|
|
||||||
|
if (e <= 0) {
|
||||||
|
ax25_delete (pp);
|
||||||
|
pp = NULL;
|
||||||
|
return (pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e != payload_len) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P Internal Error: %s(): hdr_type=%d, max_fec=%d, payload_len=%d, e=%d.\n", __func__, hdr_type, max_fec, payload_len, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ax25_set_info (pp, extracted, payload_len);
|
||||||
|
}
|
||||||
|
return (pp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Header type 0. The payload is the entire AX.25 frame.
|
||||||
|
|
||||||
|
unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE];
|
||||||
|
int e = il2p_decode_payload (epayload, payload_len, max_fec, extracted, symbols_corrected);
|
||||||
|
|
||||||
|
if (e <= 0) { // Payload was not received correctly.
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e != payload_len) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P Internal Error: %s(): hdr_type=%d, e=%d, payload_len=%d\n", __func__, hdr_type, e, payload_len);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
alevel_t alevel;
|
||||||
|
memset (&alevel, 0, sizeof(alevel));
|
||||||
|
//alevel = demod_get_audio_level (chan, subchan); // What TODO? We don't know channel here.
|
||||||
|
// I think alevel gets filled in somewhere later making
|
||||||
|
// this redundant.
|
||||||
|
|
||||||
|
pp = ax25_from_frame (extracted, payload_len, alevel);
|
||||||
|
return (pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end il2p_decode_header_payload
|
||||||
|
|
||||||
|
// end il2p_codec.c
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,673 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "ax25_pad.h"
|
||||||
|
#include "ax25_pad2.h"
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* File: il2p_header.c
|
||||||
|
*
|
||||||
|
* Purpose: Functions to deal with the IL2P header.
|
||||||
|
*
|
||||||
|
* Reference: http://tarpn.net/t/il2p/il2p-specification0-4.pdf
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Convert ASCII to/from DEC SIXBIT as defined here:
|
||||||
|
// https://en.wikipedia.org/wiki/Six-bit_character_code#DEC_six-bit_code
|
||||||
|
|
||||||
|
static inline int ascii_to_sixbit (int a)
|
||||||
|
{
|
||||||
|
if (a >= ' ' && a <= '_') return (a - ' ');
|
||||||
|
return (31); // '?' for any invalid.
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int sixbit_to_ascii (int s)
|
||||||
|
{
|
||||||
|
return (s + ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions for setting the various header fields.
|
||||||
|
// It is assumed that it was zeroed first so only the '1' bits are set.
|
||||||
|
|
||||||
|
static void set_field (unsigned char *hdr, int bit_num, int lsb_index, int width, int value)
|
||||||
|
{
|
||||||
|
while (width > 0 && value != 0) {
|
||||||
|
assert (lsb_index >= 0 && lsb_index <= 11);
|
||||||
|
if (value & 1) {
|
||||||
|
hdr[lsb_index] |= 1 << bit_num;
|
||||||
|
}
|
||||||
|
value >>= 1;
|
||||||
|
lsb_index--;
|
||||||
|
width--;
|
||||||
|
}
|
||||||
|
assert (value == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_UI(hdr,val) set_field(hdr, 6, 0, 1, val)
|
||||||
|
|
||||||
|
#define SET_PID(hdr,val) set_field(hdr, 6, 4, 4, val)
|
||||||
|
|
||||||
|
#define SET_CONTROL(hdr,val) set_field(hdr, 6, 11, 7, val)
|
||||||
|
|
||||||
|
|
||||||
|
#define SET_FEC_LEVEL(hdr,val) set_field(hdr, 7, 0, 1, val)
|
||||||
|
|
||||||
|
#define SET_HDR_TYPE(hdr,val) set_field(hdr, 7, 1, 1, val)
|
||||||
|
|
||||||
|
#define SET_PAYLOAD_BYTE_COUNT(hdr,val) set_field(hdr, 7, 11, 10, val)
|
||||||
|
|
||||||
|
|
||||||
|
// Extracting the fields.
|
||||||
|
|
||||||
|
static int get_field (unsigned char *hdr, int bit_num, int lsb_index, int width)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
lsb_index -= width - 1;
|
||||||
|
while (width > 0) {
|
||||||
|
result <<= 1;
|
||||||
|
assert (lsb_index >= 0 && lsb_index <= 11);
|
||||||
|
if (hdr[lsb_index] & (1 << bit_num)) {
|
||||||
|
result |= 1;
|
||||||
|
}
|
||||||
|
lsb_index++;
|
||||||
|
width--;
|
||||||
|
}
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GET_UI(hdr) get_field(hdr, 6, 0, 1)
|
||||||
|
|
||||||
|
#define GET_PID(hdr) get_field(hdr, 6, 4, 4)
|
||||||
|
|
||||||
|
#define GET_CONTROL(hdr) get_field(hdr, 6, 11, 7)
|
||||||
|
|
||||||
|
|
||||||
|
#define GET_FEC_LEVEL(hdr) get_field(hdr, 7, 0, 1)
|
||||||
|
|
||||||
|
#define GET_HDR_TYPE(hdr) get_field(hdr, 7, 1, 1)
|
||||||
|
|
||||||
|
#define GET_PAYLOAD_BYTE_COUNT(hdr) get_field(hdr, 7, 11, 10)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// AX.25 'I' and 'UI' frames have a protocol ID which determines how the
|
||||||
|
// information part should be interpretted.
|
||||||
|
// Here we squeeze the most common cases down to 4 bits.
|
||||||
|
// Return -1 if translation is not possible. Fall back to type 0 header in this case.
|
||||||
|
|
||||||
|
static int encode_pid (packet_t pp)
|
||||||
|
{
|
||||||
|
int pid = ax25_get_pid(pp);
|
||||||
|
|
||||||
|
if ((pid & 0x30) == 0x20) return (0x2); // AX.25 Layer 3
|
||||||
|
if ((pid & 0x30) == 0x10) return (0x2); // AX.25 Layer 3
|
||||||
|
if (pid == 0x01) return (0x3); // ISO 8208 / CCIT X.25 PLP
|
||||||
|
if (pid == 0x06) return (0x4); // Compressed TCP/IP
|
||||||
|
if (pid == 0x07) return (0x5); // Uncompressed TCP/IP
|
||||||
|
if (pid == 0x08) return (0x6); // Segmentation fragmen
|
||||||
|
if (pid == 0xcc) return (0xb); // ARPA Internet Protocol
|
||||||
|
if (pid == 0xcd) return (0xc); // ARPA Address Resolution
|
||||||
|
if (pid == 0xce) return (0xd); // FlexNet
|
||||||
|
if (pid == 0xcf) return (0xe); // TheNET
|
||||||
|
if (pid == 0xf0) return (0xf); // No L3
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert IL2P 4 bit PID to AX.25 8 bit PID.
|
||||||
|
|
||||||
|
|
||||||
|
static int decode_pid (int pid)
|
||||||
|
{
|
||||||
|
static const unsigned char axpid[16] = {
|
||||||
|
0xf0, // Should not happen. 0 is for 'S' frames.
|
||||||
|
0xf0, // Should not happen. 1 is for 'U' frames (but not UI).
|
||||||
|
0x20, // AX.25 Layer 3
|
||||||
|
0x01, // ISO 8208 / CCIT X.25 PLP
|
||||||
|
0x06, // Compressed TCP/IP
|
||||||
|
0x07, // Uncompressed TCP/IP
|
||||||
|
0x08, // Segmentation fragment
|
||||||
|
0xf0, // Future
|
||||||
|
0xf0, // Future
|
||||||
|
0xf0, // Future
|
||||||
|
0xf0, // Future
|
||||||
|
0xcc, // ARPA Internet Protocol
|
||||||
|
0xcd, // ARPA Address Resolution
|
||||||
|
0xce, // FlexNet
|
||||||
|
0xcf, // TheNET
|
||||||
|
0xf0 }; // No L3
|
||||||
|
|
||||||
|
assert (pid >= 0 && pid <= 15);
|
||||||
|
return (axpid[pid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_type_1_header
|
||||||
|
*
|
||||||
|
* Purpose: Attempt to create type 1 header from packet object.
|
||||||
|
*
|
||||||
|
* Inputs: pp - Packet object.
|
||||||
|
*
|
||||||
|
* max_fec - 1 to use maximum FEC symbols , 0 for automatic.
|
||||||
|
*
|
||||||
|
* Outputs: hdr - IL2P header with no scrambling or parity symbols.
|
||||||
|
* Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes.
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes for information part or -1 for failure.
|
||||||
|
* In case of failure, fall back to type 0 transparent encapsulation.
|
||||||
|
*
|
||||||
|
* Description: Type 1 Headers do not support AX.25 repeater callsign addressing,
|
||||||
|
* Modulo-128 extended mode window sequence numbers, nor any callsign
|
||||||
|
* characters that cannot translate to DEC SIXBIT.
|
||||||
|
* If these cases are encountered during IL2P packet encoding,
|
||||||
|
* the encoder switches to Type 0 Transparent Encapsulation.
|
||||||
|
* SABME can't be handled by type 1.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_type_1_header (packet_t pp, int max_fec, unsigned char *hdr)
|
||||||
|
{
|
||||||
|
memset (hdr, 0, IL2P_HEADER_SIZE);
|
||||||
|
|
||||||
|
if (ax25_get_num_addr(pp) != 2) {
|
||||||
|
// Only two addresses are allowed for type 1 header.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check does not apply for 'U' frames but put in one place rather than two.
|
||||||
|
|
||||||
|
if (ax25_get_modulo(pp) == 128) return(-1);
|
||||||
|
|
||||||
|
// Destination and source addresses go into low bits 0-5 for bytes 0-11.
|
||||||
|
|
||||||
|
char dst_addr[AX25_MAX_ADDR_LEN];
|
||||||
|
char src_addr[AX25_MAX_ADDR_LEN];
|
||||||
|
|
||||||
|
ax25_get_addr_no_ssid (pp, AX25_DESTINATION, dst_addr);
|
||||||
|
int dst_ssid = ax25_get_ssid (pp, AX25_DESTINATION);
|
||||||
|
|
||||||
|
ax25_get_addr_no_ssid (pp, AX25_SOURCE, src_addr);
|
||||||
|
int src_ssid = ax25_get_ssid (pp, AX25_SOURCE);
|
||||||
|
|
||||||
|
unsigned char *a = (unsigned char *)dst_addr;
|
||||||
|
for (int i = 0; *a != '\0'; i++, a++) {
|
||||||
|
if (*a < ' ' || *a > '_') {
|
||||||
|
// Shouldn't happen but follow the rule.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
hdr[i] = ascii_to_sixbit(*a);
|
||||||
|
}
|
||||||
|
|
||||||
|
a = (unsigned char *)src_addr;
|
||||||
|
for (int i = 6; *a != '\0'; i++, a++) {
|
||||||
|
if (*a < ' ' || *a > '_') {
|
||||||
|
// Shouldn't happen but follow the rule.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
hdr[i] = ascii_to_sixbit(*a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Byte 12 has DEST SSID in upper nybble and SRC SSID in lower nybble and
|
||||||
|
hdr[12] = (dst_ssid << 4) | src_ssid;
|
||||||
|
|
||||||
|
ax25_frame_type_t frame_type;
|
||||||
|
cmdres_t cr; // command or response.
|
||||||
|
char description[64];
|
||||||
|
int pf; // Poll/Final.
|
||||||
|
int nr, ns; // Sequence numbers.
|
||||||
|
|
||||||
|
frame_type = ax25_frame_type (pp, &cr, description, &pf, &nr, &ns);
|
||||||
|
|
||||||
|
//dw_printf ("%s(): %s-%d>%s-%d: %s\n", __func__, src_addr, src_ssid, dst_addr, dst_ssid, description);
|
||||||
|
|
||||||
|
switch (frame_type) {
|
||||||
|
|
||||||
|
case frame_type_S_RR: // Receive Ready - System Ready To Receive
|
||||||
|
case frame_type_S_RNR: // Receive Not Ready - TNC Buffer Full
|
||||||
|
case frame_type_S_REJ: // Reject Frame - Out of Sequence or Duplicate
|
||||||
|
case frame_type_S_SREJ: // Selective Reject - Request single frame repeat
|
||||||
|
|
||||||
|
// S frames (RR, RNR, REJ, SREJ), mod 8, have control N(R) P/F S S 0 1
|
||||||
|
// These are mapped into P/F N(R) C S S
|
||||||
|
// Bit 6 is not mentioned in documentation but it is used for P/F for the other frame types.
|
||||||
|
// C is copied from the C bit in the destination addr.
|
||||||
|
// C from source is not used here. Reception assumes it is the opposite.
|
||||||
|
// PID is set to 0, meaning none, for S frames.
|
||||||
|
|
||||||
|
SET_UI(hdr, 0);
|
||||||
|
SET_PID(hdr, 0);
|
||||||
|
SET_CONTROL(hdr, (pf<<6) | (nr<<3) | (((cr == cr_cmd) | (cr == cr_11))<<2));
|
||||||
|
|
||||||
|
// This gets OR'ed into the above.
|
||||||
|
switch (frame_type) {
|
||||||
|
case frame_type_S_RR: SET_CONTROL(hdr, 0); break;
|
||||||
|
case frame_type_S_RNR: SET_CONTROL(hdr, 1); break;
|
||||||
|
case frame_type_S_REJ: SET_CONTROL(hdr, 2); break;
|
||||||
|
case frame_type_S_SREJ: SET_CONTROL(hdr, 3); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case frame_type_U_SABM: // Set Async Balanced Mode
|
||||||
|
case frame_type_U_DISC: // Disconnect
|
||||||
|
case frame_type_U_DM: // Disconnect Mode
|
||||||
|
case frame_type_U_UA: // Unnumbered Acknowledge
|
||||||
|
case frame_type_U_FRMR: // Frame Reject
|
||||||
|
case frame_type_U_UI: // Unnumbered Information
|
||||||
|
case frame_type_U_XID: // Exchange Identification
|
||||||
|
case frame_type_U_TEST: // Test
|
||||||
|
|
||||||
|
// The encoding allows only 3 bits for frame type and SABME got left out.
|
||||||
|
// Control format: P/F opcode[3] C n/a n/a
|
||||||
|
// The grayed out n/a bits are observed as 00 in the example.
|
||||||
|
// The header UI field must also be set for UI frames.
|
||||||
|
// PID is set to 1 for all U frames other than UI.
|
||||||
|
|
||||||
|
if (frame_type == frame_type_U_UI) {
|
||||||
|
SET_UI(hdr, 1); // I guess this is how we distinguish 'I' and 'UI'
|
||||||
|
// on the receving end.
|
||||||
|
int pid = encode_pid(pp);
|
||||||
|
if (pid < 0) return (-1);
|
||||||
|
SET_PID(hdr, pid);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SET_PID(hdr, 1); // 1 for 'U' other than 'UI'.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each of the destination and source addresses has a "C" bit.
|
||||||
|
// They should normally have the opposite setting.
|
||||||
|
// IL2P has only a single bit to represent 4 possbilities.
|
||||||
|
//
|
||||||
|
// dst src il2p meaning
|
||||||
|
// --- --- ---- -------
|
||||||
|
// 0 0 0 Not valid (earlier protocol version)
|
||||||
|
// 1 0 1 Command (v2)
|
||||||
|
// 0 1 0 Response (v2)
|
||||||
|
// 1 1 1 Not valid (earlier protocol version)
|
||||||
|
//
|
||||||
|
// APRS does not mention how to set these bits and all 4 combinations
|
||||||
|
// are seen in the wild. Apparently these are ignord on receive and no
|
||||||
|
// one cares. Here we copy from the C bit in the destination address.
|
||||||
|
// It should be noted that the case of both C bits being the same can't
|
||||||
|
// be represented so the il2p encode/decode bit not produce exactly the
|
||||||
|
// same bits. We see this in the second example in the protocol spec.
|
||||||
|
// The original UI frame has both C bits of 0 so it is received as a response.
|
||||||
|
|
||||||
|
SET_CONTROL(hdr, (pf<<6) | (((cr == cr_cmd) | (cr == cr_11))<<2));
|
||||||
|
|
||||||
|
// This gets OR'ed into the above.
|
||||||
|
switch (frame_type) {
|
||||||
|
case frame_type_U_SABM: SET_CONTROL(hdr, 0<<3); break;
|
||||||
|
case frame_type_U_DISC: SET_CONTROL(hdr, 1<<3); break;
|
||||||
|
case frame_type_U_DM: SET_CONTROL(hdr, 2<<3); break;
|
||||||
|
case frame_type_U_UA: SET_CONTROL(hdr, 3<<3); break;
|
||||||
|
case frame_type_U_FRMR: SET_CONTROL(hdr, 4<<3); break;
|
||||||
|
case frame_type_U_UI: SET_CONTROL(hdr, 5<<3); break;
|
||||||
|
case frame_type_U_XID: SET_CONTROL(hdr, 6<<3); break;
|
||||||
|
case frame_type_U_TEST: SET_CONTROL(hdr, 7<<3); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case frame_type_I: // Information
|
||||||
|
|
||||||
|
// I frames (mod 8 only)
|
||||||
|
// encoded control: P/F N(R) N(S)
|
||||||
|
|
||||||
|
SET_UI(hdr, 0);
|
||||||
|
|
||||||
|
int pid2 = encode_pid(pp);
|
||||||
|
if (pid2 < 0) return (-1);
|
||||||
|
SET_PID(hdr, pid2);
|
||||||
|
|
||||||
|
SET_CONTROL(hdr, (pf<<6) | (nr<<3) | ns);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case frame_type_U_SABME: // Set Async Balanced Mode, Extended
|
||||||
|
case frame_type_U: // other Unnumbered, not used by AX.25.
|
||||||
|
case frame_not_AX25: // Could not get control byte from frame.
|
||||||
|
default:
|
||||||
|
|
||||||
|
// Fall back to the header type 0 for these.
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common for all header type 1.
|
||||||
|
|
||||||
|
// Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10]
|
||||||
|
|
||||||
|
SET_FEC_LEVEL(hdr, max_fec);
|
||||||
|
SET_HDR_TYPE(hdr, 1);
|
||||||
|
|
||||||
|
unsigned char *pinfo;
|
||||||
|
int info_len;
|
||||||
|
|
||||||
|
info_len = ax25_get_info (pp, &pinfo);
|
||||||
|
if (info_len < 0 || info_len > IL2P_MAX_PAYLOAD_SIZE) {
|
||||||
|
return (-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SET_PAYLOAD_BYTE_COUNT(hdr, info_len);
|
||||||
|
return (info_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This should create a packet from the IL2P header.
|
||||||
|
// The information part will not be filled in.
|
||||||
|
|
||||||
|
static void trim (char *stuff)
|
||||||
|
{
|
||||||
|
char *p = stuff + strlen(stuff) - 1;
|
||||||
|
while (strlen(stuff) > 0 && (*p == ' ')) {
|
||||||
|
*p = '\0';
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_decode_header_type_1
|
||||||
|
*
|
||||||
|
* Purpose: Attempt to convert type 1 header to a packet object.
|
||||||
|
*
|
||||||
|
* Inputs: hdr - IL2P header with no scrambling or parity symbols.
|
||||||
|
*
|
||||||
|
* num_sym_changed - Number of symbols changed by FEC in the header.
|
||||||
|
* Should be 0 or 1.
|
||||||
|
*
|
||||||
|
* Returns: Packet Object or NULL for failure.
|
||||||
|
*
|
||||||
|
* Description: A later step will process the payload for the information part.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
packet_t il2p_decode_header_type_1 (unsigned char *hdr, int num_sym_changed)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (GET_HDR_TYPE(hdr) != 1 ) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P Internal error. Should not be here: %s, when header type is 0.\n", __func__);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First get the addresses including SSID.
|
||||||
|
|
||||||
|
char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
|
||||||
|
int num_addr = 2;
|
||||||
|
memset (addrs, 0, 2*AX25_MAX_ADDR_LEN);
|
||||||
|
|
||||||
|
// The IL2P header uses 2 parity symbols which means a single corrupted symbol (byte)
|
||||||
|
// can always be corrected.
|
||||||
|
// However, I have seen cases, where the error rate is very high, where the RS decoder
|
||||||
|
// thinks it found a valid code block by changing one symbol but it was the wrong one.
|
||||||
|
// The result is trash. This shows up as address fields like 'R&G4"A' and 'TEW\ !'.
|
||||||
|
// I added a sanity check here to catch characters other than uppper case letters and digits.
|
||||||
|
// The frame should be rejected in this case. The question is whether to discard it
|
||||||
|
// silently or print a message so the user can see that something strange is happening?
|
||||||
|
// My current thinking is that it should be silently ignored if the header has been
|
||||||
|
// modified (correctee or more likely, made worse in this cases).
|
||||||
|
// If no changes were made, something weird is happening. We should mention it for
|
||||||
|
// troubleshooting rather than sweeping it under the rug.
|
||||||
|
|
||||||
|
// The same thing has been observed with the payload, under very high error conditions,
|
||||||
|
// and max_fec==0. Here I don't see a good solution. AX.25 information can contain
|
||||||
|
// "binary" data so I'm not sure what sort of sanity check could be added.
|
||||||
|
// This was not observed with max_fec==1. If we make that the default, same as Nino TNC,
|
||||||
|
// it would be extremely extremely unlikely unless someone explicitly selects weaker FEC.
|
||||||
|
|
||||||
|
// TODO: We could do something similar for header type 0.
|
||||||
|
// The address fields should be all binary zero values.
|
||||||
|
// Someone overly ambitious might check the addresses found in the first payload block.
|
||||||
|
|
||||||
|
for (int i = 0; i <= 5; i++) {
|
||||||
|
addrs[AX25_DESTINATION][i] = sixbit_to_ascii(hdr[i] & 0x3f);
|
||||||
|
}
|
||||||
|
trim (addrs[AX25_DESTINATION]);
|
||||||
|
for (int i = 0; i < strlen(addrs[AX25_DESTINATION]); i++) {
|
||||||
|
if (! isupper(addrs[AX25_DESTINATION][i]) && ! isdigit(addrs[AX25_DESTINATION][i])) {
|
||||||
|
if (num_sym_changed == 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P: Invalid character '%c' in destination address '%s'\n", addrs[AX25_DESTINATION][i], addrs[AX25_DESTINATION]);
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snprintf (addrs[AX25_DESTINATION]+strlen(addrs[AX25_DESTINATION]), 4, "-%d", (hdr[12] >> 4) &0xf);
|
||||||
|
|
||||||
|
for (int i = 0; i <= 5; i++) {
|
||||||
|
addrs[AX25_SOURCE][i] = sixbit_to_ascii(hdr[i+6] & 0x3f);
|
||||||
|
}
|
||||||
|
trim (addrs[AX25_SOURCE]);
|
||||||
|
for (int i = 0; i < strlen(addrs[AX25_SOURCE]); i++) {
|
||||||
|
if (! isupper(addrs[AX25_SOURCE][i]) && ! isdigit(addrs[AX25_SOURCE][i])) {
|
||||||
|
if (num_sym_changed == 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P: Invalid character '%c' in source address '%s'\n", addrs[AX25_SOURCE][i], addrs[AX25_SOURCE]);
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snprintf (addrs[AX25_SOURCE]+strlen(addrs[AX25_SOURCE]), 4, "-%d", hdr[12] &0xf);
|
||||||
|
|
||||||
|
// The PID field gives us the general type.
|
||||||
|
// 0 = 'S' frame.
|
||||||
|
// 1 = 'U' frame other than UI.
|
||||||
|
// others are either 'UI' or 'I' depending on the UI field.
|
||||||
|
|
||||||
|
int pid = GET_PID(hdr);
|
||||||
|
int ui = GET_UI(hdr);
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
|
||||||
|
// 'S' frame.
|
||||||
|
// The control field contains: P/F N(R) C S S
|
||||||
|
|
||||||
|
int control = GET_CONTROL(hdr);
|
||||||
|
cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
|
||||||
|
ax25_frame_type_t ftype;
|
||||||
|
switch (control & 0x03) {
|
||||||
|
case 0: ftype = frame_type_S_RR; break;
|
||||||
|
case 1: ftype = frame_type_S_RNR; break;
|
||||||
|
case 2: ftype = frame_type_S_REJ; break;
|
||||||
|
default: ftype = frame_type_S_SREJ; break;
|
||||||
|
}
|
||||||
|
int modulo = 8;
|
||||||
|
int nr = (control >> 3) & 0x07;
|
||||||
|
int pf = (control >> 6) & 0x01;
|
||||||
|
unsigned char *pinfo = NULL; // Any info for SREJ will be added later.
|
||||||
|
int info_len = 0;
|
||||||
|
return (ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, pinfo, info_len));
|
||||||
|
}
|
||||||
|
else if (pid == 1) {
|
||||||
|
|
||||||
|
// 'U' frame other than 'UI'.
|
||||||
|
// The control field contains: P/F OPCODE{3) C x x
|
||||||
|
|
||||||
|
int control = GET_CONTROL(hdr);
|
||||||
|
cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
|
||||||
|
int axpid = 0; // unused for U other than UI.
|
||||||
|
ax25_frame_type_t ftype;
|
||||||
|
switch ((control >> 3) & 0x7) {
|
||||||
|
case 0: ftype = frame_type_U_SABM; break;
|
||||||
|
case 1: ftype = frame_type_U_DISC; break;
|
||||||
|
case 2: ftype = frame_type_U_DM; break;
|
||||||
|
case 3: ftype = frame_type_U_UA; break;
|
||||||
|
case 4: ftype = frame_type_U_FRMR; break;
|
||||||
|
case 5: ftype = frame_type_U_UI; axpid = 0xf0; break; // Should not happen with IL2P pid == 1.
|
||||||
|
case 6: ftype = frame_type_U_XID; break;
|
||||||
|
default: ftype = frame_type_U_TEST; break;
|
||||||
|
}
|
||||||
|
int pf = (control >> 6) & 0x01;
|
||||||
|
unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later.
|
||||||
|
int info_len = 0;
|
||||||
|
return (ax25_u_frame (addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len));
|
||||||
|
}
|
||||||
|
else if (ui) {
|
||||||
|
|
||||||
|
// 'UI' frame.
|
||||||
|
// The control field contains: P/F OPCODE{3) C x x
|
||||||
|
|
||||||
|
int control = GET_CONTROL(hdr);
|
||||||
|
cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
|
||||||
|
ax25_frame_type_t ftype = frame_type_U_UI;
|
||||||
|
int pf = (control >> 6) & 0x01;
|
||||||
|
int axpid = decode_pid(GET_PID(hdr));
|
||||||
|
unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later.
|
||||||
|
int info_len = 0;
|
||||||
|
return (ax25_u_frame (addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// 'I' frame.
|
||||||
|
// The control field contains: P/F N(R) N(S)
|
||||||
|
|
||||||
|
int control = GET_CONTROL(hdr);
|
||||||
|
cmdres_t cr = cr_cmd; // Always command.
|
||||||
|
int pf = (control >> 6) & 0x01;
|
||||||
|
int nr = (control >> 3) & 0x7;
|
||||||
|
int ns = control & 0x7;
|
||||||
|
int modulo = 8;
|
||||||
|
int axpid = decode_pid(GET_PID(hdr));
|
||||||
|
unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later.
|
||||||
|
int info_len = 0;
|
||||||
|
return (ax25_i_frame (addrs, num_addr, cr, modulo, nr, ns, pf, axpid, pinfo, info_len));
|
||||||
|
}
|
||||||
|
return (NULL); // unreachable but avoid warning.
|
||||||
|
|
||||||
|
} // end
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_type_0_header
|
||||||
|
*
|
||||||
|
* Purpose: Attempt to create type 0 header from packet object.
|
||||||
|
*
|
||||||
|
* Inputs: pp - Packet object.
|
||||||
|
*
|
||||||
|
* max_fec - 1 to use maximum FEC symbols, 0 for automatic.
|
||||||
|
*
|
||||||
|
* Outputs: hdr - IL2P header with no scrambling or parity symbols.
|
||||||
|
* Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes.
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes for information part or -1 for failure.
|
||||||
|
* In case of failure, fall back to type 0 transparent encapsulation.
|
||||||
|
*
|
||||||
|
* Description: The type 0 header is used when it is not one of the restricted cases
|
||||||
|
* covered by the type 1 header.
|
||||||
|
* The AX.25 frame is put in the payload.
|
||||||
|
* This will cover: more than one address, mod 128 sequences, etc.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_type_0_header (packet_t pp, int max_fec, unsigned char *hdr)
|
||||||
|
{
|
||||||
|
memset (hdr, 0, IL2P_HEADER_SIZE);
|
||||||
|
|
||||||
|
// Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10]
|
||||||
|
|
||||||
|
SET_FEC_LEVEL(hdr, max_fec);
|
||||||
|
SET_HDR_TYPE(hdr, 0);
|
||||||
|
|
||||||
|
int frame_len = ax25_get_frame_len (pp);
|
||||||
|
|
||||||
|
if (frame_len < 14 || frame_len > IL2P_MAX_PAYLOAD_SIZE) {
|
||||||
|
return (-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SET_PAYLOAD_BYTE_COUNT(hdr, frame_len);
|
||||||
|
return (frame_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************
|
||||||
|
*
|
||||||
|
* Name: il2p_get_header_attributes
|
||||||
|
*
|
||||||
|
* Purpose: Extract a few attributes from an IL2p header.
|
||||||
|
*
|
||||||
|
* Inputs: hdr - IL2P header structure.
|
||||||
|
*
|
||||||
|
* Outputs: hdr_type - 0 or 1.
|
||||||
|
*
|
||||||
|
* max_fec - 0 for automatic or 1 for fixed maximum size.
|
||||||
|
*
|
||||||
|
* Returns: Payload byte count. (actual payload size, not the larger encoded format)
|
||||||
|
*
|
||||||
|
***********************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
int il2p_get_header_attributes (unsigned char *hdr, int *hdr_type, int *max_fec)
|
||||||
|
{
|
||||||
|
*hdr_type = GET_HDR_TYPE(hdr);
|
||||||
|
*max_fec = GET_FEC_LEVEL(hdr);
|
||||||
|
return(GET_PAYLOAD_BYTE_COUNT(hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************
|
||||||
|
*
|
||||||
|
* Name: il2p_clarify_header
|
||||||
|
*
|
||||||
|
* Purpose: Convert received header to usable form.
|
||||||
|
* This involves RS FEC then descrambling.
|
||||||
|
*
|
||||||
|
* Inputs: rec_hdr - Header as received over the radio.
|
||||||
|
*
|
||||||
|
* Outputs: corrected_descrambled_hdr - After RS FEC and unscrambling.
|
||||||
|
*
|
||||||
|
* Returns: Number of symbols that were corrected:
|
||||||
|
* 0 = No errors
|
||||||
|
* 1 = Single symbol corrected.
|
||||||
|
* <0 = Unable to obtain good header.
|
||||||
|
*
|
||||||
|
***********************************************************************************/
|
||||||
|
|
||||||
|
int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descrambled_hdr)
|
||||||
|
{
|
||||||
|
unsigned char corrected[IL2P_HEADER_SIZE+IL2P_HEADER_PARITY];
|
||||||
|
|
||||||
|
int e = il2p_decode_rs (rec_hdr, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, corrected);
|
||||||
|
|
||||||
|
il2p_descramble_block (corrected, corrected_descrambled_hdr, IL2P_HEADER_SIZE);
|
||||||
|
|
||||||
|
return (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// end il2p_header.c
|
|
@ -0,0 +1,226 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "fx25.h" // For Reed Solomon stuff.
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
|
// Interesting related stuff:
|
||||||
|
// https://www.kernel.org/doc/html/v4.15/core-api/librs.html
|
||||||
|
// https://berthub.eu/articles/posts/reed-solomon-for-programmers/
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_NROOTS 16
|
||||||
|
|
||||||
|
#define NTAB 5
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int symsize; // Symbol size, bits (1-8). Always 8 for this application.
|
||||||
|
int genpoly; // Field generator polynomial coefficients.
|
||||||
|
int fcs; // First root of RS code generator polynomial, index form.
|
||||||
|
// FX.25 uses 1 but IL2P uses 0.
|
||||||
|
int prim; // Primitive element to generate polynomial roots.
|
||||||
|
int nroots; // RS code generator polynomial degree (number of roots).
|
||||||
|
// Same as number of check bytes added.
|
||||||
|
struct rs *rs; // Pointer to RS codec control block. Filled in at init time.
|
||||||
|
} Tab[NTAB] = {
|
||||||
|
{8, 0x11d, 0, 1, 2, NULL }, // 2 parity
|
||||||
|
{8, 0x11d, 0, 1, 4, NULL }, // 4 parity
|
||||||
|
{8, 0x11d, 0, 1, 6, NULL }, // 6 parity
|
||||||
|
{8, 0x11d, 0, 1, 8, NULL }, // 8 parity
|
||||||
|
{8, 0x11d, 0, 1, 16, NULL }, // 16 parity
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int g_il2p_debug = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_init
|
||||||
|
*
|
||||||
|
* Purpose: This must be called at application start up time.
|
||||||
|
* It sets up tables for the Reed-Solomon functions.
|
||||||
|
*
|
||||||
|
* Inputs: debug - Enable debug output.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void il2p_init (int il2p_debug)
|
||||||
|
{
|
||||||
|
g_il2p_debug = il2p_debug;
|
||||||
|
|
||||||
|
for (int i = 0 ; i < NTAB ; i++) {
|
||||||
|
assert (Tab[i].nroots <= MAX_NROOTS);
|
||||||
|
Tab[i].rs = INIT_RS(Tab[i].symsize, Tab[i].genpoly, Tab[i].fcs, Tab[i].prim, Tab[i].nroots);
|
||||||
|
if (Tab[i].rs == NULL) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf("IL2P internal error: init_rs_char failed!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end il2p_init
|
||||||
|
|
||||||
|
|
||||||
|
int il2p_get_debug(void)
|
||||||
|
{
|
||||||
|
return (g_il2p_debug);
|
||||||
|
}
|
||||||
|
void il2p_set_debug(int debug)
|
||||||
|
{
|
||||||
|
g_il2p_debug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Find RS codec control block for specified number of parity symbols.
|
||||||
|
|
||||||
|
struct rs *il2p_find_rs(int nparity)
|
||||||
|
{
|
||||||
|
for (int n = 0; n < NTAB; n++) {
|
||||||
|
if (Tab[n].nroots == nparity) {
|
||||||
|
return (Tab[n].rs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P INTERNAL ERROR: il2p_find_rs: control block not found for nparity = %d.\n", nparity);
|
||||||
|
return (Tab[0].rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: void il2p_encode_rs
|
||||||
|
*
|
||||||
|
* Purpose: Add parity symbols to a block of data.
|
||||||
|
*
|
||||||
|
* Inputs: tx_data Header or other data to transmit.
|
||||||
|
* data_size Number of data bytes in above.
|
||||||
|
* num_parity Number of parity symbols to add.
|
||||||
|
* Maximum of IL2P_MAX_PARITY_SYMBOLS.
|
||||||
|
*
|
||||||
|
* Outputs: parity_out Specified number of parity symbols
|
||||||
|
*
|
||||||
|
* Restriction: data_size + num_parity <= 255 which is the RS block size.
|
||||||
|
* The caller must ensure this.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void il2p_encode_rs (unsigned char *tx_data, int data_size, int num_parity, unsigned char *parity_out)
|
||||||
|
{
|
||||||
|
assert (data_size >= 1);
|
||||||
|
assert (num_parity == 2 || num_parity == 4 || num_parity == 6 || num_parity == 8 || num_parity == 16);
|
||||||
|
assert (data_size + num_parity <= 255);
|
||||||
|
|
||||||
|
unsigned char rs_block[FX25_BLOCK_SIZE];
|
||||||
|
memset (rs_block, 0, sizeof(rs_block));
|
||||||
|
memcpy (rs_block + sizeof(rs_block) - data_size - num_parity, tx_data, data_size);
|
||||||
|
ENCODE_RS (il2p_find_rs(num_parity), rs_block, parity_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: void il2p_decode_rs
|
||||||
|
*
|
||||||
|
* Purpose: Check and attempt to fix block with FEC.
|
||||||
|
*
|
||||||
|
* Inputs: rec_block Received block composed of data and parity.
|
||||||
|
* Total size is sum of following two parameters.
|
||||||
|
* data_size Number of data bytes in above.
|
||||||
|
* num_parity Number of parity symbols (bytes) in above.
|
||||||
|
*
|
||||||
|
* Outputs: out Original with possible corrections applied.
|
||||||
|
* data_size bytes.
|
||||||
|
*
|
||||||
|
* Returns: -1 for unrecoverable.
|
||||||
|
* >= 0 for sucess. Number of symbols corrected.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_decode_rs (unsigned char *rec_block, int data_size, int num_parity, unsigned char *out)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Use zero padding in front if data size is too small.
|
||||||
|
|
||||||
|
int n = data_size + num_parity; // total size in.
|
||||||
|
|
||||||
|
unsigned char rs_block[FX25_BLOCK_SIZE];
|
||||||
|
|
||||||
|
// We could probably do this more efficiently by skipping the
|
||||||
|
// processing of the bytes known to be zero. Good enough for now.
|
||||||
|
|
||||||
|
memset (rs_block, 0, sizeof(rs_block) - n);
|
||||||
|
memcpy (rs_block + sizeof(rs_block) - n, rec_block, n);
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 3) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("============================== il2p_decode_rs ==============================\n");
|
||||||
|
dw_printf ("%d filler zeros, %d data, %d parity\n", (int)(sizeof(rs_block) - n), data_size, num_parity);
|
||||||
|
fx_hex_dump (rs_block, sizeof (rs_block));
|
||||||
|
}
|
||||||
|
|
||||||
|
int derrlocs[FX25_MAX_CHECK]; // Half would probably be OK.
|
||||||
|
|
||||||
|
int derrors = DECODE_RS(il2p_find_rs(num_parity), rs_block, derrlocs, 0);
|
||||||
|
memcpy (out, rs_block + sizeof(rs_block) - n, data_size);
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 3) {
|
||||||
|
if (derrors == 0) {
|
||||||
|
dw_printf ("No errors reported for RS block.\n");
|
||||||
|
}
|
||||||
|
else if (derrors > 0) {
|
||||||
|
dw_printf ("%d errors fixed in positions:\n", derrors);
|
||||||
|
for (int j = 0; j < derrors; j++) {
|
||||||
|
dw_printf (" %3d (0x%02x)\n", derrlocs[j] , derrlocs[j]);
|
||||||
|
}
|
||||||
|
fx_hex_dump (rs_block, sizeof (rs_block));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is possible to have a situation where too many errors are
|
||||||
|
// present but the algorithm could get a good code block by "fixing"
|
||||||
|
// one of the padding bytes that should be 0.
|
||||||
|
|
||||||
|
for (int i = 0; i < derrors; i++) {
|
||||||
|
if (derrlocs[i] < sizeof(rs_block) - n) {
|
||||||
|
if (il2p_get_debug() >= 3) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("RS DECODE ERROR! Padding position %d should be 0 but it was set to %02x.\n", derrlocs[i], rs_block[derrlocs[i]]);
|
||||||
|
}
|
||||||
|
derrors = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 3) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("============================== il2p_decode_rs returns %d ==============================\n", derrors);
|
||||||
|
}
|
||||||
|
return (derrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// end il2p_init.c
|
|
@ -0,0 +1,298 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* File: il2p_payload.c
|
||||||
|
*
|
||||||
|
* Purpose: Functions dealing with the payload.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_payload_compute
|
||||||
|
*
|
||||||
|
* Purpose: Compute number and sizes of data blocks based on total size.
|
||||||
|
*
|
||||||
|
* Inputs: payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE)
|
||||||
|
* max_fec true for 16 parity symbols, false for automatic.
|
||||||
|
*
|
||||||
|
* Outputs: *p Payload block sizes and counts.
|
||||||
|
* Number of parity symbols per block.
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes in the encoded format.
|
||||||
|
* Could be 0 for no payload blocks.
|
||||||
|
* -1 for error (i.e. invalid unencoded size: <0 or >1023)
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_payload_compute (il2p_payload_properties_t *p, int payload_size, int max_fec)
|
||||||
|
{
|
||||||
|
memset (p, 0, sizeof(il2p_payload_properties_t));
|
||||||
|
|
||||||
|
if (payload_size < 0 || payload_size > IL2P_MAX_PAYLOAD_SIZE) {
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (payload_size == 0) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_fec) {
|
||||||
|
p->payload_byte_count = payload_size;
|
||||||
|
p->payload_block_count = (p->payload_byte_count + 238) / 239;
|
||||||
|
p->small_block_size = p->payload_byte_count / p->payload_block_count;
|
||||||
|
p->large_block_size = p->small_block_size + 1;
|
||||||
|
p->large_block_count = p->payload_byte_count - (p->payload_block_count * p->small_block_size);
|
||||||
|
p->small_block_count = p->payload_block_count - p->large_block_count;
|
||||||
|
p->parity_symbols_per_block = 16;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p->payload_byte_count = payload_size;
|
||||||
|
p->payload_block_count = (p->payload_byte_count + 246) / 247;
|
||||||
|
p->small_block_size = p->payload_byte_count / p->payload_block_count;
|
||||||
|
p->large_block_size = p->small_block_size + 1;
|
||||||
|
p->large_block_count = p->payload_byte_count - (p->payload_block_count * p->small_block_size);
|
||||||
|
p->small_block_count = p->payload_block_count - p->large_block_count;
|
||||||
|
//p->parity_symbols_per_block = (p->small_block_size / 32) + 2; // Looks like error in documentation
|
||||||
|
|
||||||
|
// It would work if the number of parity symbols was based on large block size.
|
||||||
|
|
||||||
|
if (p->small_block_size <= 61) p->parity_symbols_per_block = 2;
|
||||||
|
else if (p->small_block_size <= 123) p->parity_symbols_per_block = 4;
|
||||||
|
else if (p->small_block_size <= 185) p->parity_symbols_per_block = 6;
|
||||||
|
else if (p->small_block_size <= 247) p->parity_symbols_per_block = 8;
|
||||||
|
else {
|
||||||
|
// Should not happen. But just in case...
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P parity symbol per payload block error. small_block_size = %d\n", p->small_block_size);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the total size for the encoded format.
|
||||||
|
|
||||||
|
return (p->small_block_count * (p->small_block_size + p->parity_symbols_per_block) +
|
||||||
|
p->large_block_count * (p->large_block_size + p->parity_symbols_per_block));
|
||||||
|
|
||||||
|
} // end il2p_payload_compute
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_encode_payload
|
||||||
|
*
|
||||||
|
* Purpose: Split payload into multiple blocks such that each set
|
||||||
|
* of data and parity symbols fit into a 255 byte RS block.
|
||||||
|
*
|
||||||
|
* Inputs: *payload Array of bytes.
|
||||||
|
* payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE)
|
||||||
|
* max_fec true for 16 parity symbols, false for automatic.
|
||||||
|
*
|
||||||
|
* Outputs: *enc Encoded payload for transmission.
|
||||||
|
* Up to IL2P_MAX_ENCODED_SIZE bytes.
|
||||||
|
*
|
||||||
|
* Returns: -1 for error (i.e. invalid size)
|
||||||
|
* 0 for no blocks. (i.e. size zero)
|
||||||
|
* Number of bytes generated. Maximum IL2P_MAX_ENCODED_SIZE.
|
||||||
|
*
|
||||||
|
* Note: I interpretted the protocol spec as saying the LFSR state is retained
|
||||||
|
* between data blocks. During interoperability testing, I found that
|
||||||
|
* was not the case. It is reset for each data block.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
int il2p_encode_payload (unsigned char *payload, int payload_size, int max_fec, unsigned char *enc)
|
||||||
|
{
|
||||||
|
if (payload_size > IL2P_MAX_PAYLOAD_SIZE) return (-1);
|
||||||
|
if (payload_size == 0) return (0);
|
||||||
|
|
||||||
|
// Determine number of blocks and sizes.
|
||||||
|
|
||||||
|
il2p_payload_properties_t ipp;
|
||||||
|
int e;
|
||||||
|
e = il2p_payload_compute (&ipp, payload_size, max_fec);
|
||||||
|
if (e <= 0) {
|
||||||
|
return (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *pin = payload;
|
||||||
|
unsigned char *pout = enc;
|
||||||
|
int encoded_length = 0;
|
||||||
|
unsigned char scram[256];
|
||||||
|
unsigned char parity[IL2P_MAX_PARITY_SYMBOLS];
|
||||||
|
|
||||||
|
// First the large blocks.
|
||||||
|
|
||||||
|
for (int b = 0; b < ipp.large_block_count; b++) {
|
||||||
|
|
||||||
|
il2p_scramble_block (pin, scram, ipp.large_block_size);
|
||||||
|
memcpy (pout, scram, ipp.large_block_size);
|
||||||
|
pin += ipp.large_block_size;
|
||||||
|
pout += ipp.large_block_size;
|
||||||
|
encoded_length += ipp.large_block_size;
|
||||||
|
il2p_encode_rs (scram, ipp.large_block_size, ipp.parity_symbols_per_block, parity);
|
||||||
|
memcpy (pout, parity, ipp.parity_symbols_per_block);
|
||||||
|
pout += ipp.parity_symbols_per_block;
|
||||||
|
encoded_length += ipp.parity_symbols_per_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then the small blocks.
|
||||||
|
|
||||||
|
for (int b = 0; b < ipp.small_block_count; b++) {
|
||||||
|
|
||||||
|
il2p_scramble_block (pin, scram, ipp.small_block_size);
|
||||||
|
memcpy (pout, scram, ipp.small_block_size);
|
||||||
|
pin += ipp.small_block_size;
|
||||||
|
pout += ipp.small_block_size;
|
||||||
|
encoded_length += ipp.small_block_size;
|
||||||
|
il2p_encode_rs (scram, ipp.small_block_size, ipp.parity_symbols_per_block, parity);
|
||||||
|
memcpy (pout, parity, ipp.parity_symbols_per_block);
|
||||||
|
pout += ipp.parity_symbols_per_block;
|
||||||
|
encoded_length += ipp.parity_symbols_per_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (encoded_length);
|
||||||
|
|
||||||
|
} // end il2p_encode_payload
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_decode_payload
|
||||||
|
*
|
||||||
|
* Purpose: Extract original data from encoded payload.
|
||||||
|
*
|
||||||
|
* Inputs: received Array of bytes. Size is unknown but in practice it
|
||||||
|
* must not exceeed IL2P_MAX_ENCODED_SIZE.
|
||||||
|
* payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE)
|
||||||
|
* Expected result size based on header.
|
||||||
|
* max_fec true for 16 parity symbols, false for automatic.
|
||||||
|
*
|
||||||
|
* Outputs: payload_out Recovered payload.
|
||||||
|
*
|
||||||
|
* In/Out: symbols_corrected Number of symbols corrected.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes extracted. Should be same as payload_size going in.
|
||||||
|
* -3 for unexpected internal inconsistency.
|
||||||
|
* -2 for unable to recover from signal corruption.
|
||||||
|
* -1 for invalid size.
|
||||||
|
* 0 for no blocks. (i.e. size zero)
|
||||||
|
*
|
||||||
|
* Description: Each block is scrambled separately but the LSFR state is carried
|
||||||
|
* from the first payload block to the next.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_decode_payload (unsigned char *received, int payload_size, int max_fec, unsigned char *payload_out, int *symbols_corrected)
|
||||||
|
{
|
||||||
|
// Determine number of blocks and sizes.
|
||||||
|
|
||||||
|
il2p_payload_properties_t ipp;
|
||||||
|
int e;
|
||||||
|
e = il2p_payload_compute (&ipp, payload_size, max_fec);
|
||||||
|
if (e <= 0) {
|
||||||
|
return (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *pin = received;
|
||||||
|
unsigned char *pout = payload_out;
|
||||||
|
int decoded_length = 0;
|
||||||
|
int failed = 0;
|
||||||
|
|
||||||
|
// First the large blocks.
|
||||||
|
|
||||||
|
for (int b = 0; b < ipp.large_block_count; b++) {
|
||||||
|
unsigned char corrected_block[255];
|
||||||
|
int e = il2p_decode_rs (pin, ipp.large_block_size, ipp.parity_symbols_per_block, corrected_block);
|
||||||
|
|
||||||
|
// dw_printf ("%s:%d: large block decode_rs returned status = %d\n", __FILE__, __LINE__, e);
|
||||||
|
|
||||||
|
if (e < 0) failed = 1;
|
||||||
|
*symbols_corrected += e;
|
||||||
|
|
||||||
|
il2p_descramble_block (corrected_block, pout, ipp.large_block_size);
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 2) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("Descrambled large payload block, %d bytes:\n", ipp.large_block_size);
|
||||||
|
fx_hex_dump(pout, ipp.large_block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pin += ipp.large_block_size + ipp.parity_symbols_per_block;
|
||||||
|
pout += ipp.large_block_size;
|
||||||
|
decoded_length += ipp.large_block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then the small blocks.
|
||||||
|
|
||||||
|
for (int b = 0; b < ipp.small_block_count; b++) {
|
||||||
|
unsigned char corrected_block[255];
|
||||||
|
int e = il2p_decode_rs (pin, ipp.small_block_size, ipp.parity_symbols_per_block, corrected_block);
|
||||||
|
|
||||||
|
// dw_printf ("%s:%d: small block decode_rs returned status = %d\n", __FILE__, __LINE__, e);
|
||||||
|
|
||||||
|
if (e < 0) failed = 1;
|
||||||
|
*symbols_corrected += e;
|
||||||
|
|
||||||
|
il2p_descramble_block (corrected_block, pout, ipp.small_block_size);
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 2) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("Descrambled small payload block, %d bytes:\n", ipp.small_block_size);
|
||||||
|
fx_hex_dump(pout, ipp.small_block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pin += ipp.small_block_size + ipp.parity_symbols_per_block;
|
||||||
|
pout += ipp.small_block_size;
|
||||||
|
decoded_length += ipp.small_block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failed) {
|
||||||
|
//dw_printf ("%s:%d: failed = %0x\n", __FILE__, __LINE__, failed);
|
||||||
|
return (-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoded_length != payload_size) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P Internal error: decoded_length = %d, payload_size = %d\n", decoded_length, payload_size);
|
||||||
|
return (-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (decoded_length);
|
||||||
|
|
||||||
|
} // end il2p_decode_payload
|
||||||
|
|
||||||
|
// end il2p_payload.c
|
||||||
|
|
|
@ -0,0 +1,317 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* File: il2p_payload.c
|
||||||
|
*
|
||||||
|
* Purpose: Functions dealing with the payload.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_payload_compute
|
||||||
|
*
|
||||||
|
* Purpose: Compute number and sizes of data blocks based on total size.
|
||||||
|
*
|
||||||
|
* Inputs: payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE)
|
||||||
|
* max_fec true for 16 parity symbols, false for automatic.
|
||||||
|
*
|
||||||
|
* Outputs: *p Payload block sizes and counts.
|
||||||
|
* Number of parity symbols per block.
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes in the encoded format.
|
||||||
|
* Could be 0 for no payload blocks.
|
||||||
|
* -1 for error (i.e. invalid unencoded size: <0 or >1023)
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_payload_compute (il2p_payload_properties_t *p, int payload_size, int max_fec)
|
||||||
|
{
|
||||||
|
memset (p, 0, sizeof(il2p_payload_properties_t));
|
||||||
|
|
||||||
|
if (payload_size < 0 || payload_size > IL2P_MAX_PAYLOAD_SIZE) {
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (payload_size == 0) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_fec) {
|
||||||
|
p->payload_byte_count = payload_size;
|
||||||
|
p->payload_block_count = (p->payload_byte_count + 238) / 239;
|
||||||
|
p->small_block_size = p->payload_byte_count / p->payload_block_count;
|
||||||
|
p->large_block_size = p->small_block_size + 1;
|
||||||
|
p->large_block_count = p->payload_byte_count - (p->payload_block_count * p->small_block_size);
|
||||||
|
p->small_block_count = p->payload_block_count - p->large_block_count;
|
||||||
|
p->parity_symbols_per_block = 16;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p->payload_byte_count = payload_size;
|
||||||
|
p->payload_block_count = (p->payload_byte_count + 246) / 247;
|
||||||
|
p->small_block_size = p->payload_byte_count / p->payload_block_count;
|
||||||
|
p->large_block_size = p->small_block_size + 1;
|
||||||
|
p->large_block_count = p->payload_byte_count - (p->payload_block_count * p->small_block_size);
|
||||||
|
p->small_block_count = p->payload_block_count - p->large_block_count;
|
||||||
|
//p->parity_symbols_per_block = (p->small_block_size / 32) + 2; // Looks like error in documentation
|
||||||
|
|
||||||
|
// It would work if the number of parity symbols was based on large block size.
|
||||||
|
|
||||||
|
if (p->small_block_size <= 61) p->parity_symbols_per_block = 2;
|
||||||
|
else if (p->small_block_size <= 123) p->parity_symbols_per_block = 4;
|
||||||
|
else if (p->small_block_size <= 185) p->parity_symbols_per_block = 6;
|
||||||
|
else if (p->small_block_size <= 247) p->parity_symbols_per_block = 8;
|
||||||
|
else {
|
||||||
|
// Should not happen. But just in case...
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P parity symbol per payload block error. small_block_size = %d\n", p->small_block_size);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the total size for the encoded format.
|
||||||
|
|
||||||
|
return (p->small_block_count * (p->small_block_size + p->parity_symbols_per_block) +
|
||||||
|
p->large_block_count * (p->large_block_size + p->parity_symbols_per_block));
|
||||||
|
|
||||||
|
} // end il2p_payload_compute
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_encode_payload
|
||||||
|
*
|
||||||
|
* Purpose: Split payload into multiple blocks such that each set
|
||||||
|
* of data and parity symbols fit into a 255 byte RS block.
|
||||||
|
*
|
||||||
|
* Inputs: *payload Array of bytes.
|
||||||
|
* payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE)
|
||||||
|
* max_fec true for 16 parity symbols, false for automatic.
|
||||||
|
*
|
||||||
|
* Outputs: *enc Encoded payload for transmission.
|
||||||
|
* Up to IL2P_MAX_ENCODED_SIZE bytes.
|
||||||
|
*
|
||||||
|
* Returns: -1 for error (i.e. invalid size)
|
||||||
|
* 0 for no blocks. (i.e. size zero)
|
||||||
|
* Number of bytes generated. Maximum IL2P_MAX_ENCODED_SIZE.
|
||||||
|
*
|
||||||
|
* Note: I interpretted the protocol spec as saying the LFSR state is retained
|
||||||
|
* between data blocks. During interoperability testing, I found that
|
||||||
|
* was not the case. It is reset for each data block.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
int il2p_encode_payload (unsigned char *payload, int payload_size, int max_fec, unsigned char *enc)
|
||||||
|
{
|
||||||
|
if (payload_size > IL2P_MAX_PAYLOAD_SIZE) return (-1);
|
||||||
|
if (payload_size == 0) return (0);
|
||||||
|
|
||||||
|
int tx_lsfr_state;
|
||||||
|
il2p_scramble_reset(&tx_lsfr_state);
|
||||||
|
|
||||||
|
// Determine number of blocks and sizes.
|
||||||
|
|
||||||
|
il2p_payload_properties_t ipp;
|
||||||
|
int e;
|
||||||
|
e = il2p_payload_compute (&ipp, payload_size, max_fec);
|
||||||
|
if (e <= 0) {
|
||||||
|
return (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *pin = payload;
|
||||||
|
unsigned char *pout = enc;
|
||||||
|
int encoded_length = 0;
|
||||||
|
unsigned char scram[256];
|
||||||
|
unsigned char parity[IL2P_MAX_PARITY_SYMBOLS];
|
||||||
|
|
||||||
|
// First the large blocks.
|
||||||
|
|
||||||
|
for (int b = 0; b < ipp.large_block_count; b++) {
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME experiment
|
||||||
|
il2p_scramble_reset(&tx_lsfr_state);
|
||||||
|
|
||||||
|
il2p_scramble_block (pin, scram, ipp.large_block_size, &tx_lsfr_state);
|
||||||
|
memcpy (pout, scram, ipp.large_block_size);
|
||||||
|
pin += ipp.large_block_size;
|
||||||
|
pout += ipp.large_block_size;
|
||||||
|
encoded_length += ipp.large_block_size;
|
||||||
|
il2p_encode_rs (scram, ipp.large_block_size, ipp.parity_symbols_per_block, parity);
|
||||||
|
memcpy (pout, parity, ipp.parity_symbols_per_block);
|
||||||
|
pout += ipp.parity_symbols_per_block;
|
||||||
|
encoded_length += ipp.parity_symbols_per_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then the small blocks.
|
||||||
|
|
||||||
|
for (int b = 0; b < ipp.small_block_count; b++) {
|
||||||
|
|
||||||
|
// FIXME experiment -- works - no need to pass lfsr state in and out of scramble block.
|
||||||
|
il2p_scramble_reset(&tx_lsfr_state);
|
||||||
|
|
||||||
|
il2p_scramble_block (pin, scram, ipp.small_block_size, &tx_lsfr_state);
|
||||||
|
memcpy (pout, scram, ipp.small_block_size);
|
||||||
|
pin += ipp.small_block_size;
|
||||||
|
pout += ipp.small_block_size;
|
||||||
|
encoded_length += ipp.small_block_size;
|
||||||
|
il2p_encode_rs (scram, ipp.small_block_size, ipp.parity_symbols_per_block, parity);
|
||||||
|
memcpy (pout, parity, ipp.parity_symbols_per_block);
|
||||||
|
pout += ipp.parity_symbols_per_block;
|
||||||
|
encoded_length += ipp.parity_symbols_per_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (encoded_length);
|
||||||
|
|
||||||
|
} // end il2p_encode_payload
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_decode_payload
|
||||||
|
*
|
||||||
|
* Purpose: Extract original data from encoded payload.
|
||||||
|
*
|
||||||
|
* Inputs: received Array of bytes. Size is unknown but in practice it
|
||||||
|
* must not exceeed IL2P_MAX_ENCODED_SIZE.
|
||||||
|
* payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE)
|
||||||
|
* Expected result size based on header.
|
||||||
|
* max_fec true for 16 parity symbols, false for automatic.
|
||||||
|
*
|
||||||
|
* Outputs: payload_out Recovered payload.
|
||||||
|
*
|
||||||
|
* In/Out: symbols_corrected Number of symbols corrected.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes extracted. Should be same as payload_size going in.
|
||||||
|
* -3 for unexpected internal inconsistency.
|
||||||
|
* -2 for unable to recover from signal corruption.
|
||||||
|
* -1 for invalid size.
|
||||||
|
* 0 for no blocks. (i.e. size zero)
|
||||||
|
*
|
||||||
|
* Description: Each block is scrambled separately but the LSFR state is carried
|
||||||
|
* from the first payload block to the next.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_decode_payload (unsigned char *received, int payload_size, int max_fec, unsigned char *payload_out, int *symbols_corrected)
|
||||||
|
{
|
||||||
|
// Determine number of blocks and sizes.
|
||||||
|
|
||||||
|
il2p_payload_properties_t ipp;
|
||||||
|
int e;
|
||||||
|
e = il2p_payload_compute (&ipp, payload_size, max_fec);
|
||||||
|
if (e <= 0) {
|
||||||
|
return (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *pin = received;
|
||||||
|
unsigned char *pout = payload_out;
|
||||||
|
int decoded_length = 0;
|
||||||
|
int failed = 0;
|
||||||
|
int rx_lfsr_state;
|
||||||
|
il2p_descramble_reset(&rx_lfsr_state);
|
||||||
|
|
||||||
|
|
||||||
|
// First the large blocks.
|
||||||
|
|
||||||
|
for (int b = 0; b < ipp.large_block_count; b++) {
|
||||||
|
unsigned char corrected_block[255];
|
||||||
|
int e = il2p_decode_rs (pin, ipp.large_block_size, ipp.parity_symbols_per_block, corrected_block);
|
||||||
|
|
||||||
|
// dw_printf ("%s:%d: large block decode_rs returned status = %d\n", __FILE__, __LINE__, e);
|
||||||
|
|
||||||
|
if (e < 0) failed = 1;
|
||||||
|
*symbols_corrected += e;
|
||||||
|
|
||||||
|
// FIXME - simplify
|
||||||
|
il2p_descramble_reset(&rx_lfsr_state);
|
||||||
|
|
||||||
|
il2p_descramble_block (corrected_block, pout, ipp.large_block_size, &rx_lfsr_state);
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 2) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("Descrambled large payload block, %d bytes:\n", ipp.large_block_size);
|
||||||
|
fx_hex_dump(pout, ipp.large_block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pin += ipp.large_block_size + ipp.parity_symbols_per_block;
|
||||||
|
pout += ipp.large_block_size;
|
||||||
|
decoded_length += ipp.large_block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then the small blocks.
|
||||||
|
|
||||||
|
for (int b = 0; b < ipp.small_block_count; b++) {
|
||||||
|
unsigned char corrected_block[255];
|
||||||
|
int e = il2p_decode_rs (pin, ipp.small_block_size, ipp.parity_symbols_per_block, corrected_block);
|
||||||
|
|
||||||
|
// dw_printf ("%s:%d: small block decode_rs returned status = %d\n", __FILE__, __LINE__, e);
|
||||||
|
|
||||||
|
if (e < 0) failed = 1;
|
||||||
|
*symbols_corrected += e;
|
||||||
|
|
||||||
|
// FIXME - just reset in descramble block - no need to pass around as argument.
|
||||||
|
il2p_descramble_reset(&rx_lfsr_state);
|
||||||
|
|
||||||
|
il2p_descramble_block (corrected_block, pout, ipp.small_block_size, &rx_lfsr_state);
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 2) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("Descrambled small payload block, %d bytes:\n", ipp.small_block_size);
|
||||||
|
fx_hex_dump(pout, ipp.small_block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pin += ipp.small_block_size + ipp.parity_symbols_per_block;
|
||||||
|
pout += ipp.small_block_size;
|
||||||
|
decoded_length += ipp.small_block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failed) {
|
||||||
|
//dw_printf ("%s:%d: failed = %0x\n", __FILE__, __LINE__, failed);
|
||||||
|
return (-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoded_length != payload_size) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P Internal error: decoded_length = %d, payload_size = %d\n", decoded_length, payload_size);
|
||||||
|
return (-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (decoded_length);
|
||||||
|
|
||||||
|
} // end il2p_decode_payload
|
||||||
|
|
||||||
|
// end il2p_payload.c
|
||||||
|
|
|
@ -0,0 +1,276 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
*
|
||||||
|
* File: il2p_rec.c
|
||||||
|
*
|
||||||
|
* Purpose: Extract IL2P frames from a stream of bits and process them.
|
||||||
|
*
|
||||||
|
* References: http://tarpn.net/t/il2p/il2p-specification0-4.pdf
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "il2p.h"
|
||||||
|
#include "multi_modem.h"
|
||||||
|
#include "demod.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct il2p_context_s {
|
||||||
|
|
||||||
|
enum { IL2P_SEARCHING=0, IL2P_HEADER, IL2P_PAYLOAD, IL2P_DECODE } state;
|
||||||
|
|
||||||
|
unsigned int acc; // Accumulate most recent 24 bits for sync word matching.
|
||||||
|
// Lower 8 bits are also used for accumulating bytes for
|
||||||
|
// the header and payload.
|
||||||
|
|
||||||
|
int bc; // Bit counter so we know when a complete byte has been accumulated.
|
||||||
|
|
||||||
|
int polarity; // 1 if opposite of expected polarity.
|
||||||
|
|
||||||
|
unsigned char shdr[IL2P_HEADER_SIZE+IL2P_HEADER_PARITY];
|
||||||
|
// Scrambled header as received over the radio. Includes parity.
|
||||||
|
int hc; // Number if bytes placed in above.
|
||||||
|
|
||||||
|
unsigned char uhdr[IL2P_HEADER_SIZE]; // Header after FEC and unscrambling.
|
||||||
|
|
||||||
|
int eplen; // Encoded payload length. This is not the nuumber from
|
||||||
|
// from the header but rather the number of encoded bytes to gather.
|
||||||
|
|
||||||
|
unsigned char spayload[IL2P_MAX_ENCODED_PAYLOAD_SIZE];
|
||||||
|
// Scrambled and encoded payload as received over the radio.
|
||||||
|
int pc; // Number of bytes placed in above.
|
||||||
|
|
||||||
|
int corrected; // Number of symbols corrected by RS FEC.
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct il2p_context_s *il2p_context[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************
|
||||||
|
*
|
||||||
|
* Name: il2p_rec_bit
|
||||||
|
*
|
||||||
|
* Purpose: Extract FX.25 packets from a stream of bits.
|
||||||
|
*
|
||||||
|
* Inputs: chan - Channel number.
|
||||||
|
*
|
||||||
|
* subchan - This allows multiple demodulators per channel.
|
||||||
|
*
|
||||||
|
* slice - Allows multiple slicers per demodulator (subchannel).
|
||||||
|
*
|
||||||
|
* dbit - One bit from the received data stream.
|
||||||
|
*
|
||||||
|
* Description: This is called once for each received bit.
|
||||||
|
* For each valid packet, process_rec_frame() is called for further processing.
|
||||||
|
* It can gather multiple candidates from different parallel demodulators
|
||||||
|
* ("subchannels") and slicers, then decide which one is the best.
|
||||||
|
*
|
||||||
|
***********************************************************************************/
|
||||||
|
|
||||||
|
void il2p_rec_bit (int chan, int subchan, int slice, int dbit)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Allocate context blocks only as needed.
|
||||||
|
|
||||||
|
struct il2p_context_s *F = il2p_context[chan][subchan][slice];
|
||||||
|
if (F == NULL) {
|
||||||
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
|
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
||||||
|
assert (slice >= 0 && slice < MAX_SLICERS);
|
||||||
|
F = il2p_context[chan][subchan][slice] = (struct il2p_context_s *)malloc(sizeof (struct il2p_context_s));
|
||||||
|
assert (F != NULL);
|
||||||
|
memset (F, 0, sizeof(struct il2p_context_s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate most recent 24 bits received. Most recent is LSB.
|
||||||
|
|
||||||
|
F->acc = ((F->acc << 1) | (dbit & 1)) & 0x00ffffff;
|
||||||
|
|
||||||
|
// State machine to look for sync word then gather appropriate number of header and payload bytes.
|
||||||
|
|
||||||
|
switch (F->state) {
|
||||||
|
|
||||||
|
case IL2P_SEARCHING: // Searching for the sync word.
|
||||||
|
|
||||||
|
if (__builtin_popcount (F->acc ^ IL2P_SYNC_WORD) <= 1) { // allow single bit mismatch
|
||||||
|
//text_color_set (DW_COLOR_INFO);
|
||||||
|
//dw_printf ("IL2P header has normal polarity\n");
|
||||||
|
F->polarity = 0;
|
||||||
|
F->state = IL2P_HEADER;
|
||||||
|
F->bc = 0;
|
||||||
|
F->hc = 0;
|
||||||
|
}
|
||||||
|
else if (__builtin_popcount ((~F->acc & 0x00ffffff) ^ IL2P_SYNC_WORD) <= 1) {
|
||||||
|
text_color_set (DW_COLOR_INFO);
|
||||||
|
// FIXME - this pops up occasionally with random noise. Find better way to convey information.
|
||||||
|
// This also happens for each slicer - to noisy.
|
||||||
|
dw_printf ("IL2P header has reverse polarity\n");
|
||||||
|
F->polarity = 1;
|
||||||
|
F->state = IL2P_HEADER;
|
||||||
|
F->bc = 0;
|
||||||
|
F->hc = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IL2P_HEADER: // Gathering the header.
|
||||||
|
|
||||||
|
F->bc++;
|
||||||
|
if (F->bc == 8) { // full byte has been collected.
|
||||||
|
F->bc = 0;
|
||||||
|
if ( ! F->polarity) {
|
||||||
|
F->shdr[F->hc++] = F->acc & 0xff;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
F->shdr[F->hc++] = (~ F->acc) & 0xff;
|
||||||
|
}
|
||||||
|
if (F->hc == IL2P_HEADER_SIZE+IL2P_HEADER_PARITY) { // Have all of header
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 1) {
|
||||||
|
text_color_set (DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("IL2P header as received [%d.%d.%d]:\n", chan, subchan, slice);
|
||||||
|
fx_hex_dump (F->shdr, IL2P_HEADER_SIZE+IL2P_HEADER_PARITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix any errors and descramble.
|
||||||
|
F->corrected = il2p_clarify_header(F->shdr, F->uhdr);
|
||||||
|
|
||||||
|
if (F->corrected >= 0) { // Good header.
|
||||||
|
// How much payload is expected?
|
||||||
|
il2p_payload_properties_t plprop;
|
||||||
|
int hdr_type, max_fec;
|
||||||
|
int len = il2p_get_header_attributes (F->uhdr, &hdr_type, &max_fec);
|
||||||
|
|
||||||
|
F->eplen = il2p_payload_compute (&plprop, len, max_fec);
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 1) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("IL2P header after correcting %d symbols and unscrambling [%d.%d.%d]:\n", F->corrected, chan, subchan, slice);
|
||||||
|
fx_hex_dump (F->uhdr, IL2P_HEADER_SIZE);
|
||||||
|
dw_printf ("Header type %d, max fec = %d\n", hdr_type, max_fec);
|
||||||
|
dw_printf ("Need to collect %d encoded bytes for %d byte payload.\n", F->eplen, len);
|
||||||
|
dw_printf ("%d small blocks of %d and %d large blocks of %d. %d parity symbols per block\n",
|
||||||
|
plprop.small_block_count, plprop.small_block_size,
|
||||||
|
plprop.large_block_count, plprop.large_block_size, plprop.parity_symbols_per_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (F->eplen >= 1) { // Need to gather payload.
|
||||||
|
F->pc = 0;
|
||||||
|
F->state = IL2P_PAYLOAD;
|
||||||
|
}
|
||||||
|
else if (F->eplen == 0) { // No payload.
|
||||||
|
F->pc = 0;
|
||||||
|
F->state = IL2P_DECODE;
|
||||||
|
}
|
||||||
|
else { // Error.
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 1) {
|
||||||
|
text_color_set (DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P header INVALID.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
F->state = IL2P_SEARCHING;
|
||||||
|
}
|
||||||
|
} // good header after FEC.
|
||||||
|
else {
|
||||||
|
F->state = IL2P_SEARCHING; // Header failed FEC check.
|
||||||
|
}
|
||||||
|
} // entire header has been collected.
|
||||||
|
} // full byte collected.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IL2P_PAYLOAD: // Gathering the payload, if any.
|
||||||
|
|
||||||
|
F->bc++;
|
||||||
|
if (F->bc == 8) { // full byte has been collected.
|
||||||
|
F->bc = 0;
|
||||||
|
if ( ! F->polarity) {
|
||||||
|
F->spayload[F->pc++] = F->acc & 0xff;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
F->spayload[F->pc++] = (~ F->acc) & 0xff;
|
||||||
|
}
|
||||||
|
if (F->pc == F->eplen) {
|
||||||
|
|
||||||
|
// TODO?: for symmetry it seems like we should clarify the payload before combining.
|
||||||
|
|
||||||
|
F->state = IL2P_DECODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IL2P_DECODE:
|
||||||
|
// We get here after a good header and any payload has been collected.
|
||||||
|
// Processing is delayed by one bit but I think it makes the logic cleaner.
|
||||||
|
// During unit testing be sure to send an extra bit to flush it out at the end.
|
||||||
|
|
||||||
|
// in uhdr[IL2P_HEADER_SIZE]; // Header after FEC and descrambling.
|
||||||
|
|
||||||
|
// TODO?: for symmetry, we might decode the payload here and later build the frame.
|
||||||
|
|
||||||
|
{
|
||||||
|
packet_t pp = il2p_decode_header_payload (F->uhdr, F->spayload, &(F->corrected));
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 1) {
|
||||||
|
if (pp != NULL) {
|
||||||
|
ax25_hex_dump (pp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Most likely too many FEC errors.
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("FAILED to construct frame in %s.\n", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pp != NULL) {
|
||||||
|
alevel_t alevel = demod_get_audio_level (chan, subchan);
|
||||||
|
retry_t retries = F->corrected;
|
||||||
|
int is_fx25 = 1; // FIXME: distinguish fx.25 and IL2P.
|
||||||
|
// Currently this just means that a FEC mode was used.
|
||||||
|
|
||||||
|
// TODO: Could we put last 3 arguments in packet object rather than passing around separately?
|
||||||
|
|
||||||
|
multi_modem_process_rec_packet (chan, subchan, slice, pp, alevel, retries, is_fx25);
|
||||||
|
}
|
||||||
|
} // end block for local variables.
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 1) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("-----\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
F->state = IL2P_SEARCHING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
} // end of switch
|
||||||
|
|
||||||
|
} // end il2p_rec_bit
|
||||||
|
|
||||||
|
|
||||||
|
// end il2p_rec.c
|
|
@ -0,0 +1,157 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* File: il2p_scramble.c
|
||||||
|
*
|
||||||
|
* Purpose: Scramble / descramble data as specified in the IL2P protocol specification.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Scramble bits for il2p transmit.
|
||||||
|
|
||||||
|
// Note that there is a delay of 5 until the first bit comes out.
|
||||||
|
// So we need to need to ignore the first 5 out and stick in
|
||||||
|
// an extra 5 filler bits to flush at the end.
|
||||||
|
|
||||||
|
#define INIT_TX_LSFR 0x00f
|
||||||
|
|
||||||
|
static inline int scramble_bit (int in, int *state)
|
||||||
|
{
|
||||||
|
int out = ((*state >> 4) ^ *state) & 1;
|
||||||
|
*state = ( (((in ^ *state) & 1) << 9) | (*state ^ ((*state & 1) << 4)) ) >> 1;
|
||||||
|
return (out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Undo data scrambling for il2p receive.
|
||||||
|
|
||||||
|
#define INIT_RX_LSFR 0x1f0
|
||||||
|
|
||||||
|
static inline int descramble_bit (int in, int *state)
|
||||||
|
{
|
||||||
|
int out = (in ^ *state) & 1;
|
||||||
|
*state = ((*state >> 1) | ((in & 1) << 8)) ^ ((in & 1) << 3);
|
||||||
|
return (out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_scramble_block
|
||||||
|
*
|
||||||
|
* Purpose: Scramble a block before adding RS parity.
|
||||||
|
*
|
||||||
|
* Inputs: in Array of bytes.
|
||||||
|
* len Number of bytes both in and out.
|
||||||
|
*
|
||||||
|
* Outputs: out Array of bytes.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void il2p_scramble_block (unsigned char *in, unsigned char *out, int len)
|
||||||
|
{
|
||||||
|
int tx_lfsr_state = INIT_TX_LSFR;
|
||||||
|
|
||||||
|
memset (out, 0, len);
|
||||||
|
|
||||||
|
int skipping = 1; // Discard the first 5 out.
|
||||||
|
int ob = 0; // Index to output byte.
|
||||||
|
int om = 0x80; // Output bit mask;
|
||||||
|
for (int ib = 0; ib < len; ib++) {
|
||||||
|
for (int im = 0x80; im != 0; im >>= 1) {
|
||||||
|
int s = scramble_bit((in[ib] & im) != 0, &tx_lfsr_state);
|
||||||
|
if (ib == 0 && im == 0x04) skipping = 0;
|
||||||
|
if ( ! skipping) {
|
||||||
|
if (s) {
|
||||||
|
out[ob] |= om;
|
||||||
|
}
|
||||||
|
om >>= 1;
|
||||||
|
if (om == 0) {
|
||||||
|
om = 0x80;
|
||||||
|
ob++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Flush it.
|
||||||
|
|
||||||
|
// This is a relic from when I thought the state would need to
|
||||||
|
// be passed along for the next block.
|
||||||
|
// Preserve the LSFR state from before flushing.
|
||||||
|
// This might be needed as the initial state for later payload blocks.
|
||||||
|
int x = tx_lfsr_state;
|
||||||
|
for (int n = 0; n < 5; n++) {
|
||||||
|
int s = scramble_bit(0, &x);
|
||||||
|
if (s) {
|
||||||
|
out[ob] |= om;
|
||||||
|
}
|
||||||
|
om >>=1;
|
||||||
|
if (om == 0) {
|
||||||
|
om = 0x80;
|
||||||
|
ob++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end il2p_scramble_block
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_descramble_block
|
||||||
|
*
|
||||||
|
* Purpose: Descramble a block after removing RS parity.
|
||||||
|
*
|
||||||
|
* Inputs: in Array of bytes.
|
||||||
|
* len Number of bytes both in and out.
|
||||||
|
*
|
||||||
|
* Outputs: out Array of bytes.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void il2p_descramble_block (unsigned char *in, unsigned char *out, int len)
|
||||||
|
{
|
||||||
|
int rx_lfsr_state = INIT_RX_LSFR;
|
||||||
|
|
||||||
|
memset (out, 0, len);
|
||||||
|
|
||||||
|
for (int b = 0; b < len; b++) {
|
||||||
|
for (int m = 0x80; m != 0; m >>= 1) {
|
||||||
|
int d = descramble_bit((in[b] & m) != 0, &rx_lfsr_state);
|
||||||
|
if (d) {
|
||||||
|
out[b] |= m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// end il2p_scramble.c
|
|
@ -0,0 +1,194 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
// FIXME: be consistent about descramble or descramble. not unscramble.
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* File: il2p_scramble.c
|
||||||
|
*
|
||||||
|
* Purpose: Scramble / descramble data as specified in the IL2P protocol specification.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "il2p.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Scramble bits for il2p transmit.
|
||||||
|
|
||||||
|
// Note that there is a delay of 5 until the first bit comes out.
|
||||||
|
// So we need to need to ignore the first 5 out and stick in
|
||||||
|
// an extra 5 filler bits to flush at the end.
|
||||||
|
|
||||||
|
#define INIT_TX_LSFR 0x00f
|
||||||
|
|
||||||
|
static inline int scramble_bit (int in, int *state)
|
||||||
|
{
|
||||||
|
int out = ((*state >> 4) ^ *state) & 1;
|
||||||
|
*state = ( (((in ^ *state) & 1) << 9) | (*state ^ ((*state & 1) << 4)) ) >> 1;
|
||||||
|
return (out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Undo data scrambling for il2p receive.
|
||||||
|
|
||||||
|
#define INIT_RX_LSFR 0x1f0
|
||||||
|
|
||||||
|
static inline int descramble_bit (int in, int *state)
|
||||||
|
{
|
||||||
|
int out = (in ^ *state) & 1;
|
||||||
|
*state = ((*state >> 1) | ((in & 1) << 8)) ^ ((in & 1) << 3);
|
||||||
|
return (out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_scramble_reset
|
||||||
|
*
|
||||||
|
* Purpose: Reset the TX LFSR register at the beginning of a packet or payload.
|
||||||
|
*
|
||||||
|
* Outputs: lfsr_state Current state of transmit LFSR.
|
||||||
|
* This is reset at the start of the packet and is
|
||||||
|
* cummulative across the header and data blocks.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void il2p_scramble_reset (int *lfsr_state)
|
||||||
|
{
|
||||||
|
*lfsr_state = INIT_TX_LSFR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_scramble_block
|
||||||
|
*
|
||||||
|
* Purpose: Scramble a block before adding RS parity.
|
||||||
|
*
|
||||||
|
* Inputs: in Array of bytes.
|
||||||
|
* len Number of bytes both in and out.
|
||||||
|
*
|
||||||
|
* Outputs: out Array of bytes.
|
||||||
|
*
|
||||||
|
* In/Out: lfsr_state Current state of transmit LFSR. ---- FIXME --- remove this
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void il2p_scramble_block (unsigned char *in, unsigned char *out, int len, int *lfsr_state)
|
||||||
|
{
|
||||||
|
il2p_scramble_reset (lfsr_state);
|
||||||
|
|
||||||
|
memset (out, 0, len);
|
||||||
|
|
||||||
|
int skipping = 1; // Discard the first 5 out.
|
||||||
|
int ob = 0; // Index to output byte.
|
||||||
|
int om = 0x80; // Output bit mask;
|
||||||
|
for (int ib = 0; ib < len; ib++) {
|
||||||
|
for (int im = 0x80; im != 0; im >>= 1) {
|
||||||
|
int s = scramble_bit((in[ib] & im) != 0, lfsr_state);
|
||||||
|
if (ib == 0 && im == 0x04) skipping = 0;
|
||||||
|
if ( ! skipping) {
|
||||||
|
if (s) {
|
||||||
|
out[ob] |= om;
|
||||||
|
}
|
||||||
|
om >>= 1;
|
||||||
|
if (om == 0) {
|
||||||
|
om = 0x80;
|
||||||
|
ob++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Flush it.
|
||||||
|
|
||||||
|
// Preserve the LSFR state from before flushing.
|
||||||
|
// This might be needed as the initial state for later payload blocks.
|
||||||
|
int x = *lfsr_state;
|
||||||
|
for (int n = 0; n < 5; n++) {
|
||||||
|
int s = scramble_bit(0, &x);
|
||||||
|
if (s) {
|
||||||
|
out[ob] |= om;
|
||||||
|
}
|
||||||
|
om >>=1;
|
||||||
|
if (om == 0) {
|
||||||
|
om = 0x80;
|
||||||
|
ob++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end il2p_scramble_block
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_descramble_reset
|
||||||
|
*
|
||||||
|
* Purpose: Reset the RX LFSR register at the beginning of a packet or payload.
|
||||||
|
*
|
||||||
|
* Outputs: lfsr_state Current state of transmit LFSR.
|
||||||
|
* This is cummulative across the payload data blocks.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void il2p_descramble_reset (int *lfsr_state)
|
||||||
|
{
|
||||||
|
*lfsr_state = INIT_RX_LSFR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Function: il2p_descramble_block
|
||||||
|
*
|
||||||
|
* Purpose: Unscramble a block after removing RS parity.
|
||||||
|
*
|
||||||
|
* Inputs: in Array of bytes.
|
||||||
|
* len Number of bytes both in and out.
|
||||||
|
*
|
||||||
|
* Outputs: out Array of bytes.
|
||||||
|
*
|
||||||
|
* In/Out: lfsr_state Current state of receive LSFR. --- FIXME - remove this.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void il2p_descramble_block (unsigned char *in, unsigned char *out, int len, int *lfsr_state)
|
||||||
|
{
|
||||||
|
memset (out, 0, len);
|
||||||
|
|
||||||
|
for (int b = 0; b < len; b++) {
|
||||||
|
for (int m = 0x80; m != 0; m >>= 1) {
|
||||||
|
int d = descramble_bit((in[b] & m) != 0, lfsr_state);
|
||||||
|
if (d) {
|
||||||
|
out[b] |= m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// end il2p_scramble.c
|
|
@ -0,0 +1,147 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "il2p.h"
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "audio.h"
|
||||||
|
#include "gen_tone.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "il2p_send_frame"
|
||||||
|
|
||||||
|
static void send_bytes (int chan, unsigned char *b, int count, int polarity);
|
||||||
|
static void send_bit (int chan, int b, int polarity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_send_frame
|
||||||
|
*
|
||||||
|
* Purpose: Convert frames to a stream of bits in IL2P format.
|
||||||
|
*
|
||||||
|
* Inputs: chan - Audio channel number, 0 = first.
|
||||||
|
*
|
||||||
|
* pp - Pointer to packet object.
|
||||||
|
*
|
||||||
|
* max_fec - 1 to force 16 parity symbols for each payload block.
|
||||||
|
* 0 for automatic depending on block size.
|
||||||
|
*
|
||||||
|
* polarity - 0 for normal. 1 to invert signal.
|
||||||
|
* 2 special case for testing - introduce some errors to test FEC.
|
||||||
|
*
|
||||||
|
* Outputs: Bits are shipped out by calling tone_gen_put_bit().
|
||||||
|
*
|
||||||
|
* Returns: Number of bits sent including
|
||||||
|
* - Preamble (01010101...)
|
||||||
|
* - 3 byte Sync Word.
|
||||||
|
* - 15 bytes for Header.
|
||||||
|
* - Optional payload.
|
||||||
|
* The required time can be calculated by dividing this
|
||||||
|
* number by the transmit rate of bits/sec.
|
||||||
|
* -1 is returned for failure.
|
||||||
|
*
|
||||||
|
* Description: Generate an IL2P encoded frame.
|
||||||
|
*
|
||||||
|
* Assumptions: It is assumed that the tone_gen module has been
|
||||||
|
* properly initialized so that bits sent with
|
||||||
|
* tone_gen_put_bit() are processed correctly.
|
||||||
|
*
|
||||||
|
* Errors: Return -1 for error. Probably frame too large.
|
||||||
|
*
|
||||||
|
* Note: Inconsistency here. ax25 version has just a byte array
|
||||||
|
* and length going in. Here we need the full packet object.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int il2p_send_frame (int chan, packet_t pp, int max_fec, int polarity)
|
||||||
|
{
|
||||||
|
unsigned char encoded[IL2P_MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
encoded[0] = ( IL2P_SYNC_WORD >> 16 ) & 0xff;
|
||||||
|
encoded[1] = ( IL2P_SYNC_WORD >> 8 ) & 0xff;
|
||||||
|
encoded[2] = ( IL2P_SYNC_WORD ) & 0xff;
|
||||||
|
|
||||||
|
int elen = il2p_encode_frame (pp, max_fec, encoded + IL2P_SYNC_WORD_SIZE);
|
||||||
|
if (elen <= 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("IL2P: Unable to encode frame into IL2P.\n");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
elen += IL2P_SYNC_WORD_SIZE;
|
||||||
|
|
||||||
|
number_of_bits_sent[chan] = 0;
|
||||||
|
|
||||||
|
if (il2p_get_debug() >= 1) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("IL2P frame, max_fec = %d, %d encoded bytes total\n", max_fec, elen);
|
||||||
|
fx_hex_dump (encoded, elen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clobber some bytes for testing.
|
||||||
|
if (polarity >= 2) {
|
||||||
|
for (int j = 10; j < elen; j+=100) {
|
||||||
|
encoded[j] = ~ encoded[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send bits to modulator.
|
||||||
|
|
||||||
|
static unsigned char preamble = IL2P_PREAMBLE;
|
||||||
|
|
||||||
|
send_bytes (chan, &preamble, 1, polarity);
|
||||||
|
send_bytes (chan, encoded, elen, polarity);
|
||||||
|
|
||||||
|
return (number_of_bits_sent[chan]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void send_bytes (int chan, unsigned char *b, int count, int polarity)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < count; j++) {
|
||||||
|
unsigned int x = b[j];
|
||||||
|
for (int k = 0; k < 8; k++) {
|
||||||
|
send_bit (chan, (x & 0x80) != 0, polarity);
|
||||||
|
x <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NRZI would be applied for AX.25 but IL2P does not use it.
|
||||||
|
// However we do have an option to invert the signal.
|
||||||
|
// The direwolf receive implementation will automatically compensate
|
||||||
|
// for either polarity but other implementations might not.
|
||||||
|
|
||||||
|
static void send_bit (int chan, int b, int polarity)
|
||||||
|
{
|
||||||
|
tone_gen_put_bit (chan, (b ^ polarity) & 1);
|
||||||
|
number_of_bits_sent[chan]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// end il2p_send.c
|
|
@ -0,0 +1,977 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "il2p.h"
|
||||||
|
#include "ax25_pad.h"
|
||||||
|
#include "ax25_pad2.h"
|
||||||
|
#include "multi_modem.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void test_scramble(void);
|
||||||
|
static void test_rs(void);
|
||||||
|
static void test_payload(void);
|
||||||
|
static void test_example_headers(void);
|
||||||
|
static void all_frame_types(void);
|
||||||
|
static void test_serdes(void);
|
||||||
|
static void decode_bitstream(void);
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: il2p_test.c
|
||||||
|
*
|
||||||
|
* Purpose: Unit tests for IL2P protocol functions.
|
||||||
|
*
|
||||||
|
* Errors: Die if anything goes wrong.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
int main ()
|
||||||
|
{
|
||||||
|
int enable_color = 1;
|
||||||
|
text_color_init (enable_color);
|
||||||
|
|
||||||
|
int enable_debug_out = 0;
|
||||||
|
il2p_init(enable_debug_out);
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Begin IL2P unit tests.\n");
|
||||||
|
|
||||||
|
// These start simple and later complex cases build upon earlier successes.
|
||||||
|
|
||||||
|
// Test scramble and descramble.
|
||||||
|
|
||||||
|
test_scramble();
|
||||||
|
|
||||||
|
// Test Reed Solomon error correction.
|
||||||
|
|
||||||
|
test_rs();
|
||||||
|
|
||||||
|
// Test payload functions.
|
||||||
|
|
||||||
|
test_payload();
|
||||||
|
|
||||||
|
// Try encoding the example headers in the protocol spec.
|
||||||
|
|
||||||
|
test_example_headers();
|
||||||
|
|
||||||
|
// Convert all of the AX.25 frame types to IL2P and back again.
|
||||||
|
|
||||||
|
all_frame_types();
|
||||||
|
|
||||||
|
// Use same serialize / deserialize functions used on the air.
|
||||||
|
|
||||||
|
test_serdes ();
|
||||||
|
|
||||||
|
// Decode bitstream from demodulator if data file is available.
|
||||||
|
// TODO: Very large info parts. Appropriate error if too long.
|
||||||
|
// TODO: More than 2 addresses.
|
||||||
|
|
||||||
|
decode_bitstream();
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_REC);
|
||||||
|
dw_printf ("\n----------\n\n");
|
||||||
|
dw_printf ("\nSUCCESS!\n");
|
||||||
|
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Test scrambling and descrambling.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static void test_scramble(void)
|
||||||
|
{
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Test scrambling...\n");
|
||||||
|
|
||||||
|
// First an example from the protocol specification to make sure I'm compatible.
|
||||||
|
|
||||||
|
static unsigned char scramin1[] = { 0x63, 0xf1, 0x40, 0x40, 0x40, 0x00, 0x6b, 0x2b, 0x54, 0x28, 0x25, 0x2a, 0x0f };
|
||||||
|
static unsigned char scramout1[] = { 0x6a, 0xea, 0x9c, 0xc2, 0x01, 0x11, 0xfc, 0x14, 0x1f, 0xda, 0x6e, 0xf2, 0x53 };
|
||||||
|
unsigned char scramout[sizeof(scramin1)];
|
||||||
|
|
||||||
|
il2p_scramble_block (scramin1, scramout, sizeof(scramin1));
|
||||||
|
assert (memcmp(scramout, scramout, sizeof(scramout1)) == 0);
|
||||||
|
|
||||||
|
} // end test_scramble.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Test Reed Solomon encode/decode examples found in the protocol spec.
|
||||||
|
// The data part is scrambled but that does not matter here because.
|
||||||
|
// We are only concerned abound adding the parity and verifying.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
static void test_rs()
|
||||||
|
{
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Test Reed Solomon functions...\n");
|
||||||
|
|
||||||
|
static unsigned char example_s[] = { 0x26, 0x57, 0x4d, 0x57, 0xf1, 0x96, 0xcc, 0x85, 0x42, 0xe7, 0x24, 0xf7, 0x2e,
|
||||||
|
0x8a, 0x97 };
|
||||||
|
unsigned char parity_out[2];
|
||||||
|
il2p_encode_rs (example_s, 13, 2, parity_out);
|
||||||
|
//dw_printf ("DEBUG RS encode %02x %02x\n", parity_out[0], parity_out[1]);
|
||||||
|
assert (memcmp(parity_out, example_s + 13, 2) == 0);
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned char example_u[] = { 0x6a, 0xea, 0x9c, 0xc2, 0x01, 0x11, 0xfc, 0x14, 0x1f, 0xda, 0x6e, 0xf2, 0x53,
|
||||||
|
0x91, 0xbd };
|
||||||
|
il2p_encode_rs (example_u, 13, 2, parity_out);
|
||||||
|
//dw_printf ("DEBUG RS encode %02x %02x\n", parity_out[0], parity_out[1]);
|
||||||
|
assert (memcmp(parity_out, example_u + 13, 2) == 0);
|
||||||
|
|
||||||
|
// See if we can go the other way.
|
||||||
|
|
||||||
|
unsigned char received[15];
|
||||||
|
unsigned char corrected[15];
|
||||||
|
int e;
|
||||||
|
|
||||||
|
e = il2p_decode_rs (example_s, 13, 2, corrected);
|
||||||
|
assert (e == 0);
|
||||||
|
assert (memcmp(example_s, corrected, 13) == 0);
|
||||||
|
|
||||||
|
memcpy (received, example_s, 15);
|
||||||
|
received[0] = '?';
|
||||||
|
e = il2p_decode_rs (received, 13, 2, corrected);
|
||||||
|
assert (e == 1);
|
||||||
|
assert (memcmp(example_s, corrected, 13) == 0);
|
||||||
|
|
||||||
|
e = il2p_decode_rs (example_u, 13, 2, corrected);
|
||||||
|
assert (e == 0);
|
||||||
|
assert (memcmp(example_u, corrected, 13) == 0);
|
||||||
|
|
||||||
|
memcpy (received, example_u, 15);
|
||||||
|
received[12] = '?';
|
||||||
|
e = il2p_decode_rs (received, 13, 2, corrected);
|
||||||
|
assert (e == 1);
|
||||||
|
assert (memcmp(example_u, corrected, 13) == 0);
|
||||||
|
|
||||||
|
received[1] = '?';
|
||||||
|
received[2] = '?';
|
||||||
|
e = il2p_decode_rs (received, 13, 2, corrected);
|
||||||
|
assert (e == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Test payload functions.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static void test_payload(void)
|
||||||
|
{
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Test payload functions...\n");
|
||||||
|
|
||||||
|
il2p_payload_properties_t ipp;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
// Examples in specification.
|
||||||
|
|
||||||
|
e = il2p_payload_compute (&ipp, 100, 0);
|
||||||
|
assert (ipp.small_block_size == 100);
|
||||||
|
assert (ipp.large_block_size == 101);
|
||||||
|
assert (ipp.large_block_count == 0);
|
||||||
|
assert (ipp.small_block_count == 1);
|
||||||
|
assert (ipp.parity_symbols_per_block == 4);
|
||||||
|
|
||||||
|
e = il2p_payload_compute (&ipp, 236, 0);
|
||||||
|
assert (ipp.small_block_size == 236);
|
||||||
|
assert (ipp.large_block_size == 237);
|
||||||
|
assert (ipp.large_block_count == 0);
|
||||||
|
assert (ipp.small_block_count == 1);
|
||||||
|
assert (ipp.parity_symbols_per_block == 8);
|
||||||
|
|
||||||
|
e = il2p_payload_compute (&ipp, 512, 0);
|
||||||
|
assert (ipp.small_block_size == 170);
|
||||||
|
assert (ipp.large_block_size == 171);
|
||||||
|
assert (ipp.large_block_count == 2);
|
||||||
|
assert (ipp.small_block_count == 1);
|
||||||
|
assert (ipp.parity_symbols_per_block == 6);
|
||||||
|
|
||||||
|
e = il2p_payload_compute (&ipp, 1023, 0);
|
||||||
|
assert (ipp.small_block_size == 204);
|
||||||
|
assert (ipp.large_block_size == 205);
|
||||||
|
assert (ipp.large_block_count == 3);
|
||||||
|
assert (ipp.small_block_count == 2);
|
||||||
|
assert (ipp.parity_symbols_per_block == 8);
|
||||||
|
|
||||||
|
// Now try all possible sizes for Baseline FEC Parity.
|
||||||
|
|
||||||
|
for (int n = 1; n <= IL2P_MAX_PAYLOAD_SIZE; n++) {
|
||||||
|
e = il2p_payload_compute (&ipp, n, 0);
|
||||||
|
//dw_printf ("bytecount=%d, smallsize=%d, largesize=%d, largecount=%d, smallcount=%d\n", n,
|
||||||
|
// ipp.small_block_size, ipp.large_block_size,
|
||||||
|
// ipp.large_block_count, ipp.small_block_count);
|
||||||
|
//fflush (stdout);
|
||||||
|
|
||||||
|
assert (ipp.payload_block_count >= 1 && ipp.payload_block_count <= IL2P_MAX_PAYLOAD_BLOCKS);
|
||||||
|
assert (ipp.payload_block_count == ipp.small_block_count + ipp.large_block_count);
|
||||||
|
assert (ipp.small_block_count * ipp.small_block_size +
|
||||||
|
ipp.large_block_count * ipp.large_block_size == n);
|
||||||
|
assert (ipp.parity_symbols_per_block == 2 ||
|
||||||
|
ipp.parity_symbols_per_block == 4 ||
|
||||||
|
ipp.parity_symbols_per_block == 6 ||
|
||||||
|
ipp.parity_symbols_per_block == 8);
|
||||||
|
|
||||||
|
// Data and parity must fit in RS block size of 255.
|
||||||
|
// Size test does not apply if block count is 0.
|
||||||
|
assert (ipp.small_block_count == 0 || ipp.small_block_size + ipp.parity_symbols_per_block <= 255);
|
||||||
|
assert (ipp.large_block_count == 0 || ipp.large_block_size + ipp.parity_symbols_per_block <= 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All sizes for MAX FEC.
|
||||||
|
|
||||||
|
for (int n = 1; n <= IL2P_MAX_PAYLOAD_SIZE; n++) {
|
||||||
|
e = il2p_payload_compute (&ipp, n, 1); // 1 for max fec.
|
||||||
|
//dw_printf ("bytecount=%d, smallsize=%d, largesize=%d, largecount=%d, smallcount=%d\n", n,
|
||||||
|
// ipp.small_block_size, ipp.large_block_size,
|
||||||
|
// ipp.large_block_count, ipp.small_block_count);
|
||||||
|
//fflush (stdout);
|
||||||
|
|
||||||
|
assert (ipp.payload_block_count >= 1 && ipp.payload_block_count <= IL2P_MAX_PAYLOAD_BLOCKS);
|
||||||
|
assert (ipp.payload_block_count == ipp.small_block_count + ipp.large_block_count);
|
||||||
|
assert (ipp.small_block_count * ipp.small_block_size +
|
||||||
|
ipp.large_block_count * ipp.large_block_size == n);
|
||||||
|
assert (ipp.parity_symbols_per_block == 16);
|
||||||
|
|
||||||
|
// Data and parity must fit in RS block size of 255.
|
||||||
|
// Size test does not apply if block count is 0.
|
||||||
|
assert (ipp.small_block_count == 0 || ipp.small_block_size + ipp.parity_symbols_per_block <= 255);
|
||||||
|
assert (ipp.large_block_count == 0 || ipp.large_block_size + ipp.parity_symbols_per_block <= 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now let's try encoding payloads and extracting original again.
|
||||||
|
// This will also provide exercise for scrambling and Reed Solomon under more conditions.
|
||||||
|
|
||||||
|
unsigned char original_payload[IL2P_MAX_PAYLOAD_SIZE];
|
||||||
|
for (int n = 0; n < IL2P_MAX_PAYLOAD_SIZE; n++) {
|
||||||
|
original_payload[n] = n & 0xff;
|
||||||
|
}
|
||||||
|
for (int max_fec = 0; max_fec <= 1; max_fec++) {
|
||||||
|
for (int payload_length = 1; payload_length <= IL2P_MAX_PAYLOAD_SIZE; payload_length++) {
|
||||||
|
//dw_printf ("\n--------- max_fec = %d, payload_length = %d\n", max_fec, payload_length);
|
||||||
|
unsigned char encoded[IL2P_MAX_ENCODED_PAYLOAD_SIZE];
|
||||||
|
int k = il2p_encode_payload (original_payload, payload_length, max_fec, encoded);
|
||||||
|
|
||||||
|
//dw_printf ("payload length %d %s -> %d\n", payload_length, max_fec ? "M" : "", k);
|
||||||
|
assert (k > payload_length && k <= IL2P_MAX_ENCODED_PAYLOAD_SIZE);
|
||||||
|
|
||||||
|
// Now extract.
|
||||||
|
|
||||||
|
unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE];
|
||||||
|
int symbols_corrected = 0;
|
||||||
|
int e = il2p_decode_payload (encoded, payload_length, max_fec, extracted, &symbols_corrected);
|
||||||
|
//dw_printf ("e = %d, payload_length = %d\n", e, payload_length);
|
||||||
|
assert (e == payload_length);
|
||||||
|
|
||||||
|
// if (memcmp (original_payload, extracted, payload_length) != 0) {
|
||||||
|
// dw_printf ("********** Received message not as expected. **********\n");
|
||||||
|
// fx_hex_dump(extracted, payload_length);
|
||||||
|
// }
|
||||||
|
assert (memcmp (original_payload, extracted, payload_length) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void)e;
|
||||||
|
} // end test_payload
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Test header examples found in protocol specification.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static void test_example_headers()
|
||||||
|
{
|
||||||
|
|
||||||
|
//----------- Example 1: AX.25 S-Frame --------------
|
||||||
|
|
||||||
|
// This frame sample only includes a 15 byte header, without PID field.
|
||||||
|
// Destination Callsign: ?KA2DEW-2
|
||||||
|
// Source Callsign: ?KK4HEJ-7
|
||||||
|
// N(R): 5
|
||||||
|
// P/F: 1
|
||||||
|
// C: 1
|
||||||
|
// Control Opcode: 00 (Receive Ready)
|
||||||
|
//
|
||||||
|
// AX.25 data:
|
||||||
|
// 96 82 64 88 8a ae e4 96 96 68 90 8a 94 6f b1
|
||||||
|
//
|
||||||
|
// IL2P Data Prior to Scrambling and RS Encoding:
|
||||||
|
// 2b a1 12 24 25 77 6b 2b 54 68 25 2a 27
|
||||||
|
//
|
||||||
|
// IL2P Data After Scrambling and RS Encoding:
|
||||||
|
// 26 57 4d 57 f1 96 cc 85 42 e7 24 f7 2e 8a 97
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Example 1: AX.25 S-Frame...\n");
|
||||||
|
|
||||||
|
static unsigned char example1[] = {0x96, 0x82, 0x64, 0x88, 0x8a, 0xae, 0xe4, 0x96, 0x96, 0x68, 0x90, 0x8a, 0x94, 0x6f, 0xb1};
|
||||||
|
static unsigned char header1[] = {0x2b, 0xa1, 0x12, 0x24, 0x25, 0x77, 0x6b, 0x2b, 0x54, 0x68, 0x25, 0x2a, 0x27 };
|
||||||
|
unsigned char header[IL2P_HEADER_SIZE];
|
||||||
|
unsigned char sresult[32];
|
||||||
|
memset (header, 0, sizeof(header));
|
||||||
|
memset (sresult, 0, sizeof(sresult));
|
||||||
|
unsigned char check[2];
|
||||||
|
alevel_t alevel;
|
||||||
|
memset(&alevel, 0, sizeof(alevel));
|
||||||
|
|
||||||
|
packet_t pp = ax25_from_frame (example1, sizeof(example1), alevel);
|
||||||
|
assert (pp != NULL);
|
||||||
|
int e;
|
||||||
|
e = il2p_type_1_header (pp, 0, header);
|
||||||
|
assert (e == 0);
|
||||||
|
ax25_delete(pp);
|
||||||
|
|
||||||
|
//dw_printf ("Example 1 header:\n");
|
||||||
|
//for (int i = 0 ; i < sizeof(header); i++) {
|
||||||
|
// dw_printf (" %02x", header[i]);
|
||||||
|
//}
|
||||||
|
///dw_printf ("\n");
|
||||||
|
|
||||||
|
assert (memcmp(header, header1, sizeof(header)) == 0);
|
||||||
|
|
||||||
|
il2p_scramble_block (header, sresult, 13);
|
||||||
|
//dw_printf ("Expect scrambled 26 57 4d 57 f1 96 cc 85 42 e7 24 f7 2e\n");
|
||||||
|
//for (int i = 0 ; i < sizeof(sresult); i++) {
|
||||||
|
// dw_printf (" %02x", sresult[i]);
|
||||||
|
//}
|
||||||
|
//dw_printf ("\n");
|
||||||
|
|
||||||
|
il2p_encode_rs (sresult, 13, 2, check);
|
||||||
|
|
||||||
|
//dw_printf ("expect checksum = 8a 97\n");
|
||||||
|
//dw_printf ("check = ");
|
||||||
|
//for (int i = 0 ; i < sizeof(check); i++) {
|
||||||
|
// dw_printf (" %02x", check[i]);
|
||||||
|
//}
|
||||||
|
//dw_printf ("\n");
|
||||||
|
assert (check[0] == 0x8a);
|
||||||
|
assert (check[1] == 0x97);
|
||||||
|
|
||||||
|
// Can we go from IL2P back to AX.25?
|
||||||
|
|
||||||
|
pp = il2p_decode_header_type_1 (header, 0);
|
||||||
|
assert (pp != NULL);
|
||||||
|
|
||||||
|
char dst_addr[AX25_MAX_ADDR_LEN];
|
||||||
|
char src_addr[AX25_MAX_ADDR_LEN];
|
||||||
|
|
||||||
|
ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dst_addr);
|
||||||
|
ax25_get_addr_with_ssid (pp, AX25_SOURCE, src_addr);
|
||||||
|
|
||||||
|
ax25_frame_type_t frame_type;
|
||||||
|
cmdres_t cr; // command or response.
|
||||||
|
char description[64];
|
||||||
|
int pf; // Poll/Final.
|
||||||
|
int nr, ns; // Sequence numbers.
|
||||||
|
|
||||||
|
frame_type = ax25_frame_type (pp, &cr, description, &pf, &nr, &ns);
|
||||||
|
(void)frame_type;
|
||||||
|
#if 1
|
||||||
|
dw_printf ("%s(): %s>%s: %s\n", __func__, src_addr, dst_addr, description);
|
||||||
|
#endif
|
||||||
|
// TODO: compare binary.
|
||||||
|
ax25_delete (pp);
|
||||||
|
|
||||||
|
dw_printf ("Example 1 header OK\n");
|
||||||
|
|
||||||
|
|
||||||
|
// -------------- Example 2 - UI frame, no info part ------------------
|
||||||
|
|
||||||
|
// This is an AX.25 Unnumbered Information frame, such as APRS.
|
||||||
|
// Destination Callsign: ?CQ -0
|
||||||
|
// Source Callsign: ?KK4HEJ-15
|
||||||
|
// P/F: 0
|
||||||
|
// C: 0
|
||||||
|
// Control Opcode: 3 Unnumbered Information
|
||||||
|
// PID: 0xF0 No L3
|
||||||
|
//
|
||||||
|
// AX.25 Data:
|
||||||
|
// 86 a2 40 40 40 40 60 96 96 68 90 8a 94 7f 03 f0
|
||||||
|
//
|
||||||
|
// IL2P Data Prior to Scrambling and RS Encoding:
|
||||||
|
// 63 f1 40 40 40 00 6b 2b 54 28 25 2a 0f
|
||||||
|
//
|
||||||
|
// IL2P Data After Scrambling and RS Encoding:
|
||||||
|
// 6a ea 9c c2 01 11 fc 14 1f da 6e f2 53 91 bd
|
||||||
|
|
||||||
|
|
||||||
|
//dw_printf ("---------- example 2 ------------\n");
|
||||||
|
static unsigned char example2[] = { 0x86, 0xa2, 0x40, 0x40, 0x40, 0x40, 0x60, 0x96, 0x96, 0x68, 0x90, 0x8a, 0x94, 0x7f, 0x03, 0xf0 };
|
||||||
|
static unsigned char header2[] = { 0x63, 0xf1, 0x40, 0x40, 0x40, 0x00, 0x6b, 0x2b, 0x54, 0x28, 0x25, 0x2a, 0x0f };
|
||||||
|
memset (header, 0, sizeof(header));
|
||||||
|
memset (sresult, 0, sizeof(sresult));
|
||||||
|
memset(&alevel, 0, sizeof(alevel));
|
||||||
|
|
||||||
|
pp = ax25_from_frame (example2, sizeof(example2), alevel);
|
||||||
|
assert (pp != NULL);
|
||||||
|
e = il2p_type_1_header (pp, 0, header);
|
||||||
|
assert (e == 0);
|
||||||
|
ax25_delete(pp);
|
||||||
|
|
||||||
|
//dw_printf ("Example 2 header:\n");
|
||||||
|
//for (int i = 0 ; i < sizeof(header); i++) {
|
||||||
|
// dw_printf (" %02x", header[i]);
|
||||||
|
//}
|
||||||
|
//dw_printf ("\n");
|
||||||
|
|
||||||
|
assert (memcmp(header, header2, sizeof(header2)) == 0);
|
||||||
|
|
||||||
|
il2p_scramble_block (header, sresult, 13);
|
||||||
|
//dw_printf ("Expect scrambled 6a ea 9c c2 01 11 fc 14 1f da 6e f2 53\n");
|
||||||
|
//for (int i = 0 ; i < sizeof(sresult); i++) {
|
||||||
|
// dw_printf (" %02x", sresult[i]);
|
||||||
|
//}
|
||||||
|
//dw_printf ("\n");
|
||||||
|
|
||||||
|
il2p_encode_rs (sresult, 13, 2, check);
|
||||||
|
|
||||||
|
//dw_printf ("expect checksum = 91 bd\n");
|
||||||
|
//dw_printf ("check = ");
|
||||||
|
//for (int i = 0 ; i < sizeof(check); i++) {
|
||||||
|
// dw_printf (" %02x", check[i]);
|
||||||
|
//}
|
||||||
|
//dw_printf ("\n");
|
||||||
|
assert (check[0] == 0x91);
|
||||||
|
assert (check[1] == 0xbd);
|
||||||
|
|
||||||
|
// Can we go from IL2P back to AX.25?
|
||||||
|
|
||||||
|
pp = il2p_decode_header_type_1 (header, 0);
|
||||||
|
assert (pp != NULL);
|
||||||
|
|
||||||
|
ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dst_addr);
|
||||||
|
ax25_get_addr_with_ssid (pp, AX25_SOURCE, src_addr);
|
||||||
|
|
||||||
|
frame_type = ax25_frame_type (pp, &cr, description, &pf, &nr, &ns);
|
||||||
|
(void)frame_type;
|
||||||
|
#if 1
|
||||||
|
dw_printf ("%s(): %s>%s: %s\n", __func__, src_addr, dst_addr, description);
|
||||||
|
#endif
|
||||||
|
// TODO: compare binary.
|
||||||
|
|
||||||
|
ax25_delete (pp);
|
||||||
|
// TODO: more examples
|
||||||
|
|
||||||
|
dw_printf ("Example 2 header OK\n");
|
||||||
|
|
||||||
|
|
||||||
|
// -------------- Example 3 - I Frame ------------------
|
||||||
|
|
||||||
|
// This is an AX.25 I-Frame with 9 bytes of information after the 16 byte header.
|
||||||
|
//
|
||||||
|
// Destination Callsign: ?KA2DEW-2
|
||||||
|
// Source Callsign: ?KK4HEJ-2
|
||||||
|
// P/F: 1
|
||||||
|
// C: 1
|
||||||
|
// N(R): 5
|
||||||
|
// N(S): 4
|
||||||
|
// AX.25 PID: 0xCF TheNET
|
||||||
|
// IL2P Payload Byte Count: 9
|
||||||
|
//
|
||||||
|
// AX.25 Data:
|
||||||
|
// 96 82 64 88 8a ae e4 96 96 68 90 8a 94 65 b8 cf 30 31 32 33 34 35 36 37 38
|
||||||
|
//
|
||||||
|
// IL2P Scrambled and Encoded Data:
|
||||||
|
// 26 13 6d 02 8c fe fb e8 aa 94 2d 6a 34 43 35 3c 69 9f 0c 75 5a 38 a1 7f f3 fc
|
||||||
|
|
||||||
|
|
||||||
|
//dw_printf ("---------- example 3 ------------\n");
|
||||||
|
static unsigned char example3[] = { 0x96, 0x82, 0x64, 0x88, 0x8a, 0xae, 0xe4, 0x96, 0x96, 0x68, 0x90, 0x8a, 0x94, 0x65, 0xb8, 0xcf, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
|
||||||
|
static unsigned char header3[] = { 0x2b, 0xe1, 0x52, 0x64, 0x25, 0x77, 0x6b, 0x2b, 0xd4, 0x68, 0x25, 0xaa, 0x22 };
|
||||||
|
static unsigned char complete3[] = { 0x26, 0x13, 0x6d, 0x02, 0x8c, 0xfe, 0xfb, 0xe8, 0xaa, 0x94, 0x2d, 0x6a, 0x34, 0x43, 0x35, 0x3c, 0x69, 0x9f, 0x0c, 0x75, 0x5a, 0x38, 0xa1, 0x7f, 0xf3, 0xfc };
|
||||||
|
memset (header, 0, sizeof(header));
|
||||||
|
memset (sresult, 0, sizeof(sresult));
|
||||||
|
memset(&alevel, 0, sizeof(alevel));
|
||||||
|
|
||||||
|
pp = ax25_from_frame (example3, sizeof(example3), alevel);
|
||||||
|
assert (pp != NULL);
|
||||||
|
e = il2p_type_1_header (pp, 0, header);
|
||||||
|
assert (e == 9);
|
||||||
|
ax25_delete(pp);
|
||||||
|
|
||||||
|
//dw_printf ("Example 3 header:\n");
|
||||||
|
//for (int i = 0 ; i < sizeof(header); i++) {
|
||||||
|
// dw_printf (" %02x", header[i]);
|
||||||
|
//}
|
||||||
|
//dw_printf ("\n");
|
||||||
|
|
||||||
|
assert (memcmp(header, header3, sizeof(header)) == 0);
|
||||||
|
|
||||||
|
il2p_scramble_block (header, sresult, 13);
|
||||||
|
//dw_printf ("Expect scrambled 26 13 6d 02 8c fe fb e8 aa 94 2d 6a 34\n");
|
||||||
|
//for (int i = 0 ; i < sizeof(sresult); i++) {
|
||||||
|
// dw_printf (" %02x", sresult[i]);
|
||||||
|
//}
|
||||||
|
//dw_printf ("\n");
|
||||||
|
|
||||||
|
il2p_encode_rs (sresult, 13, 2, check);
|
||||||
|
|
||||||
|
//dw_printf ("expect checksum = 43 35\n");
|
||||||
|
//dw_printf ("check = ");
|
||||||
|
//for (int i = 0 ; i < sizeof(check); i++) {
|
||||||
|
// dw_printf (" %02x", check[i]);
|
||||||
|
//}
|
||||||
|
//dw_printf ("\n");
|
||||||
|
|
||||||
|
assert (check[0] == 0x43);
|
||||||
|
assert (check[1] == 0x35);
|
||||||
|
|
||||||
|
// That was only the header. We will get to the info part in a later test.
|
||||||
|
|
||||||
|
// Can we go from IL2P back to AX.25?
|
||||||
|
|
||||||
|
pp = il2p_decode_header_type_1 (header, 0);
|
||||||
|
assert (pp != NULL);
|
||||||
|
|
||||||
|
ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dst_addr);
|
||||||
|
ax25_get_addr_with_ssid (pp, AX25_SOURCE, src_addr);
|
||||||
|
|
||||||
|
frame_type = ax25_frame_type (pp, &cr, description, &pf, &nr, &ns);
|
||||||
|
(void)frame_type;
|
||||||
|
#if 1
|
||||||
|
dw_printf ("%s(): %s>%s: %s\n", __func__, src_addr, dst_addr, description);
|
||||||
|
#endif
|
||||||
|
// TODO: compare binary.
|
||||||
|
|
||||||
|
ax25_delete (pp);
|
||||||
|
dw_printf ("Example 3 header OK\n");
|
||||||
|
|
||||||
|
// Example 3 again, this time the Information part is included.
|
||||||
|
|
||||||
|
pp = ax25_from_frame (example3, sizeof(example3), alevel);
|
||||||
|
assert (pp != NULL);
|
||||||
|
|
||||||
|
int max_fec = 0;
|
||||||
|
unsigned char iout[IL2P_MAX_PACKET_SIZE];
|
||||||
|
e = il2p_encode_frame (pp, max_fec, iout);
|
||||||
|
|
||||||
|
//dw_printf ("expected for example 3:\n");
|
||||||
|
//fx_hex_dump(complete3, sizeof(complete3));
|
||||||
|
//dw_printf ("actual result for example 3:\n");
|
||||||
|
//fx_hex_dump(iout, e);
|
||||||
|
// Does it match the example in the protocol spec?
|
||||||
|
assert (e == sizeof(complete3));
|
||||||
|
assert (memcmp(iout, complete3, sizeof(complete3)) == 0);
|
||||||
|
ax25_delete (pp);
|
||||||
|
|
||||||
|
dw_printf ("Example 3 with info OK\n");
|
||||||
|
|
||||||
|
} // end test_example_headers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Test all of the frame types.
|
||||||
|
//
|
||||||
|
// Encode to IL2P format, decode, and verify that the result is the same as the original.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
static void enc_dec_compare (packet_t pp1)
|
||||||
|
{
|
||||||
|
for (int max_fec = 0; max_fec <= 1; max_fec++) {
|
||||||
|
|
||||||
|
unsigned char encoded[IL2P_MAX_PACKET_SIZE];
|
||||||
|
int enc_len;
|
||||||
|
enc_len = il2p_encode_frame (pp1, max_fec, encoded);
|
||||||
|
assert (enc_len >= 0);
|
||||||
|
|
||||||
|
packet_t pp2;
|
||||||
|
pp2 = il2p_decode_frame (encoded);
|
||||||
|
assert (pp2 != NULL);
|
||||||
|
|
||||||
|
// Is it the same after encoding to IL2P and then decoding?
|
||||||
|
|
||||||
|
int len1 = ax25_get_frame_len (pp1);
|
||||||
|
unsigned char *data1 = ax25_get_frame_data_ptr (pp1);
|
||||||
|
|
||||||
|
int len2 = ax25_get_frame_len (pp2);
|
||||||
|
unsigned char *data2 = ax25_get_frame_data_ptr (pp2);
|
||||||
|
|
||||||
|
if (len1 != len2 || memcmp(data1, data2, len1) != 0) {
|
||||||
|
|
||||||
|
dw_printf ("\nEncode/Decode Error. Original:\n");
|
||||||
|
ax25_hex_dump (pp1);
|
||||||
|
|
||||||
|
dw_printf ("IL2P encoded as:\n");
|
||||||
|
fx_hex_dump(encoded, enc_len);
|
||||||
|
|
||||||
|
dw_printf ("Got turned into this:\n");
|
||||||
|
ax25_hex_dump (pp2);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert (len1 == len2 && memcmp(data1, data2, len1) == 0);
|
||||||
|
|
||||||
|
ax25_delete (pp2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void all_frame_types(void)
|
||||||
|
{
|
||||||
|
char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
|
||||||
|
int num_addr = 2;
|
||||||
|
cmdres_t cr;
|
||||||
|
ax25_frame_type_t ftype;
|
||||||
|
int pf = 0;
|
||||||
|
int pid = 0xf0;
|
||||||
|
int modulo;
|
||||||
|
int nr, ns;
|
||||||
|
unsigned char *pinfo = NULL;
|
||||||
|
int info_len = 0;
|
||||||
|
packet_t pp;
|
||||||
|
|
||||||
|
strcpy (addrs[0], "W2UB");
|
||||||
|
strcpy (addrs[1], "WB2OSZ-12");
|
||||||
|
num_addr = 2;
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Testing all frame types.\n");
|
||||||
|
|
||||||
|
/* U frame */
|
||||||
|
|
||||||
|
dw_printf ("\nU frames...\n");
|
||||||
|
|
||||||
|
for (ftype = frame_type_U_SABME; ftype <= frame_type_U_TEST; ftype++) {
|
||||||
|
|
||||||
|
for (pf = 0; pf <= 1; pf++) {
|
||||||
|
|
||||||
|
int cmin = 0, cmax = 1;
|
||||||
|
|
||||||
|
switch (ftype) {
|
||||||
|
// 0 = response, 1 = command
|
||||||
|
case frame_type_U_SABME: cmin = 1; cmax = 1; break;
|
||||||
|
case frame_type_U_SABM: cmin = 1; cmax = 1; break;
|
||||||
|
case frame_type_U_DISC: cmin = 1; cmax = 1; break;
|
||||||
|
case frame_type_U_DM: cmin = 0; cmax = 0; break;
|
||||||
|
case frame_type_U_UA: cmin = 0; cmax = 0; break;
|
||||||
|
case frame_type_U_FRMR: cmin = 0; cmax = 0; break;
|
||||||
|
case frame_type_U_UI: cmin = 0; cmax = 1; break;
|
||||||
|
case frame_type_U_XID: cmin = 0; cmax = 1; break;
|
||||||
|
case frame_type_U_TEST: cmin = 0; cmax = 1; break;
|
||||||
|
default: break; // avoid compiler warning.
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cr = cmin; cr <= cmax; cr++) {
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\nConstruct U frame, cr=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||||
|
|
||||||
|
pp = ax25_u_frame (addrs, num_addr, cr, ftype, pf, pid, pinfo, info_len);
|
||||||
|
ax25_hex_dump (pp);
|
||||||
|
enc_dec_compare (pp);
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* S frame */
|
||||||
|
|
||||||
|
//strcpy (addrs[2], "DIGI1-1");
|
||||||
|
//num_addr = 3;
|
||||||
|
|
||||||
|
dw_printf ("\nS frames...\n");
|
||||||
|
|
||||||
|
for (ftype = frame_type_S_RR; ftype <= frame_type_S_SREJ; ftype++) {
|
||||||
|
|
||||||
|
for (pf = 0; pf <= 1; pf++) {
|
||||||
|
|
||||||
|
modulo = 8;
|
||||||
|
nr = modulo / 2 + 1;
|
||||||
|
|
||||||
|
// SREJ can only be response.
|
||||||
|
|
||||||
|
for (cr = 0; cr <= (int)(ftype!=frame_type_S_SREJ); cr++) {
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\nConstruct S frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||||
|
|
||||||
|
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, NULL, 0);
|
||||||
|
|
||||||
|
ax25_hex_dump (pp);
|
||||||
|
enc_dec_compare (pp);
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
modulo = 128;
|
||||||
|
nr = modulo / 2 + 1;
|
||||||
|
|
||||||
|
for (cr = 0; cr <= (int)(ftype!=frame_type_S_SREJ); cr++) {
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\nConstruct S frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||||
|
|
||||||
|
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, NULL, 0);
|
||||||
|
|
||||||
|
ax25_hex_dump (pp);
|
||||||
|
enc_dec_compare (pp);
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SREJ is only S frame which can have information part. */
|
||||||
|
|
||||||
|
static unsigned char srej_info[] = { 1<<1, 2<<1, 3<<1, 4<<1 };
|
||||||
|
|
||||||
|
ftype = frame_type_S_SREJ;
|
||||||
|
for (pf = 0; pf <= 1; pf++) {
|
||||||
|
|
||||||
|
modulo = 128;
|
||||||
|
nr = 127;
|
||||||
|
cr = cr_res;
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\nConstruct Multi-SREJ S frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||||
|
|
||||||
|
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, srej_info, (int)(sizeof(srej_info)));
|
||||||
|
|
||||||
|
ax25_hex_dump (pp);
|
||||||
|
enc_dec_compare (pp);
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* I frame */
|
||||||
|
|
||||||
|
dw_printf ("\nI frames...\n");
|
||||||
|
|
||||||
|
pinfo = (unsigned char*)"The rain in Spain stays mainly on the plain.";
|
||||||
|
info_len = strlen((char*)pinfo);
|
||||||
|
|
||||||
|
for (pf = 0; pf <= 1; pf++) {
|
||||||
|
|
||||||
|
modulo = 8;
|
||||||
|
nr = 0x55 & (modulo - 1);
|
||||||
|
ns = 0xaa & (modulo - 1);
|
||||||
|
|
||||||
|
for (cr = 1; cr <= 1; cr++) { // can only be command
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\nConstruct I frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||||
|
|
||||||
|
pp = ax25_i_frame (addrs, num_addr, cr, modulo, nr, ns, pf, pid, pinfo, info_len);
|
||||||
|
|
||||||
|
ax25_hex_dump (pp);
|
||||||
|
enc_dec_compare (pp);
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
modulo = 128;
|
||||||
|
nr = 0x55 & (modulo - 1);
|
||||||
|
ns = 0xaa & (modulo - 1);
|
||||||
|
|
||||||
|
for (cr = 1; cr <= 1; cr++) {
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\nConstruct I frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||||
|
|
||||||
|
pp = ax25_i_frame (addrs, num_addr, cr, modulo, nr, ns, pf, pid, pinfo, info_len);
|
||||||
|
|
||||||
|
ax25_hex_dump (pp);
|
||||||
|
enc_dec_compare (pp);
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end all_frame_types
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Test bitstream tapped off from demodulator.
|
||||||
|
//
|
||||||
|
// 5 frames were sent to Nino TNC and a recording was made.
|
||||||
|
// This was demodulated and the resulting bit stream saved to a file.
|
||||||
|
//
|
||||||
|
// No automatic test here - must be done manually with audio recording.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static int decoding_bitstream = 0;
|
||||||
|
|
||||||
|
static void decode_bitstream(void)
|
||||||
|
{
|
||||||
|
dw_printf("-----\nReading il2p-bitstream.txt if available...\n");
|
||||||
|
|
||||||
|
FILE *fp = fopen ("il2p-bitstream.txt", "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
dw_printf ("Bitstream test file not available.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoding_bitstream = 1;
|
||||||
|
int save_previous = il2p_get_debug();
|
||||||
|
il2p_set_debug (1);
|
||||||
|
|
||||||
|
int ch;
|
||||||
|
while ( (ch = fgetc(fp)) != EOF) {
|
||||||
|
|
||||||
|
if (ch == '0' || ch == '1') {
|
||||||
|
il2p_rec_bit (0, 0, 0, ch - '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
il2p_set_debug (save_previous);
|
||||||
|
decoding_bitstream = 0;
|
||||||
|
|
||||||
|
} // end decode_bitstream
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Test serialize / deserialize.
|
||||||
|
//
|
||||||
|
// This uses same functions used on the air.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static char addrs2[] = "AA1AAA-1>ZZ9ZZZ-9";
|
||||||
|
static char addrs3[] = "AA1AAA-1>ZZ9ZZZ-9,DIGI*";
|
||||||
|
static char text[] =
|
||||||
|
"'... As I was saying, that seems to be done right - though I haven't time to look it over thoroughly just now - and that shows that there are three hundred and sixty-four days when you might get un-birthday presents -'"
|
||||||
|
"\n"
|
||||||
|
"'Certainly,' said Alice."
|
||||||
|
"\n"
|
||||||
|
"'And only one for birthday presents, you know. There's glory for you!'"
|
||||||
|
"\n"
|
||||||
|
"'I don't know what you mean by \"glory\",' Alice said."
|
||||||
|
"\n"
|
||||||
|
"Humpty Dumpty smiled contemptuously. 'Of course you don't - till I tell you. I meant \"there's a nice knock-down argument for you!\"'"
|
||||||
|
"\n"
|
||||||
|
"'But \"glory\" doesn't mean \"a nice knock-down argument\",' Alice objected."
|
||||||
|
"\n"
|
||||||
|
"'When I use a word,' Humpty Dumpty said, in rather a scornful tone, 'it means just what I choose it to mean - neither more nor less.'"
|
||||||
|
"\n"
|
||||||
|
"'The question is,' said Alice, 'whether you can make words mean so many different things.'"
|
||||||
|
"\n"
|
||||||
|
"'The question is,' said Humpty Dumpty, 'which is to be master - that's all.'"
|
||||||
|
"\n" ;
|
||||||
|
|
||||||
|
|
||||||
|
static int rec_count = -1; // disable deserialized packet test.
|
||||||
|
static int polarity = 0;
|
||||||
|
|
||||||
|
static void test_serdes (void)
|
||||||
|
{
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\nTest serialize / deserialize...\n");
|
||||||
|
rec_count = 0;
|
||||||
|
|
||||||
|
int max_fec = 1;
|
||||||
|
|
||||||
|
// try combinations of header type, max_fec, polarity, errors.
|
||||||
|
|
||||||
|
for (int hdr_type = 0; hdr_type <= 1; hdr_type++) {
|
||||||
|
char packet[1024];
|
||||||
|
snprintf (packet, sizeof(packet), "%s:%s", hdr_type ? addrs2 : addrs3, text);
|
||||||
|
packet_t pp = ax25_from_text (packet, 1);
|
||||||
|
assert (pp != NULL);
|
||||||
|
|
||||||
|
int chan = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for (max_fec = 0; max_fec <= 1; max_fec++) {
|
||||||
|
for (polarity = 0; polarity <= 2; polarity++) { // 2 means throw in some errors.
|
||||||
|
int num_bits_sent = il2p_send_frame (chan, pp, max_fec, polarity);
|
||||||
|
dw_printf ("%d bits sent.\n", num_bits_sent);
|
||||||
|
|
||||||
|
// Need extra bit at end to flush out state machine.
|
||||||
|
il2p_rec_bit (0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ax25_delete(pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
dw_printf ("Serdes receive count = %d\n", rec_count);
|
||||||
|
assert (rec_count == 12);
|
||||||
|
rec_count = -1; // disable deserialized packet test.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Serializing calls this which then simulates the demodulator output.
|
||||||
|
|
||||||
|
void tone_gen_put_bit (int chan, int data)
|
||||||
|
{
|
||||||
|
il2p_rec_bit (chan, 0, 0, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called when a complete frame has been deserialized.
|
||||||
|
|
||||||
|
void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25)
|
||||||
|
{
|
||||||
|
if (rec_count < 0) return; // Skip check before serdes test.
|
||||||
|
|
||||||
|
rec_count++;
|
||||||
|
|
||||||
|
// Does it have the the expected content?
|
||||||
|
|
||||||
|
unsigned char *pinfo;
|
||||||
|
int len = ax25_get_info(pp, &pinfo);
|
||||||
|
assert (len == strlen(text));
|
||||||
|
assert (strcmp(text, (char*)pinfo) == 0);
|
||||||
|
|
||||||
|
dw_printf ("Number of symbols corrected: %d\n", retries);
|
||||||
|
if (polarity == 2) { // expecting errors corrrected.
|
||||||
|
assert (retries == 10);
|
||||||
|
}
|
||||||
|
else { // should be no errors.
|
||||||
|
assert (retries == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
alevel_t demod_get_audio_level (int chan, int subchan)
|
||||||
|
{
|
||||||
|
alevel_t alevel;
|
||||||
|
memset (&alevel, 0, sizeof(alevel));
|
||||||
|
return (alevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// end il2p_test.c
|
File diff suppressed because it is too large
Load Diff
|
@ -83,7 +83,7 @@
|
||||||
|
|
||||||
//#define DEBUG 1
|
//#define DEBUG 1
|
||||||
|
|
||||||
#define DIGIPEATER_C
|
#define DIGIPEATER_C // Why?
|
||||||
|
|
||||||
#include "direwolf.h"
|
#include "direwolf.h"
|
||||||
|
|
||||||
|
@ -345,13 +345,20 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c
|
||||||
else {
|
else {
|
||||||
pp = ax25_from_frame (fbuf, flen, alevel);
|
pp = ax25_from_frame (fbuf, flen, alevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
multi_modem_process_rec_packet (chan, subchan, slice, pp, alevel, retries, is_fx25);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Eliminate function above and move code elsewhere?
|
||||||
|
|
||||||
|
void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25)
|
||||||
|
{
|
||||||
if (pp == NULL) {
|
if (pp == NULL) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Unexpected internal problem, %s %d\n", __FILE__, __LINE__);
|
dw_printf ("Unexpected internal problem, %s %d\n", __FILE__, __LINE__);
|
||||||
return; /* oops! why would it fail? */
|
return; /* oops! why would it fail? */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If only one demodulator/slicer, and no FX.25 in progress,
|
* If only one demodulator/slicer, and no FX.25 in progress,
|
||||||
* push it thru and forget about all this foolishness.
|
* push it thru and forget about all this foolishness.
|
||||||
|
@ -488,6 +495,9 @@ static void pick_best_candidate (int chan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: IL2p & FX.25 don't have CRC calculated. Must fill it in first.
|
||||||
|
|
||||||
|
|
||||||
/* Bump it up slightly if others nearby have the same CRC. */
|
/* Bump it up slightly if others nearby have the same CRC. */
|
||||||
|
|
||||||
for (n = 0; n < num_bars; n++) {
|
for (n = 0; n < num_bars; n++) {
|
||||||
|
|
|
@ -16,6 +16,9 @@ void multi_modem_process_sample (int c, int audio_sample);
|
||||||
|
|
||||||
int multi_modem_get_dc_average (int chan);
|
int multi_modem_get_dc_average (int chan);
|
||||||
|
|
||||||
|
// Deprecated. Replace with ...packet
|
||||||
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, int is_fx25);
|
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, int is_fx25);
|
||||||
|
|
||||||
|
void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
15
src/xmit.c
15
src/xmit.c
|
@ -766,7 +766,7 @@ static void xmit_ax25_frames (int chan, int prio, packet_t pp, int max_bundle)
|
||||||
// machine, that the transmission opportunity has arrived."
|
// machine, that the transmission opportunity has arrived."
|
||||||
|
|
||||||
pre_flags = MS_TO_BITS(xmit_txdelay[chan] * 10, chan) / 8;
|
pre_flags = MS_TO_BITS(xmit_txdelay[chan] * 10, chan) / 8;
|
||||||
num_bits = hdlc_send_flags (chan, pre_flags, 0);
|
num_bits = layer2_preamble_postamble (chan, pre_flags, 0, save_audio_config_p);
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("xmit_thread: t=%.3f, txdelay=%d [*10], pre_flags=%d, num_bits=%d\n", dtime_now()-time_ptt, xmit_txdelay[chan], pre_flags, num_bits);
|
dw_printf ("xmit_thread: t=%.3f, txdelay=%d [*10], pre_flags=%d, num_bits=%d\n", dtime_now()-time_ptt, xmit_txdelay[chan], pre_flags, num_bits);
|
||||||
|
@ -867,7 +867,7 @@ static void xmit_ax25_frames (int chan, int prio, packet_t pp, int max_bundle)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
post_flags = MS_TO_BITS(xmit_txtail[chan] * 10, chan) / 8;
|
post_flags = MS_TO_BITS(xmit_txtail[chan] * 10, chan) / 8;
|
||||||
nb = hdlc_send_flags (chan, post_flags, 1);
|
nb = layer2_preamble_postamble (chan, post_flags, 1, save_audio_config_p);
|
||||||
num_bits += nb;
|
num_bits += nb;
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
@ -962,8 +962,6 @@ static void xmit_ax25_frames (int chan, int prio, packet_t pp, int max_bundle)
|
||||||
|
|
||||||
static int send_one_frame (int c, int p, packet_t pp)
|
static int send_one_frame (int c, int p, packet_t pp)
|
||||||
{
|
{
|
||||||
unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
|
|
||||||
int flen;
|
|
||||||
char stemp[1024]; /* max size needed? */
|
char stemp[1024]; /* max size needed? */
|
||||||
int info_len;
|
int info_len;
|
||||||
unsigned char *pinfo;
|
unsigned char *pinfo;
|
||||||
|
@ -1007,10 +1005,10 @@ static int send_one_frame (int c, int p, packet_t pp)
|
||||||
ax25_format_addrs (pp, stemp);
|
ax25_format_addrs (pp, stemp);
|
||||||
info_len = ax25_get_info (pp, &pinfo);
|
info_len = ax25_get_info (pp, &pinfo);
|
||||||
text_color_set(DW_COLOR_XMIT);
|
text_color_set(DW_COLOR_XMIT);
|
||||||
#if 0
|
#if 0 // FIXME - enable this?
|
||||||
dw_printf ("[%d%c%s%s] ", c,
|
dw_printf ("[%d%c%s%s] ", c,
|
||||||
p==TQ_PRIO_0_HI ? 'H' : 'L',
|
p==TQ_PRIO_0_HI ? 'H' : 'L',
|
||||||
save_audio_config_p->fx25_xmit_enable ? "F" : "",
|
save_audio_config_p->achan[c].fx25_strength ? "F" : "",
|
||||||
ts);
|
ts);
|
||||||
#else
|
#else
|
||||||
dw_printf ("[%d%c%s] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L', ts);
|
dw_printf ("[%d%c%s] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L', ts);
|
||||||
|
@ -1064,9 +1062,6 @@ static int send_one_frame (int c, int p, packet_t pp)
|
||||||
/*
|
/*
|
||||||
* Transmit the frame.
|
* Transmit the frame.
|
||||||
*/
|
*/
|
||||||
flen = ax25_pack (pp, fbuf);
|
|
||||||
assert (flen >= 1 && flen <= (int)(sizeof(fbuf)));
|
|
||||||
|
|
||||||
int send_invalid_fcs2 = 0;
|
int send_invalid_fcs2 = 0;
|
||||||
|
|
||||||
if (save_audio_config_p->xmit_error_rate != 0) {
|
if (save_audio_config_p->xmit_error_rate != 0) {
|
||||||
|
@ -1079,7 +1074,7 @@ static int send_one_frame (int c, int p, packet_t pp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nb = hdlc_send_frame (c, fbuf, flen, send_invalid_fcs2, save_audio_config_p->fx25_xmit_enable);
|
nb = layer2_send_frame (c, pp, send_invalid_fcs2, save_audio_config_p);
|
||||||
|
|
||||||
// Optionally send confirmation to AGW client app if monitoring enabled.
|
// Optionally send confirmation to AGW client app if monitoring enabled.
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ set(GEN_PACKETS_BIN "${CMAKE_BINARY_DIR}/src/gen_packets${CMAKE_EXECUTABLE_SUFFI
|
||||||
set(ATEST_BIN "${CMAKE_BINARY_DIR}/src/atest${CMAKE_EXECUTABLE_SUFFIX}")
|
set(ATEST_BIN "${CMAKE_BINARY_DIR}/src/atest${CMAKE_EXECUTABLE_SUFFIX}")
|
||||||
set(FXSEND_BIN "${CMAKE_BINARY_DIR}/test/fxsend${CMAKE_EXECUTABLE_SUFFIX}")
|
set(FXSEND_BIN "${CMAKE_BINARY_DIR}/test/fxsend${CMAKE_EXECUTABLE_SUFFIX}")
|
||||||
set(FXREC_BIN "${CMAKE_BINARY_DIR}/test/fxrec${CMAKE_EXECUTABLE_SUFFIX}")
|
set(FXREC_BIN "${CMAKE_BINARY_DIR}/test/fxrec${CMAKE_EXECUTABLE_SUFFIX}")
|
||||||
|
set(IL2P_TEST_BIN "${CMAKE_BINARY_DIR}/test/il2p_test${CMAKE_EXECUTABLE_SUFFIX}")
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(CUSTOM_SCRIPT_SUFFIX ".bat")
|
set(CUSTOM_SCRIPT_SUFFIX ".bat")
|
||||||
|
@ -14,7 +15,9 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(TEST_CHECK-FX25_FILE "check-fx25")
|
set(TEST_CHECK-FX25_FILE "check-fx25")
|
||||||
|
set(TEST_CHECK-IL2P_FILE "check-il2p")
|
||||||
set(TEST_CHECK-MODEM1200_FILE "check-modem1200")
|
set(TEST_CHECK-MODEM1200_FILE "check-modem1200")
|
||||||
|
set(TEST_CHECK-MODEM1200_IL2P_FILE "check-modem1200-i")
|
||||||
set(TEST_CHECK-MODEM300_FILE "check-modem300")
|
set(TEST_CHECK-MODEM300_FILE "check-modem300")
|
||||||
set(TEST_CHECK-MODEM9600_FILE "check-modem9600")
|
set(TEST_CHECK-MODEM9600_FILE "check-modem9600")
|
||||||
set(TEST_CHECK-MODEM19200_FILE "check-modem19200")
|
set(TEST_CHECK-MODEM19200_FILE "check-modem19200")
|
||||||
|
@ -31,12 +34,24 @@ configure_file(
|
||||||
@ONLY
|
@ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${CUSTOM_TEST_SCRIPTS_DIR}/${TEST_CHECK-IL2P_FILE}"
|
||||||
|
"${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-IL2P_FILE}${CUSTOM_SCRIPT_SUFFIX}"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CUSTOM_TEST_SCRIPTS_DIR}/${TEST_CHECK-MODEM1200_FILE}"
|
"${CUSTOM_TEST_SCRIPTS_DIR}/${TEST_CHECK-MODEM1200_FILE}"
|
||||||
"${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM1200_FILE}${CUSTOM_SCRIPT_SUFFIX}"
|
"${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM1200_FILE}${CUSTOM_SCRIPT_SUFFIX}"
|
||||||
@ONLY
|
@ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${CUSTOM_TEST_SCRIPTS_DIR}/${TEST_CHECK-MODEM1200_IL2P_FILE}"
|
||||||
|
"${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM1200_IL2P_FILE}${CUSTOM_SCRIPT_SUFFIX}"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CUSTOM_TEST_SCRIPTS_DIR}/${TEST_CHECK-MODEM300_FILE}"
|
"${CUSTOM_TEST_SCRIPTS_DIR}/${TEST_CHECK-MODEM300_FILE}"
|
||||||
"${CUSTOM_TEST_BINARY_DIR}${CUSTOM_SCRIPT_SUFFIX}"
|
"${CUSTOM_TEST_BINARY_DIR}${CUSTOM_SCRIPT_SUFFIX}"
|
||||||
|
@ -100,63 +115,12 @@ if(WIN32 OR CYGWIN)
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Why is there a special atest9 instead of using the normal one?
|
|
||||||
|
|
||||||
# Unit test for demodulators
|
|
||||||
list(APPEND atest9_SOURCES
|
|
||||||
${CUSTOM_SRC_DIR}/atest.c
|
|
||||||
${CUSTOM_SRC_DIR}/ais.c
|
|
||||||
${CUSTOM_SRC_DIR}/demod.c
|
|
||||||
${CUSTOM_SRC_DIR}/dsp.c
|
|
||||||
${CUSTOM_SRC_DIR}/demod_afsk.c
|
|
||||||
${CUSTOM_SRC_DIR}/demod_psk.c
|
|
||||||
${CUSTOM_SRC_DIR}/demod_9600.c
|
|
||||||
${CUSTOM_SRC_DIR}/fx25_extract.c
|
|
||||||
${CUSTOM_SRC_DIR}/fx25_init.c
|
|
||||||
${CUSTOM_SRC_DIR}/fx25_rec.c
|
|
||||||
${CUSTOM_SRC_DIR}/hdlc_rec.c
|
|
||||||
${CUSTOM_SRC_DIR}/hdlc_rec2.c
|
|
||||||
${CUSTOM_SRC_DIR}/multi_modem.c
|
|
||||||
${CUSTOM_SRC_DIR}/rrbb.c
|
|
||||||
${CUSTOM_SRC_DIR}/fcs_calc.c
|
|
||||||
${CUSTOM_SRC_DIR}/ax25_pad.c
|
|
||||||
${CUSTOM_SRC_DIR}/decode_aprs.c
|
|
||||||
${CUSTOM_SRC_DIR}/dwgpsnmea.c
|
|
||||||
${CUSTOM_SRC_DIR}/dwgps.c
|
|
||||||
${CUSTOM_SRC_DIR}/dwgpsd.c
|
|
||||||
${CUSTOM_SRC_DIR}/serial_port.c
|
|
||||||
${CUSTOM_SRC_DIR}/latlong.c
|
|
||||||
${CUSTOM_SRC_DIR}/symbols.c
|
|
||||||
${CUSTOM_SRC_DIR}/textcolor.c
|
|
||||||
${CUSTOM_SRC_DIR}/telemetry.c
|
|
||||||
${CUSTOM_SRC_DIR}/dtime_now.c
|
|
||||||
${CUSTOM_SRC_DIR}/tt_text.c
|
|
||||||
)
|
|
||||||
|
|
||||||
if(WIN32 OR CYGWIN)
|
if(WIN32 OR CYGWIN)
|
||||||
list(REMOVE_ITEM atest9_SOURCES
|
list(REMOVE_ITEM atest9_SOURCES
|
||||||
${CUSTOM_SRC_DIR}/dwgpsd.c
|
${CUSTOM_SRC_DIR}/dwgpsd.c
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(atest9
|
|
||||||
${atest9_SOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(atest9
|
|
||||||
${MISC_LIBRARIES}
|
|
||||||
${REGEX_LIBRARIES}
|
|
||||||
${GPSD_LIBRARIES}
|
|
||||||
Threads::Threads
|
|
||||||
)
|
|
||||||
|
|
||||||
if(WIN32 OR CYGWIN)
|
|
||||||
set_target_properties(atest9
|
|
||||||
PROPERTIES COMPILE_FLAGS "-DUSE_REGEX_STATIC"
|
|
||||||
)
|
|
||||||
target_link_libraries(atest9 ws2_32)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# Unit test for inner digipeater algorithm
|
# Unit test for inner digipeater algorithm
|
||||||
list(APPEND dtest_SOURCES
|
list(APPEND dtest_SOURCES
|
||||||
|
@ -468,6 +432,39 @@ set_target_properties(fxrec
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Unit Test IL2P with out modems.
|
||||||
|
|
||||||
|
list(APPEND il2p_test_SOURCES
|
||||||
|
${CUSTOM_SRC_DIR}/il2p_test.c
|
||||||
|
${CUSTOM_SRC_DIR}/il2p_init.c
|
||||||
|
${CUSTOM_SRC_DIR}/il2p_rec.c
|
||||||
|
${CUSTOM_SRC_DIR}/il2p_send.c
|
||||||
|
${CUSTOM_SRC_DIR}/il2p_codec.c
|
||||||
|
${CUSTOM_SRC_DIR}/il2p_payload.c
|
||||||
|
${CUSTOM_SRC_DIR}/il2p_header.c
|
||||||
|
${CUSTOM_SRC_DIR}/il2p_scramble.c
|
||||||
|
${CUSTOM_SRC_DIR}/ax25_pad.c
|
||||||
|
${CUSTOM_SRC_DIR}/ax25_pad2.c
|
||||||
|
${CUSTOM_SRC_DIR}/fx25_encode.c
|
||||||
|
${CUSTOM_SRC_DIR}/fx25_extract.c
|
||||||
|
${CUSTOM_SRC_DIR}/fx25_init.c
|
||||||
|
${CUSTOM_SRC_DIR}/fcs_calc.c
|
||||||
|
${CUSTOM_SRC_DIR}/textcolor.c
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(il2p_test
|
||||||
|
${il2p_test_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
#FIXME - remove if not needed.
|
||||||
|
#set_target_properties(il2p_test
|
||||||
|
# PROPERTIES COMPILE_FLAGS "-DXXXXX"
|
||||||
|
# )
|
||||||
|
|
||||||
|
target_link_libraries(il2p_test
|
||||||
|
${MISC_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
# doing ctest on previous programs
|
# doing ctest on previous programs
|
||||||
|
|
||||||
add_test(dtest dtest)
|
add_test(dtest dtest)
|
||||||
|
@ -483,7 +480,9 @@ add_test(xidtest xidtest)
|
||||||
add_test(dtmftest dtmftest)
|
add_test(dtmftest dtmftest)
|
||||||
|
|
||||||
add_test(check-fx25 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-FX25_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
add_test(check-fx25 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-FX25_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
|
add_test(check-il2p "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-IL2P_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
add_test(check-modem1200 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM1200_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
add_test(check-modem1200 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM1200_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
|
add_test(check-modem1200-i "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM1200_IL2P_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
add_test(check-modem300 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM300_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
add_test(check-modem300 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM300_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
add_test(check-modem9600 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM9600_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
add_test(check-modem9600 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM9600_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
add_test(check-modem19200 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM19200_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
add_test(check-modem19200 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM19200_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
|
@ -492,8 +491,6 @@ add_test(check-modem2400-b "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-b_F
|
||||||
add_test(check-modem2400-g "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-g_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
add_test(check-modem2400-g "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-g_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
add_test(check-modem4800 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM4800_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
add_test(check-modem4800 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM4800_FILE}${CUSTOM_SCRIPT_SUFFIX}")
|
||||||
|
|
||||||
# TODO miss the audio file
|
|
||||||
# ./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------- Manual tests and experiments ---------------------------
|
# ----------------------------- Manual tests and experiments ---------------------------
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
@CUSTOM_SHELL_SHABANG@
|
||||||
|
|
||||||
|
@IL2P_TEST_BIN@
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
@CUSTOM_SHELL_SHABANG@
|
||||||
|
|
||||||
|
@GEN_PACKETS_BIN@ -I1 -n 100 -o test12-il2p.wav
|
||||||
|
@ATEST_BIN@ -P+ -D1 -L92 -G94 test12-il2p.wav
|
Loading…
Reference in New Issue