2023-06-12 05:21:52 +02:00
|
|
|
#include <TinyGPS++.h>
|
2023-07-30 23:12:50 +02:00
|
|
|
#include <WiFi.h>
|
2023-06-06 18:19:56 +02:00
|
|
|
#include "configuration.h"
|
2024-11-05 22:20:44 +01:00
|
|
|
#include "board_pinout.h"
|
2023-06-08 06:58:10 +02:00
|
|
|
#include "gps_utils.h"
|
2024-06-07 23:29:55 +02:00
|
|
|
#include "display.h"
|
|
|
|
|
#include "utils.h"
|
2023-06-06 18:19:56 +02:00
|
|
|
|
2024-10-14 22:04:28 +02:00
|
|
|
#ifdef GPS_BAUDRATE
|
|
|
|
|
#define GPS_BAUD GPS_BAUDRATE
|
|
|
|
|
#else
|
|
|
|
|
#define GPS_BAUD 9600
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
extern Configuration Config;
|
|
|
|
|
extern WiFiClient espClient;
|
|
|
|
|
extern HardwareSerial gpsSerial;
|
|
|
|
|
extern TinyGPSPlus gps;
|
|
|
|
|
String distance, iGateBeaconPacket, iGateLoRaBeaconPacket;
|
2023-06-06 18:19:56 +02:00
|
|
|
|
2024-02-25 16:00:44 +01:00
|
|
|
|
2023-06-06 18:19:56 +02:00
|
|
|
namespace GPS_Utils {
|
|
|
|
|
|
2024-05-14 15:23:22 +02:00
|
|
|
String getiGateLoRaBeaconPacket() {
|
|
|
|
|
return iGateLoRaBeaconPacket;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-09 17:08:23 +02:00
|
|
|
char *ax25_base91enc(char *s, uint8_t n, uint32_t v) {
|
|
|
|
|
for(s += n, *s = '\0'; n; n--) {
|
|
|
|
|
*(--s) = v % 91 + 33;
|
|
|
|
|
v /= 91;
|
2024-02-24 14:09:05 +01:00
|
|
|
}
|
2024-04-09 17:08:23 +02:00
|
|
|
return(s);
|
2024-02-24 14:09:05 +01:00
|
|
|
}
|
2023-06-06 18:19:56 +02:00
|
|
|
|
2024-10-14 22:04:28 +02:00
|
|
|
float roundToTwoDecimals(float degrees) {
|
|
|
|
|
return round(degrees * 100) / 100;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-15 23:47:29 +02:00
|
|
|
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol) {
|
2024-04-09 17:08:23 +02:00
|
|
|
String encodedData = overlay;
|
|
|
|
|
uint32_t aprs_lat, aprs_lon;
|
2024-10-14 22:04:28 +02:00
|
|
|
|
|
|
|
|
float processedLatitude = latitude;
|
|
|
|
|
float processedLongitude = longitude;
|
|
|
|
|
if (Config.beacon.gpsActive && Config.beacon.gpsAmbiguity) {
|
|
|
|
|
processedLatitude = roundToTwoDecimals(latitude);
|
|
|
|
|
processedLongitude = roundToTwoDecimals(longitude);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aprs_lat = 900000000 - processedLatitude * 10000000;
|
2024-04-09 17:08:23 +02:00
|
|
|
aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
|
2024-10-14 22:04:28 +02:00
|
|
|
aprs_lon = 900000000 + processedLongitude * 10000000 / 2;
|
2024-04-09 17:08:23 +02:00
|
|
|
aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615;
|
|
|
|
|
|
|
|
|
|
String Ns, Ew, helper;
|
2024-10-14 22:04:28 +02:00
|
|
|
if(processedLatitude < 0) { Ns = "S"; } else { Ns = "N"; }
|
|
|
|
|
if(processedLatitude < 0) { processedLatitude = -processedLatitude; }
|
2024-04-09 17:08:23 +02:00
|
|
|
|
2024-10-14 22:04:28 +02:00
|
|
|
if(processedLongitude < 0) { Ew = "W"; } else { Ew = "E"; }
|
|
|
|
|
if(processedLongitude < 0) { processedLongitude = -processedLongitude; }
|
2024-04-09 17:08:23 +02:00
|
|
|
|
|
|
|
|
char helper_base91[] = {"0000\0"};
|
|
|
|
|
int i;
|
|
|
|
|
ax25_base91enc(helper_base91, 4, aprs_lat);
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
|
encodedData += helper_base91[i];
|
2024-02-24 14:09:05 +01:00
|
|
|
}
|
2024-04-09 17:08:23 +02:00
|
|
|
ax25_base91enc(helper_base91, 4, aprs_lon);
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
|
encodedData += helper_base91[i];
|
2024-02-24 14:09:05 +01:00
|
|
|
}
|
2024-06-21 03:12:54 +02:00
|
|
|
encodedData += symbol;
|
2024-09-22 18:34:13 +02:00
|
|
|
encodedData += " ";
|
2024-06-21 03:12:54 +02:00
|
|
|
encodedData += "\x47";
|
2024-04-09 17:08:23 +02:00
|
|
|
return encodedData;
|
2023-09-21 07:08:10 +02:00
|
|
|
}
|
2024-02-24 14:09:05 +01:00
|
|
|
|
2024-10-14 22:04:28 +02:00
|
|
|
void generateBeaconFirstPart() {
|
2024-06-07 06:10:04 +02:00
|
|
|
String beaconPacket = Config.callsign;
|
|
|
|
|
beaconPacket += ">APLRG1";
|
2024-06-08 20:39:15 +02:00
|
|
|
if (Config.beacon.path.indexOf("WIDE") == 0) {
|
2024-05-30 22:27:07 +02:00
|
|
|
beaconPacket += ",";
|
|
|
|
|
beaconPacket += Config.beacon.path;
|
2024-05-14 05:30:15 +02:00
|
|
|
}
|
2024-06-08 20:39:15 +02:00
|
|
|
iGateBeaconPacket = beaconPacket;
|
|
|
|
|
iGateBeaconPacket += ",qAC:!";
|
|
|
|
|
iGateLoRaBeaconPacket = beaconPacket;
|
|
|
|
|
iGateLoRaBeaconPacket += ":!";
|
2024-10-14 22:04:28 +02:00
|
|
|
}
|
2024-06-08 20:39:15 +02:00
|
|
|
|
2024-10-14 22:04:28 +02:00
|
|
|
void generateBeacons() {
|
|
|
|
|
if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) {
|
|
|
|
|
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
|
|
|
|
|
Config.loramodule.txActive = false;
|
|
|
|
|
Config.aprs_is.messagesToRF = false;
|
|
|
|
|
Config.aprs_is.objectsToRF = false;
|
|
|
|
|
Config.beacon.sendViaRF = false;
|
|
|
|
|
Config.digi.mode = 0;
|
|
|
|
|
Config.backupDigiMode = false;
|
|
|
|
|
}
|
|
|
|
|
generateBeaconFirstPart();
|
2024-06-08 20:39:15 +02:00
|
|
|
String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);
|
|
|
|
|
iGateBeaconPacket += encodedGPS;
|
|
|
|
|
iGateLoRaBeaconPacket += encodedGPS;
|
2023-07-31 05:53:59 +02:00
|
|
|
}
|
2023-06-06 18:19:56 +02:00
|
|
|
|
2024-02-24 14:09:05 +01:00
|
|
|
double calculateDistanceTo(double latitude, double longitude) {
|
2024-03-07 17:46:38 +01:00
|
|
|
return TinyGPSPlus::distanceBetween(Config.beacon.latitude,Config.beacon.longitude, latitude, longitude) / 1000.0;
|
2024-01-12 03:22:58 +01:00
|
|
|
}
|
2023-06-12 05:21:52 +02:00
|
|
|
|
2024-05-15 23:47:29 +02:00
|
|
|
String decodeEncodedGPS(const String& packet) {
|
2025-03-18 22:50:08 +01:00
|
|
|
int indexOfExclamation = packet.indexOf(":!");
|
|
|
|
|
int indexOfEqual = packet.indexOf(":=");
|
|
|
|
|
|
|
|
|
|
const uint8_t OFFSET = 3; // Offset for encoded data in the packet
|
|
|
|
|
String GPSPacket;
|
|
|
|
|
if (indexOfExclamation > 10) {
|
|
|
|
|
GPSPacket = packet.substring(indexOfExclamation + OFFSET);
|
|
|
|
|
} else if (indexOfEqual > 10) {
|
|
|
|
|
GPSPacket = packet.substring(indexOfEqual + OFFSET);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String encodedLatitude = GPSPacket.substring(0,4);
|
|
|
|
|
int Y1 = encodedLatitude[0] - 33;
|
|
|
|
|
int Y2 = encodedLatitude[1] - 33;
|
|
|
|
|
int Y3 = encodedLatitude[2] - 33;
|
|
|
|
|
int Y4 = encodedLatitude[3] - 33;
|
|
|
|
|
float decodedLatitude = 90.0 - (((Y1 * pow(91,3)) + (Y2 * pow(91,2)) + (Y3 * 91) + Y4) / 380926.0);
|
|
|
|
|
|
|
|
|
|
String encodedLongitude = GPSPacket.substring(4,8);
|
|
|
|
|
int X1 = encodedLongitude[0] - 33;
|
|
|
|
|
int X2 = encodedLongitude[1] - 33;
|
|
|
|
|
int X3 = encodedLongitude[2] - 33;
|
|
|
|
|
int X4 = encodedLongitude[3] - 33;
|
|
|
|
|
float decodedLongitude = -180.0 + (((X1 * pow(91,3)) + (X2 * pow(91,2)) + (X3 * 91) + X4) / 190463.0);
|
2024-06-21 03:12:54 +02:00
|
|
|
|
2024-02-24 14:09:05 +01:00
|
|
|
distance = String(calculateDistanceTo(decodedLatitude, decodedLongitude),1);
|
2024-06-21 03:12:54 +02:00
|
|
|
|
|
|
|
|
String decodedGPS = String(decodedLatitude,5);
|
|
|
|
|
decodedGPS += "N / ";
|
|
|
|
|
decodedGPS += String(decodedLongitude,5);
|
|
|
|
|
decodedGPS += "E / ";
|
|
|
|
|
decodedGPS += distance;
|
|
|
|
|
decodedGPS += "km";
|
|
|
|
|
|
2025-03-18 22:50:08 +01:00
|
|
|
String comment = GPSPacket.substring(12);
|
2024-05-20 05:34:29 +02:00
|
|
|
if (comment != "") {
|
2024-06-21 03:12:54 +02:00
|
|
|
decodedGPS += " / ";
|
|
|
|
|
decodedGPS += comment;
|
2024-05-20 05:34:29 +02:00
|
|
|
}
|
2024-06-21 03:12:54 +02:00
|
|
|
return decodedGPS;
|
2024-01-03 02:12:10 +01:00
|
|
|
}
|
2023-06-12 05:21:52 +02:00
|
|
|
|
2024-05-15 23:47:29 +02:00
|
|
|
String getReceivedGPS(const String& packet) {
|
2025-03-18 22:50:08 +01:00
|
|
|
int indexOfExclamation = packet.indexOf(":!");
|
|
|
|
|
int indexOfEqual = packet.indexOf(":=");
|
|
|
|
|
int indexOfAt = packet.indexOf(":@");
|
|
|
|
|
|
2024-02-24 14:09:05 +01:00
|
|
|
String infoGPS;
|
2025-03-18 22:50:08 +01:00
|
|
|
if (indexOfExclamation > 10) {
|
|
|
|
|
infoGPS = packet.substring(indexOfExclamation + 2);
|
|
|
|
|
} else if (indexOfEqual > 10) {
|
|
|
|
|
infoGPS = packet.substring(indexOfEqual + 2);
|
|
|
|
|
} else if (indexOfAt > 10) {
|
|
|
|
|
infoGPS = packet.substring(indexOfAt + 9); // 9 = 2+7 (when 7 is timestamp characters)
|
2024-02-24 14:09:05 +01:00
|
|
|
}
|
2025-03-18 22:50:08 +01:00
|
|
|
|
|
|
|
|
String Latitude = infoGPS.substring(0,8); // First 8 characters are Latitude
|
|
|
|
|
float convertedLatitude = Latitude.substring(0,2).toFloat(); // First 2 digits (Degrees)
|
|
|
|
|
convertedLatitude += Latitude.substring(2,4).toFloat() / 60; // Next 2 digits (Minutes)
|
|
|
|
|
convertedLatitude += Latitude.substring(Latitude.indexOf(".") + 1, Latitude.indexOf(".") + 3).toFloat() / (60*100);
|
|
|
|
|
if (Latitude.endsWith("S")) convertedLatitude = -convertedLatitude; // Handle Southern Hemisphere
|
|
|
|
|
|
|
|
|
|
String Longitude = infoGPS.substring(9,18); // Next 9 characters are Longitude
|
|
|
|
|
float convertedLongitude = Longitude.substring(0,3).toFloat(); // First 3 digits (Degrees)
|
|
|
|
|
convertedLongitude += Longitude.substring(3,5).toFloat() / 60; // Next 2 digits (Minutes)
|
|
|
|
|
convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3).toFloat() / (60*100);
|
|
|
|
|
if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere
|
2024-06-21 03:12:54 +02:00
|
|
|
|
2024-02-24 14:09:05 +01:00
|
|
|
distance = String(calculateDistanceTo(convertedLatitude, convertedLongitude),1);
|
2024-06-21 03:12:54 +02:00
|
|
|
|
|
|
|
|
String decodedGPS = String(convertedLatitude,5);
|
|
|
|
|
decodedGPS += "N / ";
|
|
|
|
|
decodedGPS += String(convertedLongitude,5);
|
|
|
|
|
decodedGPS += "E / ";
|
|
|
|
|
decodedGPS += distance;
|
|
|
|
|
decodedGPS += "km";
|
|
|
|
|
|
2025-03-18 22:50:08 +01:00
|
|
|
String comment = infoGPS.substring(19);
|
2024-05-20 05:34:29 +02:00
|
|
|
if (comment != "") {
|
2024-06-21 03:12:54 +02:00
|
|
|
decodedGPS += " / ";
|
|
|
|
|
decodedGPS += comment;
|
2024-05-20 05:34:29 +02:00
|
|
|
}
|
2024-06-21 03:12:54 +02:00
|
|
|
return decodedGPS;
|
|
|
|
|
}
|
2024-02-24 14:09:05 +01:00
|
|
|
|
2024-05-20 05:34:29 +02:00
|
|
|
String getDistanceAndComment(const String& packet) {
|
2025-03-18 22:50:08 +01:00
|
|
|
int indexOfAt = packet.indexOf(":@");
|
|
|
|
|
if (indexOfAt > 10) {
|
|
|
|
|
return getReceivedGPS(packet);
|
|
|
|
|
} else {
|
|
|
|
|
const uint8_t ENCODED_BYTE_OFFSET = 14; // Offset for encoded data in the packet
|
|
|
|
|
int indexOfExclamation = packet.indexOf(":!");
|
|
|
|
|
int indexOfEqual = packet.indexOf(":=");
|
|
|
|
|
uint8_t encodedBytePosition = 0;
|
|
|
|
|
if (indexOfExclamation > 10) { // Determine the position where encoded data starts
|
|
|
|
|
encodedBytePosition = indexOfExclamation + ENCODED_BYTE_OFFSET;
|
|
|
|
|
} else if (indexOfEqual > 10) {
|
|
|
|
|
encodedBytePosition = indexOfEqual + ENCODED_BYTE_OFFSET;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (encodedBytePosition != 0) {
|
|
|
|
|
char currentChar = packet[encodedBytePosition];
|
2025-03-18 23:10:15 +01:00
|
|
|
if (currentChar == 'G' || currentChar == 'Q' || currentChar == '[' || currentChar == 'H' || currentChar == 'X') {
|
2025-03-18 22:50:08 +01:00
|
|
|
return decodeEncodedGPS(packet); // If valid encoded data position is found, decode it
|
|
|
|
|
} else {
|
|
|
|
|
return getReceivedGPS(packet);
|
|
|
|
|
}
|
2024-02-24 14:09:05 +01:00
|
|
|
} else {
|
2025-03-18 22:50:08 +01:00
|
|
|
return " _ / _ / _ ";
|
2024-02-24 14:09:05 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-08-15 17:04:54 +02:00
|
|
|
}
|
2023-06-12 05:21:52 +02:00
|
|
|
|
2024-10-14 22:04:28 +02:00
|
|
|
void setup() {
|
|
|
|
|
#ifdef HAS_GPS
|
|
|
|
|
if (Config.beacon.gpsActive) {
|
|
|
|
|
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_TX, GPS_RX);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
generateBeacons();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void getData() {
|
|
|
|
|
while (gpsSerial.available() > 0) {
|
|
|
|
|
gps.encode(gpsSerial.read());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-06 18:19:56 +02:00
|
|
|
}
|