You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
13 KiB
483 lines
13 KiB
/*********************************************************************************
|
|
*
|
|
* lora_aprs_node_pico is a LoRa APRS KISS modem with additional PE1RXF telemetry
|
|
* capabilities. It runs on a Raspberry Pi Pico.
|
|
*
|
|
* (C)2023 M.T. Konstapel https://meezenest.nl/mees
|
|
*
|
|
* This file is part of lora_aprs_node_pico.
|
|
*
|
|
* lora_aprs_node_pico 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.
|
|
*
|
|
* lora_aprs_node_pico 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 lora_aprs_node_pico. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
**********************************************************************************/
|
|
#include "kiss.h"
|
|
|
|
/*
|
|
* KISS encoder uses two steps:
|
|
*
|
|
* Step 1: encodes the LoRa APRS frame into an AX.25 frame. DONE
|
|
* Step 2: encapsulates the AX.25 frame into a KISS frame. DONE
|
|
*
|
|
* struct aprs_frame {
|
|
* uint8_t source_address[10];
|
|
* uint8_t digi_path[255];
|
|
* uint8_t data_field[255];
|
|
* uint8_t message[255];
|
|
* uint8_t digis[10][10];
|
|
* uint8_t acknowledge_number[255];
|
|
* bool acknowledge_request = false;
|
|
* uint16_t server_command = 0;
|
|
* uint16_t number_of_digipeaters = 0;
|
|
*
|
|
* uint8_t valid_apsr_data = false;
|
|
* };
|
|
*
|
|
*/
|
|
uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame *ax25frame)
|
|
{
|
|
uint8_t *encoded_call;
|
|
uint16_t cnt=0;
|
|
uint16_t position = 0;
|
|
uint8_t digi_cnt = 0;
|
|
uint8_t escaped_string_cnt=0;
|
|
|
|
//printf("Encode KISS");
|
|
|
|
// Destination call
|
|
//printf("Destination: ");
|
|
encoded_call = EncodeCall(aprsframe->digis[0]);
|
|
cnt = 0;
|
|
while (cnt < 7)
|
|
{
|
|
ax25frame->complete[position] = *(encoded_call+cnt);
|
|
//printf("0x%X ", *(encoded_call+cnt));
|
|
//printf("0x%X ", ax25frame->complete[position]);
|
|
cnt++;
|
|
position++;
|
|
}
|
|
//printf("\n");
|
|
|
|
// Source call
|
|
//printf("Source: ");
|
|
cnt=0;
|
|
encoded_call = EncodeCall(aprsframe->source_address);
|
|
while (cnt < 7)
|
|
{
|
|
ax25frame->complete[position] = *(encoded_call+cnt);
|
|
//printf("0x%X ", *(encoded_call+cnt));
|
|
//printf("0x%X ", ax25frame->complete[position]);
|
|
cnt++;
|
|
position++;
|
|
}
|
|
//printf("\n");
|
|
|
|
// No digipeaters in path, so destination is the last call and therefore the 'last' flag should be set
|
|
if (aprsframe->number_of_digipeaters == 0)
|
|
{
|
|
ax25frame->complete[position-1] |= 0x01;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Path (digipeaters)
|
|
//printf("Path: ");
|
|
cnt=0;
|
|
digi_cnt = 1; // Start at position 1 as position 0 contains destination (which we already encoded)
|
|
while (aprsframe->number_of_digipeaters-- != 0) {
|
|
|
|
encoded_call = EncodeCall(aprsframe->digis[digi_cnt]);
|
|
while (cnt < 7) {
|
|
ax25frame->complete[position] = *(encoded_call+cnt);
|
|
//printf("0x%X ", *(encoded_call+cnt));
|
|
//printf("0x%X ", ax25frame->complete[position]);
|
|
cnt++;
|
|
position++;
|
|
}
|
|
digi_cnt++;
|
|
cnt = 0;
|
|
|
|
}
|
|
//printf("\n");
|
|
|
|
// Set 'last' flag
|
|
ax25frame->complete[position-1] |= 0x01;
|
|
}
|
|
|
|
// Add control fields
|
|
ax25frame->complete[position++] = 0x03;
|
|
ax25frame->complete[position++] = 0xF0;
|
|
|
|
// All the dificult bits are done, now we just add the payload.
|
|
//printf("Data:");
|
|
cnt = 0;
|
|
while (aprsframe->data_field[cnt] != 0 && cnt < 256) {
|
|
ax25frame->complete[position] = aprsframe->data_field[cnt];
|
|
//printf("0x%X ", ax25frame->complete[position]);
|
|
cnt++;
|
|
position++;
|
|
}
|
|
//printf( "\n");
|
|
|
|
// Store length of AX25 frame
|
|
ax25frame->lenght = position;
|
|
|
|
// Encapsulate AX.25 frame in KISS frame (including escaping FEND codes)
|
|
putchar(FEND);
|
|
putchar(CMD_DATA);
|
|
escaped_string_cnt=0;
|
|
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FEND;
|
|
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = CMD_DATA;
|
|
cnt=0;
|
|
position=ax25frame->lenght;
|
|
while (position-- != 0)
|
|
{
|
|
// Escape FESC and TFEND
|
|
if (ax25frame->complete[cnt] == FEND) {
|
|
putchar(FESC);
|
|
putchar(TFEND);
|
|
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FESC;
|
|
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = TFEND;
|
|
} else if (ax25frame->complete[cnt] == FESC) {
|
|
putchar(FESC);
|
|
putchar(TFESC);
|
|
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FESC;
|
|
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = TFESC;
|
|
} else {
|
|
putchar(ax25frame->complete[cnt]);
|
|
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = ax25frame->complete[cnt];
|
|
}
|
|
cnt++;
|
|
}
|
|
putchar(FEND);
|
|
ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FEND;
|
|
//printf("\n");
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Decodes a KISS frame from the usb serial port
|
|
*
|
|
* Input : string starting with FEND and ending with FEND
|
|
* Output: filled struct AprsFrame
|
|
* Return: 0 = OK, 1 = ERROR, 2 = EXIT KISS MODE
|
|
*/
|
|
uint16_t KissClass::DecodeFrame(uint8_t string[], struct kiss_tx_frame *kisstxframe)
|
|
{
|
|
uint16_t position =0 ;
|
|
uint16_t loop_counter = 0;
|
|
uint16_t cnt =0 ;
|
|
uint16_t ssid = 0;
|
|
uint16_t last_digi = 0;
|
|
uint16_t digi_cnt = 0;
|
|
|
|
//printf("Decode KISS");
|
|
|
|
kisstxframe->valid_data = false;
|
|
|
|
// Not a valid frame
|
|
if (string[position] != FEND)
|
|
return 1;
|
|
|
|
position++;
|
|
|
|
// Is data frame: we de-escape the string and put it back in the same string with a NULL at the end as terminator.
|
|
if (string[position] == CMD_DATA)
|
|
{
|
|
position++;
|
|
while (string[position] != FEND)
|
|
{
|
|
// De-escape codes
|
|
if (string[position] == FESC && string[position+1] == TFEND)
|
|
{
|
|
string[cnt] = FEND;
|
|
position++;
|
|
}
|
|
else if (string[position] == FESC && string[position+1] == TFESC)
|
|
{
|
|
string[cnt] = FESC;
|
|
position++;
|
|
}
|
|
else {
|
|
string[cnt] = string[position];
|
|
}
|
|
cnt++;
|
|
position++;
|
|
}
|
|
string[cnt] = 0; //Terminate string
|
|
|
|
}
|
|
// Is command to exit KISS MODE
|
|
else if (string[position] == CMD_EXIT_KISS) {
|
|
return 2;
|
|
}
|
|
|
|
// If we are here we have a valid AX.25 frame in 'string' which is decapsulated from its KISS frame
|
|
/*
|
|
0-5 destination
|
|
6 destination ssid
|
|
7-12 source
|
|
13 desitination sidd
|
|
|
|
vanaf byte 13 LAST FLAG zoeken
|
|
|
|
03
|
|
f0
|
|
payload tot aan NULL
|
|
*/
|
|
|
|
// Decode destination call
|
|
position = 0;
|
|
cnt = 0;
|
|
loop_counter = 0;
|
|
while (loop_counter < 6)
|
|
{
|
|
kisstxframe->digis[0][cnt] = string[position] >> 1;
|
|
// Remove space
|
|
if (kisstxframe->digis[0][cnt] == ' ')
|
|
cnt--;
|
|
position++;
|
|
loop_counter++;
|
|
cnt++;
|
|
//printf("%u ", position);
|
|
}
|
|
|
|
// Extract SSID
|
|
//printf("position: %u\n", position);
|
|
ssid = (string[position] & 0b00011110) >> 1;
|
|
//printf("DIGI SSID: %u (%u)\n", ssid, position);
|
|
|
|
// Place SSID behind call or not if it is zero
|
|
if (ssid != 0) {
|
|
kisstxframe->digis[0][cnt++] = '-';
|
|
if (ssid < 10)
|
|
{
|
|
kisstxframe->digis[0][cnt++] = ssid + 48;
|
|
}
|
|
else
|
|
{
|
|
kisstxframe->digis[0][cnt++] = '1';
|
|
kisstxframe->digis[0][cnt++] = (ssid%10)+48;
|
|
}
|
|
}
|
|
// Is has-been-repeated flag set?
|
|
if(string[position] & 0b10000000) {
|
|
kisstxframe->digis[0][cnt] = '*';
|
|
cnt++;
|
|
}
|
|
kisstxframe->digis[0][cnt] = 0; //terminate string
|
|
position++;
|
|
|
|
// Decode source call
|
|
cnt = 0;
|
|
loop_counter=0;
|
|
while (loop_counter < 6)
|
|
{
|
|
kisstxframe->source_address[cnt] = string[position] >> 1;
|
|
// Remove space
|
|
if (kisstxframe->source_address[cnt] == ' ')
|
|
cnt--;
|
|
position++;
|
|
loop_counter++;
|
|
cnt++;
|
|
//printf("%u ", position);
|
|
}
|
|
|
|
// Extract SSID
|
|
ssid = (string[position] & 0b00011110) >> 1;
|
|
//printf("sources SSID: %u (%u)\n", ssid, position);
|
|
|
|
// Place SSID behind call or not if it is zero
|
|
if (ssid != 0) {
|
|
kisstxframe->source_address[cnt++] = '-';
|
|
if (ssid < 10)
|
|
{
|
|
kisstxframe->source_address[cnt++] = ssid + 48;
|
|
}
|
|
else
|
|
{
|
|
kisstxframe->source_address[cnt++] = '1';
|
|
kisstxframe->source_address[cnt++] = (ssid%10)+48;
|
|
}
|
|
}
|
|
// Is has-been-repeated flag set?
|
|
if(string[position] & 0b10000000) {
|
|
kisstxframe->source_address[cnt] = '*';
|
|
cnt++;
|
|
}
|
|
kisstxframe->source_address[cnt] = 0; //terminate string
|
|
|
|
// Check LAST flag
|
|
if ( (string[position] & 0b00000001))
|
|
last_digi = 1;
|
|
|
|
position++;
|
|
|
|
// Decode digi path
|
|
digi_cnt=1;
|
|
while (last_digi == 0) {
|
|
|
|
cnt = 0;
|
|
loop_counter=0;
|
|
while (loop_counter < 6)
|
|
{
|
|
kisstxframe->digis[digi_cnt][cnt] = string[position] >> 1;
|
|
// Remove space
|
|
if (kisstxframe->digis[digi_cnt][cnt] == ' ')
|
|
cnt--;
|
|
position++;
|
|
loop_counter++;
|
|
cnt++;
|
|
//printf("%u ", position);
|
|
}
|
|
|
|
// Extract SSID
|
|
ssid = (string[position] & 0b00011110) >> 1;
|
|
//printf("DIGI SSID: %u (%u)\n", ssid, position);
|
|
|
|
// Place SSID behind call or not if it is zero
|
|
if (ssid != 0) {
|
|
kisstxframe->digis[digi_cnt][cnt++] = '-';
|
|
if (ssid < 10)
|
|
{
|
|
kisstxframe->digis[digi_cnt][cnt++] = ssid + 48;
|
|
}
|
|
else
|
|
{
|
|
kisstxframe->digis[digi_cnt][cnt++] = '1';
|
|
kisstxframe->digis[digi_cnt][cnt++] = (ssid%10) + 48;
|
|
}
|
|
}
|
|
|
|
// Is has-been-repeated flag set?
|
|
if(string[position] & 0b10000000) {
|
|
kisstxframe->digis[digi_cnt][cnt] = '*';
|
|
cnt++;
|
|
}
|
|
kisstxframe->digis[digi_cnt][cnt] = 0; //terminate string
|
|
|
|
// Check LAST flag (also ends when more than 10 digis)
|
|
if ( (string[position] & 0b00000001) || digi_cnt > 8)
|
|
last_digi = 1;
|
|
|
|
digi_cnt++;
|
|
position++;
|
|
}
|
|
kisstxframe->number_of_digipeaters = digi_cnt-1;
|
|
//printf("Digipeaters: %u \n",kisstxframe->number_of_digipeaters );
|
|
|
|
//Skip the two control fields
|
|
position++;
|
|
position++;
|
|
|
|
//Rest of string up to NULL is payload
|
|
cnt = 0;
|
|
kisstxframe->data_field[cnt++] = ':';
|
|
while (string[position] != 0 && position < 512)
|
|
{
|
|
kisstxframe->data_field[cnt] = string[position];
|
|
position++;
|
|
cnt++;
|
|
}
|
|
kisstxframe->data_field[cnt] = 0; //terminate string
|
|
|
|
kisstxframe->valid_data = true;
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Encodes call according to AX.25 specs.
|
|
*
|
|
* input : string with call and ssid as readable characters
|
|
* output: pointer to memory location (7 bytes with the encoded call)
|
|
*
|
|
*/
|
|
uint8_t * KissClass::EncodeCall(uint8_t string[])
|
|
{
|
|
uint8_t position = 0;
|
|
uint8_t cnt = 0;
|
|
uint8_t repeat_flag = 0;
|
|
|
|
static uint8_t call[7] = { 0,0,0,0,0,0,0} ;
|
|
uint8_t ssid = 0;
|
|
|
|
// if asterix is pressent in string, than the message has been repeated, so set the 'has been repeated' flag in the ssid register
|
|
// At the same time delete the asterix and place a NULL (string terminator) at its place. As the asterix should be the last character in the string, this should work.
|
|
position = 0;
|
|
while( string[position] != 0)
|
|
{
|
|
if (string[position] == '*') {
|
|
repeat_flag = 1;
|
|
string[position] == 0;
|
|
break;
|
|
}
|
|
|
|
position++;
|
|
}
|
|
|
|
position = 0;
|
|
// extract call
|
|
while( string[position] != 0 || cnt < 6)
|
|
{
|
|
if ( string[position] == '-') {
|
|
position++;
|
|
break;
|
|
} else {
|
|
call[cnt] = string[position] << 1;
|
|
}
|
|
cnt++;
|
|
position++;
|
|
}
|
|
// pad with spaces to a length of 6
|
|
while( cnt < 6 )
|
|
{
|
|
call[cnt++] = ' ' << 1;
|
|
}
|
|
|
|
// extract ssid
|
|
cnt=0;
|
|
while( string[position] != 0 )
|
|
{
|
|
if (string[position] >= 48 && string[position] <= 57) {
|
|
ssid = 10*ssid + string[position]-48;
|
|
position++;
|
|
}
|
|
}
|
|
|
|
ssid = ssid << 1;
|
|
ssid |= 0b01100000;
|
|
|
|
if (repeat_flag == 1)
|
|
ssid |= 0b10000000;
|
|
|
|
// add encoded ssid to encoded call array
|
|
call[6] = ssid;
|
|
|
|
/*
|
|
//printf("SSID: 0x%X\n", ssid);
|
|
|
|
cnt = 0;
|
|
while (cnt < 7)
|
|
{
|
|
//printf("0x%X ", call[cnt++]);
|
|
}
|
|
//printf("\n");
|
|
*/
|
|
|
|
return call;
|
|
|
|
}
|
|
|