Files
HaloKeymind/src/helpers/SensorManager.cpp
T

176 lines
5.8 KiB
C++

#include "SensorManager.h"
#if ENV_INCLUDE_GPS
#ifndef GPS_TELEMETRY_CACHE_INTERVAL_SEC
#define GPS_TELEMETRY_CACHE_INTERVAL_SEC (2UL * 60UL * 60UL)
#endif
#ifndef GPS_TELEMETRY_MAX_ACQUIRE_SEC
#define GPS_TELEMETRY_MAX_ACQUIRE_SEC (15UL * 60UL)
#endif
#ifndef GPS_TELEMETRY_STABLE_SEC
#define GPS_TELEMETRY_STABLE_SEC 30UL
#endif
#ifndef GPS_TELEMETRY_HOLD_SEC
#define GPS_TELEMETRY_HOLD_SEC (2UL * 60UL * 60UL)
#endif
#ifndef GPS_TELEMETRY_MAX_STALE_SEC
#define GPS_TELEMETRY_MAX_STALE_SEC (12UL * 60UL * 60UL)
#endif
#ifndef GPS_TELEMETRY_STABLE_RADIUS_M
#define GPS_TELEMETRY_STABLE_RADIUS_M 100.0f
#endif
static bool millisDue(unsigned long now, unsigned long deadline) {
return (long)(now - deadline) >= 0;
}
static float gpsDistanceSquaredMeters(float lat1, float lon1, float lat2, float lon2) {
static const float METERS_PER_DEGREE = 111320.0f;
float x = (lon2 - lon1) * METERS_PER_DEGREE;
float y = (lat2 - lat1) * METERS_PER_DEGREE;
return x * x + y * y;
}
bool SensorManager::gpsTelemetryHoldActive(unsigned long now) const {
return gps_hold_until != 0 && !millisDue(now, gps_hold_until);
}
bool SensorManager::gpsTelemetryCacheFresh(unsigned long now) const {
return gps_cache_valid &&
(unsigned long)(now - gps_cache_updated_at) <= GPS_TELEMETRY_MAX_STALE_SEC * 1000UL;
}
void SensorManager::updateGpsTelemetryCache(float lat, float lon, float altitude, unsigned long now) {
gps_cache_lat = lat;
gps_cache_lon = lon;
gps_cache_altitude = altitude;
gps_cache_updated_at = now;
gps_cache_valid = true;
}
void SensorManager::maybeStopGpsForTelemetry(unsigned long now) {
#if defined(PERSISTANT_GPS) || defined(FORCE_GPS_ALIVE)
(void)now;
#else
if (telemetryGpsActive() && !gps_user_enabled && !gps_acquiring && !gpsTelemetryHoldActive(now)) {
telemetryGpsStop();
gps_next_cache_update_at = now + GPS_TELEMETRY_CACHE_INTERVAL_SEC * 1000UL;
}
#endif
}
void SensorManager::beginGpsTelemetryAcquisition(unsigned long now) {
if (!telemetryGpsDetected() || gps_acquiring) return;
gps_acquiring = true;
gps_acquire_has_fix = false;
gps_acquire_started_at = now;
gps_stable_started_at = now;
gps_weighted_lat = 0;
gps_weighted_lon = 0;
gps_weighted_altitude = 0;
gps_weight_sum = 0;
gps_weight_count = 0;
if (!telemetryGpsActive()) telemetryGpsStart();
}
void SensorManager::finishGpsTelemetryAcquisition(unsigned long now, bool use_weighted_average) {
if (use_weighted_average && gps_weight_sum > 0) {
updateGpsTelemetryCache(gps_weighted_lat / gps_weight_sum,
gps_weighted_lon / gps_weight_sum,
gps_weighted_altitude / gps_weight_sum,
now);
}
gps_acquiring = false;
gps_next_cache_update_at = now + GPS_TELEMETRY_CACHE_INTERVAL_SEC * 1000UL;
maybeStopGpsForTelemetry(now);
}
bool SensorManager::queryGpsTelemetry(uint8_t requester_permissions, CayenneLPP& telemetry) {
if (!(requester_permissions & TELEM_PERM_LOCATION) || !telemetryGpsDetected()) return false;
unsigned long now = millis();
gps_hold_until = now + GPS_TELEMETRY_HOLD_SEC * 1000UL;
if (!telemetryGpsActive()) telemetryGpsStart();
if (!gpsTelemetryCacheFresh(now) && !gps_acquiring) beginGpsTelemetryAcquisition(now);
if (!gpsTelemetryCacheFresh(now)) return false;
telemetry.addGPS(TELEM_CHANNEL_SELF, gps_cache_lat, gps_cache_lon, gps_cache_altitude);
return true;
}
void SensorManager::processGpsTelemetryFix(float lat, float lon, float altitude, unsigned long now) {
if (!gps_acquiring) {
if (gps_user_enabled || gpsTelemetryHoldActive(now)) {
updateGpsTelemetryCache(lat, lon, altitude, now);
}
return;
}
if (!gps_acquire_has_fix) {
gps_acquire_has_fix = true;
gps_stable_started_at = now;
gps_stable_origin_lat = lat;
gps_stable_origin_lon = lon;
}
static const float STABLE_RADIUS_M2 =
GPS_TELEMETRY_STABLE_RADIUS_M * GPS_TELEMETRY_STABLE_RADIUS_M;
if (gpsDistanceSquaredMeters(gps_stable_origin_lat, gps_stable_origin_lon, lat, lon) > STABLE_RADIUS_M2) {
updateGpsTelemetryCache(lat, lon, altitude, now);
finishGpsTelemetryAcquisition(now, false);
return;
}
float weight = (float)++gps_weight_count;
gps_weighted_lat += lat * weight;
gps_weighted_lon += lon * weight;
gps_weighted_altitude += altitude * weight;
gps_weight_sum += weight;
if (millisDue(now, gps_stable_started_at + GPS_TELEMETRY_STABLE_SEC * 1000UL)) {
finishGpsTelemetryAcquisition(now, true);
}
}
void SensorManager::loopGpsTelemetry(unsigned long now) {
if (!gps_user_enabled && !gpsTelemetryHoldActive(now) && !gps_acquiring) {
maybeStopGpsForTelemetry(now);
}
if (gps_location_access_available && telemetryGpsDetected() && !gps_user_enabled &&
!gpsTelemetryHoldActive(now) && !gps_acquiring &&
(gps_next_cache_update_at == 0 || millisDue(now, gps_next_cache_update_at))) {
beginGpsTelemetryAcquisition(now);
}
if (gps_acquiring && millisDue(now, gps_acquire_started_at + GPS_TELEMETRY_MAX_ACQUIRE_SEC * 1000UL)) {
finishGpsTelemetryAcquisition(now, gps_acquire_has_fix && gps_weight_sum > 0);
}
}
void SensorManager::setGpsTelemetryUserEnabled(bool enabled) {
gps_user_enabled = enabled;
unsigned long now = millis();
if (enabled) {
if (telemetryGpsDetected() && !telemetryGpsActive()) telemetryGpsStart();
} else {
maybeStopGpsForTelemetry(now);
}
}
#endif
void SensorManager::setTelemetryLocationAccessAvailable(bool available) {
#if ENV_INCLUDE_GPS
unsigned long now = millis();
if (gps_location_access_available == available) return;
gps_location_access_available = available;
if (available) {
gps_next_cache_update_at = 0;
} else if (gps_acquiring && !gps_user_enabled && !gpsTelemetryHoldActive(now)) {
gps_acquiring = false;
maybeStopGpsForTelemetry(now);
}
#else
(void)available;
#endif
}