Overview
Trip Mode transforms FT8AF into a flagship companion for mobile amateur radio operators traveling by car, RV, train, or other vehicle. When active, Trip Mode continuously records the operator's GPS route, associates every QSO with its geographic coordinates, tracks vehicle speed and heading, detects U.S. state transitions, and surfaces real-time statistics throughout the journey. Completed trips are persisted and can be replayed on an interactive map.
This is intended as a production-quality, architecturally sound feature — not a prototype — built to be extensible for future capabilities such as RTOTA, county/interstate tracking, APRS integration, GPX/KML export, live sharing, and cloud sync.
Objectives
- Record GPS location continuously throughout a trip
- Associate every QSO logged during the trip with its GPS coordinates
- Record vehicle speed and heading (when available from the GPS provider)
- Detect and record U.S. state transitions
- Record trip start/end timestamps
- Calculate total miles traveled
- Track distance between the vehicle and each contacted station
- Calculate real-time statistics:
- QSOs per hour
- Miles per QSO
- Longest QSO distance
- Average QSO distance
- Display all statistics live while the trip is in progress
User Experience
Entry Point
Add a Trip Mode entry in the main navigation drawer/menu.
Trip Start Flow
- On first launch of Trip Mode, request
ACCESS_FINE_LOCATION (and ACCESS_BACKGROUND_LOCATION on Android 10+) if not already granted.
- On permission grant, start a background trip session via a foreground
Service with a persistent notification.
- Display the live Trip Mode UI.
Trip Mode UI — Live Dashboard
| Field |
Description |
| Trip Duration |
Elapsed time since trip start (HH:MM:SS) |
| GPS Coordinates |
Current latitude/longitude |
| Speed |
Current speed (MPH or KPH, user preference) |
| Heading |
Compass bearing with cardinal direction |
| Total Miles |
Odometer for the trip |
| Total QSOs |
Count of contacts logged during trip |
| Average QSO Distance |
Mean great-circle distance to all worked stations |
| Miles per QSO |
Total miles ÷ total QSOs |
| QSOs/Hour |
Rolling rate |
| Current State |
Detected U.S. state (or N/A outside USA) |
| Last Worked Station |
Callsign of most recent QSO |
| Last Worked Distance |
Distance to most recent QSO |
The dashboard must survive screen rotation and handle Android lifecycle correctly. The underlying trip session must continue if the user navigates away from the Trip Mode screen.
Trip Replay Screen
A dedicated Trip Replay screen accessible from both the active trip and trip history.
Map Display
- Plot the full GPS route as a polyline
- Place a marker for every FT8 contact made during the trip
- Draw lines connecting the vehicle's position at time-of-QSO to the contacted station's grid square center
- Auto-zoom/pan to fit the entire trip on load
Contact Markers — Tap Detail
Tapping a contact marker opens a bottom sheet or dialog with:
- Callsign
- Grid square
- Time of QSO
- Distance (mi/km)
- Band
- Mode
- Sent / received signal reports
Map Abstraction
Define a MapProvider interface so alternative map backends (OSMDroid, Mapbox, Google Maps, etc.) can be swapped in without changing the replay screen. The initial implementation should use one open/free provider suitable for distribution on F-Droid as well as Google Play.
Trip History
Persist completed trips to local storage (Room database).
Trip List Screen
Displays all saved trips with:
- Date (and day of week)
- Duration
- Distance traveled
- Number of QSOs
- States visited (comma-separated or badge list)
- Average contact distance
Tapping any row reopens that trip in the Trip Replay screen.
Architecture
Follow the existing FT8AF layered architecture. Do not place business logic inside Activities or Fragments.
Proposed Package Structure
com.ft8af.trip
├── data
│ ├── db/ # Room entities, DAOs, TripDatabase
│ ├── model/ # TripSession, TripQso, TripPoint, TripSummary
│ └── repository/ # TripRepository
├── location
│ ├── LocationProvider.kt (interface)
│ ├── FusedLocationProvider.kt
│ └── GpsLocationProvider.kt (fallback)
├── service
│ └── TripRecordingService.kt # Foreground service
├── stats
│ ├── TripStatistics.kt
│ ├── QsoStatistics.kt
│ ├── LocationStatistics.kt
│ ├── DistanceCalculator.kt
│ ├── StateTracker.kt
│ └── TripRecorder.kt
├── map
│ ├── MapProvider.kt (interface)
│ ├── OsmMapProvider.kt
│ └── model/ # RouteOverlay, QsoMarker, ContactLine
├── ui
│ ├── dashboard/ # TripDashboardFragment + ViewModel
│ ├── replay/ # TripReplayFragment + ViewModel
│ └── history/ # TripHistoryFragment + ViewModel
└── di/ # Dependency injection bindings
Key Classes
| Class |
Responsibility |
TripRecordingService |
Foreground service; owns the GPS subscription and calls TripRecorder |
TripRecorder |
Coordinates recording: receives location updates and QSO events, delegates to stats and repository |
TripRepository |
Single source of truth; wraps Room DAOs and exposes Flow/LiveData |
DistanceCalculator |
Haversine great-circle calculations; pure, stateless, fully testable |
StateTracker |
Determines current U.S. state from a lat/lon; designed to be swappable (offline polygon lookup or reverse-geocode API) |
TripStatistics |
Aggregate trip-level stats; operates on List<TripPoint> and List<TripQso> |
QsoStatistics |
Per-QSO and cross-QSO distance/rate stats |
LocationStatistics |
Speed/heading smoothing, odometer accumulation |
MapProvider |
Interface abstracting all map SDK calls |
Principles
- Prefer constructor injection (Hilt or manual DI consistent with existing app)
- Avoid static state and singletons where possible
- All stats classes must be unit-testable without Android framework dependencies
StateTracker and DistanceCalculator should be pure Kotlin/Java
Data Model (Room)
TripSession id, startTime, endTime, totalDistanceMiles, qsoCount, statesVisited, notes
TripPoint id, tripId, timestamp, latitude, longitude, speedMph, headingDeg, accuracy
TripQso id, tripId, timestamp, callsign, grid, band, mode, sentReport, rcvdReport,
latitude, longitude, distanceMiles
Extension columns / tables can be added later for county, POTA reference, RTOTA segment, etc.
Performance & Battery
- Use
FusedLocationProviderClient with a LocationRequest tuned for driving speeds (e.g., 5-second interval, 10-meter displacement threshold)
- Batch Room inserts; do not write every GPS point individually on the UI thread
- Downsample GPS points for map rendering when point count exceeds a configurable threshold (default: 5,000 points)
- Recover gracefully from process death: on service restart, reload the active trip from the database and continue appending
- All database operations must be performed off the main thread
Extension Points for Future Features
Design these interfaces/hooks now, even if unimplemented:
| Future Feature |
Extension Point |
| RTOTA |
TripSegment table + SegmentTracker interface |
| County tracking |
CountyTracker interface (similar to StateTracker) |
| Interstate tracking |
InterstateTracker interface |
| GPX / KML / GeoJSON export |
TripExporter interface with format-specific implementations |
| Live trip sharing |
TripBroadcaster interface |
| APRS integration |
AprsPositionReporter interface |
| POTA overlays |
PoiOverlayProvider interface in map abstraction |
| Replay animation |
TripAnimator in replay screen ViewModel |
| Cloud sync |
RemoteTripRepository implementing TripRepository |
| Public trip pages |
TripShareToken field on TripSession |
| Awards |
AwardEvaluator interface called post-trip |
Implementation Plan
Phase 1 — Foundation
Phase 2 — Recording Service
Phase 3 — Live Dashboard UI
Phase 4 — Map & Replay
Phase 5 — Trip History
Phase 6 — Polish & Tests
Acceptance Criteria
Notes
- Keep UI styling consistent with existing FT8AF screens (colors, typography, icon set)
- The
MapProvider abstraction should be forward-compatible with F-Droid distribution (no proprietary SDK hard-dependency)
- Prefer
Flow/LiveData from the repository layer consistent with existing patterns in FT8AF
- Document all new public classes with KDoc/Javadoc
Overview
Trip Mode transforms FT8AF into a flagship companion for mobile amateur radio operators traveling by car, RV, train, or other vehicle. When active, Trip Mode continuously records the operator's GPS route, associates every QSO with its geographic coordinates, tracks vehicle speed and heading, detects U.S. state transitions, and surfaces real-time statistics throughout the journey. Completed trips are persisted and can be replayed on an interactive map.
This is intended as a production-quality, architecturally sound feature — not a prototype — built to be extensible for future capabilities such as RTOTA, county/interstate tracking, APRS integration, GPX/KML export, live sharing, and cloud sync.
Objectives
User Experience
Entry Point
Add a Trip Mode entry in the main navigation drawer/menu.
Trip Start Flow
ACCESS_FINE_LOCATION(andACCESS_BACKGROUND_LOCATIONon Android 10+) if not already granted.Servicewith a persistent notification.Trip Mode UI — Live Dashboard
The dashboard must survive screen rotation and handle Android lifecycle correctly. The underlying trip session must continue if the user navigates away from the Trip Mode screen.
Trip Replay Screen
A dedicated Trip Replay screen accessible from both the active trip and trip history.
Map Display
Contact Markers — Tap Detail
Tapping a contact marker opens a bottom sheet or dialog with:
Map Abstraction
Define a
MapProviderinterface so alternative map backends (OSMDroid, Mapbox, Google Maps, etc.) can be swapped in without changing the replay screen. The initial implementation should use one open/free provider suitable for distribution on F-Droid as well as Google Play.Trip History
Persist completed trips to local storage (Room database).
Trip List Screen
Displays all saved trips with:
Tapping any row reopens that trip in the Trip Replay screen.
Architecture
Follow the existing FT8AF layered architecture. Do not place business logic inside Activities or Fragments.
Proposed Package Structure
Key Classes
TripRecordingServiceTripRecorderTripRecorderTripRepositoryFlow/LiveDataDistanceCalculatorStateTrackerTripStatisticsList<TripPoint>andList<TripQso>QsoStatisticsLocationStatisticsMapProviderPrinciples
StateTrackerandDistanceCalculatorshould be pure Kotlin/JavaData Model (Room)
Extension columns / tables can be added later for county, POTA reference, RTOTA segment, etc.
Performance & Battery
FusedLocationProviderClientwith aLocationRequesttuned for driving speeds (e.g., 5-second interval, 10-meter displacement threshold)Extension Points for Future Features
Design these interfaces/hooks now, even if unimplemented:
TripSegmenttable +SegmentTrackerinterfaceCountyTrackerinterface (similar toStateTracker)InterstateTrackerinterfaceTripExporterinterface with format-specific implementationsTripBroadcasterinterfaceAprsPositionReporterinterfacePoiOverlayProviderinterface in map abstractionTripAnimatorin replay screen ViewModelRemoteTripRepositoryimplementingTripRepositoryTripShareTokenfield onTripSessionAwardEvaluatorinterface called post-tripImplementation Plan
Phase 1 — Foundation
TripSession,TripPoint,TripQso)TripRepositoryDistanceCalculatorwith unit testsLocationStatistics(odometer, smoothing)StateTracker(offline or reverse-geocode)Phase 2 — Recording Service
TripRecordingService(foreground service, persistent notification)TripRecorder(wires GPS updates + QSO events → repository)TripRecorder(non-breaking)Phase 3 — Live Dashboard UI
TripDashboardFragment+TripDashboardViewModelPhase 4 — Map & Replay
MapProviderinterfaceOsmMapProviderTripReplayFragment+TripReplayViewModelPhase 5 — Trip History
TripHistoryFragment+TripHistoryViewModelPhase 6 — Polish & Tests
DistanceCalculatorandStateTrackerTripRepositoryAcceptance Criteria
Notes
MapProviderabstraction should be forward-compatible with F-Droid distribution (no proprietary SDK hard-dependency)Flow/LiveDatafrom the repository layer consistent with existing patterns in FT8AF