summaryrefslogtreecommitdiff
path: root/SESSION_STATE.md
blob: a0d19fb70c1585dfbfe6533e3ec80b453d26e966 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# SESSION_STATE.md

## Current Task Goal
AIS repository layer (AisRepository, AisHubSource, AisHubApiService) — COMPLETE (2026-03-15)

## Verified (2026-03-15)
- All 38 tests GREEN via test-runner (BUILD SUCCESSFUL): 22 GPS/NMEA + 16 AIS
- NmeaParser extended with MWV (wind), DBT (depth), HDG/HDM (heading) parsers
- Sensor data classes added: WindData, DepthData, HeadingData
- NmeaStreamManager added for TCP stream management

## Completed Items

### [APPROVED] GpsPosition data class
- File: `app/src/main/kotlin/org/terst/nav/gps/GpsPosition.kt`
- Package: `org.terst.nav.gps`
- Fields: latitude, longitude, sog (knots), cog (degrees true), timestampMs

### [APPROVED] GpsProvider / GpsListener interfaces
- File: `app/src/main/kotlin/org/terst/nav/gps/GpsProvider.kt`
- `GpsProvider`: start/stop, position property, addListener/removeListener
- `GpsListener`: onPositionUpdate(GpsPosition), onFixLost()

### [APPROVED] DeviceGpsProvider
- File: `app/src/main/kotlin/org/terst/nav/gps/DeviceGpsProvider.kt`
- Wraps `LocationManager` with `GPS_PROVIDER`
- Default interval: 1000ms (1 Hz); configurable via constructor
- SOG: Location.speed (m/s) × 1.94384 → knots
- COG: Location.bearing (degrees true, no conversion)
- Fix-lost timer: fires `onFixLost()` after 10s with no update
- Thread-safe listener list (synchronized)
- Android dependency: Context, LocationManager, Handler — device only

### [APPROVED] FakeGpsProvider + GpsProviderTest (9 tests — all GREEN)
- File: `app/src/test/kotlin/org/terst/nav/gps/GpsProviderTest.kt`
- No Android dependencies — pure JVM
- Tests:
  - `start sets started to true`
  - `stop sets started to false`
  - `listener receives position update`
  - `listener notified of fix lost`
  - `multiple listeners all receive position update`
  - `multiple listeners all notified of fix lost`
  - `removing listener stops notifications`
  - `position property reflects last simulated position`
  - `SOG conversion sanity check - 1 mps is approximately 1_94384 knots`

## Build Notes
- `app/build` and `app/.kotlin/sessions` are root-owned from a prior run.
  Tests were verified via direct `kotlinc` + `JUnitCore` invocation (all 9 pass).
  Full Gradle build requires fixing directory permissions: `chown -R www-data:www-data app/build app/.kotlin`
- Pre-existing XML layout error in `activity_main.xml:300` (unrelated to GPS work)

### [APPROVED] NmeaParser — RMC parser
- File: `app/src/main/kotlin/org/terst/nav/nmea/NmeaParser.kt`
- Parses any `*RMC` sentence (GP, GN, etc.)
- Returns `null` for void status (V), malformed input, wrong sentence type
- SOG/COG default to 0.0 when fields are empty
- Latitude: positive = North, negative = South
- Longitude: positive = East, negative = West
- Timestamp: HHMMSS + DDMMYY → Unix epoch millis UTC (YY < 70 → 2000+YY)
- No Android dependencies — pure JVM

### [APPROVED] GpsPositionTest + NmeaParserTest (22 tests — all GREEN)
- `app/src/test/kotlin/org/terst/nav/gps/GpsPositionTest.kt` (2 tests)
- `app/src/test/kotlin/org/terst/nav/nmea/NmeaParserTest.kt` (11 tests)
- `app/src/test/kotlin/org/terst/nav/gps/GpsProviderTest.kt` (9 tests, pre-existing)
- All verified via direct `kotlinc` (1.9.22) + `JUnitCore` invocation

### [APPROVED] AisVessel data class
- File: `app/src/main/kotlin/org/terst/nav/ais/AisVessel.kt`
- Package: `org.terst.nav.ais`
- Fields: mmsi, name, callsign, lat, lon, sog, cog, heading, vesselType, timestampMs
- Note: `com/example/androidapp` tree is root-owned; files placed under `org/terst/nav/` (actual project package)

### [APPROVED] CpaCalculator object
- File: `app/src/main/kotlin/org/terst/nav/ais/CpaCalculator.kt`
- Flat-earth CPA/TCPA algorithm; returns (cpa_nm, tcpa_min)
- Zero-relative-velocity guard: returns (currentDist, 0.0)

### [APPROVED] AisVdmParser class
- File: `app/src/main/kotlin/org/terst/nav/nmea/AisVdmParser.kt`
- Parses !AIVDM/!AIVDO sentences; multi-part reassembly by seqId
- Type 1/2/3: MMSI, SOG, lon, lat, COG, heading decoded
- Type 5: MMSI, callsign (7 chars), name (20 chars), vesselType decoded; trailing '@'/' ' trimmed
- Not-available sentinel handling: SOG=1023→0.0, COG=3600→0.0, lon=0x6791AC0→0.0, lat=0x3412140→0.0

### [APPROVED] AIS Tests (16 tests — all GREEN)
- `test-runner/src/test/kotlin/org/terst/nav/ais/AisVesselTest.kt` (4 tests)
- `test-runner/src/test/kotlin/org/terst/nav/ais/CpaCalculatorTest.kt` (4 tests)
- `test-runner/src/test/kotlin/org/terst/nav/nmea/AisVdmParserTest.kt` (8 tests)
- Verification harness: `/tmp/ais-test-runner/` (JUnit5, com.example.androidapp package)
- Production test files also in `android-app/app/src/test/kotlin/org/terst/nav/ais/`

### [APPROVED] AisRepository class
- File: `app/src/main/kotlin/org/terst/nav/ais/AisRepository.kt`
- Upserts position targets by MMSI; merges type-5 static data (name/callsign/vesselType)
- Pending static map: holds type-5 data until position arrives for same MMSI
- `evictStale(nowMs)`: removes vessels older than `staleTimeoutMs` (default 10 min)
- `AisDataSource` interface (with Flow<String>) defined in same file

### [APPROVED] AisHubApiService + AisHubVessel
- File: `app/src/main/kotlin/org/terst/nav/ais/AisHubApiService.kt`
- Note: placed in `ais/` directory (writable) with package `org.terst.nav.data.api`
- `AisHubVessel` data class with Moshi `@Json` and `@JsonClass(generateAdapter=true)` annotations
- `AisHubApiService` Retrofit interface: GET /0/down with lat/lon bounding box params

### [APPROVED] AisHubSource object
- File: `app/src/main/kotlin/org/terst/nav/ais/AisHubSource.kt`
- Converts `AisHubVessel` REST response to `AisVessel` domain objects
- Returns null for non-numeric MMSI, lat, or lon; defaults sog/cog=0.0, heading=511, vesselType=0

### [APPROVED] AIS Repository Tests (11 tests — all GREEN)
- `app/src/test/kotlin/org/terst/nav/ais/AisRepositoryTest.kt` (7 tests)
- `app/src/test/kotlin/org/terst/nav/ais/AisHubSourceTest.kt` (4 tests)
- Harness: `/tmp/ais-repo-test-runner/` (JUnit5, org.terst.nav package, stub annotations for offline build)

## Next 3 Specific Steps
1. **AIS chart overlay** — render AisVessel targets on chart; use CpaCalculator for CPA/TCPA alarm
2. **AIS TCP ingestion** — extend NmeaStreamManager to feed !AIVDM sentences to AisVdmParser via AisRepository
3. **AISHub polling** — wire AisHubApiService + AisHubSource into a periodic polling ViewModel/UseCase

## Scripts Added
- `test-runner/` — standalone Kotlin/JVM Gradle project; runs all 22 GPS/NMEA tests without Android SDK
  - Command: `cd /workspace/nav/test-runner && GRADLE_USER_HOME=/tmp/gradle-home ./gradlew test`

## Process Improvements
- Gradle builds blocked by Android SDK requirement; added `test-runner/` JVM-only subproject as reliable test runner
- All 22 tests verified GREEN via `test-runner/` JVM project (2026-03-14)