diff --git a/common_settings.ini b/common_settings.ini
index 18670bb..34ceac2 100644
--- a/common_settings.ini
+++ b/common_settings.ini
@@ -37,7 +37,7 @@ lib_deps =
mathieucarbou/AsyncTCP @ 3.2.5
mathieucarbou/ESPAsyncWebServer @ 3.2.3
mikalhart/TinyGPSPlus @ 1.0.3
- richonguzman/APRSPacketLib @1.0.0
+ richonguzman/APRSPacketLib @1.0.4
display_libs =
adafruit/Adafruit GFX Library @ 1.11.9
adafruit/Adafruit SSD1306 @ 2.5.10
diff --git a/data/igate_conf.json b/data/igate_conf.json
index 231658d..6148480 100644
--- a/data/igate_conf.json
+++ b/data/igate_conf.json
@@ -21,7 +21,7 @@
"statusActive": false,
"statusPacket": "",
"gpsActive": false,
- "gpsAmbiguity": false
+ "ambiguityLevel": 0
},
"aprs_is": {
"active": false,
diff --git a/data_embed/index.html b/data_embed/index.html
index e3231e0..0690b51 100644
--- a/data_embed/index.html
+++ b/data_embed/index.html
@@ -189,18 +189,10 @@
name="action.symbol"
id="action.symbol"
>
-
-
-
-
+
+
+
+
-
-
+
+
@@ -616,6 +604,23 @@
+
+
+
+
-
-
-
-
-
-
@@ -723,15 +713,9 @@
name="digi.mode"
id="digi.mode"
>
-
-
-
+
+
+
@@ -748,15 +732,9 @@
name="digi.ecoMode"
id="digi.ecoMode"
>
-
-
-
+
+
+
diff --git a/data_embed/script.js b/data_embed/script.js
index 7d9c16c..285d7a7 100644
--- a/data_embed/script.js
+++ b/data_embed/script.js
@@ -131,7 +131,7 @@ function loadSettings(settings) {
StatusPacket.disabled = !StatusCheckbox.checked;
document.getElementById("beacon.gpsActive").checked = settings.beacon.gpsActive;
- document.getElementById("beacon.gpsAmbiguity").checked = settings.beacon.gpsAmbiguity;
+ document.getElementById("beacon.ambiguityLevel").value = settings.beacon.ambiguityLevel;
// Black List
document.getElementById("blacklist").value = settings.blacklist;
diff --git a/include/configuration.h b/include/configuration.h
index 22d0bb3..609c7b1 100644
--- a/include/configuration.h
+++ b/include/configuration.h
@@ -51,7 +51,7 @@ public:
bool statusActive;
String statusPacket;
bool gpsActive;
- bool gpsAmbiguity;
+ int ambiguityLevel;
};
class APRS_IS {
diff --git a/src/LoRa_APRS_iGate.cpp b/src/LoRa_APRS_iGate.cpp
index db14e6d..bc685a4 100644
--- a/src/LoRa_APRS_iGate.cpp
+++ b/src/LoRa_APRS_iGate.cpp
@@ -67,8 +67,8 @@ ___________________________________________________________________*/
#endif
-String versionDate = "2025-11-29";
-String versionNumber = "3.1.4.1";
+String versionDate = "2025-11-30";
+String versionNumber = "3.1.5";
Configuration Config;
WiFiClient aprsIsClient;
WiFiClient mqttClient;
diff --git a/src/aprs_is_utils.cpp b/src/aprs_is_utils.cpp
index 1e79493..0505ea3 100644
--- a/src/aprs_is_utils.cpp
+++ b/src/aprs_is_utils.cpp
@@ -16,6 +16,7 @@
* along with LoRa APRS iGate. If not, see .
*/
+#include
#include
#include "configuration.h"
#include "aprs_is_utils.h"
@@ -236,12 +237,7 @@ namespace APRS_IS_Utils {
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType) {
String packet = aprsisPacket;
packet.trim();
- String outputPacket = Config.callsign;
- outputPacket += ">APLRG1";
- if (Config.beacon.path != "") {
- outputPacket += ",";
- outputPacket += Config.beacon.path;
- }
+ String outputPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
outputPacket += ":}";
outputPacket += packet.substring(0, packet.indexOf(",")); // Callsign>Tocall
outputPacket.concat(",TCPIP,");
diff --git a/src/configuration.cpp b/src/configuration.cpp
index aec718b..9f763af 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -78,7 +78,7 @@ bool Configuration::writeFile() {
data["beacon"]["statusPacket"] = beacon.statusPacket;
data["beacon"]["gpsActive"] = beacon.gpsActive;
- data["beacon"]["gpsAmbiguity"] = beacon.gpsAmbiguity;
+ data["beacon"]["ambiguityLevel"] = beacon.ambiguityLevel;
data["personalNote"] = personalNote;
@@ -232,7 +232,7 @@ bool Configuration::readFile() {
!data["beacon"].containsKey("statusActive") ||
!data["beacon"].containsKey("statusPacket") ||
!data["beacon"].containsKey("gpsActive") ||
- !data["beacon"].containsKey("gpsAmbiguity")) needsRewrite = true;
+ !data["beacon"].containsKey("ambiguityLevel")) needsRewrite = true;
beacon.latitude = data["beacon"]["latitude"] | 0.0;
beacon.longitude = data["beacon"]["longitude"] | 0.0;
beacon.comment = data["beacon"]["comment"] | "LoRa APRS";
@@ -246,7 +246,7 @@ bool Configuration::readFile() {
beacon.statusActive = data["beacon"]["statusActive"] | false;
beacon.statusPacket = data["beacon"]["statusPacket"] | "";
beacon.gpsActive = data["beacon"]["gpsActive"] | false;
- beacon.gpsAmbiguity = data["beacon"]["gpsAmbiguity"] | false;
+ beacon.ambiguityLevel = data["beacon"]["ambiguityLevel"] | 0;
if (!data.containsKey("personalNote")) needsRewrite = true;
personalNote = data["personalNote"] | "personal note here";
@@ -449,7 +449,7 @@ void Configuration::setDefaultValues() {
beacon.statusPacket = "";
beacon.gpsActive = false;
- beacon.gpsAmbiguity = false;
+ beacon.ambiguityLevel = 0;
personalNote = "";
diff --git a/src/gps_utils.cpp b/src/gps_utils.cpp
index 6d5d128..d40a804 100644
--- a/src/gps_utils.cpp
+++ b/src/gps_utils.cpp
@@ -16,6 +16,7 @@
* along with LoRa APRS iGate. If not, see .
*/
+#include
#include
#include
#include "configuration.h"
@@ -42,70 +43,6 @@ namespace GPS_Utils {
return iGateLoRaBeaconPacket;
}
- char *ax25_base91enc(char *s, uint8_t n, uint32_t v) {
- for(s += n, *s = '\0'; n; n--) {
- *(--s) = v % 91 + 33;
- v /= 91;
- }
- return(s);
- }
-
- float roundToTwoDecimals(float degrees) {
- return round(degrees * 100) / 100;
- }
-
- String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol) {
- String encodedData = overlay;
- uint32_t aprs_lat, aprs_lon;
-
- float processedLatitude = latitude;
- float processedLongitude = longitude;
- if (Config.beacon.gpsActive && Config.beacon.gpsAmbiguity) {
- processedLatitude = roundToTwoDecimals(latitude);
- processedLongitude = roundToTwoDecimals(longitude);
- }
-
- aprs_lat = 900000000 - processedLatitude * 10000000;
- aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615;
- aprs_lon = 900000000 + processedLongitude * 10000000 / 2;
- aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615;
-
- String Ns, Ew, helper;
- if(processedLatitude < 0) { Ns = "S"; } else { Ns = "N"; }
- if(processedLatitude < 0) { processedLatitude = -processedLatitude; }
-
- if(processedLongitude < 0) { Ew = "W"; } else { Ew = "E"; }
- if(processedLongitude < 0) { processedLongitude = -processedLongitude; }
-
- char helper_base91[] = {"0000\0"};
- int i;
- ax25_base91enc(helper_base91, 4, aprs_lat);
- for (i = 0; i < 4; i++) {
- encodedData += helper_base91[i];
- }
- ax25_base91enc(helper_base91, 4, aprs_lon);
- for (i = 0; i < 4; i++) {
- encodedData += helper_base91[i];
- }
- encodedData += symbol;
- encodedData += " ";
- encodedData += "\x47";
- return encodedData;
- }
-
- void generateBeaconFirstPart() {
- String beaconPacket = Config.callsign;
- beaconPacket += ">APLRG1";
- if (Config.beacon.path.indexOf("WIDE") == 0) {
- beaconPacket += ",";
- beaconPacket += Config.beacon.path;
- }
- iGateBeaconPacket = beaconPacket;
- iGateBeaconPacket += ",qAC:!";
- iGateLoRaBeaconPacket = beaconPacket;
- iGateLoRaBeaconPacket += ":!";
- }
-
void generateBeacons() {
if (Config.callsign.indexOf("NOCALL-10") != 0 && !Utils::checkValidCallsign(Config.callsign)) {
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
@@ -116,9 +53,17 @@ namespace GPS_Utils {
Config.digi.mode = 0;
Config.backupDigiMode = false;
}
- generateBeaconFirstPart();
- String encodedGPS = encodeGPS(Config.beacon.latitude, Config.beacon.longitude, Config.beacon.overlay, Config.beacon.symbol);
+ String beaconPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
+ String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(Config.beacon.latitude, Config.beacon.longitude, 0, 0, Config.beacon.symbol, true, 0, true, Config.beacon.ambiguityLevel);
+
+ iGateBeaconPacket = beaconPacket;
+ iGateBeaconPacket += ",qAC:!";
+ iGateBeaconPacket += Config.beacon.overlay;
iGateBeaconPacket += encodedGPS;
+
+ iGateLoRaBeaconPacket = beaconPacket;
+ iGateLoRaBeaconPacket += ":=";
+ iGateLoRaBeaconPacket += Config.beacon.overlay;
iGateLoRaBeaconPacket += encodedGPS;
}
@@ -126,47 +71,40 @@ namespace GPS_Utils {
return TinyGPSPlus::distanceBetween(Config.beacon.latitude,Config.beacon.longitude, latitude, longitude) / 1000.0;
}
+ String buildDistanceAndComment(float latitude, float longitude, const String& comment) {
+ distance = String(calculateDistanceTo(latitude, longitude),1);
+
+ String distanceAndComment = String(latitude,5);
+ distanceAndComment += "N / ";
+ distanceAndComment += String(longitude,5);
+ distanceAndComment += "E / ";
+ distanceAndComment += distance;
+ distanceAndComment += "km";
+
+ if (comment != "") {
+ distanceAndComment += " / ";
+ distanceAndComment += comment;
+ }
+ return distanceAndComment;
+
+ }
+
String decodeEncodedGPS(const String& packet) {
int indexOfExclamation = packet.indexOf(":!");
int indexOfEqual = packet.indexOf(":=");
const uint8_t OFFSET = 3; // Offset for encoded data in the packet
- String GPSPacket;
+ String infoGPS;
if (indexOfExclamation > 10) {
- GPSPacket = packet.substring(indexOfExclamation + OFFSET);
+ infoGPS = packet.substring(indexOfExclamation + OFFSET);
} else if (indexOfEqual > 10) {
- GPSPacket = packet.substring(indexOfEqual + OFFSET);
+ infoGPS = 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);
+ float decodedLatitude = APRSPacketLib::decodeBase91EncodedLatitude(infoGPS.substring(0,4));
+ float decodedLongitude = APRSPacketLib::decodeBase91EncodedLongitude(infoGPS.substring(4,8));
- 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);
-
- distance = String(calculateDistanceTo(decodedLatitude, decodedLongitude),1);
-
- String decodedGPS = String(decodedLatitude,5);
- decodedGPS += "N / ";
- decodedGPS += String(decodedLongitude,5);
- decodedGPS += "E / ";
- decodedGPS += distance;
- decodedGPS += "km";
-
- String comment = GPSPacket.substring(12);
- if (comment != "") {
- decodedGPS += " / ";
- decodedGPS += comment;
- }
- return decodedGPS;
+ return buildDistanceAndComment(decodedLatitude, decodedLongitude, infoGPS.substring(12));
}
String getReceivedGPS(const String& packet) {
@@ -195,21 +133,7 @@ namespace GPS_Utils {
convertedLongitude += Longitude.substring(Longitude.indexOf(".") + 1, Longitude.indexOf(".") + 3).toFloat() / (60*100);
if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere
- distance = String(calculateDistanceTo(convertedLatitude, convertedLongitude),1);
-
- String decodedGPS = String(convertedLatitude,5);
- decodedGPS += "N / ";
- decodedGPS += String(convertedLongitude,5);
- decodedGPS += "E / ";
- decodedGPS += distance;
- decodedGPS += "km";
-
- String comment = infoGPS.substring(19);
- if (comment != "") {
- decodedGPS += " / ";
- decodedGPS += comment;
- }
- return decodedGPS;
+ return buildDistanceAndComment(convertedLatitude, convertedLongitude, infoGPS.substring(19));
}
String getDistanceAndComment(const String& packet) {
diff --git a/src/utils.cpp b/src/utils.cpp
index 45dbae0..e952faa 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -16,6 +16,7 @@
* along with LoRa APRS iGate. If not, see .
*/
+#include
#include
#include
#include "telemetry_utils.h"
@@ -71,12 +72,8 @@ String secondaryBeaconPacket;
namespace Utils {
void processStatus() {
- String status = Config.callsign;
- status.concat(">APLRG1");
- if (Config.beacon.path.indexOf("WIDE") == 0) {
- status.concat(",");
- status.concat(Config.beacon.path);
- }
+ String status = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
+
if (WiFi.status() == WL_CONNECTED && Config.aprs_is.active && Config.beacon.sendViaAPRSIS) {
delay(1000);
status.concat(",qAC:>");
@@ -161,18 +158,27 @@ namespace Utils {
showActiveStations();
- beaconPacket = iGateBeaconPacket;
- secondaryBeaconPacket = iGateLoRaBeaconPacket;
#ifdef HAS_GPS
if (Config.beacon.gpsActive && Config.digi.ecoMode == 0) {
GPS_Utils::getData();
if (gps.location.isUpdated() && gps.location.lat() != 0.0 && gps.location.lng() != 0.0) {
- GPS_Utils::generateBeaconFirstPart();
- String encodedGPS = GPS_Utils::encodeGPS(gps.location.lat(), gps.location.lng(), Config.beacon.overlay, Config.beacon.symbol);
- beaconPacket = iGateBeaconPacket + encodedGPS;
- secondaryBeaconPacket = iGateLoRaBeaconPacket + encodedGPS;
+ String basePacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
+ String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), 0, 0, Config.beacon.symbol, true, 0, true, Config.beacon.ambiguityLevel);
+
+ beaconPacket = basePacket;
+ beaconPacket += ",qAC:!";
+ beaconPacket += Config.beacon.overlay;
+ beaconPacket += encodedGPS;
+
+ secondaryBeaconPacket = basePacket;
+ secondaryBeaconPacket += ":=";
+ secondaryBeaconPacket += Config.beacon.overlay;
+ secondaryBeaconPacket += encodedGPS;
}
}
+ #else
+ beaconPacket = iGateBeaconPacket;
+ secondaryBeaconPacket = iGateLoRaBeaconPacket;
#endif
if (Config.wxsensor.active) {
diff --git a/src/web_utils.cpp b/src/web_utils.cpp
index 72fcf25..f8bfc46 100644
--- a/src/web_utils.cpp
+++ b/src/web_utils.cpp
@@ -191,7 +191,7 @@ namespace WEB_Utils {
}
Config.beacon.gpsActive = request->hasParam("beacon.gpsActive", true);
- Config.beacon.gpsAmbiguity = request->hasParam("beacon.gpsAmbiguity", true);
+ Config.beacon.ambiguityLevel = getParamIntSafe("beacon.ambiguityLevel", Config.beacon.ambiguityLevel);
Config.personalNote = getParamStringSafe("personalNote", Config.personalNote);
diff --git a/src/wifi_utils.cpp b/src/wifi_utils.cpp
index 64c1d7d..a5b8889 100644
--- a/src/wifi_utils.cpp
+++ b/src/wifi_utils.cpp
@@ -60,7 +60,7 @@ namespace WIFI_Utils {
Serial.print(millis());
Serial.println("Reconnecting to WiFi...");
WiFi.disconnect();
- WIFI_Utils::startWiFi();//WiFi.reconnect();
+ WIFI_Utils::startWiFi();
previousWiFiMillis = millis();
if (Config.backupDigiMode) {