2023-09-04 19:06:44 +01:00
/*
Copyright ( C ) 2019 - 2020 Andrei Kopanchuk UZ7HO
This file is part of QtSoundModem
QtSoundModem 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 3 of the License , or
( at your option ) any later version .
QtSoundModem 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 QtSoundModem . If not , see http : //www.gnu.org/licenses
*/
// UZ7HO Soundmodem Port by John Wiseman G8BPQ
// IL2P code. Based on Direwolf code, under the following copyright
//
// 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/>.
//
// IP2P receive code (il2p_rec_bit) is called from the bit receiving code in ax25_demod.c, so includes parallel decoders
2023-12-17 13:53:32 +00:00
# include "UZ7HOStuff.h"
# include <stdint.h> // for uint64_t
2024-07-23 21:26:30 +01:00
void debugHexDump ( unsigned char * Data , int Len , char Dirn ) ;
extern void debugTimeStamp ( char * Text , char Dirn ) ;
extern int useTimedPTT ;
2023-12-17 13:53:32 +00:00
// Oct 2023 Nino has added an optional crc
// Hamming(7,4) Encoding Table
// Enter this table with the 4-bit value to be encoded.
// Returns 7-bit encoded value, with high bit zero'd.
uint8_t Hamming74EncodeTable [ 16 ] = { 0x0 , 0x71 , 0x62 , 0x13 , 0x54 , 0x25 , 0x36 , 0x47 , 0x38 , 0x49 , 0x5a , 0x2b , 0x6c , 0x1d , 0xe , 0x7f } ;
// Hamming(7,4) Decoding Table
// Enter this table with 7-bit encoded value, high bit masked.
// Returns 4-bit decoded value.
uint16_t Hamming74DecodeTable [ 128 ] = { \
0x0 , 0x0 , 0x0 , 0x3 , 0x0 , 0x5 , 0xe , 0x7 , \
0x0 , 0x9 , 0xe , 0xb , 0xe , 0xd , 0xe , 0xe , \
0x0 , 0x3 , 0x3 , 0x3 , 0x4 , 0xd , 0x6 , 0x3 , \
0x8 , 0xd , 0xa , 0x3 , 0xd , 0xd , 0xe , 0xd , \
0x0 , 0x5 , 0x2 , 0xb , 0x5 , 0x5 , 0x6 , 0x5 , \
0x8 , 0xb , 0xb , 0xb , 0xc , 0x5 , 0xe , 0xb , \
0x8 , 0x1 , 0x6 , 0x3 , 0x6 , 0x5 , 0x6 , 0x6 , \
0x8 , 0x8 , 0x8 , 0xb , 0x8 , 0xd , 0x6 , 0xf , \
0x0 , 0x9 , 0x2 , 0x7 , 0x4 , 0x7 , 0x7 , 0x7 , \
0x9 , 0x9 , 0xa , 0x9 , 0xc , 0x9 , 0xe , 0x7 , \
0x4 , 0x1 , 0xa , 0x3 , 0x4 , 0x4 , 0x4 , 0x7 , \
0xa , 0x9 , 0xa , 0xa , 0x4 , 0xd , 0xa , 0xf , \
0x2 , 0x1 , 0x2 , 0x2 , 0xc , 0x5 , 0x2 , 0x7 , \
0xc , 0x9 , 0x2 , 0xb , 0xc , 0xc , 0xc , 0xf , \
0x1 , 0x1 , 0x2 , 0x1 , 0x4 , 0x1 , 0x6 , 0xf , \
0x8 , 0x1 , 0xa , 0xf , 0xc , 0xf , 0xf , 0xf } ;
2023-09-04 19:06:44 +01:00
void Debugprintf ( const char * format , . . . ) ;
2023-09-12 21:38:15 +01:00
int SMUpdatePhaseConstellation ( int chan , float * Phases , float * Mags , int intPSKPhase , int Count ) ;
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
extern int openTraceLog ( ) ;
2024-10-29 22:45:21 +00:00
extern uint64_t writeTraceLog ( char * Data ) ;
2024-07-23 21:26:30 +01:00
extern void closeTraceLog ( ) ;
2023-09-04 19:06:44 +01:00
# define MAX_ADEVS 3
# define MAX_RADIO_CHANS ((MAX_ADEVS) * 2)
# define MAX_CHANS MAX_RADIO_CHANS // TODO: Replace all former with latter to avoid confusion with following.
# define MAX_TOTAL_CHANS 16 // v1.7 allows additional virtual channels which are connected
// to something other than radio modems.
// Total maximum channels is based on the 4 bit KISS field.
// Someone with very unusual requirements could increase this and
// use only the AGW network protocol.
# define MAX_SUBCHANS 9
# define MAX_SLICERS 9
# define max(x, y) ((x) > (y) ? (x) : (y))
# define min(x, y) ((x) < (y) ? (x) : (y))
2023-09-12 21:38:15 +01:00
extern int nPhases [ 4 ] [ 16 ] [ nr_emph + 1 ] ;
extern float Phases [ 4 ] [ 16 ] [ nr_emph + 1 ] [ 4096 ] ;
extern float Mags [ 4 ] [ 16 ] [ nr_emph + 1 ] [ 4096 ] ;
2023-09-04 19:06:44 +01:00
/* For option to try fixing frames with bad CRC. */
typedef enum retry_e {
RETRY_NONE = 0 ,
RETRY_INVERT_SINGLE = 1 ,
RETRY_INVERT_DOUBLE = 2 ,
RETRY_INVERT_TRIPLE = 3 ,
RETRY_INVERT_TWO_SEP = 4 ,
RETRY_MAX = 5
} retry_t ;
typedef struct alevel_s {
int rec ;
int mark ;
int space ;
//float ms_ratio; // TODO: take out after temporary investigation.
} alevel_t ;
alevel_t demod_get_audio_level ( int chan , int subchan ) ;
2024-04-13 17:48:58 +01:00
void tone_gen_put_bit ( int chan , int dat , int scramble ) ;
2023-09-04 19:06:44 +01:00
int ax25memdebug = 1 ;
// Code to try to determine centre freq
float MagOut [ 4096 ] ;
float MaxMagOut = 0 ;
int MaxMagIndex = 0 ;
// FFT Bin Size is 12000 / FFTSize
# ifndef FX25_H
# define FX25_H
2023-09-12 21:38:15 +01:00
extern unsigned int pskStates [ 4 ] ;
2023-09-04 19:06:44 +01:00
/* Reed-Solomon codec control block */
struct rs {
unsigned int mm ; /* Bits per symbol */
unsigned int nn ; /* Symbols per block (= (1<<mm)-1) */
unsigned char * alpha_to ; /* log lookup table */
unsigned char * index_of ; /* Antilog lookup table */
unsigned char * genpoly ; /* Generator polynomial */
unsigned int nroots ; /* Number of generator roots = number of parity symbols */
unsigned char fcr ; /* First consecutive root, index form */
unsigned char prim ; /* Primitive element, index form */
unsigned char iprim ; /* prim-th root of 1, index form */
} ;
# define MM (rs->mm)
# define NN (rs->nn)
# define ALPHA_TO (rs->alpha_to)
# define INDEX_OF (rs->index_of)
# define GENPOLY (rs->genpoly)
# define NROOTS (rs->nroots)
# define FCR (rs->fcr)
# define PRIM (rs->prim)
# define IPRIM (rs->iprim)
# define A0 (NN)
int __builtin_popcountll ( unsigned long long int i )
{
return 0 ;
}
int __builtin_popcount ( unsigned int n )
{
unsigned int count = 0 ;
while ( n )
{
count + = n & 1 ;
n > > = 1 ;
}
return count ;
}
static inline int modnn ( struct rs * rs , int x ) {
while ( x > = rs - > nn ) {
x - = rs - > nn ;
x = ( x > > rs - > mm ) + ( x & rs - > nn ) ;
}
return x ;
}
# define MODNN(x) modnn(rs,x)
# define ENCODE_RS encode_rs_char
# define DECODE_RS decode_rs_char
# define INIT_RS init_rs_char
# define FREE_RS free_rs_char
# define DTYPE unsigned char
void ENCODE_RS ( struct rs * rs , DTYPE * data , DTYPE * bb ) ;
int DECODE_RS ( struct rs * rs , DTYPE * data , int * eras_pos , int no_eras ) ;
struct rs * INIT_RS ( unsigned int symsize , unsigned int gfpoly ,
unsigned int fcr , unsigned int prim , unsigned int nroots ) ;
void FREE_RS ( struct rs * rs ) ;
// These 3 are the external interface.
// Maybe these should be in a different file, separated from the internal stuff.
void fx25_init ( int debug_level ) ;
int fx25_send_frame ( int chan , unsigned char * fbuf , int flen , int fx_mode ) ;
void fx25_rec_bit ( int chan , int subchan , int slice , int dbit ) ;
int fx25_rec_busy ( int chan ) ;
// Other functions in fx25_init.c.
struct rs * fx25_get_rs ( int ctag_num ) ;
uint64_t fx25_get_ctag_value ( int ctag_num ) ;
int fx25_get_k_data_radio ( int ctag_num ) ;
int fx25_get_k_data_rs ( int ctag_num ) ;
int fx25_get_nroots ( int ctag_num ) ;
int fx25_get_debug ( void ) ;
int fx25_tag_find_match ( uint64_t t ) ;
int fx25_pick_mode ( int fx_mode , int dlen ) ;
void fx_hex_dump ( unsigned char * x , int len ) ;
/*-------------------------------------------------------------------
*
* Name : ax25_pad . h
*
* Purpose : Header file for using ax25_pad . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# ifndef AX25_PAD_H
# define AX25_PAD_H 1
# define AX25_MAX_REPEATERS 8
# define AX25_MIN_ADDRS 2 /* Destination & Source. */
# define AX25_MAX_ADDRS 10 /* Destination, Source, 8 digipeaters. */
# define AX25_DESTINATION 0 /* Address positions in frame. */
# define AX25_SOURCE 1
# define AX25_REPEATER_1 2
# define AX25_REPEATER_2 3
# define AX25_REPEATER_3 4
# define AX25_REPEATER_4 5
# define AX25_REPEATER_5 6
# define AX25_REPEATER_6 7
# define AX25_REPEATER_7 8
# define AX25_REPEATER_8 9
# define AX25_MAX_ADDR_LEN 12 /* In theory, you would expect the maximum length */
/* to be 6 letters, dash, 2 digits, and nul for a */
/* total of 10. However, object labels can be 10 */
/* characters so throw in a couple extra bytes */
/* to be safe. */
# define AX25_MIN_INFO_LEN 0 /* Previously 1 when considering only APRS. */
# define AX25_MAX_INFO_LEN 2048 /* Maximum size for APRS. */
/* AX.25 starts out with 256 as the default max */
/* length but the end stations can negotiate */
/* something different. */
/* version 0.8: Change from 256 to 2028 to */
/* handle the larger paclen for Linux AX25. */
/* These don't include the 2 bytes for the */
/* HDLC frame FCS. */
/*
* Previously , for APRS only .
* # define AX25_MIN_PACKET_LEN ( 2 * 7 + 2 + AX25_MIN_INFO_LEN )
* # define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + AX25_MAX_INFO_LEN )
*/
/* The more general case. */
/* An AX.25 frame can have a control byte and no protocol. */
# define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 )
# define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN)
/*
* packet_t is a pointer to a packet object .
*
* The actual implementation is not visible outside ax25_pad . c .
*/
# define AX25_UI_FRAME 3 /* Control field value. */
# define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */
# define AX25_PID_SEGMENTATION_FRAGMENT 0x08
# define AX25_PID_ESCAPE_CHARACTER 0xff
struct packet_s {
int magic1 ; /* for error checking. */
int seq ; /* unique sequence number for debugging. */
double release_time ; /* Time stamp in format returned by dtime_now(). */
/* When to release from the SATgate mode delay queue. */
# define MAGIC 0x41583235
struct packet_s * nextp ; /* Pointer to next in queue. */
int num_addr ; /* Number of addresses in frame. */
/* Range of AX25_MIN_ADDRS .. AX25_MAX_ADDRS for AX.25. */
/* It will be 0 if it doesn't look like AX.25. */
/* -1 is used temporarily at allocation to mean */
/* not determined yet. */
/*
* The 7 th octet of each address contains :
*
* Bits : H R R SSID 0
*
* H for digipeaters set to 0 initially .
* Changed to 1 when position has been used .
*
* for source & destination it is called
* command / response . Normally both 1 for APRS .
* They should be opposites for connected mode .
*
* R R Reserved . Normally set to 1 1.
*
* SSID Substation ID . Range of 0 - 15.
*
* 0 Usually 0 but 1 for last address .
*/
# define SSID_H_MASK 0x80
# define SSID_H_SHIFT 7
# define SSID_RR_MASK 0x60
# define SSID_RR_SHIFT 5
# define SSID_SSID_MASK 0x1e
# define SSID_SSID_SHIFT 1
# define SSID_LAST_MASK 0x01
int frame_len ; /* Frame length without CRC. */
int modulo ; /* I & S frames have sequence numbers of either 3 bits (modulo 8) */
/* or 7 bits (modulo 128). This is conveyed by either 1 or 2 */
/* control bytes. Unfortunately, we can't determine this by looking */
/* at an isolated frame. We need to know about the context. If we */
/* are part of the conversation, we would know. But if we are */
/* just listening to others, this would be more difficult to determine. */
/* For U frames: set to 0 - not applicable */
/* For I & S frames: 8 or 128 if known. 0 if unknown. */
unsigned char frame_data [ AX25_MAX_PACKET_LEN + 1 ] ;
/* Raw frame contents, without the CRC. */
2023-12-17 13:53:32 +00:00
unsigned char crc [ 4 ] ; // received crc
2023-09-04 19:06:44 +01:00
int magic2 ; /* Will get stomped on if above overflows. */
} ;
typedef struct packet_s * packet_t ;
typedef enum cmdres_e { cr_00 = 2 , cr_cmd = 1 , cr_res = 0 , cr_11 = 3 } cmdres_t ;
extern packet_t ax25_new ( void ) ;
# ifdef AX25_PAD_C /* Keep this hidden - implementation could change. */
/*
* APRS always has one control octet of 0x03 but the more
* general AX .25 case is one or two control bytes depending on
* whether " modulo 128 operation " is in effect .
*/
//#define DEBUGX 1
static inline int ax25_get_control_offset ( packet_t this_p )
{
return ( this_p - > num_addr * 7 ) ;
}
static inline int ax25_get_num_control ( packet_t this_p )
{
int c ;
c = this_p - > frame_data [ ax25_get_control_offset ( this_p ) ] ;
if ( ( c & 0x01 ) = = 0 ) { /* I xxxx xxx0 */
# if DEBUGX
Debugprintf ( " ax25_get_num_control, %02x is I frame, returns %d \n " , c , ( this_p - > modulo = = 128 ) ? 2 : 1 ) ;
# endif
return ( ( this_p - > modulo = = 128 ) ? 2 : 1 ) ;
}
if ( ( c & 0x03 ) = = 1 ) { /* S xxxx xx01 */
# if DEBUGX
Debugprintf ( " ax25_get_num_control, %02x is S frame, returns %d \n " , c , ( this_p - > modulo = = 128 ) ? 2 : 1 ) ;
# endif
return ( ( this_p - > modulo = = 128 ) ? 2 : 1 ) ;
}
# if DEBUGX
Debugprintf ( " ax25_get_num_control, %02x is U frame, always returns 1. \n " , c ) ;
# endif
return ( 1 ) ; /* U xxxx xx11 */
}
/*
* APRS always has one protocol octet of 0xF0 meaning no level 3
* protocol but the more general case is 0 , 1 or 2 protocol ID octets .
*/
static inline int ax25_get_pid_offset ( packet_t this_p )
{
return ( ax25_get_control_offset ( this_p ) + ax25_get_num_control ( this_p ) ) ;
}
static int ax25_get_num_pid ( packet_t this_p )
{
int c ;
int pid ;
c = this_p - > frame_data [ ax25_get_control_offset ( this_p ) ] ;
if ( ( c & 0x01 ) = = 0 | | /* I xxxx xxx0 */
c = = 0x03 | | c = = 0x13 ) { /* UI 000x 0011 */
pid = this_p - > frame_data [ ax25_get_pid_offset ( this_p ) ] ;
# if DEBUGX
Debugprintf ( " ax25_get_num_pid, %02x is I or UI frame, pid = %02x, returns %d \n " , c , pid , ( pid = = AX25_PID_ESCAPE_CHARACTER ) ? 2 : 1 ) ;
# endif
if ( pid = = AX25_PID_ESCAPE_CHARACTER ) {
return ( 2 ) ; /* pid 1111 1111 means another follows. */
}
return ( 1 ) ;
}
# if DEBUGX
Debugprintf ( " ax25_get_num_pid, %02x is neither I nor UI frame, returns 0 \n " , c ) ;
# endif
return ( 0 ) ;
}
/*
* AX .25 has info field for 5 frame types depending on the control field .
*
* xxxx xxx0 I
* 000 x 0011 UI ( which includes APRS )
* 101 x 1111 XID
* 111 x 0011 TEST
* 100 x 0111 FRMR
*
* APRS always has an Information field with at least one octet for the Data Type Indicator .
*/
static inline int ax25_get_info_offset ( packet_t this_p )
{
int offset = ax25_get_control_offset ( this_p ) + ax25_get_num_control ( this_p ) + ax25_get_num_pid ( this_p ) ;
# if DEBUGX
Debugprintf ( " ax25_get_info_offset, returns %d \n " , offset ) ;
# endif
return ( offset ) ;
}
static inline int ax25_get_num_info ( packet_t this_p )
{
int len ;
/* assuming AX.25 frame. */
len = this_p - > frame_len - this_p - > num_addr * 7 - ax25_get_num_control ( this_p ) - ax25_get_num_pid ( this_p ) ;
if ( len < 0 ) {
len = 0 ; /* print error? */
}
return ( len ) ;
}
# endif
typedef enum ax25_modulo_e { modulo_unknown = 0 , modulo_8 = 8 , modulo_128 = 128 } ax25_modulo_t ;
typedef enum ax25_frame_type_e {
frame_type_I = 0 , // Information
frame_type_S_RR , // Receive Ready - System Ready To Receive
frame_type_S_RNR , // Receive Not Ready - TNC Buffer Full
frame_type_S_REJ , // Reject Frame - Out of Sequence or Duplicate
frame_type_S_SREJ , // Selective Reject - Request single frame repeat
frame_type_U_SABME , // Set Async Balanced Mode, Extended
frame_type_U_SABM , // Set Async Balanced Mode
frame_type_U_DISC , // Disconnect
frame_type_U_DM , // Disconnect Mode
frame_type_U_UA , // Unnumbered Acknowledge
frame_type_U_FRMR , // Frame Reject
frame_type_U_UI , // Unnumbered Information
frame_type_U_XID , // Exchange Identification
frame_type_U_TEST , // Test
frame_type_U , // other Unnumbered, not used by AX.25.
frame_not_AX25 // Could not get control byte from frame.
// This must be last because value plus 1 is
// for the size of an array.
} ax25_frame_type_t ;
/*
* Originally this was a single number .
* Let ' s try something new in version 1.2 .
* Also collect AGC values from the mark and space filters .
*/
# ifndef AXTEST
// TODO: remove this?
# define AX25MEMDEBUG 1
# endif
extern packet_t ax25_from_text ( char * monitor , int strict ) ;
extern packet_t ax25_from_frame ( unsigned char * data , int len , alevel_t alevel ) ;
extern packet_t ax25_dup ( packet_t copy_from ) ;
extern void ax25_delete ( packet_t pp ) ;
extern int ax25_parse_addr ( int position , char * in_addr , int strict , char * out_addr , int * out_ssid , int * out_heard ) ;
extern int ax25_check_addresses ( packet_t pp ) ;
extern packet_t ax25_unwrap_third_party ( packet_t from_pp ) ;
extern void ax25_set_addr ( packet_t pp , int , char * ) ;
extern void ax25_insert_addr ( packet_t this_p , int n , char * ad ) ;
extern void ax25_remove_addr ( packet_t this_p , int n ) ;
extern int ax25_get_num_addr ( packet_t pp ) ;
extern int ax25_get_num_repeaters ( packet_t this_p ) ;
extern void ax25_get_addr_with_ssid ( packet_t pp , int n , char * station ) ;
extern void ax25_get_addr_no_ssid ( packet_t pp , int n , char * station ) ;
extern int ax25_get_ssid ( packet_t pp , int n ) ;
extern void ax25_set_ssid ( packet_t this_p , int n , int ssid ) ;
extern int ax25_get_h ( packet_t pp , int n ) ;
extern void ax25_set_h ( packet_t pp , int n ) ;
extern int ax25_get_heard ( packet_t this_p ) ;
extern int ax25_get_first_not_repeated ( packet_t pp ) ;
extern int ax25_get_rr ( packet_t this_p , int n ) ;
extern int ax25_get_info ( packet_t pp , unsigned char * * paddr ) ;
extern void ax25_set_info ( packet_t pp , unsigned char * info_ptr , int info_len ) ;
extern int ax25_cut_at_crlf ( packet_t this_p ) ;
extern void ax25_set_nextp ( packet_t this_p , packet_t next_p ) ;
extern int ax25_get_dti ( packet_t this_p ) ;
extern packet_t ax25_get_nextp ( packet_t this_p ) ;
extern void ax25_set_release_time ( packet_t this_p , double release_time ) ;
extern double ax25_get_release_time ( packet_t this_p ) ;
extern void ax25_set_modulo ( packet_t this_p , int modulo ) ;
extern int ax25_get_modulo ( packet_t this_p ) ;
extern void ax25_format_addrs ( packet_t pp , char * ) ;
extern void ax25_format_via_path ( packet_t this_p , char * result , size_t result_size ) ;
extern int ax25_pack ( packet_t pp , unsigned char result [ AX25_MAX_PACKET_LEN ] ) ;
extern ax25_frame_type_t ax25_frame_type ( packet_t this_p , cmdres_t * cr , char * desc , int * pf , int * nr , int * ns ) ;
extern void ax25_hex_dump ( packet_t this_p ) ;
extern int ax25_is_aprs ( packet_t pp ) ;
extern int ax25_is_null_frame ( packet_t this_p ) ;
extern int ax25_get_control ( packet_t this_p ) ;
extern int ax25_get_c2 ( packet_t this_p ) ;
extern int ax25_get_pid ( packet_t this_p ) ;
extern int ax25_get_frame_len ( packet_t this_p ) ;
extern unsigned char * ax25_get_frame_data_ptr ( packet_t this_p ) ;
extern unsigned short ax25_dedupe_crc ( packet_t pp ) ;
extern unsigned short ax25_m_m_crc ( packet_t pp ) ;
extern void ax25_safe_print ( char * , int , int ascii_only ) ;
# define AX25_ALEVEL_TO_TEXT_SIZE 40 // overkill but safe.
extern int ax25_alevel_to_text ( alevel_t alevel , char text [ AX25_ALEVEL_TO_TEXT_SIZE ] ) ;
# endif /* AX25_PAD_H */
/* end ax25_pad.h */
# define CTAG_MIN 0x01
# define CTAG_MAX 0x0B
// Maximum sizes of "data" and "check" parts.
# define FX25_MAX_DATA 239 // i.e. RS(255,239)
# define FX25_MAX_CHECK 64 // e.g. RS(255, 191)
# define FX25_BLOCK_SIZE 255 // Block size always 255 for 8 bit symbols.
# endif // FX25_H
# 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)
float GuessCentreFreq ( int i )
{
float Freq = 0 ;
float Start ;
float End ;
int n ;
float Max = 0 ;
int Index = 0 ;
float BinSize = 12000.0 / FFTSize ;
Start = ( rx_freq [ i ] - RCVR [ i ] * rcvr_offset [ i ] ) / BinSize ;
End = ( rx_freq [ i ] + RCVR [ i ] * rcvr_offset [ i ] ) / BinSize ;
Start = ( active_rx_freq [ i ] - RCVR [ i ] * rcvr_offset [ i ] ) / BinSize ;
End = ( active_rx_freq [ i ] + RCVR [ i ] * rcvr_offset [ i ] ) / BinSize ;
for ( n = Start ; n < = End ; n + + )
{
if ( MagOut [ n ] > Max )
{
Max = MagOut [ n ] ;
Index = n ;
}
}
Freq = Index * BinSize ;
return Freq ;
}
/*------------------------------------------------------------------------------
*
* Name : ax25_new
*
* Purpose : Allocate memory for a new packet object .
*
* Returns : Identifier for a new packet object .
* In the current implementation this happens to be a pointer .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int last_seq_num = 0 ;
int new_count = 0 ;
int delete_count = 0 ;
packet_t ax25_new ( void )
{
struct packet_s * this_p ;
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
Debugprintf ( " ax25_new(): before alloc, new=%d, delete=%d \n " , new_count , delete_count ) ;
# endif
last_seq_num + + ;
new_count + + ;
/*
* check for memory leak .
*/
// version 1.4 push up the threshold. We could have considerably more with connected mode.
//if (new_count > delete_count + 100) {
if ( new_count > delete_count + 256 ) {
2023-09-12 21:38:15 +01:00
Debugprintf ( " Memory leak for packet objects. new=%d, delete=%d \n " , new_count , delete_count ) ;
2023-09-04 19:06:44 +01:00
# if AX25MEMDEBUG
# endif
}
this_p = calloc ( sizeof ( struct packet_s ) , ( size_t ) 1 ) ;
if ( this_p = = NULL ) {
Debugprintf ( " ERROR - can't allocate memory in ax25_new. \n " ) ;
}
// assert(this_p != NULL);
this_p - > magic1 = MAGIC ;
this_p - > seq = last_seq_num ;
this_p - > magic2 = MAGIC ;
this_p - > num_addr = ( - 1 ) ;
return ( this_p ) ;
}
/*------------------------------------------------------------------------------
*
* Name : ax25_delete
*
* Purpose : Destroy a packet object , freeing up memory it was using .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void ax25_delete ( packet_t this_p )
{
if ( this_p = = NULL ) {
Debugprintf ( " ERROR - NULL pointer passed to ax25_delete. \n " ) ;
return ;
}
delete_count + + ;
// assert(this_p->magic1 == MAGIC);
// assert(this_p->magic2 == MAGIC);
this_p - > magic1 = 0 ;
this_p - > magic1 = 0 ;
free ( this_p ) ;
}
/*------------------------------------------------------------------------------
*
* Name : ax25_s_frame
*
* Purpose : Construct an S frame .
*
* Input : addrs - Array of addresses .
*
* num_addr - Number of addresses , range 2 . . 10.
*
* cr - cr_cmd command frame , cr_res for a response frame .
*
* ftype - One of :
* frame_type_S_RR , // Receive Ready - System Ready To Receive
* frame_type_S_RNR , // Receive Not Ready - TNC Buffer Full
* frame_type_S_REJ , // Reject Frame - Out of Sequence or Duplicate
* frame_type_S_SREJ , // Selective Reject - Request single frame repeat
*
* modulo - 8 or 128. Determines if we have 1 or 2 control bytes .
*
* nr - N ( R ) field - - - describe .
*
* pf - Poll / Final flag .
*
* pinfo - Pointer to data for Info field . Allowed only for SREJ .
*
* info_len - Length for Info field .
*
*
* Returns : Pointer to new packet object .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
packet_t ax25_s_frame ( char addrs [ AX25_MAX_ADDRS ] [ AX25_MAX_ADDR_LEN ] , int num_addr , cmdres_t cr , ax25_frame_type_t ftype , int modulo , int nr , int pf , unsigned char * pinfo , int info_len )
{
packet_t this_p ;
unsigned char * p ;
int ctrl = 0 ;
this_p = ax25_new ( ) ;
if ( this_p = = NULL ) return ( NULL ) ;
if ( ! set_addrs ( this_p , addrs , num_addr , cr ) ) {
Debugprintf ( " Internal error in %s: Could not set addresses for S frame. \n " , __func__ ) ;
ax25_delete ( this_p ) ;
return ( NULL ) ;
}
if ( modulo ! = 8 & & modulo ! = 128 ) {
Debugprintf ( " Internal error in %s: Invalid modulo %d for S frame. \n " , __func__ , modulo ) ;
modulo = 8 ;
}
this_p - > modulo = modulo ;
if ( nr < 0 | | nr > = modulo ) {
Debugprintf ( " Internal error in %s: Invalid N(R) %d for S frame. \n " , __func__ , nr ) ;
nr & = ( modulo - 1 ) ;
}
// Erratum: The AX.25 spec is not clear about whether SREJ should be command, response, or both.
// The underlying X.25 spec clearly says it is response only. Let's go with that.
if ( ftype = = frame_type_S_SREJ & & cr ! = cr_res ) {
Debugprintf ( " Internal error in %s: SREJ must be response. \n " , __func__ ) ;
}
switch ( ftype ) {
case frame_type_S_RR : ctrl = 0x01 ; break ;
case frame_type_S_RNR : ctrl = 0x05 ; break ;
case frame_type_S_REJ : ctrl = 0x09 ; break ;
case frame_type_S_SREJ : ctrl = 0x0d ; break ;
default :
Debugprintf ( " Internal error in %s: Invalid ftype %d for S frame. \n " , __func__ , ftype ) ;
ax25_delete ( this_p ) ;
return ( NULL ) ;
break ;
}
p = this_p - > frame_data + this_p - > frame_len ;
if ( modulo = = 8 ) {
if ( pf ) ctrl | = 0x10 ;
ctrl | = nr < < 5 ;
* p + + = ctrl ;
this_p - > frame_len + + ;
}
else {
* p + + = ctrl ;
this_p - > frame_len + + ;
ctrl = pf & 1 ;
ctrl | = nr < < 1 ;
* p + + = ctrl ;
this_p - > frame_len + + ;
}
if ( ftype = = frame_type_S_SREJ ) {
if ( pinfo ! = NULL & & info_len > 0 ) {
if ( info_len > AX25_MAX_INFO_LEN ) {
Debugprintf ( " Internal error in %s: SREJ frame, Invalid information field length %d. \n " , __func__ , info_len ) ;
info_len = AX25_MAX_INFO_LEN ;
}
memcpy ( p , pinfo , info_len ) ;
p + = info_len ;
this_p - > frame_len + = info_len ;
}
}
else {
if ( pinfo ! = NULL | | info_len ! = 0 ) {
Debugprintf ( " Internal error in %s: Info part not allowed for RR, RNR, REJ frame. \n " , __func__ ) ;
}
}
* p = ' \0 ' ;
return ( this_p ) ;
} /* end ax25_s_frame */
/*------------------------------------------------------------------------------
*
* Name : ax25_i_frame
*
* Purpose : Construct an I frame .
*
* Input : addrs - Array of addresses .
*
* num_addr - Number of addresses , range 2 . . 10.
*
* cr - cr_cmd command frame , cr_res for a response frame .
*
* modulo - 8 or 128.
*
* nr - N ( R ) field - - - describe .
*
* ns - N ( S ) field - - - describe .
*
* pf - Poll / Final flag .
*
* pid - Protocol ID .
* Normally 0xf0 meaning no level 3.
* Could be other values for NET / ROM , etc .
*
* pinfo - Pointer to data for Info field .
*
* info_len - Length for Info field .
*
*
* Returns : Pointer to new packet object .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
packet_t ax25_i_frame ( char addrs [ AX25_MAX_ADDRS ] [ AX25_MAX_ADDR_LEN ] , int num_addr , cmdres_t cr , int modulo , int nr , int ns , int pf , int pid , unsigned char * pinfo , int info_len )
{
packet_t this_p ;
unsigned char * p ;
int ctrl = 0 ;
this_p = ax25_new ( ) ;
if ( this_p = = NULL ) return ( NULL ) ;
if ( ! set_addrs ( this_p , addrs , num_addr , cr ) ) {
Debugprintf ( " Internal error in %s: Could not set addresses for I frame. \n " , __func__ ) ;
ax25_delete ( this_p ) ;
return ( NULL ) ;
}
if ( modulo ! = 8 & & modulo ! = 128 ) {
Debugprintf ( " Internal error in %s: Invalid modulo %d for I frame. \n " , __func__ , modulo ) ;
modulo = 8 ;
}
this_p - > modulo = modulo ;
if ( nr < 0 | | nr > = modulo ) {
Debugprintf ( " Internal error in %s: Invalid N(R) %d for I frame. \n " , __func__ , nr ) ;
nr & = ( modulo - 1 ) ;
}
if ( ns < 0 | | ns > = modulo ) {
Debugprintf ( " Internal error in %s: Invalid N(S) %d for I frame. \n " , __func__ , ns ) ;
ns & = ( modulo - 1 ) ;
}
p = this_p - > frame_data + this_p - > frame_len ;
if ( modulo = = 8 ) {
ctrl = ( nr < < 5 ) | ( ns < < 1 ) ;
if ( pf ) ctrl | = 0x10 ;
* p + + = ctrl ;
this_p - > frame_len + + ;
}
else {
ctrl = ns < < 1 ;
* p + + = ctrl ;
this_p - > frame_len + + ;
ctrl = nr < < 1 ;
if ( pf ) ctrl | = 0x01 ;
* p + + = ctrl ;
this_p - > frame_len + + ;
}
// Definitely don't want pid value of 0 (not in valid list)
// or 0xff (which means more bytes follow).
if ( pid < 0 | | pid = = 0 | | pid = = 0xff ) {
Debugprintf ( " Warning: Client application provided invalid PID value, 0x%02x, for I frame. \n " , pid ) ;
pid = AX25_PID_NO_LAYER_3 ;
}
* p + + = pid ;
this_p - > frame_len + + ;
if ( pinfo ! = NULL & & info_len > 0 ) {
if ( info_len > AX25_MAX_INFO_LEN ) {
Debugprintf ( " Internal error in %s: I frame, Invalid information field length %d. \n " , __func__ , info_len ) ;
info_len = AX25_MAX_INFO_LEN ;
}
memcpy ( p , pinfo , info_len ) ;
p + = info_len ;
this_p - > frame_len + = info_len ;
}
* p = ' \0 ' ;
return ( this_p ) ;
} /* end ax25_i_frame */
extern TStringList detect_list [ 5 ] ;
extern TStringList detect_list_c [ 5 ] ;
void multi_modem_process_rec_packet ( int snd_ch , int subchan , int slice , packet_t pp , alevel_t alevel , retry_t retries , int is_fx25 , int emph , int centreFreq )
{
// Convert to QtSM internal format
struct TDetector_t * pDET = & DET [ emph ] [ subchan ] ;
string * data = newString ( ) ;
2023-09-12 21:38:15 +01:00
char Mode [ 32 ] = " IL2P " ;
int Quality = 0 ;
2024-07-23 21:26:30 +01:00
int CRCOK = 1 ;
char debugmsg [ 256 ] ;
2023-09-04 19:06:44 +01:00
sprintf ( Mode , " IL2P %d " , centreFreq ) ;
2024-07-23 21:26:30 +01:00
unsigned char * axcall = & pp - > frame_data [ 7 ] ;
char call [ 10 ] ;
call [ ConvFromAX25 ( axcall , call ) ] = 0 ;
2023-09-04 19:06:44 +01:00
2023-12-17 13:53:32 +00:00
// check crc if enabled
2024-07-23 21:26:30 +01:00
if ( il2p_crc [ snd_ch ] & 1 )
2023-12-17 13:53:32 +00:00
{
unsigned short CRCMSG ;
unsigned short CRCCALC ;
uint8_t crc [ 4 ] ;
// check crc if enabled
// The four encoded CRC bytes are arranged :
// | CRC3 | CRC2 | CRC1 | CRC0 | but we store as received, so F->crc[0] is CRC3
// CRC3 encoded from high nibble of 16 - bit CRC value (from crc2)
// CRC0 encoded from low nibble of 16 - bit CRC value (from crc1)
crc [ 0 ] = Hamming74DecodeTable [ ( pp - > crc [ 0 ] & 0x7f ) ] ;
crc [ 1 ] = Hamming74DecodeTable [ ( pp - > crc [ 1 ] & 0x7f ) ] ;
crc [ 2 ] = Hamming74DecodeTable [ ( pp - > crc [ 2 ] & 0x7f ) ] ;
crc [ 3 ] = Hamming74DecodeTable [ ( pp - > crc [ 3 ] & 0x7f ) ] ;
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " CRC after Hamming decode is " , ' R ' ) ;
debugHexDump ( crc , 4 , ' R ' ) ;
2023-12-17 13:53:32 +00:00
CRCMSG = crc [ 0 ] < < 12 | crc [ 1 ] < < 8 | crc [ 2 ] < < 4 | crc [ 3 ] ;
CRCCALC = get_fcs ( pp - > frame_data , pp - > frame_len ) ;
if ( CRCCALC ! = CRCMSG )
{
2024-07-23 21:26:30 +01:00
CRCOK = 0 ;
if ( ( il2p_crc [ snd_ch ] & 2 ) = = 0 ) // Ignore CRC Error
{
Debugprintf ( " CRC Error from %s Decoder %d Calculated %x Received %x FEC corrections %d But ignore CRC Set " , call , subchan , CRCCALC , CRCMSG , retries ) ;
sprintf ( debugmsg , " CRC Error from %s Decoder %d Calculated %x Received %x FEC corrections %d But ignore CRC Set " , call , subchan , CRCCALC , CRCMSG , retries ) ;
debugTimeStamp ( debugmsg , ' R ' ) ;
}
else
{
Debugprintf ( " CRC Error from %s Decoder %d Calculated %x Received %x FEC corrections %d " , call , subchan , CRCCALC , CRCMSG , retries ) ;
freeString ( data ) ;
ax25_delete ( pp ) ;
return ;
}
2023-12-17 13:53:32 +00:00
}
}
2023-09-04 19:06:44 +01:00
stringAdd ( data , pp - > frame_data , pp - > frame_len + 2 ) ; // QTSM assumes a CRC
ax25_delete ( pp ) ;
if ( retries )
{
pDET - > rx_decoded = decodedFEC ;
pDET - > emph_decoded = decodedFEC ;
pDET - > errors = retries ;
}
else
{
pDET - > rx_decoded = decodedNormal ;
pDET - > emph_decoded = decodedNormal ;
pDET - > errors = 0 ;
}
2024-07-23 21:26:30 +01:00
if ( detect_list [ snd_ch ] . Count > 0 )
2023-09-04 19:06:44 +01:00
{
2024-07-23 21:26:30 +01:00
int index = my_indexof ( & detect_list [ snd_ch ] , data ) ;
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
if ( index > = 0 )
{
// Already have a copy of this frame
// See if new one has fewer corrections
string * xx = Strings ( & detect_list_c [ snd_ch ] , index ) ; // Should be corresponding frame info
string * olddata = Strings ( & detect_list [ snd_ch ] , index ) ;
if ( xx )
{
int oldRetries = xx - > Data [ 255 ] ;
int oldCRCOK = xx - > Data [ 254 ] ;
if ( ( oldCRCOK = = 0 & & CRCOK = = 1 ) | | ( oldRetries > retries ) )
{
replaceString ( & detect_list [ snd_ch ] , index , data ) ;
freeString ( olddata ) ;
// Just update the metadata
Debugprintf ( " Replacing il2p frame from %s rcvr %d emph %d FEC corrections %d CRCOK %d " , call , subchan , slice , retries , CRCOK ) ;
memset ( xx - > Data , 0 , 16 ) ;
if ( pskStates [ snd_ch ] )
{
Quality = SMUpdatePhaseConstellation ( snd_ch , & Phases [ snd_ch ] [ subchan ] [ slice ] [ 0 ] , & Mags [ snd_ch ] [ subchan ] [ slice ] [ 0 ] , pskStates [ snd_ch ] , nPhases [ snd_ch ] [ subchan ] [ slice ] ) ;
sprintf ( Mode , " %s][Q%d " , Mode , Quality ) ;
}
xx - > Length = sprintf ( xx - > Data , " %s " , Mode ) ;
xx - > Data [ 254 ] = CRCOK ;
xx - > Data [ 255 ] = retries ;
return ;
}
}
freeString ( data ) ;
Debugprintf ( " Discarding copy rcvr %d emph %d FEC corrections %d " , subchan , slice , retries ) ;
return ;
}
2023-09-04 19:06:44 +01:00
}
2024-07-23 21:26:30 +01:00
Debugprintf ( " Good il2p frame from %s rcvr %d emph %d FEC corrections %d " , call , subchan , slice , retries ) ;
sprintf ( debugmsg , " Good il2p frame from %s rcvr %d emph %d FEC corrections %d " , call , subchan , slice , retries ) ;
debugTimeStamp ( debugmsg , ' R ' ) ;
2023-09-04 19:06:44 +01:00
string * xx = newString ( ) ;
memset ( xx - > Data , 0 , 16 ) ;
2023-09-12 21:38:15 +01:00
if ( pskStates [ snd_ch ] )
{
Quality = SMUpdatePhaseConstellation ( snd_ch , & Phases [ snd_ch ] [ subchan ] [ slice ] [ 0 ] , & Mags [ snd_ch ] [ subchan ] [ slice ] [ 0 ] , pskStates [ snd_ch ] , nPhases [ snd_ch ] [ subchan ] [ slice ] ) ;
sprintf ( Mode , " %s][Q%d " , Mode , Quality ) ;
}
2023-09-04 19:06:44 +01:00
Add ( & detect_list_c [ snd_ch ] , xx ) ;
Add ( & detect_list [ snd_ch ] , data ) ;
// if (retries)
// sprintf(Mode, "IP2P-%d", retries);
stringAdd ( xx , Mode , strlen ( Mode ) ) ;
2024-07-23 21:26:30 +01:00
xx - > Data [ 254 ] = CRCOK ;
xx - > Data [ 255 ] = retries ;
2023-09-12 21:38:15 +01:00
2024-07-23 21:26:30 +01:00
closeTraceLog ( ) ;
openTraceLog ( ) ;
2023-09-12 21:38:15 +01:00
2023-09-04 19:06:44 +01:00
return ;
}
alevel_t demod_get_audio_level ( int chan , int subchan )
{
alevel_t alevel ;
alevel . rec = 0 ;
alevel . mark = 0 ;
alevel . space = 0 ;
return ( alevel ) ;
}
void ax25_hex_dump ( packet_t this_p )
{ }
/*------------------------------------------------------------------------------
*
* Name : ax25_from_frame
*
* Purpose : Split apart an HDLC frame to components .
*
* Inputs : fbuf - Pointer to beginning of frame .
*
* flen - Length excluding the two FCS bytes .
*
* alevel - Audio level of received signal .
* Maximum range 0 - 100.
* - 1 might be used when not applicable .
*
* Returns : Pointer to new packet object or NULL if error .
*
* Outputs : Use the " get " functions to retrieve information in different ways .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
packet_t ax25_from_frame ( unsigned char * fbuf , int flen , alevel_t alevel )
{
packet_t this_p ;
/*
* First make sure we have an acceptable length :
*
* We are not concerned with the FCS ( CRC ) because someone else checked it .
*
* Is is possible to have zero length for info ?
*
* In the original version , assuming APRS , the answer was no .
* We always had at least 3 octets after the address part :
* control , protocol , and first byte of info part for data type .
*
* In later versions , this restriction was relaxed so other
* variations of AX .25 could be used . Now the minimum length
* is 7 + 7 for addresses plus 1 for control .
*
*/
if ( flen < AX25_MIN_PACKET_LEN | | flen > AX25_MAX_PACKET_LEN )
{
Debugprintf ( " Frame length %d not in allowable range of %d to %d. " , flen , AX25_MIN_PACKET_LEN , AX25_MAX_PACKET_LEN ) ;
return ( NULL ) ;
}
this_p = ax25_new ( ) ;
/* Copy the whole thing intact. */
memcpy ( this_p - > frame_data , fbuf , flen ) ;
this_p - > frame_data [ flen ] = 0 ;
this_p - > frame_len = flen ;
/* Find number of addresses. */
this_p - > num_addr = ( - 1 ) ;
( void ) ax25_get_num_addr ( this_p ) ;
return ( this_p ) ;
}
/*------------------------------------------------------------------------------
*
* Name : ax25_get_num_addr
*
* Purpose : Return number of addresses in current packet .
*
* Assumption : ax25_from_text or ax25_from_frame was called first .
*
* Returns : Number of addresses in the current packet .
* Should be in the range of 2 . . AX25_MAX_ADDRS .
*
* Version 0.9 : Could be zero for a non AX .25 frame in KISS mode .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int ax25_get_num_addr ( packet_t this_p )
{
//unsigned char *pf;
int a ;
int addr_bytes ;
// assert(this_p->magic1 == MAGIC);
// assert(this_p->magic2 == MAGIC);
/* Use cached value if already set. */
if ( this_p - > num_addr > = 0 ) {
return ( this_p - > num_addr ) ;
}
/* Otherwise, determine the number ofaddresses. */
this_p - > num_addr = 0 ; /* Number of addresses extracted. */
addr_bytes = 0 ;
for ( a = 0 ; a < this_p - > frame_len & & addr_bytes = = 0 ; a + + ) {
if ( this_p - > frame_data [ a ] & SSID_LAST_MASK ) {
addr_bytes = a + 1 ;
}
}
if ( addr_bytes % 7 = = 0 ) {
int addrs = addr_bytes / 7 ;
if ( addrs > = AX25_MIN_ADDRS & & addrs < = AX25_MAX_ADDRS ) {
this_p - > num_addr = addrs ;
}
}
return ( this_p - > num_addr ) ;
}
void ax25_get_addr_with_ssid ( packet_t pp , int n , char * station )
{ }
/*------------------------------------------------------------------------------
*
* Name : ax25_get_addr_no_ssid
*
* Purpose : Return specified address WITHOUT any SSID .
*
* Inputs : n - Index of address . Use the symbols
* AX25_DESTINATION , AX25_SOURCE , AX25_REPEATER1 , etc .
*
* Outputs : station - String representation of the station , WITHOUT the SSID .
* e . g . " WB2OSZ "
* Usually variables will be AX25_MAX_ADDR_LEN bytes
* but 7 would be adequate .
*
* Bugs : No bounds checking is performed . Be careful .
*
* Assumption : ax25_from_text or ax25_from_frame was called first .
*
* Returns : Character string in usual human readable format ,
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void ax25_get_addr_no_ssid ( packet_t this_p , int n , char * station )
{
int i ;
//assert(this_p->magic1 == MAGIC);
//assert(this_p->magic2 == MAGIC);
if ( n < 0 ) {
Debugprintf ( " Internal error detected in ax25_get_addr_no_ssid, %s, line %d. \n " , __FILE__ , __LINE__ ) ;
Debugprintf ( " Address index, %d, is less than zero. \n " , n ) ;
strcpy ( station , " ?????? " ) ;
return ;
}
if ( n > = this_p - > num_addr ) {
Debugprintf ( " Internal error detected in ax25_get_no_with_ssid, %s, line %d. \n " , __FILE__ , __LINE__ ) ;
Debugprintf ( " Address index, %d, is too large for number of addresses, %d. \n " , n , this_p - > num_addr ) ;
strcpy ( station , " ?????? " ) ;
return ;
}
// At one time this would stop at the first space, on the assumption we would have only trailing spaces.
// Then there was a forum discussion where someone encountered the address " WIDE2" with a leading space.
// In that case, we would have returned a zero length string here.
// Now we return exactly what is in the address field and trim trailing spaces.
// This will provide better information for troubleshooting.
for ( i = 0 ; i < 6 ; i + + ) {
station [ i ] = ( this_p - > frame_data [ n * 7 + i ] > > 1 ) & 0x7f ;
}
station [ 6 ] = ' \0 ' ;
for ( i = 5 ; i > = 0 ; i - - ) {
if ( station [ i ] = = ' ' )
station [ i ] = ' \0 ' ;
else
break ;
}
if ( strlen ( station ) = = 0 ) {
Debugprintf ( " Station address, in position %d, is empty! This is not a valid AX.25 frame. \n " , n ) ;
}
} /* end ax25_get_addr_no_ssid */
/*------------------------------------------------------------------------------
*
* Name : ax25_get_ssid
*
* Purpose : Return SSID of specified address in current packet .
*
* Inputs : n - Index of address . Use the symbols
* AX25_DESTINATION , AX25_SOURCE , AX25_REPEATER1 , etc .
*
* Assumption : ax25_from_text or ax25_from_frame was called first .
*
* Returns : Substation id , as integer 0 . . 15.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int ax25_get_ssid ( packet_t this_p , int n )
{
// assert(this_p->magic1 == MAGIC);
// assert(this_p->magic2 == MAGIC);
if ( n > = 0 & & n < this_p - > num_addr ) {
return ( ( this_p - > frame_data [ n * 7 + 6 ] & SSID_SSID_MASK ) > > SSID_SSID_SHIFT ) ;
}
else {
Debugprintf ( " Internal error: ax25_get_ssid(%d), num_addr=%d \n " , n , this_p - > num_addr ) ;
return ( 0 ) ;
}
}
static inline int ax25_get_pid_offset ( packet_t this_p )
{
return ( ax25_get_control_offset ( this_p ) + ax25_get_num_control ( this_p ) ) ;
}
static int ax25_get_num_pid ( packet_t this_p )
{
int c ;
int pid ;
c = this_p - > frame_data [ ax25_get_control_offset ( this_p ) ] ;
if ( ( c & 0x01 ) = = 0 | | /* I xxxx xxx0 */
c = = 0x03 | | c = = 0x13 ) { /* UI 000x 0011 */
pid = this_p - > frame_data [ ax25_get_pid_offset ( this_p ) ] ;
if ( pid = = AX25_PID_ESCAPE_CHARACTER ) {
return ( 2 ) ; /* pid 1111 1111 means another follows. */
}
return ( 1 ) ;
}
return ( 0 ) ;
}
inline int ax25_get_control_offset ( packet_t this_p )
{
return ( this_p - > num_addr * 7 ) ;
}
inline int ax25_get_num_control ( packet_t this_p )
{
int c ;
c = this_p - > frame_data [ ax25_get_control_offset ( this_p ) ] ;
if ( ( c & 0x01 ) = = 0 ) { /* I xxxx xxx0 */
return ( ( this_p - > modulo = = 128 ) ? 2 : 1 ) ;
}
if ( ( c & 0x03 ) = = 1 ) { /* S xxxx xx01 */
return ( ( this_p - > modulo = = 128 ) ? 2 : 1 ) ;
}
return ( 1 ) ; /* U xxxx xx11 */
}
int ax25_get_info_offset ( packet_t this_p )
{
int offset = ax25_get_control_offset ( this_p ) + ax25_get_num_control ( this_p ) + ax25_get_num_pid ( this_p ) ;
return ( offset ) ;
}
int ax25_get_num_info ( packet_t this_p )
{
int len ;
/* assuming AX.25 frame. */
len = this_p - > frame_len - this_p - > num_addr * 7 - ax25_get_num_control ( this_p ) - ax25_get_num_pid ( this_p ) ;
if ( len < 0 ) {
len = 0 ; /* print error? */
}
return ( len ) ;
}
/*------------------------------------------------------------------------------
*
* Name : ax25_get_info
*
* Purpose : Obtain Information part of current packet .
*
* Inputs : this_p - Packet object pointer .
*
* Outputs : paddr - Starting address of information part is returned here .
*
* Assumption : ax25_from_text or ax25_from_frame was called first .
*
* Returns : Number of octets in the Information part .
* Should be in the range of AX25_MIN_INFO_LEN . . AX25_MAX_INFO_LEN .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int ax25_get_info ( packet_t this_p , unsigned char * * paddr )
{
unsigned char * info_ptr ;
int info_len ;
//assert(this_p->magic1 == MAGIC);
//assert(this_p->magic2 == MAGIC);
if ( this_p - > num_addr > = 2 ) {
/* AX.25 */
info_ptr = this_p - > frame_data + ax25_get_info_offset ( this_p ) ;
info_len = ax25_get_num_info ( this_p ) ;
}
else {
/* Not AX.25. Treat Whole packet as info. */
info_ptr = this_p - > frame_data ;
info_len = this_p - > frame_len ;
}
/* Add nul character in case caller treats as printable string. */
// assert(info_len >= 0);
info_ptr [ info_len ] = ' \0 ' ;
* paddr = info_ptr ;
return ( info_len ) ;
} /* end ax25_get_info */
void ax25_set_info ( packet_t this_p , unsigned char * new_info_ptr , int new_info_len )
{
unsigned char * old_info_ptr ;
int old_info_len = ax25_get_info ( this_p , & old_info_ptr ) ;
this_p - > frame_len - = old_info_len ;
if ( new_info_len < 0 ) new_info_len = 0 ;
if ( new_info_len > AX25_MAX_INFO_LEN ) new_info_len = AX25_MAX_INFO_LEN ;
memcpy ( old_info_ptr , new_info_ptr , new_info_len ) ;
this_p - > frame_len + = new_info_len ;
}
int ax25_get_pid ( packet_t this_p )
{
// assert(this_p->magic1 == MAGIC);
// assert(this_p->magic2 == MAGIC);
// TODO: handle 2 control byte case.
// TODO: sanity check: is it I or UI frame?
if ( this_p - > frame_len = = 0 ) return ( - 1 ) ;
if ( this_p - > num_addr > = 2 ) {
return ( this_p - > frame_data [ ax25_get_pid_offset ( this_p ) ] ) ;
}
return ( - 1 ) ;
}
int ax25_get_frame_len ( packet_t this_p )
{
// assert(this_p->magic1 == MAGIC);
// assert(this_p->magic2 == MAGIC);
// assert(this_p->frame_len >= 0 && this_p->frame_len <= AX25_MAX_PACKET_LEN);
return ( this_p - > frame_len ) ;
} /* end ax25_get_frame_len */
unsigned char * ax25_get_frame_data_ptr ( packet_t this_p )
{
// assert(this_p->magic1 == MAGIC);
// assert(this_p->magic2 == MAGIC);
return ( this_p - > frame_data ) ;
} /* end ax25_get_frame_data_ptr */
int ax25_get_modulo ( packet_t this_p )
{
return 7 ;
}
/*------------------------------------------------------------------
*
* Function : ax25_get_control
ax25_get_c2
*
* Purpose : Get Control field from packet .
*
* Inputs : this_p - pointer to packet object .
*
* Returns : APRS uses AX25_UI_FRAME .
* This could also be used in other situations .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int ax25_get_control ( packet_t this_p )
{
// assert(this_p->magic1 == MAGIC);
// assert(this_p->magic2 == MAGIC);
if ( this_p - > frame_len = = 0 ) return ( - 1 ) ;
if ( this_p - > num_addr > = 2 ) {
return ( this_p - > frame_data [ ax25_get_control_offset ( this_p ) ] ) ;
}
return ( - 1 ) ;
}
/*------------------------------------------------------------------
*
* Function : ax25_frame_type
*
* Purpose : Extract the type of frame .
* This is derived from the control byte ( s ) but
* is an enumerated type for easier handling .
*
* Inputs : this_p - pointer to packet object .
*
* Outputs : desc - Text description such as " I frame " or
* " U frame SABME " .
* Supply 56 bytes to be safe .
*
* cr - Command or response ?
*
* pf - P / F - Poll / Final or - 1 if not applicable
*
* nr - N ( R ) - receive sequence or - 1 if not applicable .
*
* ns - N ( S ) - send sequence or - 1 if not applicable .
*
* Returns : Frame type from enum ax25_frame_type_e .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// TODO: need someway to ensure caller allocated enough space.
// Should pass in as parameter.
# define DESC_SIZ 56
ax25_frame_type_t ax25_frame_type ( packet_t this_p , cmdres_t * cr , char * desc , int * pf , int * nr , int * ns )
{
int c ; // U frames are always one control byte.
int c2 = 0 ; // I & S frames can have second Control byte.
// assert(this_p->magic1 == MAGIC);
// assert(this_p->magic2 == MAGIC);
strcpy ( desc , " ???? " ) ;
* cr = cr_11 ;
* pf = - 1 ;
* nr = - 1 ;
* ns = - 1 ;
c = ax25_get_control ( this_p ) ;
if ( c < 0 ) {
strcpy ( desc , " Not AX.25 " ) ;
return ( frame_not_AX25 ) ;
}
/*
* TERRIBLE HACK : - ( for display purposes .
*
* I and S frames can have 1 or 2 control bytes but there is
* no good way to determine this without dipping into the data
* link state machine . Can we guess ?
*
* S frames have no protocol id or information so if there is one
* more byte beyond the control field , we could assume there are
* two control bytes .
*
* For I frames , the protocol id will usually be 0xf0 . If we find
* that as the first byte of the information field , it is probably
* the pid and not part of the information . Ditto for segments 0x08 .
* Not fool proof but good enough for troubleshooting text out .
*
* If we have a link to the peer station , this will be set properly
* before it needs to be used for other reasons .
*
* Setting one of the RR bits ( find reference ! ) is sounding better and better .
* It ' s in common usage so I should lobby to get that in the official protocol spec .
*/
// Dont support mod 128
/*
if ( this_p - > modulo = = 0 & & ( c & 3 ) = = 1 & & ax25_get_c2 ( this_p ) ! = - 1 ) {
this_p - > modulo = modulo_128 ;
}
else if ( this_p - > modulo = = 0 & & ( c & 1 ) = = 0 & & this_p - > frame_data [ ax25_get_info_offset ( this_p ) ] = = 0xF0 ) {
this_p - > modulo = modulo_128 ;
}
else if ( this_p - > modulo = = 0 & & ( c & 1 ) = = 0 & & this_p - > frame_data [ ax25_get_info_offset ( this_p ) ] = = 0x08 ) { // same for segments
this_p - > modulo = modulo_128 ;
}
if ( this_p - > modulo = = modulo_128 ) {
c2 = ax25_get_c2 ( this_p ) ;
}
*/
int dst_c = this_p - > frame_data [ AX25_DESTINATION * 7 + 6 ] & SSID_H_MASK ;
int src_c = this_p - > frame_data [ AX25_SOURCE * 7 + 6 ] & SSID_H_MASK ;
char cr_text [ 8 ] ;
char pf_text [ 8 ] ;
if ( dst_c ) {
if ( src_c ) { * cr = cr_11 ; strcpy ( cr_text , " cc=11 " ) ; strcpy ( pf_text , " p/f " ) ; }
else { * cr = cr_cmd ; strcpy ( cr_text , " cmd " ) ; strcpy ( pf_text , " p " ) ; }
}
else {
if ( src_c ) { * cr = cr_res ; strcpy ( cr_text , " res " ) ; strcpy ( pf_text , " f " ) ; }
else { * cr = cr_00 ; strcpy ( cr_text , " cc=00 " ) ; strcpy ( pf_text , " p/f " ) ; }
}
if ( ( c & 1 ) = = 0 ) {
// Information rrr p sss 0 or sssssss 0 rrrrrrr p
if ( this_p - > modulo = = modulo_128 ) {
* ns = ( c > > 1 ) & 0x7f ;
* pf = c2 & 1 ;
* nr = ( c2 > > 1 ) & 0x7f ;
}
else {
* ns = ( c > > 1 ) & 7 ;
* pf = ( c > > 4 ) & 1 ;
* nr = ( c > > 5 ) & 7 ;
}
//snprintf (desc, DESC_SIZ, "I %s, n(s)=%d, n(r)=%d, %s=%d", cr_text, *ns, *nr, pf_text, *pf);
sprintf ( desc , " I %s, n(s)=%d, n(r)=%d, %s=%d, pid=0x%02x " , cr_text , * ns , * nr , pf_text , * pf , ax25_get_pid ( this_p ) ) ;
return ( frame_type_I ) ;
}
else if ( ( c & 2 ) = = 0 ) {
// Supervisory rrr p/f ss 0 1 or 0000 ss 0 1 rrrrrrr p/f
if ( this_p - > modulo = = modulo_128 ) {
* pf = c2 & 1 ;
* nr = ( c2 > > 1 ) & 0x7f ;
}
else {
* pf = ( c > > 4 ) & 1 ;
* nr = ( c > > 5 ) & 7 ;
}
switch ( ( c > > 2 ) & 3 ) {
case 0 : sprintf ( desc , " RR %s, n(r)=%d, %s=%d " , cr_text , * nr , pf_text , * pf ) ; return ( frame_type_S_RR ) ; break ;
case 1 : sprintf ( desc , " RNR %s, n(r)=%d, %s=%d " , cr_text , * nr , pf_text , * pf ) ; return ( frame_type_S_RNR ) ; break ;
case 2 : sprintf ( desc , " REJ %s, n(r)=%d, %s=%d " , cr_text , * nr , pf_text , * pf ) ; return ( frame_type_S_REJ ) ; break ;
case 3 : sprintf ( desc , " SREJ %s, n(r)=%d, %s=%d " , cr_text , * nr , pf_text , * pf ) ; return ( frame_type_S_SREJ ) ; break ;
}
}
else {
// Unnumbered mmm p/f mm 1 1
* pf = ( c > > 4 ) & 1 ;
switch ( c & 0xef ) {
case 0x6f : sprintf ( desc , " SABME %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_SABME ) ; break ;
case 0x2f : sprintf ( desc , " SABM %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_SABM ) ; break ;
case 0x43 : sprintf ( desc , " DISC %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_DISC ) ; break ;
case 0x0f : sprintf ( desc , " DM %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_DM ) ; break ;
case 0x63 : sprintf ( desc , " UA %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_UA ) ; break ;
case 0x87 : sprintf ( desc , " FRMR %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_FRMR ) ; break ;
case 0x03 : sprintf ( desc , " UI %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_UI ) ; break ;
case 0xaf : sprintf ( desc , " XID %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_XID ) ; break ;
case 0xe3 : sprintf ( desc , " TEST %s, %s=%d " , cr_text , pf_text , * pf ) ; return ( frame_type_U_TEST ) ; break ;
default : sprintf ( desc , " U other??? " ) ; return ( frame_type_U ) ; break ;
}
}
// Should be unreachable but compiler doesn't realize that.
// Here only to suppress "warning: control reaches end of non-void function"
return ( frame_not_AX25 ) ;
} /* end ax25_frame_type */
packet_t ax25_u_frame ( char addrs [ AX25_MAX_ADDRS ] [ AX25_MAX_ADDR_LEN ] , int num_addr , cmdres_t cr , ax25_frame_type_t ftype , int pf , int pid , unsigned char * pinfo , int info_len )
{
packet_t this_p ;
unsigned char * p ;
int ctrl = 0 ;
unsigned int t = 999 ; // 1 = must be cmd, 0 = must be response, 2 = can be either.
int i = 0 ; // Is Info part allowed?
this_p = ax25_new ( ) ;
if ( this_p = = NULL ) return ( NULL ) ;
this_p - > modulo = 0 ;
if ( ! set_addrs ( this_p , addrs , num_addr , cr ) ) {
Debugprintf ( " Internal error in %s: Could not set addresses for U frame. \n " , __func__ ) ;
ax25_delete ( this_p ) ;
return ( NULL ) ;
}
switch ( ftype ) {
// 1 = cmd only, 0 = res only, 2 = either
case frame_type_U_SABME : ctrl = 0x6f ; t = 1 ; break ;
case frame_type_U_SABM : ctrl = 0x2f ; t = 1 ; break ;
case frame_type_U_DISC : ctrl = 0x43 ; t = 1 ; break ;
case frame_type_U_DM : ctrl = 0x0f ; t = 0 ; break ;
case frame_type_U_UA : ctrl = 0x63 ; t = 0 ; break ;
case frame_type_U_FRMR : ctrl = 0x87 ; t = 0 ; i = 1 ; break ;
case frame_type_U_UI : ctrl = 0x03 ; t = 2 ; i = 1 ; break ;
case frame_type_U_XID : ctrl = 0xaf ; t = 2 ; i = 1 ; break ;
case frame_type_U_TEST : ctrl = 0xe3 ; t = 2 ; i = 1 ; break ;
default :
Debugprintf ( " Internal error in %s: Invalid ftype %d for U frame. \n " , __func__ , ftype ) ;
ax25_delete ( this_p ) ;
return ( NULL ) ;
break ;
}
if ( pf ) ctrl | = 0x10 ;
if ( t ! = 2 ) {
if ( cr ! = t ) {
Debugprintf ( " Internal error in %s: U frame, cr is %d but must be %d. ftype=%d \n " , __func__ , cr , t , ftype ) ;
}
}
p = this_p - > frame_data + this_p - > frame_len ;
* p + + = ctrl ;
this_p - > frame_len + + ;
if ( ftype = = frame_type_U_UI ) {
// Definitely don't want pid value of 0 (not in valid list)
// or 0xff (which means more bytes follow).
if ( pid < 0 | | pid = = 0 | | pid = = 0xff ) {
Debugprintf ( " Internal error in %s: U frame, Invalid pid value 0x%02x. \n " , __func__ , pid ) ;
pid = AX25_PID_NO_LAYER_3 ;
}
* p + + = pid ;
this_p - > frame_len + + ;
}
if ( i ) {
if ( pinfo ! = NULL & & info_len > 0 ) {
if ( info_len > AX25_MAX_INFO_LEN ) {
Debugprintf ( " Internal error in %s: U frame, Invalid information field length %d. \n " , __func__ , info_len ) ;
info_len = AX25_MAX_INFO_LEN ;
}
memcpy ( p , pinfo , info_len ) ;
p + = info_len ;
this_p - > frame_len + = info_len ;
}
}
else {
if ( pinfo ! = NULL & & info_len > 0 ) {
Debugprintf ( " Internal error in %s: Info part not allowed for U frame type. \n " , __func__ ) ;
}
}
* p = ' \0 ' ;
//assert(p == this_p->frame_data + this_p->frame_len);
//assert(this_p->magic1 == MAGIC);
//assert(this_p->magic2 == MAGIC);
# if PAD2TEST
ax25_frame_type_t check_ftype ;
cmdres_t check_cr ;
char check_desc [ 80 ] ;
int check_pf ;
int check_nr ;
int check_ns ;
check_ftype = ax25_frame_type ( this_p , & check_cr , check_desc , & check_pf , & check_nr , & check_ns ) ;
text_color_set ( DW_COLOR_DEBUG ) ;
Debugprintf ( " check: ftype=%d, desc= \" %s \" , pf=%d \n " , check_ftype , check_desc , check_pf ) ;
assert ( check_cr = = cr ) ;
assert ( check_ftype = = ftype ) ;
assert ( check_pf = = pf ) ;
assert ( check_nr = = - 1 ) ;
assert ( check_ns = = - 1 ) ;
# endif
return ( this_p ) ;
} /* end ax25_u_frame */
static const char * position_name [ 1 + AX25_MAX_ADDRS ] = {
" " , " Destination " , " Source " ,
" Digi1 " , " Digi2 " , " Digi3 " , " Digi4 " ,
" Digi5 " , " Digi6 " , " Digi7 " , " Digi8 " } ;
int ax25_parse_addr ( int position , char * in_addr , int strict , char * out_addr , int * out_ssid , int * out_heard )
{
char * p ;
char sstr [ 8 ] ; /* Should be 1 or 2 digits for SSID. */
int i , j , k ;
int maxlen ;
* out_addr = ' \0 ' ;
* out_ssid = 0 ;
* out_heard = 0 ;
// Debugprintf ("ax25_parse_addr in: position=%d, '%s', strict=%d\n", position, in_addr, strict);
if ( position < - 1 ) position = - 1 ;
if ( position > AX25_REPEATER_8 ) position = AX25_REPEATER_8 ;
position + + ; /* Adjust for position_name above. */
if ( strlen ( in_addr ) = = 0 ) {
Debugprintf ( " %sAddress \" %s \" is empty. \n " , position_name [ position ] , in_addr ) ;
return 0 ;
}
if ( strict & & strlen ( in_addr ) > = 2 & & strncmp ( in_addr , " qA " , 2 ) = = 0 ) {
Debugprintf ( " %sAddress \" %s \" is a \" q-construct \" used for communicating with \n " , position_name [ position ] , in_addr ) ;
Debugprintf ( " APRS Internet Servers. It should never appear when going over the radio. \n " ) ;
}
// Debugprintf ("ax25_parse_addr in: %s\n", in_addr);
maxlen = strict ? 6 : ( AX25_MAX_ADDR_LEN - 1 ) ;
p = in_addr ;
i = 0 ;
for ( p = in_addr ; * p ! = ' \0 ' & & * p ! = ' - ' & & * p ! = ' * ' ; p + + ) {
if ( i > = maxlen ) {
Debugprintf ( " %sAddress is too long. \" %s \" has more than %d characters. \n " , position_name [ position ] , in_addr , maxlen ) ;
return 0 ;
}
if ( ! isalnum ( * p ) ) {
Debugprintf ( " %sAddress, \" %s \" contains character other than letter or digit in character position %d. \n " , position_name [ position ] , in_addr , ( int ) ( long ) ( p - in_addr ) + 1 ) ;
return 0 ;
}
out_addr [ i + + ] = * p ;
out_addr [ i ] = ' \0 ' ;
# if DECAMAIN // Hack when running in decode_aprs utility.
// Exempt the "qA..." case because it was already mentioned.
if ( strict & & islower ( * p ) & & strncmp ( in_addr , " qA " , 2 ) ! = 0 ) {
text_color_set ( DW_COLOR_ERROR ) ;
Debugprintf ( " %sAddress has lower case letters. \" %s \" must be all upper case. \n " , position_name [ position ] , in_addr ) ;
}
# else
if ( strict & & islower ( * p ) ) {
Debugprintf ( " %sAddress has lower case letters. \" %s \" must be all upper case. \n " , position_name [ position ] , in_addr ) ;
return 0 ;
}
# endif
}
j = 0 ;
sstr [ j ] = ' \0 ' ;
if ( * p = = ' - ' ) {
for ( p + + ; isalnum ( * p ) ; p + + ) {
if ( j > = 2 ) {
Debugprintf ( " %sSSID is too long. SSID part of \" %s \" has more than 2 characters. \n " , position_name [ position ] , in_addr ) ;
return 0 ;
}
sstr [ j + + ] = * p ;
sstr [ j ] = ' \0 ' ;
if ( strict & & ! isdigit ( * p ) ) {
Debugprintf ( " %sSSID must be digits. \" %s \" has letters in SSID. \n " , position_name [ position ] , in_addr ) ;
return 0 ;
}
}
k = atoi ( sstr ) ;
if ( k < 0 | | k > 15 ) {
Debugprintf ( " %sSSID out of range. SSID of \" %s \" not in range of 0 to 15. \n " , position_name [ position ] , in_addr ) ;
return 0 ;
}
* out_ssid = k ;
}
if ( * p = = ' * ' ) {
* out_heard = 1 ;
p + + ;
if ( strict = = 2 ) {
Debugprintf ( " \" * \" is not allowed at end of address \" %s \" here. \n " , in_addr ) ;
return 0 ;
}
}
if ( * p ! = ' \0 ' ) {
Debugprintf ( " Invalid character \" %c \" found in %saddress \" %s \" . \n " , * p , position_name [ position ] , in_addr ) ;
return 0 ;
}
// Debugprintf ("ax25_parse_addr out: '%s' %d %d\n", out_addr, *out_ssid, *out_heard);
return ( 1 ) ;
} /* end ax25_parse_addr */
int set_addrs ( packet_t pp , char addrs [ AX25_MAX_ADDRS ] [ AX25_MAX_ADDR_LEN ] , int num_addr , cmdres_t cr )
{
int n ;
//assert(pp->frame_len == 0);
//assert(cr == cr_cmd || cr == cr_res);
if ( num_addr < AX25_MIN_ADDRS | | num_addr > AX25_MAX_ADDRS ) {
Debugprintf ( " INTERNAL ERROR: %s %s %d, num_addr = %d \n " , __FILE__ , __func__ , __LINE__ , num_addr ) ;
return ( 0 ) ;
}
for ( n = 0 ; n < num_addr ; n + + ) {
unsigned char * pa = pp - > frame_data + n * 7 ;
int ok ;
int strict = 1 ;
char oaddr [ AX25_MAX_ADDR_LEN ] ;
int ssid ;
int heard ;
int j ;
ok = ax25_parse_addr ( n , addrs [ n ] , strict , oaddr , & ssid , & heard ) ;
if ( ! ok ) return ( 0 ) ;
// Fill in address.
memset ( pa , ' ' < < 1 , 6 ) ;
for ( j = 0 ; oaddr [ j ] ; j + + ) {
pa [ j ] = oaddr [ j ] < < 1 ;
}
pa + = 6 ;
// Fill in SSID.
* pa = 0x60 | ( ( ssid & 0xf ) < < 1 ) ;
// Command / response flag.
switch ( n ) {
case AX25_DESTINATION :
if ( cr = = cr_cmd ) * pa | = 0x80 ;
break ;
case AX25_SOURCE :
if ( cr = = cr_res ) * pa | = 0x80 ;
break ;
default :
break ;
}
// Is this the end of address field?
if ( n = = num_addr - 1 ) {
* pa | = 1 ;
}
pp - > frame_len + = 7 ;
}
pp - > num_addr = num_addr ;
return ( 1 ) ;
} /* end set_addrs */
///////////////////////////////////////////////////////////////////////////////
//
// il2p_init.c
//
///////////////////////////////////////////////////////////////////////////////
// Init must be called at start of application.
extern void il2p_init ( int debug ) ;
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
//
///////////////////////////////////////////////////////////////////////////////
// Send bit stream to modulator.
string * il2p_send_frame ( int chan , packet_t pp , int max_fec , int polarity ) ;
///////////////////////////////////////////////////////////////////////////////
//
// il2p_codec.c
//
///////////////////////////////////////////////////////////////////////////////
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
// 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 ) {
Debugprintf ( " IL2P internal error: init_rs_char failed! \n " ) ;
exit ( 0 ) ;
}
}
2024-07-23 21:26:30 +01:00
openTraceLog ( ) ;
2023-09-04 19:06:44 +01:00
} // 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 ) ;
}
}
Debugprintf ( " 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 success . 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 ) {
Debugprintf ( " ============================== il2p_decode_rs ============================== \n " ) ;
Debugprintf ( " %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 ) ;
2023-09-12 21:38:15 +01:00
2023-09-04 19:06:44 +01:00
memcpy ( out , rs_block + sizeof ( rs_block ) - n , data_size ) ;
if ( il2p_get_debug ( ) > = 3 ) {
if ( derrors = = 0 ) {
Debugprintf ( " No errors reported for RS block. \n " ) ;
}
else if ( derrors > 0 ) {
Debugprintf ( " %d errors fixed in positions: \n " , derrors ) ;
for ( int j = 0 ; j < derrors ; j + + ) {
Debugprintf ( " %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 ) {
Debugprintf ( " 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 ) {
Debugprintf ( " ============================== il2p_decode_rs returns %d ============================== \n " , derrors ) ;
}
return ( derrors ) ;
}
// end il2p_init.c
void ENCODE_RS ( struct rs * rs , DTYPE * data , DTYPE * bb )
{
int i , j ;
DTYPE feedback ;
memset ( bb , 0 , NROOTS * sizeof ( DTYPE ) ) ; // clear out the FEC data area
for ( i = 0 ; i < NN - NROOTS ; i + + ) {
feedback = INDEX_OF [ data [ i ] ^ bb [ 0 ] ] ;
if ( feedback ! = A0 ) { /* feedback term is non-zero */
for ( j = 1 ; j < NROOTS ; j + + )
bb [ j ] ^ = ALPHA_TO [ MODNN ( feedback + GENPOLY [ NROOTS - j ] ) ] ;
}
/* Shift */
memmove ( & bb [ 0 ] , & bb [ 1 ] , sizeof ( DTYPE ) * ( NROOTS - 1 ) ) ;
if ( feedback ! = A0 )
bb [ NROOTS - 1 ] = ALPHA_TO [ MODNN ( feedback + GENPOLY [ 0 ] ) ] ;
else
bb [ NROOTS - 1 ] = 0 ;
}
}
int DECODE_RS ( struct rs * rs , DTYPE * data , int * eras_pos , int no_eras ) {
int deg_lambda , el , deg_omega ;
int i , j , r , k ;
DTYPE u , q , tmp , num1 , num2 , den , discr_r ;
// DTYPE lambda[NROOTS+1], s[NROOTS]; /* Err+Eras Locator poly and syndrome poly */
// DTYPE b[NROOTS+1], t[NROOTS+1], omega[NROOTS+1];
// DTYPE root[NROOTS], reg[NROOTS+1], loc[NROOTS];
DTYPE lambda [ FX25_MAX_CHECK + 1 ] , s [ FX25_MAX_CHECK ] ; /* Err+Eras Locator poly and syndrome poly */
DTYPE b [ FX25_MAX_CHECK + 1 ] , t [ FX25_MAX_CHECK + 1 ] , omega [ FX25_MAX_CHECK + 1 ] ;
DTYPE root [ FX25_MAX_CHECK ] , reg [ FX25_MAX_CHECK + 1 ] , loc [ FX25_MAX_CHECK ] ;
int syn_error , count ;
/* form the syndromes; i.e., evaluate data(x) at roots of g(x) */
for ( i = 0 ; i < NROOTS ; i + + )
s [ i ] = data [ 0 ] ;
for ( j = 1 ; j < NN ; j + + ) {
for ( i = 0 ; i < NROOTS ; i + + ) {
if ( s [ i ] = = 0 ) {
s [ i ] = data [ j ] ;
}
else {
s [ i ] = data [ j ] ^ ALPHA_TO [ MODNN ( INDEX_OF [ s [ i ] ] + ( FCR + i ) * PRIM ) ] ;
}
}
}
/* Convert syndromes to index form, checking for nonzero condition */
syn_error = 0 ;
for ( i = 0 ; i < NROOTS ; i + + ) {
syn_error | = s [ i ] ;
s [ i ] = INDEX_OF [ s [ i ] ] ;
}
// fprintf(stderr,"syn_error = %4x\n",syn_error);
if ( ! syn_error ) {
/* if syndrome is zero, data[] is a codeword and there are no
* errors to correct . So return data [ ] unmodified
*/
count = 0 ;
goto finish ;
}
memset ( & lambda [ 1 ] , 0 , NROOTS * sizeof ( lambda [ 0 ] ) ) ;
lambda [ 0 ] = 1 ;
if ( no_eras > 0 ) {
/* Init lambda to be the erasure locator polynomial */
lambda [ 1 ] = ALPHA_TO [ MODNN ( PRIM * ( NN - 1 - eras_pos [ 0 ] ) ) ] ;
for ( i = 1 ; i < no_eras ; i + + ) {
u = MODNN ( PRIM * ( NN - 1 - eras_pos [ i ] ) ) ;
for ( j = i + 1 ; j > 0 ; j - - ) {
tmp = INDEX_OF [ lambda [ j - 1 ] ] ;
if ( tmp ! = A0 )
lambda [ j ] ^ = ALPHA_TO [ MODNN ( u + tmp ) ] ;
}
}
# if DEBUG >= 1
/* Test code that verifies the erasure locator polynomial just constructed
Needed only for decoder debugging . */
/* find roots of the erasure location polynomial */
for ( i = 1 ; i < = no_eras ; i + + )
reg [ i ] = INDEX_OF [ lambda [ i ] ] ;
count = 0 ;
for ( i = 1 , k = IPRIM - 1 ; i < = NN ; i + + , k = MODNN ( k + IPRIM ) ) {
q = 1 ;
for ( j = 1 ; j < = no_eras ; j + + )
if ( reg [ j ] ! = A0 ) {
reg [ j ] = MODNN ( reg [ j ] + j ) ;
q ^ = ALPHA_TO [ reg [ j ] ] ;
}
if ( q ! = 0 )
continue ;
/* store root and error location number indices */
root [ count ] = i ;
loc [ count ] = k ;
count + + ;
}
if ( count ! = no_eras ) {
fprintf ( stderr , " count = %d no_eras = %d \n lambda(x) is WRONG \n " , count , no_eras ) ;
count = - 1 ;
goto finish ;
}
# if DEBUG >= 2
fprintf ( stderr , " \n Erasure positions as determined by roots of Eras Loc Poly: \n " ) ;
for ( i = 0 ; i < count ; i + + )
fprintf ( stderr , " %d " , loc [ i ] ) ;
fprintf ( stderr , " \n " ) ;
# endif
# endif
}
for ( i = 0 ; i < NROOTS + 1 ; i + + )
b [ i ] = INDEX_OF [ lambda [ i ] ] ;
/*
* Begin Berlekamp - Massey algorithm to determine error + erasure
* locator polynomial
*/
r = no_eras ;
el = no_eras ;
while ( + + r < = NROOTS ) { /* r is the step number */
/* Compute discrepancy at the r-th step in poly-form */
discr_r = 0 ;
for ( i = 0 ; i < r ; i + + ) {
if ( ( lambda [ i ] ! = 0 ) & & ( s [ r - i - 1 ] ! = A0 ) ) {
discr_r ^ = ALPHA_TO [ MODNN ( INDEX_OF [ lambda [ i ] ] + s [ r - i - 1 ] ) ] ;
}
}
discr_r = INDEX_OF [ discr_r ] ; /* Index form */
if ( discr_r = = A0 ) {
/* 2 lines below: B(x) <-- x*B(x) */
memmove ( & b [ 1 ] , b , NROOTS * sizeof ( b [ 0 ] ) ) ;
b [ 0 ] = A0 ;
}
else {
/* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
t [ 0 ] = lambda [ 0 ] ;
for ( i = 0 ; i < NROOTS ; i + + ) {
if ( b [ i ] ! = A0 )
t [ i + 1 ] = lambda [ i + 1 ] ^ ALPHA_TO [ MODNN ( discr_r + b [ i ] ) ] ;
else
t [ i + 1 ] = lambda [ i + 1 ] ;
}
if ( 2 * el < = r + no_eras - 1 ) {
el = r + no_eras - el ;
/*
* 2 lines below : B ( x ) < - - inv ( discr_r ) *
* lambda ( x )
*/
for ( i = 0 ; i < = NROOTS ; i + + )
b [ i ] = ( lambda [ i ] = = 0 ) ? A0 : MODNN ( INDEX_OF [ lambda [ i ] ] - discr_r + NN ) ;
}
else {
/* 2 lines below: B(x) <-- x*B(x) */
memmove ( & b [ 1 ] , b , NROOTS * sizeof ( b [ 0 ] ) ) ;
b [ 0 ] = A0 ;
}
memcpy ( lambda , t , ( NROOTS + 1 ) * sizeof ( t [ 0 ] ) ) ;
}
}
/* Convert lambda to index form and compute deg(lambda(x)) */
deg_lambda = 0 ;
for ( i = 0 ; i < NROOTS + 1 ; i + + ) {
lambda [ i ] = INDEX_OF [ lambda [ i ] ] ;
if ( lambda [ i ] ! = A0 )
deg_lambda = i ;
}
/* Find roots of the error+erasure locator polynomial by Chien search */
memcpy ( & reg [ 1 ] , & lambda [ 1 ] , NROOTS * sizeof ( reg [ 0 ] ) ) ;
count = 0 ; /* Number of roots of lambda(x) */
for ( i = 1 , k = IPRIM - 1 ; i < = NN ; i + + , k = MODNN ( k + IPRIM ) ) {
q = 1 ; /* lambda[0] is always 0 */
for ( j = deg_lambda ; j > 0 ; j - - ) {
if ( reg [ j ] ! = A0 ) {
reg [ j ] = MODNN ( reg [ j ] + j ) ;
q ^ = ALPHA_TO [ reg [ j ] ] ;
}
}
if ( q ! = 0 )
continue ; /* Not a root */
/* store root (index-form) and error location number */
# if DEBUG>=2
fprintf ( stderr , " count %d root %d loc %d \n " , count , i , k ) ;
# endif
root [ count ] = i ;
loc [ count ] = k ;
/* If we've already found max possible roots,
* abort the search to save time
*/
if ( + + count = = deg_lambda )
break ;
}
if ( deg_lambda ! = count ) {
/*
* deg ( lambda ) unequal to number of roots = > uncorrectable
* error detected
*/
count = - 1 ;
goto finish ;
}
/*
* Compute err + eras evaluator poly omega ( x ) = s ( x ) * lambda ( x ) ( modulo
* x * * NROOTS ) . in index form . Also find deg ( omega ) .
*/
deg_omega = 0 ;
for ( i = 0 ; i < NROOTS ; i + + ) {
tmp = 0 ;
j = ( deg_lambda < i ) ? deg_lambda : i ;
for ( ; j > = 0 ; j - - ) {
if ( ( s [ i - j ] ! = A0 ) & & ( lambda [ j ] ! = A0 ) )
tmp ^ = ALPHA_TO [ MODNN ( s [ i - j ] + lambda [ j ] ) ] ;
}
if ( tmp ! = 0 )
deg_omega = i ;
omega [ i ] = INDEX_OF [ tmp ] ;
}
omega [ NROOTS ] = A0 ;
/*
* Compute error values in poly - form . num1 = omega ( inv ( X ( l ) ) ) , num2 =
* inv ( X ( l ) ) * * ( FCR - 1 ) and den = lambda_pr ( inv ( X ( l ) ) ) all in poly - form
*/
for ( j = count - 1 ; j > = 0 ; j - - ) {
num1 = 0 ;
for ( i = deg_omega ; i > = 0 ; i - - ) {
if ( omega [ i ] ! = A0 )
num1 ^ = ALPHA_TO [ MODNN ( omega [ i ] + i * root [ j ] ) ] ;
}
num2 = ALPHA_TO [ MODNN ( root [ j ] * ( FCR - 1 ) + NN ) ] ;
den = 0 ;
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
for ( i = min ( deg_lambda , NROOTS - 1 ) & ~ 1 ; i > = 0 ; i - = 2 ) {
if ( lambda [ i + 1 ] ! = A0 )
den ^ = ALPHA_TO [ MODNN ( lambda [ i + 1 ] + i * root [ j ] ) ] ;
}
if ( den = = 0 ) {
# if DEBUG >= 1
fprintf ( stderr , " \n ERROR: denominator = 0 \n " ) ;
# endif
count = - 1 ;
goto finish ;
}
/* Apply error to data */
if ( num1 ! = 0 ) {
data [ loc [ j ] ] ^ = ALPHA_TO [ MODNN ( INDEX_OF [ num1 ] + INDEX_OF [ num2 ] + NN - INDEX_OF [ den ] ) ] ;
}
}
finish :
if ( eras_pos ! = NULL ) {
for ( i = 0 ; i < count ; i + + )
eras_pos [ i ] = loc [ i ] ;
}
return count ;
}
struct rs * INIT_RS ( unsigned int symsize , unsigned int gfpoly , unsigned fcr , unsigned prim ,
unsigned int nroots ) {
struct rs * rs ;
int i , j , sr , root , iprim ;
if ( symsize > 8 * sizeof ( DTYPE ) )
return NULL ; /* Need version with ints rather than chars */
if ( fcr > = ( 1 < < symsize ) )
return NULL ;
if ( prim = = 0 | | prim > = ( 1 < < symsize ) )
return NULL ;
if ( nroots > = ( 1 < < symsize ) )
return NULL ; /* Can't have more roots than symbol values! */
rs = ( struct rs * ) calloc ( 1 , sizeof ( struct rs ) ) ;
if ( rs = = NULL ) {
Debugprintf ( " FATAL ERROR: Out of memory. \n " ) ;
exit ( 0 ) ;
}
rs - > mm = symsize ;
rs - > nn = ( 1 < < symsize ) - 1 ;
rs - > alpha_to = ( DTYPE * ) calloc ( ( rs - > nn + 1 ) , sizeof ( DTYPE ) ) ;
if ( rs - > alpha_to = = NULL ) {
Debugprintf ( " FATAL ERROR: Out of memory. \n " ) ;
exit ( 0 ) ;
}
rs - > index_of = ( DTYPE * ) calloc ( ( rs - > nn + 1 ) , sizeof ( DTYPE ) ) ;
if ( rs - > index_of = = NULL ) {
Debugprintf ( " FATAL ERROR: Out of memory. \n " ) ;
exit ( 0 ) ;
}
/* Generate Galois field lookup tables */
rs - > index_of [ 0 ] = A0 ; /* log(zero) = -inf */
rs - > alpha_to [ A0 ] = 0 ; /* alpha**-inf = 0 */
sr = 1 ;
for ( i = 0 ; i < rs - > nn ; i + + ) {
rs - > index_of [ sr ] = i ;
rs - > alpha_to [ i ] = sr ;
sr < < = 1 ;
if ( sr & ( 1 < < symsize ) )
sr ^ = gfpoly ;
sr & = rs - > nn ;
}
if ( sr ! = 1 ) {
/* field generator polynomial is not primitive! */
free ( rs - > alpha_to ) ;
free ( rs - > index_of ) ;
free ( rs ) ;
return NULL ;
}
/* Form RS code generator polynomial from its roots */
rs - > genpoly = ( DTYPE * ) calloc ( ( nroots + 1 ) , sizeof ( DTYPE ) ) ;
if ( rs - > genpoly = = NULL ) {
Debugprintf ( " FATAL ERROR: Out of memory. \n " ) ;
exit ( 0 ) ;
}
rs - > fcr = fcr ;
rs - > prim = prim ;
rs - > nroots = nroots ;
/* Find prim-th root of 1, used in decoding */
for ( iprim = 1 ; ( iprim % prim ) ! = 0 ; iprim + = rs - > nn )
;
rs - > iprim = iprim / prim ;
rs - > genpoly [ 0 ] = 1 ;
for ( i = 0 , root = fcr * prim ; i < nroots ; i + + , root + = prim ) {
rs - > genpoly [ i + 1 ] = 1 ;
/* Multiply rs->genpoly[] by @**(root + x) */
for ( j = i ; j > 0 ; j - - ) {
if ( rs - > genpoly [ j ] ! = 0 )
rs - > genpoly [ j ] = rs - > genpoly [ j - 1 ] ^ rs - > alpha_to [ modnn ( rs , rs - > index_of [ rs - > genpoly [ j ] ] + root ) ] ;
else
rs - > genpoly [ j ] = rs - > genpoly [ j - 1 ] ;
}
/* rs->genpoly[0] can never be zero */
rs - > genpoly [ 0 ] = rs - > alpha_to [ modnn ( rs , rs - > index_of [ rs - > genpoly [ 0 ] ] + root ) ] ;
}
/* convert rs->genpoly[] to index form for quicker encoding */
for ( i = 0 ; i < = nroots ; i + + ) {
rs - > genpoly [ i ] = rs - > index_of [ rs - > genpoly [ i ] ] ;
}
// diagnostic prints
#if 0
printf ( " Alpha To: \n \r " ) ;
for ( i = 0 ; i < sizeof ( DTYPE ) * ( rs - > nn + 1 ) ; i + + )
printf ( " 0x%2x, " , rs - > alpha_to [ i ] ) ;
printf ( " \n \r " ) ;
printf ( " Index Of: \n \r " ) ;
for ( i = 0 ; i < sizeof ( DTYPE ) * ( rs - > nn + 1 ) ; i + + )
printf ( " 0x%2x, " , rs - > index_of [ i ] ) ;
printf ( " \n \r " ) ;
printf ( " GenPoly: \n \r " ) ;
for ( i = 0 ; i < = nroots ; i + + )
printf ( " 0x%2x, " , rs - > genpoly [ i ] ) ;
printf ( " \n \r " ) ;
# endif
return rs ;
}
// TEMPORARY!!!
// FIXME: We already have multiple copies of this.
// Consolidate them into one somewhere.
void fx_hex_dump ( unsigned char * p , int len )
{
int n , i , offset ;
offset = 0 ;
while ( len > 0 ) {
n = len < 16 ? len : 16 ;
Debugprintf ( " %03x: " , offset ) ;
for ( i = 0 ; i < n ; i + + ) {
Debugprintf ( " %02x " , p [ i ] ) ;
}
for ( i = n ; i < 16 ; i + + ) {
Debugprintf ( " " ) ;
}
Debugprintf ( " " ) ;
for ( i = 0 ; i < n ; i + + ) {
Debugprintf ( " %c " , isprint ( p [ i ] ) ? p [ i ] : ' . ' ) ;
}
Debugprintf ( " \n " ) ;
p + = 16 ;
offset + = 16 ;
len - = 16 ;
}
}
/*-------------------------------------------------------------
*
* 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 ;
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " TX Raw Packet is " , ' T ' ) ;
debugHexDump ( pp - > frame_data , pp - > frame_len , ' T ' ) ;
2023-09-04 19:06:44 +01:00
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 ) {
// Success. No info part.
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " TX Type 1 IL2P Packet no info is " , ' T ' ) ;
debugHexDump ( iout , out_len , ' R ' ) ;
2023-09-04 19:06:44 +01:00
return ( out_len ) ;
}
// Payload is AX.25 info part.
2023-09-12 21:38:15 +01:00
2023-09-04 19:06:44 +01:00
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.
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " TX Type 1 IL2P Packet is " , ' T ' ) ;
debugHexDump ( iout , out_len , ' T ' ) ;
2023-09-04 19:06:44 +01:00
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.
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " TX Type 2 IL2P Packet is " , ' T ' ) ;
debugHexDump ( iout , out_len , ' T ' ) ;
2023-09-04 19:06:44 +01:00
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 ) {
Debugprintf ( " 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 ) {
Debugprintf ( " 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
/*--------------------------------------------------------------------------------
*
* 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 interpreted.
// 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 ) ;
2023-12-17 13:53:32 +00:00
if ( ( pp - > frame_data [ 6 ] & 0x80 ) = = ( pp - > frame_data [ 13 ] & 0x80 ) )
{
// Both C bits are the same (ax.25 v1) so can't be sent as type 1 as will be changed
return - 1 ;
}
2023-09-04 19:06:44 +01:00
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 ) ;
//Debugprintf ("%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 receiving 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 ignored 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 ) {
Debugprintf ( " 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 upper 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 ) {
// This can pop up sporadically when receiving random noise.
// Would be better to show only when debug is enabled but variable not available here.
// TODO: For now we will just suppress it.
//text_color_set(DW_COLOR_ERROR);
//Debugprintf ("IL2P: Invalid character '%c' in destination address '%s'\n", addrs[AX25_DESTINATION][i], addrs[AX25_DESTINATION]);
}
return ( NULL ) ;
}
}
sprintf ( addrs [ AX25_DESTINATION ] + strlen ( addrs [ AX25_DESTINATION ] ) , " -%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 ) {
// This can pop up sporadically when receiving random noise.
// Would be better to show only when debug is enabled but variable not available here.
// TODO: For now we will just suppress it.
//text_color_set(DW_COLOR_ERROR);
//Debugprintf ("IL2P: Invalid character '%c' in source address '%s'\n", addrs[AX25_SOURCE][i], addrs[AX25_SOURCE]);
}
return ( NULL ) ;
}
}
sprintf ( addrs [ AX25_SOURCE ] + strlen ( addrs [ AX25_SOURCE ] ) , " -%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 ) ;
2023-09-12 21:38:15 +01:00
if ( e > 1 ) // only have 2 rs bytes so can only detect 1 error
{
Debugprintf ( " Header correction seems ok but errors > 1 " ) ;
return - 1 ;
}
2023-09-04 19:06:44 +01:00
il2p_descramble_block ( corrected , corrected_descrambled_hdr , IL2P_HEADER_SIZE ) ;
return ( e ) ;
}
// end il2p_header.c
/*--------------------------------------------------------------------------------
*
* 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...
Debugprintf ( " 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 interpreted 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 exceed 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 ) ;
// Debugprintf ("%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 ) {
Debugprintf ( " 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 ) ;
// Debugprintf ("%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 ) {
Debugprintf ( " 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 ) {
//Debugprintf ("%s:%d: failed = %0x\n", __FILE__, __LINE__, failed);
return ( - 2 ) ;
}
if ( decoded_length ! = payload_size ) {
Debugprintf ( " 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
2023-09-12 21:38:15 +01:00
struct il2p_context_s * il2p_context [ 4 ] [ 16 ] [ 3 ] ;
2023-09-04 19:06:44 +01:00
/***********************************************************************************
*
* Name : il2p_rec_bit
*
2024-04-13 17:48:58 +01:00
* Purpose : Extract il2p packets from a stream of bits .
2023-09-04 19:06:44 +01:00
*
* 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 .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int centreFreq [ 4 ] = { 0 , 0 , 0 , 0 } ;
void il2p_rec_bit ( int chan , int subchan , int slice , int dbit )
{
// Allocate context blocks only as needed.
if ( dbit )
dbit = 1 ;
2024-04-13 17:48:58 +01:00
else
dbit = 0 ;
2023-09-04 19:06:44 +01:00
struct il2p_context_s * F = il2p_context [ chan ] [ subchan ] [ slice ] ;
2024-04-13 17:48:58 +01:00
2023-09-04 19:06:44 +01:00
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);
2024-04-13 17:48:58 +01:00
memset ( F , 0 , sizeof ( struct il2p_context_s ) ) ;
2023-09-04 19:06:44 +01:00
}
// 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);
//Debugprintf ("IL2P header has normal polarity\n");
F - > polarity = 0 ;
F - > state = IL2P_HEADER ;
F - > bc = 0 ;
F - > hc = 0 ;
2023-09-12 21:38:15 +01:00
nPhases [ chan ] [ subchan ] [ slice ] = 0 ;
2023-09-04 19:06:44 +01:00
// Determine Centre Freq
2023-12-17 13:53:32 +00:00
2023-09-04 19:06:44 +01:00
centreFreq [ chan ] = GuessCentreFreq ( chan ) ;
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " SYNC Detected " , ' R ' ) ;
2023-09-04 19:06:44 +01:00
}
2024-04-13 17:48:58 +01:00
else if ( __builtin_popcount ( ( ~ ( F - > acc ) & 0x00ffffff ) ^ IL2P_SYNC_WORD ) < = 1 ) {
2023-09-04 19:06:44 +01:00
// FIXME - this pops up occasionally with random noise. Find better way to convey information.
// This also happens for each slicer - to noisy.
//Debugprintf ("IL2P header has reverse polarity\n");
F - > polarity = 1 ;
F - > state = IL2P_HEADER ;
F - > bc = 0 ;
F - > hc = 0 ;
centreFreq [ chan ] = GuessCentreFreq ( chan ) ;
2023-09-12 21:38:15 +01:00
nPhases [ chan ] [ subchan ] [ slice ] = 0 ;
2023-09-04 19:06:44 +01:00
}
2023-12-17 13:53:32 +00:00
2023-09-04 19:06:44 +01:00
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)
//{
// Debugprintf("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 ) ;
2024-07-23 21:26:30 +01:00
if ( il2p_get_debug ( ) > = 2 )
2023-12-17 13:53:32 +00:00
{
2023-09-04 19:06:44 +01:00
Debugprintf ( " Header type %d, max fec = %d " , hdr_type , max_fec ) ;
Debugprintf ( " Need to collect %d encoded bytes for %d byte payload. " , F - > eplen , len ) ;
Debugprintf ( " %d small blocks of %d and %d large blocks of %d. %d parity symbols per block " ,
plprop . small_block_count , plprop . small_block_size ,
plprop . large_block_count , plprop . large_block_size , plprop . parity_symbols_per_block ) ;
}
2023-09-12 21:38:15 +01:00
if ( len > 340 )
{
Debugprintf ( " Packet too big for QtSM " ) ;
2023-10-07 12:21:48 +01:00
F - > state = IL2P_SEARCHING ;
return ;
2023-09-12 21:38:15 +01:00
}
2023-09-04 19:06:44 +01:00
if ( F - > eplen > = 1 ) { // Need to gather payload.
F - > pc = 0 ;
F - > state = IL2P_PAYLOAD ;
}
2023-12-17 13:53:32 +00:00
else if ( F - > eplen = = 0 )
{
// No payload.
2023-09-04 19:06:44 +01:00
F - > pc = 0 ;
2023-12-17 13:53:32 +00:00
if ( il2p_crc [ chan ] )
{
// enter collect crc state
F - > crccount = 0 ;
F - > state = IL2P_CRC ;
}
else
F - > state = IL2P_DECODE ;
2023-09-04 19:06:44 +01:00
}
else { // Error.
if ( il2p_get_debug ( ) > = 1 ) {
Debugprintf ( " 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 ;
}
2023-12-17 13:53:32 +00:00
if ( F - > pc = = F - > eplen )
{
// got frame. See if need crc
2023-09-04 19:06:44 +01:00
2023-12-17 13:53:32 +00:00
if ( il2p_crc [ chan ] )
{
// enter collect crc state
2023-09-04 19:06:44 +01:00
2023-12-17 13:53:32 +00:00
F - > crccount = 0 ;
F - > state = IL2P_CRC ;
}
else
F - > state = IL2P_DECODE ;
2023-09-04 19:06:44 +01:00
}
}
break ;
2023-12-17 13:53:32 +00:00
case IL2P_CRC :
F - > bc + + ;
if ( F - > bc = = 8 )
{
// full byte has been collected.
F - > bc = 0 ;
if ( ! F - > polarity )
F - > crc [ F - > crccount + + ] = F - > acc & 0xff ;
else
F - > crc [ F - > crccount + + ] = ( ~ F - > acc ) & 0xff ;
if ( F - > crccount = = 4 )
{
// have all crc bytes. enter DECODE
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " CRC Complete Header is " , ' R ' ) ;
debugHexDump ( F - > shdr , 15 , ' R ' ) ;
if ( F - > pc )
{
debugTimeStamp ( " Payload is " , ' R ' ) ;
debugHexDump ( F - > spayload , F - > pc , ' R ' ) ;
}
debugTimeStamp ( " CRC is " , ' R ' ) ;
debugHexDump ( F - > crc , 4 , ' R ' ) ;
2023-12-17 13:53:32 +00:00
F - > state = IL2P_DECODE ;
}
}
2024-07-23 21:26:30 +01:00
break ;
2023-12-17 13:53:32 +00:00
2023-09-04 19:06:44 +01:00
case IL2P_DECODE :
2023-12-17 13:53:32 +00:00
2023-09-04 19:06:44 +01:00
// 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 )
{
// Most likely too many FEC errors.
Debugprintf ( " FAILED to construct frame in %s. \n " , __func__ ) ;
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " Packet Decode failed " , ' R ' ) ;
2023-09-04 19:06:44 +01:00
}
}
2024-07-23 21:26:30 +01:00
if ( pp ! = NULL )
{
2023-09-04 19:06:44 +01:00
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?
2023-12-17 13:53:32 +00:00
// if using crc pass received crc to packet object
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " Decoded Packet is " , ' R ' ) ;
debugHexDump ( pp - > frame_data , pp - > frame_len , ' R ' ) ;
2023-12-17 13:53:32 +00:00
if ( il2p_crc [ chan ] )
{
//copy crc bytes to packet object
pp - > crc [ 0 ] = F - > crc [ 0 ] ;
pp - > crc [ 1 ] = F - > crc [ 1 ] ;
pp - > crc [ 2 ] = F - > crc [ 2 ] ;
pp - > crc [ 3 ] = F - > crc [ 3 ] ;
}
2024-07-23 21:26:30 +01:00
debugTimeStamp ( " CRC raw bytes " , ' R ' ) ;
debugHexDump ( pp - > crc , 4 , ' R ' ) ;
2023-09-04 19:06:44 +01:00
multi_modem_process_rec_packet ( chan , subchan , slice , pp , alevel , retries , is_fx25 , slice , centreFreq [ chan ] ) ;
}
} // end block for local variables.
2024-07-23 21:26:30 +01:00
if ( il2p_get_debug ( ) > = 2 )
2023-09-04 19:06:44 +01:00
Debugprintf ( " ----- " ) ;
F - > state = IL2P_SEARCHING ;
break ;
} // end of switch
} // end il2p_rec_bit
// 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
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 .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2023-12-17 13:53:32 +00:00
2023-09-04 19:06:44 +01:00
string * il2p_send_frame ( int chan , packet_t pp , int max_fec , int polarity )
{
2023-12-17 13:53:32 +00:00
unsigned char encoded [ IL2P_MAX_PACKET_SIZE ] = " " ;
2023-09-04 19:06:44 +01:00
string * packet = newString ( ) ;
int preamblecount ;
unsigned char preamble [ 1024 ] ;
2023-12-17 13:53:32 +00:00
// The data includes the 2 byte crc but length doesn't
uint8_t crc1 = pp - > frame_data [ pp - > frame_len ] ; // Low 8 bits
uint8_t crc2 = pp - > frame_data [ pp - > frame_len + 1 ] ; // High 8 bits
2023-09-04 19:06:44 +01:00
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 ) ;
2023-09-12 21:38:15 +01:00
2023-09-04 19:06:44 +01:00
if ( elen < = 0 ) {
Debugprintf ( " IL2P: Unable to encode frame into IL2P. \n " ) ;
return ( packet ) ;
}
elen + = IL2P_SYNC_WORD_SIZE ;
2023-12-17 13:53:32 +00:00
// if we are using crc add it now. elen should point to end of data
// crc should be at pp->frame_data[pp->frame_len]
2024-07-23 21:26:30 +01:00
if ( il2p_crc [ chan ] & 1 )
2023-12-17 13:53:32 +00:00
{
// The four encoded CRC bytes are arranged :
// | CRC3 | CRC2 | CRC1 | CRC0 |
// CRC3 encoded from high nibble of 16 - bit CRC value (from crc2)
// CRC0 encoded from low nibble of 16 - bit CRC value (from crc1)
encoded [ elen + + ] = Hamming74EncodeTable [ crc2 > > 4 ] ;
encoded [ elen + + ] = Hamming74EncodeTable [ crc2 & 0xf ] ;
encoded [ elen + + ] = Hamming74EncodeTable [ crc1 > > 4 ] ;
encoded [ elen + + ] = Hamming74EncodeTable [ crc1 & 0xf ] ;
}
2023-09-04 19:06:44 +01:00
number_of_bits_sent [ chan ] = 0 ;
2024-07-23 21:26:30 +01:00
if ( il2p_get_debug ( ) > = 2 ) {
2023-09-04 19:06:44 +01:00
Debugprintf ( " IL2P frame, max_fec = %d, %d encoded bytes total " , max_fec , elen ) ;
// fx_hex_dump(encoded, elen);
}
// Send bits to modulator.
// Try using preaamble for txdelay
2023-09-12 21:38:15 +01:00
// Nino now uses 00 as preamble for QPSK
// We don't need txdelay between frames in one transmission
2023-09-04 19:06:44 +01:00
2023-09-12 21:38:15 +01:00
if ( Continuation [ chan ] = = 0 )
{
preamblecount = ( txdelay [ chan ] * tx_bitrate [ chan ] ) / 8000 ; // 8 for bits, 1000 for mS
if ( preamblecount > 1024 )
preamblecount = 1024 ;
if ( pskStates [ chan ] ) // PSK Modes
memset ( preamble , 01 , preamblecount ) ;
else
memset ( preamble , IL2P_PREAMBLE , preamblecount ) ;
stringAdd ( packet , preamble , preamblecount ) ;
Continuation [ chan ] = 1 ;
}
2023-09-04 19:06:44 +01:00
stringAdd ( packet , encoded , elen ) ;
2024-07-23 21:26:30 +01:00
// Add bytes for tail and TX padding, but don't send if another packet is available (?? how ??)
number_of_bits_sent [ chan ] = 0 ;
2023-09-04 19:06:44 +01:00
tx_fx25_size [ chan ] = packet - > Length * 8 ;
return packet ;
}
// TX Code. Builds whole packet then sends a bit at a time
# define TX_SILENCE 0
# define TX_DELAY 1
# define TX_TAIL 2
# define TX_NO_DATA 3
# define TX_FRAME 4
# define TX_WAIT_BPF 5
# define TX_BIT0 0
# define TX_BIT1 1
# define FRAME_EMPTY 0
# define FRAME_FULL 1
# define FRAME_NO_FRAME 2
# define FRAME_NEW_FRAME 3
# define BYTE_EMPTY 0
# define BYTE_FULL 1
extern UCHAR tx_frame_status [ 5 ] ;
extern UCHAR tx_byte_status [ 5 ] ;
extern string * tx_data [ 5 ] ;
extern int tx_data_len [ 5 ] ;
extern UCHAR tx_bit_stream [ 5 ] ;
extern UCHAR tx_bit_cnt [ 5 ] ;
extern long tx_tail_cnt [ 5 ] ;
extern BOOL tx_bs_bit [ 5 ] ;
string * fill_il2p_data ( int snd_ch , string * data )
{
string * result ;
packet_t pp = ax25_new ( ) ;
// Call il2p_send_frame to build the bit stream
pp - > frame_len = data - > Length - 2 ; // Included CRC
2023-12-17 13:53:32 +00:00
memcpy ( pp - > frame_data , data - > Data , data - > Length ) ; // Copy the crc in case we are going to send it
2023-09-04 19:06:44 +01:00
result = il2p_send_frame ( snd_ch , pp , 1 , 0 ) ;
2024-07-23 21:26:30 +01:00
ax25_delete ( pp ) ;
debugTimeStamp ( " TX Complete packet including Preamble and CRC and Tail " , ' T ' ) ;
debugHexDump ( result - > Data , result - > Length , ' T ' ) ;
2023-09-04 19:06:44 +01:00
return result ;
}
void il2p_get_new_frame ( int snd_ch , TStringList * frame_stream )
{
string * myTemp ;
tx_bs_bit [ snd_ch ] = 0 ;
tx_bit_cnt [ snd_ch ] = 0 ;
tx_fx25_size_cnt [ snd_ch ] = 0 ;
tx_fx25_size [ snd_ch ] = 1 ;
tx_frame_status [ snd_ch ] = FRAME_NEW_FRAME ;
tx_byte_status [ snd_ch ] = BYTE_EMPTY ;
if ( frame_stream - > Count = = 0 )
tx_frame_status [ snd_ch ] = FRAME_NO_FRAME ;
else
{
// We now pass control byte and ack bytes on front and pointer to socket on end if ackmode
myTemp = Strings ( frame_stream , 0 ) ; // get message
if ( ( myTemp - > Data [ 0 ] & 0x0f ) = = 12 ) // ACKMODE
{
// Save copy then copy data up 3 bytes
Add ( & KISS_acked [ snd_ch ] , duplicateString ( myTemp ) ) ;
mydelete ( myTemp , 0 , 3 ) ;
myTemp - > Length - = sizeof ( void * ) ;
}
else
{
// Just remove control
mydelete ( myTemp , 0 , 1 ) ;
}
AGW_AX25_frame_analiz ( snd_ch , FALSE , myTemp ) ;
put_frame ( snd_ch , myTemp , " " , TRUE , FALSE ) ;
tx_data [ snd_ch ] = fill_il2p_data ( snd_ch , myTemp ) ;
Delete ( frame_stream , 0 ) ; // This will invalidate temp
}
}
// Original code
/*
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 ] + + ;
}
*/
int il2p_get_new_bit ( int snd_ch , Byte bit )
{
string * s ;
if ( tx_frame_status [ snd_ch ] = = FRAME_EMPTY )
{
il2p_get_new_frame ( snd_ch , & all_frame_buf [ snd_ch ] ) ;
if ( tx_frame_status [ snd_ch ] = = FRAME_NEW_FRAME )
tx_frame_status [ snd_ch ] = FRAME_FULL ;
}
if ( tx_frame_status [ snd_ch ] = = FRAME_FULL )
{
if ( tx_byte_status [ snd_ch ] = = BYTE_EMPTY )
{
if ( tx_data [ snd_ch ] - > Length )
{
s = tx_data [ snd_ch ] ;
tx_bit_stream [ snd_ch ] = s - > Data [ 0 ] ;
tx_frame_status [ snd_ch ] = FRAME_FULL ;
tx_byte_status [ snd_ch ] = BYTE_FULL ;
tx_bit_cnt [ snd_ch ] = 0 ;
mydelete ( tx_data [ snd_ch ] , 0 , 1 ) ;
}
else
tx_frame_status [ snd_ch ] = FRAME_EMPTY ;
}
if ( tx_byte_status [ snd_ch ] = = BYTE_FULL )
{
// il2p sends high order bit first
bit = tx_bit_stream [ snd_ch ] > > 7 ; // top bit to bottom
tx_bit_stream [ snd_ch ] = tx_bit_stream [ snd_ch ] < < 1 ;
tx_bit_cnt [ snd_ch ] + + ;
tx_fx25_size_cnt [ snd_ch ] + + ;
if ( tx_bit_cnt [ snd_ch ] > = 8 )
tx_byte_status [ snd_ch ] = BYTE_EMPTY ;
if ( tx_fx25_size_cnt [ snd_ch ] = = tx_fx25_size [ snd_ch ] )
tx_frame_status [ snd_ch ] = FRAME_EMPTY ;
}
}
if ( tx_frame_status [ snd_ch ] = = FRAME_EMPTY )
{
il2p_get_new_frame ( snd_ch , & all_frame_buf [ snd_ch ] ) ;
switch ( tx_frame_status [ snd_ch ] )
{
case FRAME_NEW_FRAME :
tx_frame_status [ snd_ch ] = FRAME_FULL ;
break ;
case FRAME_NO_FRAME :
2024-07-23 21:26:30 +01:00
// I dont really like this state machine. We have run out of frames to send so
// should go straight to tail. This way we add an extra bit. Or does this really matter ??
2023-09-04 19:06:44 +01:00
tx_tail_cnt [ snd_ch ] = 0 ;
tx_frame_status [ snd_ch ] = FRAME_EMPTY ;
tx_status [ snd_ch ] = TX_TAIL ;
break ;
}
}
return bit ;
}
2024-07-23 21:26:30 +01:00
extern int txLatency ;
extern int useTImedPTT ;
int il2p_get_new_bit_tail ( UCHAR snd_ch , UCHAR bit )
{
// This sends reversals. It is an experiment
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
int tailbits = ( txtail [ snd_ch ] * tx_baudrate [ snd_ch ] ) / 1000 ;
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
# ifndef WIN32
if ( useTimedPTT )
tailbits + = ( txLatency * tx_baudrate [ snd_ch ] ) / 1000 ; // add padding to tx buffer to make sure we don't send silence
# endif
if ( tx_tail_cnt [ snd_ch ] + + > tailbits )
tx_status [ snd_ch ] = TX_WAIT_BPF ;
return ( tx_tail_cnt [ snd_ch ] & 1 ) ; // altenating 1/0
}
void debugHexDump ( unsigned char * Data , int Len , char Dirn )
{
char Line [ 256 ] ;
# ifndef LOGTX
if ( Dirn = = ' T ' )
return ;
# endif
# ifndef LOGRX
if ( Dirn = = ' R ' )
return ;
# endif
while ( Len > 0 )
{
sprintf ( Line , " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \n " ,
Data [ 0 ] , Data [ 1 ] , Data [ 2 ] , Data [ 3 ] , Data [ 4 ] , Data [ 5 ] , Data [ 6 ] , Data [ 7 ] ,
Data [ 8 ] , Data [ 9 ] , Data [ 10 ] , Data [ 11 ] , Data [ 12 ] , Data [ 13 ] , Data [ 14 ] , Data [ 15 ] ) ;
if ( Len < 16 )
{
Line [ Len * 3 ] = 10 ;
Line [ Len * 3 + 1 ] = 0 ;
}
2024-10-29 22:45:21 +00:00
writeTraceLog ( Line ) ;
2024-07-23 21:26:30 +01:00
Data + = 16 ;
Len - = 16 ;
}
}
// Hamming experiments
// from https://github.com/nasserkessas/hamming-codes/blob/master/hamming.c
# define block unsigned short // 16 bits
# define bit uint8_t // 8 bits (only last is used)
int multipleXor ( int * indicies , int len )
{
int val = indicies [ 0 ] ;
for ( int i = 1 ; i < len ; i + + )
{
val = val ^ indicies [ i ] ;
}
return val ;
}
bit getBit ( unsigned short b , int i )
{
return ( b < < i ) & ( int ) pow ( 2 , ( sizeof ( unsigned short ) * 8 - 1 ) ) ;
}
unsigned short toggleBit ( unsigned short b , int i )
{
return b ^ ( 1 < < i ) ;
}
bit getCharBit ( char b , int i )
{
return ( b < < i ) & ( int ) pow ( 2 , ( sizeof ( char ) * 8 - 1 ) ) ;
}
block modifyBit ( block n , int p , bit b )
{
return ( ( n & ~ ( 1 < < ( sizeof ( block ) * 8 - 1 - p ) ) ) | ( b < < ( sizeof ( block ) * 8 - 1 - p ) ) ) ;
}
void encode ( char * input , int len , FILE * ptr ) {
// Amount of bits in a block //
int bits = sizeof ( block ) * 8 ;
// Amount of bits per block used to carry the message //
int messageBits = bits - log2 ( bits ) - 1 ;
// Amount of blocks needed to encode message //
int blocks = ceil ( ( float ) len / messageBits ) ;
// Array of encoded blocks //
block encoded [ 16 ] ;
// Loop through each block //
for ( int i = 0 ; i < blocks + 1 ; i + + ) {
printf ( " On Block %d: \n " , i ) ;
// Final encoded block variable //
block thisBlock = 0 ;
// Amount of "skipped" bits (used for parity) //
int skipped = 0 ;
// Count of how many bits are "on" //
int onCount = 0 ;
// Array of "on" bits //
int onList [ 64 ] ;
// Loop through each message bit in this block to populate final block //
for ( int j = 0 ; j < bits ; j + + ) {
// Skip bit if reserved for parity bit //
if ( ( j & ( j - 1 ) ) = = 0 ) { // Check if j is a power of two or 0
skipped + + ;
continue ;
}
bit thisBit ;
if ( i ! = blocks ) {
// Current overall bit number //
int currentBit = i * messageBits + ( j - skipped ) ;
// Current character //
int currentChar = currentBit / ( sizeof ( char ) * 8 ) ; // int division
// Value of current bit //
thisBit = currentBit < len * sizeof ( char ) * 8 ? getCharBit ( input [ currentChar ] , currentBit - currentChar * 8 ) : 0 ;
}
else {
thisBit = getBit ( len / 8 , j - skipped + ( sizeof ( block ) * 8 - messageBits ) ) ;
}
// If bit is "on", add to onList and onCount //
if ( thisBit ) {
onList [ onCount ] = j ;
onCount + + ;
}
// Populate final message block //
thisBlock = modifyBit ( thisBlock , j , thisBit ) ;
}
// Calculate values of parity bits //
block parityBits = multipleXor ( onList , onCount ) ;
// Loop through skipped bits (parity bits) //
for ( int k = 1 ; k < skipped ; k + + ) { // skip bit 0
// If bit is "on", add to onCount
if ( getBit ( parityBits , sizeof ( block ) * 8 - skipped + k ) ) {
onCount + + ;
}
// Add parity bit to final block //
thisBlock = modifyBit ( thisBlock , ( int ) pow ( 2 , skipped - k - 1 ) , getBit ( parityBits , sizeof ( block ) * 8 - skipped + k ) ) ;
}
// Add overall parity bit (total parity of onCount) //
thisBlock = modifyBit ( thisBlock , 0 , onCount & 1 ) ;
// Output final block //
// printBlock(thisBlock);
// putchar('\n');
// Add block to encoded blocks //
encoded [ i ] = thisBlock ;
}
// Write encoded message to file //
fwrite ( encoded , sizeof ( block ) , blocks + 1 , ptr ) ;
}
void decode ( block input [ ] , int len , FILE * ptr )
{
// Amount of bits in a block //
int bits = sizeof ( block ) * 8 ;
for ( int b = 0 ; b < ( len / sizeof ( block ) ) ; b + + ) {
printf ( " On Block %d: \n " , b ) ;
// Print initial block //
// printBlock(input[b]);
// Count of how many bits are "on" //
int onCount = 0 ;
// Array of "on" bits //
int onList [ 64 ] ;
// Populate onCount and onList //
for ( int i = 1 ; i < bits ; i + + ) {
getBit ( input [ b ] , i ) ;
if ( getBit ( input [ b ] , i ) ) {
onList [ onCount ] = i ;
onCount + + ;
}
}
// Check for single errors //
int errorLoc = multipleXor ( onList , onCount ) ;
if ( errorLoc ) {
// Check for multiple errors //
if ( ! ( onCount & 1 ^ getBit ( input [ b ] , 0 ) ) ) { // last bit of onCount (total parity) XOR first bit of block (parity bit)
printf ( " \n More than one error detected. Aborting. \n " ) ;
exit ( 1 ) ;
}
// Flip error bit //
else {
printf ( " \n Detected error at position %d, flipping bit. \n " , errorLoc ) ;
input [ b ] = toggleBit ( input [ b ] , ( bits - 1 ) - errorLoc ) ;
// Re-print block for comparison //
// printBlock(input[b]);
}
}
putchar ( ' \n ' ) ;
}
}