Created
September 3, 2025 21:47
-
-
Save sjmurdoch/72051bd256022ece3f3898d21a96abb6 to your computer and use it in GitHub Desktop.
Adding ambiguity to APRS message
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/include/APRSPacketLib.h b/include/APRSPacketLib.h | |
| index aed7772..9171477 100644 | |
| --- a/include/APRSPacketLib.h | |
| +++ b/include/APRSPacketLib.h | |
| @@ -45,10 +45,10 @@ namespace APRSPacketLib { | |
| String generateDigipeatedPacket(const String& packet, const String &callsign, const String& path); | |
| - String encodeGPSIntoBase91(float latitude, float longitude, float course, float speed, const String& symbol, bool sendAltitude = false, int altitude = 0, bool sendStandingUpdate = false); | |
| + String encodeGPSIntoBase91(float latitude, float longitude, float course, float speed, const String& symbol, bool sendAltitude = false, int altitude = 0, bool sendStandingUpdate = false, int ambiguity_level = 2); | |
| String generateBase91GPSBeaconPacket(const String& callsign, const String& tocall, const String& path, const String& overlay, const String& gpsData); | |
| - String generateMiceGPSBeaconPacket(const String& miceMsgType, const String& callsign, const String& symbol, const String& overlay, const String& path, float latitude, float longitude, float course, float speed, int altitude); | |
| + String generateMiceGPSBeaconPacket(const String& miceMsgType, const String& callsign, const String& symbol, const String& overlay, const String& path, float latitude, float longitude, float course, float speed, int altitude, int ambiguity_level = 2); | |
| APRSPacket processReceivedPacket(const String& receivedPacket, int rssi, float snr, int freqError); | |
| diff --git a/src/APRSPacketLib.cpp b/src/APRSPacketLib.cpp | |
| index 48daa1c..3ff008b 100644 | |
| --- a/src/APRSPacketLib.cpp | |
| +++ b/src/APRSPacketLib.cpp | |
| @@ -28,9 +28,46 @@ _______________________________________________*/ | |
| #include <APRSPacketLib.h> | |
| +#include <cmath> | |
| namespace APRSPacketLib { | |
| + /** | |
| + * @brief Applies ambiguity to a GPS coordinate by reducing its precision. | |
| + * | |
| + * This function implements the standard APRS position ambiguity by truncating | |
| + * the decimal part of the coordinate's minutes. The new, less-precise | |
| + * coordinate is then used for encoding. | |
| + * | |
| + * @param coordinate The original coordinate in decimal degrees. | |
| + * @param ambiguity_level An integer from 0 to 4. | |
| + * - 0: No ambiguity. | |
| + * - 1: Reduces precision to 0.1 minutes (e.g., 4903.5_N). | |
| + * - 2: Reduces precision to 1 minute (e.g., 4903.__N). | |
| + * - 3: Reduces precision to 10 minutes (e.g., 490_.__N). | |
| + * - 4: Reduces precision to 1 degree (e.g., 49__.__N). | |
| + * @return The coordinate with reduced precision. | |
| + */ | |
| + float apply_ambiguity(float coordinate, int ambiguity_level) { | |
| + if (ambiguity_level <= 0) { | |
| + return coordinate; | |
| + } | |
| + | |
| + float deg = floorf(fabsf(coordinate)); | |
| + float dec_minutes = (fabsf(coordinate) - deg) * 60.0f; | |
| + | |
| + switch (ambiguity_level) { | |
| + case 1: dec_minutes = floorf(dec_minutes * 10.0f) / 10.0f; break; | |
| + case 2: dec_minutes = floorf(dec_minutes); break; | |
| + case 3: dec_minutes = floorf(dec_minutes / 10.0f) * 10.0f; break; | |
| + default: dec_minutes = 0.0f; break; // Level 4 and higher | |
| + } | |
| + | |
| + float new_coordinate = deg + dec_minutes / 60.0f; | |
| + | |
| + return copysignf(new_coordinate, coordinate); | |
| + } | |
| + | |
| String generateBasePacket(const String& callsign, const String& tocall, const String& path) { | |
| String packet = callsign; | |
| packet += ">"; | |
| @@ -91,7 +128,14 @@ namespace APRSPacketLib { | |
| return(s); | |
| } | |
| - String encodeGPSIntoBase91(float latitude, float longitude, float course, float speed, const String& symbol, bool sendAltitude, int altitude, bool sendStandingUpdate) { | |
| + String encodeGPSIntoBase91(float latitude, float longitude, float course, float speed, const String& symbol, bool sendAltitude, int altitude, bool sendStandingUpdate, int ambiguity_level) { | |
| + // Apply ambiguity to the coordinates if requested. | |
| + // This truncates the precision before encoding. | |
| + if (ambiguity_level > 0) { | |
| + latitude = apply_ambiguity(latitude, ambiguity_level); | |
| + longitude = apply_ambiguity(longitude, ambiguity_level); | |
| + } | |
| + | |
| String encodedData; | |
| uint32_t aprs_lat, aprs_lon; | |
| aprs_lat = 900000000 - latitude * 10000000; | |
| @@ -386,7 +430,7 @@ namespace APRSPacketLib { | |
| buf[2] = h28; | |
| } | |
| - void encodeMiceDestinationField(const String& msgType, uint8_t *buf, const gpsLatitudeStruct *lat, const gpsLongitudeStruct *lon) { | |
| + void encodeMiceDestinationField(const String& msgType, uint8_t *buf, const gpsLatitudeStruct *lat, const gpsLongitudeStruct *lon, int ambiguity_level) { | |
| uint32_t temp; | |
| temp = lat->degrees / 10; // degrees | |
| buf[0] = (temp + 0x30); | |
| @@ -402,6 +446,18 @@ namespace APRSPacketLib { | |
| temp = lat->minuteHundredths/10; // minute hundredths | |
| buf[4] = (temp + 0x30) + ((lon->degrees >= 100 || lon->degrees <= 9) ? 0x20 : 0); // Longitude Offset | |
| buf[5] = (lat->minuteHundredths - temp * 10 + 0x30) + (!lon->east ? 0x20 : 0); // West validation | |
| + | |
| + // Per APRS spec, ambiguity in Mic-E is shown by replacing characters with spaces. | |
| + // This is done *after* encoding the (already truncated) coordinate value. | |
| + if (ambiguity_level >= 1) { | |
| + buf[5] = ' '; // Obscure 1/100th of a minute | |
| + } | |
| + if (ambiguity_level >= 2) { | |
| + buf[4] = ' '; // Obscure 1/10th of a minute | |
| + } | |
| + if (ambiguity_level >= 3) { | |
| + buf[3] = ' '; // Obscure 1 minute | |
| + } | |
| } | |
| String doubleToString(double n, int ndec) { | |
| @@ -494,12 +550,18 @@ namespace APRSPacketLib { | |
| return miceLongitudeStruct; | |
| } | |
| - String generateMiceGPSBeaconPacket(const String& miceMsgType, const String& callsign, const String& symbol, const String& overlay, const String& path, float latitude, float longitude, float course, float speed, int altitude) { | |
| + String generateMiceGPSBeaconPacket(const String& miceMsgType, const String& callsign, const String& symbol, const String& overlay, const String& path, float latitude, float longitude, float course, float speed, int altitude, int ambiguity_level) { | |
| + // Apply ambiguity to the coordinates if requested. This truncates the precision. | |
| + if (ambiguity_level > 0) { | |
| + latitude = apply_ambiguity(latitude, ambiguity_level); | |
| + longitude = apply_ambiguity(longitude, ambiguity_level); | |
| + } | |
| + | |
| gpsLatitudeStruct latitudeStruct = gpsDecimalToDegreesMiceLatitude(latitude); | |
| gpsLongitudeStruct longitudeStruct = gpsDecimalToDegreesMiceLongitude(longitude); | |
| uint8_t miceDestinationArray[7]; | |
| - encodeMiceDestinationField(miceMsgType, &miceDestinationArray[0], &latitudeStruct, &longitudeStruct); | |
| + encodeMiceDestinationField(miceMsgType, &miceDestinationArray[0], &latitudeStruct, &longitudeStruct, ambiguity_level); | |
| miceDestinationArray[6] = 0x00; // por repetidor? | |
| String miceDestination = (char*)miceDestinationArray; | |
| diff --git a/src/keyboard_utils.cpp b/src/keyboard_utils.cpp | |
| index bd7b22c..a0ed6ad 100644 | |
| --- a/src/keyboard_utils.cpp | |
| +++ b/src/keyboard_utils.cpp | |
| @@ -746,7 +746,7 @@ namespace KEYBOARD_Utils { | |
| } else if (key == 13 && messageText.length() > 0) { | |
| messageText.trim(); | |
| if (messageText.length() > 67) messageText = messageText.substring(0, 67); | |
| - String packet = APRSPacketLib::generateBase91GPSBeaconPacket(currentBeacon->callsign, "APLRT1", Config.path, currentBeacon->overlay, APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), gps.course.deg(), gps.speed.knots(), currentBeacon->symbol, Config.sendAltitude, gps.altitude.feet(), sendStandingUpdate)); | |
| + String packet = APRSPacketLib::generateBase91GPSBeaconPacket(currentBeacon->callsign, "APLRT1", Config.path, currentBeacon->overlay, APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), gps.course.deg(), gps.speed.knots(), currentBeacon->symbol, Config.sendAltitude, gps.altitude.feet(), sendStandingUpdate, 2)); | |
| packet += messageText; | |
| displayShow("<<< TX >>>", "", packet,100); | |
| LoRa_Utils::sendNewPacket(packet); | |
| diff --git a/src/station_utils.cpp b/src/station_utils.cpp | |
| index 07f7b87..10eb694 100644 | |
| --- a/src/station_utils.cpp | |
| +++ b/src/station_utils.cpp | |
| @@ -198,9 +198,9 @@ namespace STATION_Utils { | |
| if (gps.speed.kmph() > 200 || gps.altitude.meters() > 9000) path = ""; // avoid plane speed and altitude | |
| String packet; | |
| if (miceActive) { | |
| - packet = APRSPacketLib::generateMiceGPSBeaconPacket(currentBeacon->micE, currentBeacon->callsign, currentBeacon->symbol, currentBeacon->overlay, path, gps.location.lat(), gps.location.lng(), gps.course.deg(), gps.speed.knots(), gps.altitude.meters()); | |
| + packet = APRSPacketLib::generateMiceGPSBeaconPacket(currentBeacon->micE, currentBeacon->callsign, currentBeacon->symbol, currentBeacon->overlay, path, gps.location.lat(), gps.location.lng(), gps.course.deg(), gps.speed.knots(), gps.altitude.meters(), 2); | |
| } else { | |
| - packet = APRSPacketLib::generateBase91GPSBeaconPacket(currentBeacon->callsign, "APLRT1", path, currentBeacon->overlay, APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), gps.course.deg(), gps.speed.knots(), currentBeacon->symbol, Config.sendAltitude, gps.altitude.feet(), sendStandingUpdate)); | |
| + packet = APRSPacketLib::generateBase91GPSBeaconPacket(currentBeacon->callsign, "APLRT1", path, currentBeacon->overlay, APRSPacketLib::encodeGPSIntoBase91(gps.location.lat(),gps.location.lng(), gps.course.deg(), gps.speed.knots(), currentBeacon->symbol, Config.sendAltitude, gps.altitude.feet(), sendStandingUpdate, 2)); | |
| } | |
| String batteryVoltage = BATTERY_Utils::getBatteryInfoVoltage(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment