From 812c53bd0ab449a454529a78279f36fd5a911654 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 21 Oct 2026 20:58:23 +0200 Subject: [PATCH] fix: end the course with a timer stop_all event at the last record Garmin's EncodeCourse example ends a course with a timer event of type STOP_ALL; gexporter wrote STOP_DISABLE_ALL. The event now also carries the timestamp of the last written record instead of the last track point, which can differ when that point is dropped as too close to the previous record. --- .../java/org/surfsite/gexporter/Gpx2Fit.java | 7 ++++--- .../org/surfsite/gexporter/TestCourseFit.java | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/surfsite/gexporter/Gpx2Fit.java b/app/src/main/java/org/surfsite/gexporter/Gpx2Fit.java index fe18665..4d62019 100644 --- a/app/src/main/java/org/surfsite/gexporter/Gpx2Fit.java +++ b/app/src/main/java/org/surfsite/gexporter/Gpx2Fit.java @@ -786,6 +786,7 @@ public void writeFit(File outfile) { } + DateTime lastRecordTime = timestamp; for (WayPoint wpt : trkPoints) { boolean written = false; i += 1; @@ -822,6 +823,7 @@ public void writeFit(File outfile) { } encode.write(r); + lastRecordTime = timestamp; ldist = dist; ltimestamp = l; written = true; @@ -841,10 +843,9 @@ public void writeFit(File outfile) { eventMesg.setLocalNum(0); eventMesg.setEvent(Event.TIMER); - eventMesg.setEventType(EventType.STOP_DISABLE_ALL); + eventMesg.setEventType(EventType.STOP_ALL); eventMesg.setEventGroup((short) 0); - //timestamp.add(2); - eventMesg.setTimestamp(timestamp); + eventMesg.setTimestamp(lastRecordTime); encode.write(eventMesg); } diff --git a/app/src/test/java/org/surfsite/gexporter/TestCourseFit.java b/app/src/test/java/org/surfsite/gexporter/TestCourseFit.java index 5571e0f..17d9046 100644 --- a/app/src/test/java/org/surfsite/gexporter/TestCourseFit.java +++ b/app/src/test/java/org/surfsite/gexporter/TestCourseFit.java @@ -6,6 +6,9 @@ import com.garmin.fit.CoursePointMesg; import com.garmin.fit.Decode; +import com.garmin.fit.Event; +import com.garmin.fit.EventMesg; +import com.garmin.fit.EventType; import com.garmin.fit.MesgBroadcaster; import com.garmin.fit.RecordMesg; @@ -32,6 +35,7 @@ public class TestCourseFit { private final List records = new ArrayList<>(); private final List coursePoints = new ArrayList<>(); + private final List events = new ArrayList<>(); /** A straight track north, 0.01° (about 1.1 km) per point. */ private File gpx(String wpts, String timeFormat) throws Exception { @@ -64,6 +68,7 @@ private void convertAndDecode(File gpx) throws Exception { MesgBroadcaster broadcaster = new MesgBroadcaster(decode); broadcaster.addListener((com.garmin.fit.RecordMesgListener) records::add); broadcaster.addListener((com.garmin.fit.CoursePointMesgListener) coursePoints::add); + broadcaster.addListener((com.garmin.fit.EventMesgListener) events::add); try (InputStream in = new FileInputStream(fit)) { assertTrue("FIT integrity", decode.checkFileIntegrity(in)); } @@ -115,4 +120,16 @@ public void currentTimesAreKept() throws Exception { long expected = (1788602400000L - 631065600000L) / 1000L; assertEquals(expected, (long) records.get(0).getTimestamp().getTimestamp()); } + + @Test + public void courseEndsWithTimerStopAllAtTheLastRecord() throws Exception { + // as Garmin's EncodeCourse example + convertAndDecode(gpx("", "2026-09-05T10:%02d:00Z")); + + EventMesg stop = events.get(events.size() - 1); + assertEquals(Event.TIMER, stop.getEvent()); + assertEquals(EventType.STOP_ALL, stop.getEventType()); + assertEquals(records.get(records.size() - 1).getTimestamp().getTimestamp(), + stop.getTimestamp().getTimestamp()); + } }