Skip to content

File NavTelemetry.cpp

File List > Components > NavTelemetry > NavTelemetry.cpp

Go to the documentation of this file

#include "NavTelemetry.hpp"

#include <cmath>
#include <cstdlib>

#include "Utils/SimSatClient.hpp"

namespace Orion {

// Poll SimSat every POLL_INTERVAL_TICKS schedIn calls.
// At 1 Hz rate group this means every 5 seconds.
static constexpr U32 POLL_INTERVAL_TICKS = 5;

// Earth's mean radius in km (for Haversine calculation)
static constexpr F64 EARTH_RADIUS_KM = 6371.0;

// Env var helpers for ground station config (default: EPFL, Ecublens, Switzerland)
static F64 getGsLat() {
    const char* p = ::getenv("ORION_GS_LAT");
    return p ? ::atof(p) : 46.5191;
}
static F64 getGsLon() {
    const char* p = ::getenv("ORION_GS_LON");
    return p ? ::atof(p) : 6.5668;
}
static F64 getGsRangeKm() {
    const char* p = ::getenv("ORION_GS_RANGE_KM");
    return p ? ::atof(p) : 2000.0;
}

NavTelemetry::NavTelemetry(const char* compName)
    : NavTelemetryComponentBase(compName),
      m_lat(0.0),
      m_lon(0.0),
      m_alt(0.0),
      m_gsDistanceKm(0.0),
      m_inCommWindow(false),
      m_schedCounter(0),
      m_gsLat(getGsLat()),
      m_gsLon(getGsLon()),
      m_gsRangeKm(getGsRangeKm()) {}

NavTelemetry::~NavTelemetry() {}

// ---------------------------------------------------------------------------
// Port handler — synchronous getter
// ---------------------------------------------------------------------------

NavState NavTelemetry::navStateGet_handler(FwIndexType portNum) {
    NavState state;
    state.set_lat(m_lat);
    state.set_lon(m_lon);
    state.set_alt(m_alt);
    state.set_inCommWindow(m_inCommWindow);
    state.set_gsDistanceKm(m_gsDistanceKm);
    return state;
}

// ---------------------------------------------------------------------------
// Schedule handler — rate group driven
// ---------------------------------------------------------------------------

void NavTelemetry::schedIn_handler(FwIndexType portNum, U32 context) {
    m_schedCounter++;
    if (m_schedCounter < POLL_INTERVAL_TICKS) {
        return;
    }
    m_schedCounter = 0;

    pollSimSat();

    // Always update comm window (even in manual override mode)
    updateCommWindow();

    // Emit telemetry every poll cycle
    this->tlmWrite_CurrentLat(m_lat);
    this->tlmWrite_CurrentLon(m_lon);
    this->tlmWrite_CurrentAlt(m_alt);
    this->tlmWrite_InCommWindow(m_inCommWindow);
}

// ---------------------------------------------------------------------------
// SimSat polling
// ---------------------------------------------------------------------------

void NavTelemetry::pollSimSat() {
    F64 lat = 0.0, lon = 0.0, alt = 0.0;

    if (!SimSatClient::fetchPosition(lat, lon, alt)) {
        this->log_WARNING_HI_SimSatConnectionFailed();
        return;
    }

    m_lat = lat;
    m_lon = lon;
    m_alt = alt;

    this->log_ACTIVITY_LO_SimSatPositionUpdate(m_lat, m_lon, m_alt);
}

// ---------------------------------------------------------------------------
// Comm window logic
// ---------------------------------------------------------------------------

F64 NavTelemetry::haversineDistanceKm(F64 lat1, F64 lon1, F64 lat2, F64 lon2) {
    // Convert degrees to radians
    F64 dLat = (lat2 - lat1) * M_PI / 180.0;
    F64 dLon = (lon2 - lon1) * M_PI / 180.0;
    F64 lat1Rad = lat1 * M_PI / 180.0;
    F64 lat2Rad = lat2 * M_PI / 180.0;

    F64 a = sin(dLat / 2.0) * sin(dLat / 2.0) + cos(lat1Rad) * cos(lat2Rad) * sin(dLon / 2.0) * sin(dLon / 2.0);
    F64 c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a));

    return EARTH_RADIUS_KM * c;
}

void NavTelemetry::updateCommWindow() {
    m_gsDistanceKm = haversineDistanceKm(m_lat, m_lon, m_gsLat, m_gsLon);

    // Hysteresis: enter comm window at gsRange, exit at gsRange * 1.1.
    // Prevents oscillation when the satellite is near the boundary.
    if (m_inCommWindow) {
        m_inCommWindow = (m_gsDistanceKm < m_gsRangeKm * 1.1);
    } else {
        m_inCommWindow = (m_gsDistanceKm < m_gsRangeKm);
    }
}

}  // namespace Orion