Add IL2P.

This commit is contained in:
wb2osz 2021-10-22 17:29:20 -04:00
parent 17b9336ce0
commit 53e9ff7908
36 changed files with 6888 additions and 141 deletions

View File

@ -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)

View File

@ -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:

View File

@ -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"

View File

@ -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"

View File

@ -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})

View File

@ -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();

View File

@ -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.

View File

@ -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)
{ {

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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.
} }
/* /*

View File

@ -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];

View File

@ -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 */

145
src/il2p.h Normal file
View File

@ -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

150
src/il2p.h-bak16 Normal file
View File

@ -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

263
src/il2p_codec.c Normal file
View File

@ -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

265
src/il2p_codec.c-bak16 Normal file
View File

@ -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

673
src/il2p_header.c Normal file
View File

@ -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

226
src/il2p_init.c Normal file
View File

@ -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

298
src/il2p_payload.c Normal file
View File

@ -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

317
src/il2p_payload.c-bak16 Normal file
View File

@ -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

276
src/il2p_rec.c Normal file
View File

@ -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

157
src/il2p_scramble.c Normal file
View File

@ -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

194
src/il2p_scramble.c-bak16 Normal file
View File

@ -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

147
src/il2p_send.c Normal file
View File

@ -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

977
src/il2p_test.c Normal file
View File

@ -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

1030
src/il2p_test.c-bak16 Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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++) {

View File

@ -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

1284
src/tnctest.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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 ---------------------------

4
test/scripts/check-il2p Normal file
View File

@ -0,0 +1,4 @@
@CUSTOM_SHELL_SHABANG@
@IL2P_TEST_BIN@

View File

@ -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