diff options
| author | Claudomator Agent <agent@claudomator> | 2026-03-15 00:58:54 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-03-25 04:53:52 +0000 |
| commit | c943c22954132b21f3067b526b3c13f3300113dd (patch) | |
| tree | 20357247605caa0043a2bcd60fdc4237f01f97c3 /android-app/app/src/test/kotlin/com | |
| parent | a9d87b600848178b03b85a06ccdfd53b11e38c38 (diff) | |
Add GpsPosition data class and NMEA RMC parser with tests
- GpsPosition: latitude, longitude, sog (knots), cog (degrees true), timestampMs
- NmeaParser.parseRmc: handles GP/GN talker IDs, void status, malformed input
- SOG/COG default to 0.0 when fields absent; S/W coords are negative
- 13 unit tests: GpsPositionTest (2), NmeaParserTest (11) — all GREEN
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'android-app/app/src/test/kotlin/com')
| -rw-r--r-- | android-app/app/src/test/kotlin/com/example/androidapp/gps/GpsPositionTest.kt | 33 | ||||
| -rw-r--r-- | android-app/app/src/test/kotlin/com/example/androidapp/nmea/NmeaParserTest.kt | 105 |
2 files changed, 138 insertions, 0 deletions
diff --git a/android-app/app/src/test/kotlin/com/example/androidapp/gps/GpsPositionTest.kt b/android-app/app/src/test/kotlin/com/example/androidapp/gps/GpsPositionTest.kt new file mode 100644 index 0000000..8b2753c --- /dev/null +++ b/android-app/app/src/test/kotlin/com/example/androidapp/gps/GpsPositionTest.kt @@ -0,0 +1,33 @@ +package com.example.androidapp.gps + +import org.junit.Assert.* +import org.junit.Test + +class GpsPositionTest { + + @Test + fun `GpsPosition holds correct values`() { + val pos = GpsPosition( + latitude = 41.5, + longitude = -71.0, + sog = 5.2, + cog = 180.0, + timestampMs = 1_000L + ) + assertEquals(41.5, pos.latitude, 0.0) + assertEquals(-71.0, pos.longitude, 0.0) + assertEquals(5.2, pos.sog, 0.0) + assertEquals(180.0, pos.cog, 0.0) + assertEquals(1_000L, pos.timestampMs) + } + + @Test + fun `GpsPosition equality works as expected for data class`() { + val pos1 = GpsPosition(41.5, -71.0, 5.2, 180.0, 1_000L) + val pos2 = GpsPosition(41.5, -71.0, 5.2, 180.0, 1_000L) + val pos3 = GpsPosition(42.0, -70.0, 3.0, 90.0, 2_000L) + + assertEquals(pos1, pos2) + assertNotEquals(pos1, pos3) + } +} diff --git a/android-app/app/src/test/kotlin/com/example/androidapp/nmea/NmeaParserTest.kt b/android-app/app/src/test/kotlin/com/example/androidapp/nmea/NmeaParserTest.kt new file mode 100644 index 0000000..b8a878a --- /dev/null +++ b/android-app/app/src/test/kotlin/com/example/androidapp/nmea/NmeaParserTest.kt @@ -0,0 +1,105 @@ +package com.example.androidapp.nmea + +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +class NmeaParserTest { + + private lateinit var parser: NmeaParser + + @Before + fun setUp() { + parser = NmeaParser() + } + + // $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A + // lat: 48 + 7.038/60 = 48.1173°N, lon: 11 + 31.000/60 = 11.51667°E + // SOG 22.4 kn, COG 84.4° + + @Test + fun `valid RMC sentence parses latitude and longitude`() { + val sentence = "\$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A" + val pos = parser.parseRmc(sentence) + assertNotNull(pos) + assertEquals(48.1173, pos!!.latitude, 0.0001) + assertEquals(11.51667, pos.longitude, 0.0001) + } + + @Test + fun `valid RMC sentence parses SOG and COG`() { + val sentence = "\$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A" + val pos = parser.parseRmc(sentence) + assertNotNull(pos) + assertEquals(22.4, pos!!.sog, 0.001) + assertEquals(84.4, pos.cog, 0.001) + } + + @Test + fun `void status V returns null`() { + val sentence = "\$GPRMC,123519,V,4807.038,N,01131.000,E,,,230394,003.1,W" + assertNull(parser.parseRmc(sentence)) + } + + @Test + fun `malformed sentence with too few fields returns null`() { + assertNull(parser.parseRmc("\$GPRMC,123519,A")) + } + + @Test + fun `empty string returns null`() { + assertNull(parser.parseRmc("")) + } + + @Test + fun `non-RMC sentence returns null`() { + val sentence = "\$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,," + assertNull(parser.parseRmc(sentence)) + } + + @Test + fun `south latitude is negative`() { + // lat: -(42 + 50.5589/60) = -42.84265 + val sentence = "\$GPRMC,092204.999,A,4250.5589,S,14718.5084,E,0.00,89.68,211200,," + val pos = parser.parseRmc(sentence) + assertNotNull(pos) + assertTrue("South latitude must be negative", pos!!.latitude < 0) + assertEquals(-42.84265, pos.latitude, 0.0001) + } + + @Test + fun `west longitude is negative`() { + // lon: -(11 + 31.000/60) = -11.51667 + val sentence = "\$GPRMC,123519,A,4807.038,N,01131.000,W,022.4,084.4,230394,003.1,E" + val pos = parser.parseRmc(sentence) + assertNotNull(pos) + assertTrue("West longitude must be negative", pos!!.longitude < 0) + assertEquals(-11.51667, pos.longitude, 0.0001) + } + + @Test + fun `SOG and COG parse with decimal precision`() { + val sentence = "\$GPRMC,093456,A,3352.1234,N,11801.5678,W,12.345,270.5,140326,," + val pos = parser.parseRmc(sentence) + assertNotNull(pos) + assertEquals(12.345, pos!!.sog, 0.0001) + assertEquals(270.5, pos.cog, 0.0001) + } + + @Test + fun `empty SOG and COG fields default to zero`() { + val sentence = "\$GPRMC,123519,A,4807.038,N,01131.000,E,,,230394,003.1,W" + val pos = parser.parseRmc(sentence) + assertNotNull(pos) + assertEquals(0.0, pos!!.sog, 0.001) + assertEquals(0.0, pos.cog, 0.001) + } + + @Test + fun `GNRMC talker ID is also accepted`() { + val sentence = "\$GNRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W" + val pos = parser.parseRmc(sentence) + assertNotNull(pos) + assertEquals(48.1173, pos!!.latitude, 0.0001) + } +} |
