import 'dart:math' as math; // --------------------------------------------------------------------------- // Attendance math + filter rules (shared by UI and background service). // // Simple version: // - Wake every 5 min, take ONE gps reading, sleep again (battery safe). // - Throw away weak readings (error > 50 m). // - Throw away tiny jumps (< 20 m) so sitting still never invents fake km. // --------------------------------------------------------------------------- /// How often the background service takes one GPS reading. const Duration kFixInterval = Duration(minutes: 5); /// Max seconds to wait for a single GPS fix before giving up. const Duration kFixTimeout = Duration(seconds: 30); /// Discard any fix whose own accuracy error is worse than this (meters). const double kMaxAcceptableAccuracyMeters = 50.0; /// Ignore movement smaller than this (meters) - kills stationary drift. const double kMinMoveMeters = 20.0; /// Straight-line distance between two lat/lng points, in meters. double haversineMeters(double lat1, double lon1, double lat2, double lon2) { const double earthRadius = 6371000.0; // meters final double dLat = _toRadians(lat2 - lat1); final double dLon = _toRadians(lon2 - lon1); final double a = math.sin(dLat / 2) * math.sin(dLat / 2) + math.cos(_toRadians(lat1)) * math.cos(_toRadians(lat2)) * math.sin(dLon / 2) * math.sin(dLon / 2); final double c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)); return earthRadius * c; } double _toRadians(double degrees) => degrees * math.pi / 180.0; /// Decides whether a new fix is worth storing. Returns true only when the /// reading is strong enough AND the person actually moved. bool shouldStorePoint({ required double accuracy, required double distanceFromLastMeters, required bool isFirstPoint, }) { if (accuracy > kMaxAcceptableAccuracyMeters) return false; // weak signal if (!isFirstPoint && distanceFromLastMeters < kMinMoveMeters) { return false; // standing still (drift) } return true; }