aprs message decoding

master
Mateusz Lubecki 2024-04-22 05:34:41 +02:00
rodzic 15829704be
commit 428f6a2360
9 zmienionych plików z 565 dodań i 85 usunięć

Wyświetl plik

@ -12,6 +12,7 @@ C_SRCS += \
../system/src/aprs/crc.c \
../system/src/aprs/dac.c \
../system/src/aprs/digi.c \
../system/src/aprs/message.c \
../system/src/aprs/status.c \
../system/src/aprs/telemetry.c \
../system/src/aprs/wx.c
@ -25,6 +26,7 @@ OBJS += \
./system/src/aprs/crc.o \
./system/src/aprs/dac.o \
./system/src/aprs/digi.o \
./system/src/aprs/message.o \
./system/src/aprs/status.o \
./system/src/aprs/telemetry.o \
./system/src/aprs/wx.o
@ -38,6 +40,7 @@ C_DEPS += \
./system/src/aprs/crc.d \
./system/src/aprs/dac.d \
./system/src/aprs/digi.d \
./system/src/aprs/message.d \
./system/src/aprs/status.d \
./system/src/aprs/telemetry.d \
./system/src/aprs/wx.d

Wyświetl plik

@ -50,7 +50,7 @@ void aprsis_init(
aprsis_return_t aprsis_connect_and_login(const char * address, uint8_t address_ln, uint16_t port, uint8_t auto_send_beacon);
aprsis_return_t aprsis_connect_and_login_default(uint8_t auto_send_beacon);
sim800_return_t aprsis_disconnect(void);
void aprsis_receive_callback(srl_context_t* srl_context);
//void aprsis_receive_callback(srl_context_t* srl_context);
void aprsis_check_alive(void);
int aprsis_check_connection_attempt_alive(void);

Wyświetl plik

@ -25,10 +25,6 @@
* $WIZ$ type = "int"
* $WIZ$ min = 1
*/
#include <stdint.h>
//extern uint8_t kiss_txdelay;
//#define CONFIG_AFSK_PREAMBLE_LEN (kiss_txdelay*10UL)
#define CONFIG_AFSK_PREAMBLE_LEN 400UL /// 300
/**
@ -36,8 +32,6 @@
* $WIZ$ type = "int"
* $WIZ$ min = 1
*/
//extern uint8_t kiss_txtail;
//#define CONFIG_AFSK_TRAILER_LEN (kiss_txtail*10UL)
#define CONFIG_AFSK_TRAILER_LEN 50UL

Wyświetl plik

@ -45,7 +45,7 @@
/**
* Do not uncomment this on production devices
*/
//#define INHIBIT_CUTOFF
#define INHIBIT_CUTOFF
/**
* Intermediate STOP2 cycle lenght within L7 or L6 mode.

Wyświetl plik

@ -9,6 +9,7 @@
#include "etc/aprsis_config.h"
#include "text.h"
#include "aprs/status.h"
#include "aprs/message.h"
#include "gsm/sim800c.h"
#include "gsm/sim800c_poolers.h"
@ -19,6 +20,12 @@
#include <stdio.h>
#include <string.h>
#ifdef UNIT_TEST
#define STATIC
#else
#define STATIC static
#endif
srl_context_t * aprsis_serial_port;
/**
@ -180,6 +187,72 @@ char aprsis_login_string_reveived[APRSIS_LOGIN_STRING_RECEIVED_LN];
*/
#define MAXIMUM_CALL_SSID_DASH_LN 10
/**
* Checks if data in a buffer contains APRS message
* @param message
* @param message_ln
* @return position at which content of message starts
*/
STATIC int aprsis_check_is_message(const uint8_t * const message, const uint16_t message_ln) {
// example message
// Details:"SP8EBC>APX216,TCPIP*,qAC,NINTH::SR9WXZ :tedt{0s}\r\n", '\0' <repeats 715 times>
// go through a buffer and look for double ':'
int message_start_position = 0;
for (int i = 0; i < message_ln; i++) {
const uint8_t * this_character = message + i;
const uint8_t * next_character = message + i + 1;
if (*this_character == 0x00) {
break;
}
if ((*this_character == ':') && (*next_character == ':')) {
message_start_position = i + 2;
break;
}
}
return message_start_position;
}
/**
*
* @param srl_context
*/
STATIC void aprsis_receive_callback(srl_context_t* srl_context) {
const uint8_t * buffer = srl_get_rx_buffer(srl_context);
const uint16_t message_ln = srl_context->srl_rx_bytes_counter;
// if something was actually received
if (srl_context->srl_rx_state == SRL_RX_DONE) {
// check if this is keepalive message
if (*buffer == '#') {
aprsis_last_keepalive_ts = main_get_master_time();
aprsis_last_keepalive_long_ts = main_get_master_time();
aprsis_keepalive_received_counter++;
gsm_sim800_tcpip_async_receive(aprsis_serial_port, aprsis_gsm_modem_state, 0, 61000, aprsis_receive_callback);
}
else if (aprsis_check_is_message(buffer, message_ln) == 1) {
}
else {
aprsis_another_received_counter++;
gsm_sim800_tcpip_async_receive(aprsis_serial_port, aprsis_gsm_modem_state, 0, 61000, aprsis_receive_callback);
}
}
}
void aprsis_init(
srl_context_t * context,
gsm_sim800_state_t * gsm_modem_state,
@ -394,29 +467,6 @@ sim800_return_t aprsis_disconnect(void) {
return out;
}
void aprsis_receive_callback(srl_context_t* srl_context) {
// if something was actually received
if (srl_context->srl_rx_state == SRL_RX_DONE) {
// check if this is keepalive message
if (*(srl_get_rx_buffer(srl_context)) == '#') {
aprsis_last_keepalive_ts = main_get_master_time();
aprsis_last_keepalive_long_ts = main_get_master_time();
aprsis_keepalive_received_counter++;
gsm_sim800_tcpip_async_receive(aprsis_serial_port, aprsis_gsm_modem_state, 0, 61000, aprsis_receive_callback);
}
else {
aprsis_another_received_counter++;
gsm_sim800_tcpip_async_receive(aprsis_serial_port, aprsis_gsm_modem_state, 0, 61000, aprsis_receive_callback);
}
}
}
/**
* Pooler function which check periodically if APRS-IS connection is alive.
*/

Wyświetl plik

@ -6,6 +6,7 @@
#include "main_master_time.h"
#include "ax25_t.h"
#include "cfifo.h"
#include "afsk.h"
@ -50,15 +51,15 @@
struct AX25Msg; // fwd declaration
/**
* Type for AX25 messages callback.
*/
typedef void (*ax25_callback_t)(struct AX25Msg *ax25_rxed_frame);
/**
* Type for channel free wait timeout callback
* Create an AX25Call structure on the fly.
* \param str callsign, can be 6 characters or shorter.
* \param id ssid associated with the callsign.
*/
typedef void (*ax25_ch_free_timeout_callback_t)(void);
#define AX25_CALL(str, id) {.call = (str), .ssid = (id) }
#define AX25_PATH(dst, src, ...) { dst, src, ## __VA_ARGS__ }
typedef struct AX25Ctx
{
@ -81,54 +82,6 @@ typedef struct AX25Ctx
} AX25Ctx;
/**
* AX25 Call sign.
*/
typedef struct AX25Call
{
char call[6]; ///< Call string, max 6 character
uint8_t ssid; ///< SSID (secondary station ID) for the call
} AX25Call;
/**
* Create an AX25Call structure on the fly.
* \param str callsign, can be 6 characters or shorter.
* \param id ssid associated with the callsign.
*/
#define AX25_CALL(str, id) {.call = (str), .ssid = (id) }
#define AX25_PATH(dst, src, ...) { dst, src, ## __VA_ARGS__ }
/**
* Maximum number of Repeaters in a AX25 message.
*/
#define AX25_MAX_RPT 8
/**
* AX25 Message.
* Used to handle AX25 sent/received messages.
*/
typedef struct AX25Msg
{
AX25Call src; ///< Source adress
AX25Call dst; ///< Destination address
AX25Call rpt_lst[AX25_MAX_RPT]; ///< List of repeaters
uint8_t rpt_cnt; ///< Number of repeaters in this message
uint8_t rpt_flags; ///< Has-been-repeated flags for each repeater (bit-mapped)
#define AX25_REPEATED(msg, idx) ((msg)->rpt_flags & BV(idx))
uint16_t ctrl; ///< AX25 control field
uint8_t pid; ///< AX25 PID field
const uint8_t *info; ///< Pointer to the info field (payload) of the message
uint16_t len; ///< Payload length
uint8_t raw_data[CONFIG_AX25_FRAME_BUF_LEN]; /// Surowa zawarto<74><6F> ramki przekopiowana z Ctx->buff
short int raw_msg_len; // wielkosc surowej ramki
} AX25Msg;
extern AX25Msg ax25_rxed_frame;
extern char ax25_new_msg_rx_flag;

Wyświetl plik

@ -0,0 +1,61 @@
/*
* ax25_t.h
*
* Created on: Apr 20, 2024
* Author: mateusz
*/
#ifndef INCLUDE_APRS_AX25_T_H_
#define INCLUDE_APRS_AX25_T_H_
#include "stdint.h"
#include "ax25_config.h"
/**
* Maximum number of Repeaters in a AX25 message.
*/
#define AX25_MAX_RPT 8
/**
* AX25 Call sign.
*/
typedef struct AX25Call
{
char call[6]; ///< Call string, max 6 character
uint8_t ssid; ///< SSID (secondary station ID) for the call
} AX25Call;
/**
* AX25 Message.
* Used to handle AX25 sent/received messages.
*/
typedef struct AX25Msg
{
AX25Call src; ///< Source adress
AX25Call dst; ///< Destination address
AX25Call rpt_lst[AX25_MAX_RPT]; ///< List of repeaters
uint8_t rpt_cnt; ///< Number of repeaters in this message
uint8_t rpt_flags; ///< Has-been-repeated flags for each repeater (bit-mapped)
#define AX25_REPEATED(msg, idx) ((msg)->rpt_flags & BV(idx))
uint16_t ctrl; ///< AX25 control field
uint8_t pid; ///< AX25 PID field
const uint8_t *info; ///< Pointer to the info field (payload) of the message
uint16_t len; ///< Payload length
uint8_t raw_data[CONFIG_AX25_FRAME_BUF_LEN]; /// Surowa zawarto<74><6F> ramki przekopiowana z Ctx->buff
short int raw_msg_len; // wielkosc surowej ramki
} AX25Msg;
/**
* Type for AX25 messages callback.
*/
typedef void (*ax25_callback_t)(struct AX25Msg *ax25_rxed_frame);
/**
* Type for channel free wait timeout callback
*/
typedef void (*ax25_ch_free_timeout_callback_t)(void);
#endif /* INCLUDE_APRS_AX25_T_H_ */

Wyświetl plik

@ -0,0 +1,58 @@
/*
* message.h
*
* Created on: Apr 20, 2024
* Author: mateusz
*/
#ifndef INCLUDE_APRS_MESSAGE_H_
#define INCLUDE_APRS_MESSAGE_H_
#include "ax25_t.h"
#include "stdint.h"
#include "./stored_configuration_nvm/config_data.h"
#define MESSAGE_MAX_LENGHT 67
typedef struct message_t {
AX25Call from;
AX25Call to;
uint8_t content[MESSAGE_MAX_LENGHT];
uint8_t number;
}message_t;
typedef enum message_source_t {
MESSAGE_SOURCE_APRSIS,
MESSAGE_SOURCE_RADIO
}message_source_t;
/**
* Decode received data to look for an APRS message and put it into a structure
* @param message pointer to data received from APRS-IS
* @param message_ln lenght of a buffer content
* @param content_position optional position of an APRS message content (recipient callsign). if set to zero function will look for it
* @param src
* @param output parsed APRS message content
* @return zero if message has been found and decoded, non zero if parsing failed
*/
uint8_t message_decode_from_aprsis(const uint8_t * const message, const uint16_t message_ln, uint16_t content_position, message_source_t src, message_t * output);
/**
*
* @param config_data
* @param message
* @return zero if this is a message to us, non zero otherwise
*/
uint8_t message_is_for_me(config_data_basic_t * config_data, const message_t * const message);
/**
*
* @param out_buffer
* @param out_buffer_ln
* @param message
* @param src how this message has been received
*/
void message_create_ack_for(uint8_t * out_buffer, const uint16_t out_buffer_ln, const message_t * const message, const message_source_t src);
#endif /* INCLUDE_APRS_MESSAGE_H_ */

Wyświetl plik

@ -0,0 +1,361 @@
/*
* message.c
*
* Created on: Apr 20, 2024
* Author: mateusz
*/
#include "message.h"
#include "string.h"
#include "stdio.h"
#define MESSAGE_RECIPIENT_FIELD_SIZE 9
#define MESSAGE_SSID_CHARS_LN 2
#define MESSAGE_ATOI_BUFFER 5 ///!< include room of null terminator
#define MESSAGE_SENDER_CALL_SSID_MAXLEN 9
#define MESSAGE_CURRENT_CHAR *(message + content_position + i)
#define MESSAGE_CURRENT_SENDER_CHAR *(message + i)
#define MESSAGE_IS_DIGIT(c) (c >= '0' && c <= '9')
#define MESSAGE_ACK_REMAINING_BUF (out_buffer_ln - current_pos)
#define MESSAGE_ACK_CURRENT_POS (char*)(out_buffer + current_pos)
static char message_atoi_buffer[MESSAGE_ATOI_BUFFER];
/**
* Decode received data to look for an APRS message and put it into a structure
* @param message pointer to data received from APRS-IS
* @param message_ln lenght of a buffer content
* @param content_position optional position of an APRS message content (recipient callsign). if set to zero function will look for it
* @param output parsed APRS message content
* @return zero if message has been found and decoded, non zero if parsing failed
*/
uint8_t message_decode_from_aprsis(const uint8_t * const message, const uint16_t message_ln, uint16_t content_position, message_source_t src, message_t * output) {
// example message:: SP8EBC>APX216,TCPIP*,qAC,NINTH::SR9WXZ :tedt{0s}\r\n
// result returned by the function, although shamesly it is also used as local aux variable
// in few places of this function :( this is what You sometimes do to save some stack.
uint8_t result = 0xFF;
uint16_t i = 0;
if (output == 0x00) {
return result;
}
if (message_ln < (MESSAGE_SENDER_CALL_SSID_MAXLEN + MESSAGE_RECIPIENT_FIELD_SIZE)) {
return result;
}
memset(message_atoi_buffer, 0x00, MESSAGE_ATOI_BUFFER);
// if start position of APRS message (position of recipient callsign) has not been provided
if ((src == MESSAGE_SOURCE_APRSIS) && (content_position == 0)) {
// look for it
for (i = 0; i < message_ln; i++) {
const uint8_t * this_character = message + i;
const uint8_t * next_character = message + i + 1;
// break on an end of input string
if (*this_character == 0x00) {
break;
}
// check if double semicolon has been found
if ((*this_character == ':') && (*next_character == ':')) {
// APRS message starts after second semicolong
content_position = i + 2;
break;
}
}
}
// clear the iterator, it will be used across this function
i = 0;
// check content position one more time to verify
// if input data contains APRS message at all
if (
((src == MESSAGE_SOURCE_APRSIS) && (content_position != 0)) ||
(src == MESSAGE_SOURCE_RADIO)
) {
// set this variable to zero as now it will be used as a local
result = 0;
// clear output structure to make room for new data
memset(output, 0x00, sizeof(message_t));
//extract sender call and SSID only if this message has been received from APRS-IS in pure text form
if (src == MESSAGE_SOURCE_APRSIS) {
// fast forward any potential whitespace at the begining
while (MESSAGE_CURRENT_SENDER_CHAR == ' ') {
i++;
if (i >= message_ln) {
break;
}
}
// extract sender callsign
for (; i < MESSAGE_SENDER_CALL_SSID_MAXLEN; i++) {
// if SSID separator or sender end character ('>') has been reached
if (MESSAGE_CURRENT_SENDER_CHAR == '-' || MESSAGE_CURRENT_SENDER_CHAR == '>') {
break;
}
output->from.call[result++] = MESSAGE_CURRENT_SENDER_CHAR;
}
// check if sender has SSID
if (MESSAGE_CURRENT_SENDER_CHAR == '-') {
// jumps to next character. otherwise separating '-'
// will be interpreted as a minus/negitive sign
i++;
result = 0; // here used as a local iterator
// extract SSID
for (; i < MESSAGE_RECIPIENT_FIELD_SIZE; i++) {
// copy characters to aux buffer
message_atoi_buffer[result++] = MESSAGE_CURRENT_SENDER_CHAR;
// check if there isn't enough characters
if (result == MESSAGE_ATOI_BUFFER) {
break;
}
}
// convert SSID to int
output->from.ssid = atoi(message_atoi_buffer);
}
// clear the iterator, it will be used across this function
i = 0;
}
else {
// clear content_position iterator
content_position = 0;
// find a begining of the message in data from radio channel
while (MESSAGE_CURRENT_CHAR != ':') {
content_position++;
if (content_position >= message_ln) {
break;
}
}
// jump over ':'
content_position++;
}
// extract recipient
for (; i < MESSAGE_RECIPIENT_FIELD_SIZE; i++) {
// look if end of callsign or separating '-' has been found
if (MESSAGE_CURRENT_CHAR == ' ' || MESSAGE_CURRENT_CHAR == '-') {
break; // and go for SSID reading
}
// copy recipient callsign character per character
output->to.call[i] = MESSAGE_CURRENT_CHAR;
}
// check if callsign has SSID set
if (MESSAGE_CURRENT_CHAR == '-') {
// jumps to next character. otherwise separating '-'
// will be interpreted as a minus/negitive sign
i++;
result = 0; // here used as a local iterator
// extract SSID
for (; i < MESSAGE_RECIPIENT_FIELD_SIZE; i++) {
// copy characters to aux buffer
message_atoi_buffer[result++] = MESSAGE_CURRENT_CHAR;
// check if there isn't enough characters
if (result == MESSAGE_ATOI_BUFFER) {
break;
}
}
// convert SSID to int
output->to.ssid = atoi(message_atoi_buffer);
}
if (result != MESSAGE_ATOI_BUFFER && /* if SSID extraction was OK and end of a buffer hasn't been reached */
(
(i < MESSAGE_RECIPIENT_FIELD_SIZE) || /* if recipient and callsign has been read before ':' separating from message content */
((i == MESSAGE_RECIPIENT_FIELD_SIZE) && (MESSAGE_CURRENT_CHAR == ':')) /* if a position of separating ':' was reached and ':' is there */
)
) {
// reinitialize buffer before next usage
memset(message_atoi_buffer, 0x00, MESSAGE_ATOI_BUFFER);
// check if the iterator is set now to position of ':' separating
// recipient an the message itself
while(MESSAGE_CURRENT_CHAR != ':' && i <= MESSAGE_RECIPIENT_FIELD_SIZE) {
i++;
}
// one more incrementation to jump over ':' and land on the first character of the message
i++;
result = 0;
// then copy message, which ends on a counter, something like '{1'
while(MESSAGE_CURRENT_CHAR != ':' && i + content_position < message_ln) {
output->content[result++] = MESSAGE_CURRENT_CHAR;
i++;
// break on message counter separator
if (MESSAGE_CURRENT_CHAR == '{') {
i++; // move to first digit of a counter
break;
}
}
// check which condition has ended previous 'while' loop and if an end of the buffer has been reached
if (i + content_position < message_ln) {
result = 0;
// now iterator is set (should be set) on a first digit of message counter
// copy everything until first non digit character is found
while (MESSAGE_IS_DIGIT(MESSAGE_CURRENT_CHAR)) {
message_atoi_buffer[result++] = MESSAGE_CURRENT_CHAR;
i++;
// check if there isn't enough characters
if (result == MESSAGE_ATOI_BUFFER) {
break;
}
}
// convert message counter from string to int
output->number = atoi(message_atoi_buffer);
if (result < MESSAGE_ATOI_BUFFER) {
// new we are done (??)
result = 0;
}
}
else {
result = 0xFF;
}
}
else {
result = 0xFF;
}
}
return result;
}
/**
*
* @param config_data
* @param message
* @return zero if this is a message to us, non zero otherwise
*/
uint8_t message_is_for_me(config_data_basic_t * config_data, const message_t * const message)
{
const int callsign = strncmp(config_data->callsign, message->to.call, 6);
if (callsign == 0 && (config_data->ssid == message->to.ssid)) {
return 0;
}
else {
return 1;
}
}
/**
*
* @param out_buffer
* @param out_buffer_ln
* @param message
* @param src how this message has been received
*/
void message_create_ack_for(uint8_t * out_buffer, const uint16_t out_buffer_ln, const message_t * const message, const message_source_t src)
{
int current_pos = 0;
uint8_t call_position = 0;
// clear output buffer
memset(out_buffer, 0x00, out_buffer_ln);
if (src == MESSAGE_SOURCE_APRSIS) {
// put my callsign
for (; call_position < 6; call_position++) {
// break on null character
if (message->to.call[call_position] == 0x00) {
break;
}
// copy callsign data
out_buffer[current_pos + call_position] = message->to.call[call_position];
}
current_pos += call_position;
call_position = 0;
// check if I have a SSID
if (message->to.ssid != 0) {
current_pos += snprintf(MESSAGE_ACK_CURRENT_POS, MESSAGE_ACK_REMAINING_BUF, "-%d", message->to.ssid);
}
// constant part
current_pos += snprintf(MESSAGE_ACK_CURRENT_POS, MESSAGE_ACK_REMAINING_BUF, ">AKLPRZ::");
// put sender callsign, station I received this message from
for (; call_position < 6; call_position++) {
// break on null character
if (message->from.call[call_position] == 0x00) {
break;
}
// copy callsign data
out_buffer[current_pos + call_position] = message->from.call[call_position];
}
// put sender SSID, station I received this message from
if (message->from.ssid != 0) {
call_position += snprintf(MESSAGE_ACK_CURRENT_POS + call_position, MESSAGE_ACK_REMAINING_BUF, "-%d", message->from.ssid);
}
// check if callsign was shorter than 6 characters
while (call_position < 9) {
// copy callsign data
out_buffer[current_pos + call_position] = ' ';
call_position++;
}
// callsign + ssid + padding must be exactly 9 characters long
current_pos += 9;
// then put 'ackXX' where X is message number
current_pos += snprintf(MESSAGE_ACK_CURRENT_POS, MESSAGE_ACK_REMAINING_BUF, ":ack%d", message->number);
}
else {
}
}