kopia lustrzana https://github.com/sq2ips/m20-custom-firmware
136 wiersze
3.9 KiB
C
136 wiersze
3.9 KiB
C
/*
|
|
* aprs.c
|
|
* By SQ2IPS
|
|
* Based on https://joshuajer.red/blog/2023-01-04-APRS-and-ax25, https://www.aprs.org/doc/APRS101.PDF, https://github.com/projecthorus/sondehub-aprs-gateway/blob/main/README.md
|
|
*/
|
|
|
|
#include "aprs.h"
|
|
#include "config.h"
|
|
#include "utils.h"
|
|
|
|
#include <string.h>
|
|
|
|
static uint16_t calculateFcs(uint8_t *input_data, uint16_t len) {
|
|
uint16_t crc = 0xFFFF;
|
|
uint16_t crc16_table[] = {0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285,
|
|
0x6306, 0x7387, 0x8408, 0x9489, 0xa50a, 0xb58b,
|
|
0xc60c, 0xd68d, 0xe70e, 0xf78f};
|
|
|
|
uint16_t iterator = 0;
|
|
while (len--) {
|
|
crc =
|
|
(crc >> 4) ^ crc16_table[(crc & 0xf) ^ (input_data[iterator] & 0xf)];
|
|
crc =
|
|
(crc >> 4) ^ crc16_table[(crc & 0xf) ^ (input_data[iterator] >> 4)];
|
|
iterator++;
|
|
}
|
|
|
|
return (~crc);
|
|
}
|
|
|
|
static uint8_t generate_ax25_frame(uint8_t *info_field, uint8_t info_field_size, uint8_t *buff){
|
|
uint8_t pos = 0;
|
|
uint8_t d_pos = 0;
|
|
|
|
memset(buff, 0, sizeof(250));
|
|
|
|
for(; pos<6; pos++){ // Destination adress
|
|
if(pos >= sizeof(APRS_D_CALL)-1){
|
|
buff[pos] = APRS_SPACE_SYMBOL<<1;
|
|
}else buff[pos] = APRS_D_CALL[pos]<<1;
|
|
}
|
|
buff[pos++] = (APRS_D_CALL_SSID<<1) | 0b11100000; // Destination adress SSID
|
|
|
|
d_pos = pos;
|
|
for(; pos-d_pos<6; pos++){ // Source adress
|
|
if(pos-d_pos >= sizeof(APRS_S_CALL)-1){
|
|
buff[pos] = APRS_SPACE_SYMBOL<<1;
|
|
}else buff[pos] = APRS_S_CALL[pos-d_pos]<<1;
|
|
}
|
|
buff[pos++] = (APRS_S_CALL_SSID<<1) | 0b11100000; // Source adress SSID
|
|
|
|
d_pos = pos;
|
|
for(; pos-d_pos<6; pos++){ // Path
|
|
if(pos-d_pos >= sizeof(APRS_PATH)-1){
|
|
buff[pos] = APRS_SPACE_SYMBOL<<1;
|
|
}else buff[pos] = APRS_PATH[pos-d_pos]<<1;
|
|
}
|
|
buff[pos++] = (APRS_PATH_SSID<<1) | 0b11100001; // Path SSID (1 at end as last adress)
|
|
|
|
buff[pos++] = APRS_CONTROL_FIELD; // Control field
|
|
buff[pos++] = APRS_PROTOCOL_ID; // Protocol ID
|
|
|
|
memcpy(buff+pos, info_field, info_field_size);
|
|
pos+=info_field_size;
|
|
|
|
uint16_t fcs = calculateFcs(buff, pos);
|
|
|
|
buff[pos++] = fcs & 0xff;
|
|
buff[pos++] = (fcs>>8) & 0xff;
|
|
|
|
return pos;
|
|
}
|
|
|
|
static uint8_t compress_pos(float lat, float lon, uint8_t *buff){
|
|
uint32_t lat_base10 = Round(380926 * (90-lat));
|
|
uint8_t cnt = 0;
|
|
|
|
buff[cnt++] = (lat_base10 / 753571) + 33;
|
|
buff[cnt++] = ((lat_base10 % 753571) / 8281) + 33;
|
|
buff[cnt++] = ((lat_base10 % 753571) % 8281) / 91 + 33;
|
|
buff[cnt++] = ((lat_base10 % 753571) % 8281) % 91 + 33;
|
|
|
|
|
|
uint32_t lon_base10 = Round(190463 * (180+lat));
|
|
buff[cnt++] = (lon_base10 / 753571) + 33;
|
|
buff[cnt++] = ((lon_base10 % 753571) / 8281) + 33;
|
|
buff[cnt++] = ((lon_base10 % 753571) % 8281) / 91 + 33;
|
|
buff[cnt++] = ((lon_base10 % 753571) % 8281) % 91 + 33;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
uint8_t encode_APRS_packet(APRSPacket Packet, uint8_t *buff){
|
|
uint8_t info_field[APRS_MAX_INFO_LEN];
|
|
uint8_t pos = 0;
|
|
|
|
info_field[pos++] = '@';
|
|
|
|
info_field[pos++] = Packet.Hours%10+'0';
|
|
info_field[pos++] = Packet.Hours/10+'0';
|
|
|
|
info_field[pos++] = Packet.Minutes%10+'0';
|
|
info_field[pos++] = Packet.Minutes/10+'0';
|
|
|
|
info_field[pos++] = Packet.Seconds%10+'0';
|
|
info_field[pos++] = Packet.Seconds/10+'0';
|
|
|
|
info_field[pos++] = 'z';
|
|
|
|
info_field[pos++] = APRS_SYMBOL[0];
|
|
|
|
pos += compress_pos(Packet.Lat, Packet.Lon, info_field+pos);
|
|
|
|
info_field[pos++] = APRS_SYMBOL[1];
|
|
|
|
info_field[pos++] = 33; // no course data
|
|
|
|
info_field[pos++] = Round((Log(Packet.Speed*0.5399565f+1)/Log(1.08f)))+33;
|
|
|
|
info_field[pos++] = 67;
|
|
|
|
info_field[pos++] = '/';
|
|
info_field[pos++] = 'A';
|
|
info_field[pos++] = '=';
|
|
uint32_t alt_ft = (uint32_t)(Packet.Alt*3.28084f);
|
|
uint32_t a = 1000000;
|
|
for(uint8_t i = 0; i<6; i++){
|
|
info_field[pos++] = (alt_ft%(a*10))/a;
|
|
a/=10;
|
|
}
|
|
|
|
memcpy(info_field+pos, "test", 4);
|
|
pos+=4;
|
|
|
|
return generate_ax25_frame(info_field, pos, buff);
|
|
} |