#include "track.h" #include #include #include namespace roro::gnss { namespace { constexpr double kEarthRadiusM = 6371000.0; double radians(double degrees) { return degrees * M_PI / 180.0; } // UTC fields from seconds since 1970 (gmtime_r works on the device and the PC alike). struct tm utc(int64_t seconds) { time_t t = static_cast(seconds); struct tm out; gmtime_r(&t, &out); return out; } } // namespace double distanceMeters(double lat1, double lon1, double lat2, double lon2) { double dLat = radians(lat2 - lat1), dLon = radians(lon2 - lon1); double h = std::sin(dLat / 2) * std::sin(dLat / 2) + std::cos(radians(lat1)) * std::cos(radians(lat2)) * std::sin(dLon / 2) * std::sin(dLon / 2); return 2 * kEarthRadiusM * std::asin(std::sqrt(h)); } bool TrackRule::due(uint32_t nowMs, double lat, double lon) { if (any_ && (nowMs - lastMs_ < kEveryMs || distanceMeters(lat_, lon_, lat, lon) < kMinMeters)) return false; any_ = true; lastMs_ = nowMs; lat_ = lat; lon_ = lon; return true; } std::string trackPath(int64_t utcSeconds) { struct tm t = utc(utcSeconds); char buf[48]; std::snprintf(buf, sizeof buf, "/gnss/tracks/%04d%02d%02d-%02d%02d%02d.gpx", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); return buf; } namespace gpx { std::string header(const std::string& name) { return "\n" "\n" "" + name + ""; } std::string point(double lat, double lon, bool hasElevation, float elevationM, int64_t utcSeconds) { struct tm t = utc(utcSeconds); char buf[160], ele[32] = ""; if (hasElevation) std::snprintf(ele, sizeof ele, "%.1f", elevationM); std::snprintf(buf, sizeof buf, "%s", lat, lon, ele, t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); return buf; } std::string footer() { return ""; } bool finished(const std::string& tail) { size_t end = tail.find_last_not_of(" \r\n\t"); return end != std::string::npos && end + 1 >= 6 && tail.compare(end + 1 - 6, 6, "") == 0; } } // namespace gpx } // namespace roro::gnss