diff --git a/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/aprs/AprsPacket.kt b/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/aprs/AprsPacket.kt index 37e84237..3b1d4c0f 100644 --- a/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/aprs/AprsPacket.kt +++ b/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/aprs/AprsPacket.kt @@ -39,17 +39,28 @@ object AprsPacket { return String.format(Locale.ROOT, "r/%.3f/%.3f/%d", latitude, longitude, distKm) } - /** Altitude extension /A=00000 (feet) */ + /** + * Altitude extension /A=000000 (feet). The field is a fixed six-digit + * decimal, so a negative altitude (below sea level, or a bad GPS fix) must + * be clamped: "%06d" of -164 yields "/A=-00164", which is not a valid + * extension and corrupts the rest of the comment field. + */ fun formatAltitude(altitudeMeters: Double?): String { if (altitudeMeters == null) return "" - return String.format(Locale.ROOT, "/A=%06d", (altitudeMeters * 3.2808399).toInt()) + val feet = (altitudeMeters * 3.2808399).toInt().coerceIn(0, 999999) + return String.format(Locale.ROOT, "/A=%06d", feet) } - /** Speed/course extension (knots/degrees) */ + /** + * Speed/course extension /CCC/SSS (degrees/knots). Course wraps into + * 0..359 and speed is clamped to three digits, because "%03d" of an + * out-of-range value widens the field and breaks the fixed-width format. + */ fun formatCourseSpeed(speedMps: Double?, bearing: Float?): String { if (speedMps == null || bearing == null) return "" - val knots = (speedMps * 1.94384449).toInt() - return String.format(Locale.ROOT, "/%03d/%03d", bearing.toInt(), knots) + val knots = (speedMps * 1.94384449).toInt().coerceIn(0, 999) + val course = ((bearing.toInt() % 360) + 360) % 360 + return String.format(Locale.ROOT, "/%03d/%03d", course, knots) } } diff --git a/core/domain/src/test/java/com/rtbishop/look4sat/core/domain/aprs/AprsPacketLocaleTest.kt b/core/domain/src/test/java/com/rtbishop/look4sat/core/domain/aprs/AprsPacketLocaleTest.kt index 6ed412f4..c1843575 100644 --- a/core/domain/src/test/java/com/rtbishop/look4sat/core/domain/aprs/AprsPacketLocaleTest.kt +++ b/core/domain/src/test/java/com/rtbishop/look4sat/core/domain/aprs/AprsPacketLocaleTest.kt @@ -62,6 +62,22 @@ class AprsPacketLocaleTest { assertEquals("r/39.904/116.407/100", filter) } + @Test + fun altitude_clampsNegativeToKeepSixDigitField() { + // "%06d" of a negative value yields "/A=-00164": the '-' takes a digit + // slot, so the extension is no longer a valid fixed-width field. + assertEquals("/A=000000", AprsPacket.formatAltitude(-50.0)) + assertEquals("/A=000000", AprsPacket.formatAltitude(-1.0)) + assertEquals("/A=000328", AprsPacket.formatAltitude(100.0)) + } + + @Test + fun courseSpeed_wrapsCourseIntoValidRange() { + assertEquals("/000/019", AprsPacket.formatCourseSpeed(10.0, 360f)) + assertEquals("/359/019", AprsPacket.formatCourseSpeed(10.0, -1f)) + assertEquals("/090/019", AprsPacket.formatCourseSpeed(10.0, 90f)) + } + @Test fun ambiguousPosition_staysAsciiUnderArabicLocale() { Locale.setDefault(Locale.forLanguageTag("ar-EG"))