Sort by timestamp not seq, filter teleportation glitches

Track 22 May showed 31km instead of 12.6km because 4 points had
wrong seq order — native service and WebView GPS interleaved.
Sorting by seq placed them wrong, creating 2km+ teleport jumps.

Fixes:
- All sort calls now use timestamp only (seq was unreliable)
- computeStats skips segments > 180 km/h (GPS teleportation)
- After recalculate: 22 May track should show ~12.6 km
This commit is contained in:
4aiman 2026-05-25 09:25:50 +03:00
commit 0dd4a67e7b
2 changed files with 8 additions and 12 deletions

View file

@ -40,11 +40,8 @@ GPS.distance = function(lat1, lon1, lat2, lon2) {
GPS.computeStats = function(points, weightKg, opts) {
if (points.length < 2) return { distance: 0, elevGain: 0, elevLoss: 0, avgSpeed: 0, maxSpeed: 0, kcal: 0, duration: 0, avgCadence: null };
// Sort by seq (if present) or timestamp to fix out-of-order points from native service
const sorted = points.slice().sort((a, b) => {
if (a.seq != null && b.seq != null) return a.seq - b.seq;
return new Date(a.ts) - new Date(b.ts);
});
// Always sort by timestamp — seq can be wrong if points arrived out of order during recording
const sorted = points.slice().sort((a, b) => new Date(a.ts) - new Date(b.ts));
let distance = 0, elevGain = 0, elevLoss = 0;
const speeds = []; // m/s per segment
@ -84,9 +81,13 @@ GPS.computeStats = function(points, weightKg, opts) {
for (let i = 1; i < sorted.length; i++) {
const p = sorted[i], prev = sorted[i - 1];
const d = GPS.distance(prev.lat, prev.lon, p.lat, p.lon);
distance += d;
const dt = (new Date(p.ts) - new Date(prev.ts)) / 1000;
// Skip teleportation glitches (> 180 km/h = GPS jump)
if (dt > 0 && d / dt > 50) continue;
distance += d;
const v = dt > 0 ? d / dt : 0; // m/s
speeds.push(v);

View file

@ -1870,7 +1870,6 @@ function renderRecordingInline(container) {
// Restore trail if already recording — sort by seq/ts to avoid jumps
if (GPS.recording.points.length > 0) {
const sorted = GPS.recording.points.slice().sort((a, b) => {
if (a.seq != null && b.seq != null) return a.seq - b.seq;
return new Date(a.ts) - new Date(b.ts);
});
const coords = sorted.map(p => [p.lat, p.lon]);
@ -1883,7 +1882,6 @@ function renderRecordingInline(container) {
// Rebuilds polyline from all sorted points instead of adding one-by-one.
GPS.recording.onBatchUpdate = () => {
const sorted = GPS.recording.points.slice().sort((a, b) => {
if (a.seq != null && b.seq != null) return a.seq - b.seq;
return new Date(a.ts) - new Date(b.ts);
});
const coords = sorted.map(p => [p.lat, p.lon]);
@ -1923,10 +1921,7 @@ function setupLiveUpdates(container) {
GPS.recording.onBatchUpdate = () => {
if (!gpsTrailLine || !gpsMap) return;
const sorted = GPS.recording.points.slice().sort((a, b) => {
if (a.seq != null && b.seq != null) return a.seq - b.seq;
return new Date(a.ts) - new Date(b.ts);
});
const sorted = GPS.recording.points.slice().sort((a, b) => new Date(a.ts) - new Date(b.ts));
const coords = sorted.map(p => [p.lat, p.lon]);
gpsTrailLine.setLatLngs(coords);
if (coords.length) gpsMap.panTo(coords[coords.length - 1]);