Program Listing for File gnss_measurement.h

Return to documentation for file (measurements/gnss_measurement.h)

#pragma once

#include <functional>
#include <optional>
#include <vector>

#include "lupnt/agents/gnss_constellation.h"
#include "lupnt/dynamics/clock_dynamics.h"
#include "lupnt/environment/plasma/gcpm/iri_interface.h"
#include "lupnt/environment/plasma/tec/raytrace.h"
#include "lupnt/filters/filter.h"
#include "lupnt/measurements/measurement.h"
#include "lupnt/numerics/cheby_fit.h"

namespace lupnt {

  enum class GnssObservable { PSEUDORANGE, DOPPLER, CARRIER_PHASE };

  struct GnssMeasurementStateIndices {
    int position = 0;
    int velocity = 3;
    int clock_bias = 6;
    int clock_drift = 7;
    int carrier_integer = -1;
  };

  struct GnssMeasurementOptions {
    std::vector<GnssObservable> observables
        = {GnssObservable::PSEUDORANGE, GnssObservable::DOPPLER, GnssObservable::CARRIER_PHASE};
    GnssMeasurementStateIndices indices;
    ClockBiasUnit clock_bias_unit = ClockBiasUnit::SECONDS;

    Time receive_time_scale = Time::TAI;

    Time ephemeris_time_scale = Time::TAI;

    Frame frame = Frame::ECI;

    bool solve_light_time = true;
    bool recompute_transmit_time_from_ephemeris = false;
    int light_time_max_iterations = 10;
    Real light_time_tolerance_s = 1.0e-11;
    bool apply_transmitter_relativity = true;
    bool apply_shapiro_delay = true;
    Real shapiro_mu = GM_SUN;
    Vec3 shapiro_body_position = Vec3::Zero();
    bool apply_visibility = true;
    bool apply_cn0_threshold = true;
    Real cn0_threshold_dbhz = 15.0;
    bool apply_ionosphere_plasma_delay = false;
    Real default_ionosphere_plasma_delay_m = 0.0;
  };

  struct GnssChannel {
    GnssConst gnss_const = GnssConst::GPS;
    int prn = 0;
    GnssFreq frequency = GnssFreq::L1;
    Real receive_time = 0.0;
    Time receive_time_scale = Time::TAI;
    Real transmit_time = 0.0;
    Time transmit_time_scale = Time::TAI;
    Time ephemeris_time_scale = Time::TAI;
    Frame frame = Frame::ECI;

    Vec6 tx_state = Vec6::Zero();
    VecXd ephemeris_times;
    MatXd ephemeris_tx_states;
    ChebyshevFitModel ephemeris_chebyshev;
    Real tx_clock_bias_s = 0.0;
    Real tx_clock_drift = 0.0;
    Real relativistic_correction_s = 0.0;
    Real group_delay_s = 0.0;
    Real shapiro_delay_m = 0.0;
    Real ionosphere_plasma_delay_m = 0.0;
    Real phase_bias_cycles = 0.0;
    Real integer_ambiguity_cycles = 0.0;

    Real cn0_dbhz = NAN;
    Real sigma_pseudorange_m = NAN;
    Real sigma_doppler_hz = NAN;
    Real sigma_carrier_phase_cycles = NAN;

    Real Wavelength() const;

    bool HasEphemeris() const;

    Vec6 GetTransmitState(Real t) const;

    Real EffectiveTransmitClockBiasSeconds() const;

    Real EffectiveTransmitClockDrift() const;
  };

  struct GnssMeasurementValue {
    Real pseudorange_m = NAN;
    Real doppler_hz = NAN;
    Real carrier_phase_cycles = NAN;
    Real carrier_integer_cycles = 0.0;

    VecXd AsVector(const std::vector<GnssObservable>& observables) const;
  };

  struct GNSSMeasurementsEpoch {
    Real receive_time = 0.0;
    Time receive_time_scale = Time::TAI;
    Real time = 0.0;  // Backward-compatible alias of `receive_time`.

    std::vector<GnssChannel> channels;
    VecXd values;
    MatXd jacobian;
    MatXd covariance;
  };

  enum class GnssIonospherePlasmaRayTraceDelayMode { TEC_ONLY, TEC_PLUS_HIGHER_ORDER };

  struct GnssIonospherePlasmaRayTraceOptions {
    pecsim::RayTraceConfig config;

    Frame raytrace_frame = Frame::ECEF;

    Time raytrace_epoch_scale = Time::UTC;

    bool use_channel_frequency = true;

    pecsim::IRIModel iri_model = pecsim::IRIModel::NONE;

    GnssIonospherePlasmaRayTraceDelayMode delay_mode
        = GnssIonospherePlasmaRayTraceDelayMode::TEC_ONLY;
    bool debug_propagation = false;
    bool debug_correction = false;
  };

  class GNSSMeasurements;

  class GnssMeasurement : public Measurement {
  public:
    GnssMeasurement() = default;

    explicit GnssMeasurement(const GnssChannel& channel);

    void SetChannel(const GnssChannel& channel) { channel_ = channel; }

    const GnssChannel& GetChannel() const { return channel_; }

    GnssMeasurementValue ComputeValue(const State& user_state,
                                      const GnssMeasurementOptions& options = {}) const;

    VecXd Compute(const State& user_state, MatXd* H = nullptr,
                  const GnssMeasurementOptions& options = {}) const;

    FilterMeasurementFunction CreateFunction(const GnssMeasurementOptions& options = {}) const;

    static std::vector<GNSSMeasurementsEpoch> Precompute(const GNSSMeasurements& measurements,
                                                         const std::vector<Real>& receive_times,
                                                         const std::vector<State>& user_states,
                                                         bool compute_jacobians = false);

  private:
    GnssChannel channel_;

    static Real ClockBiasToMeters(Real bias, ClockBiasUnit unit);

    static Real ClockDriftToMetersPerSecond(Real drift, ClockBiasUnit unit);

    static Real ClockBiasMetersDerivative(ClockBiasUnit unit);

    static Real ClockDriftMetersPerSecondDerivative(ClockBiasUnit unit);
  };

  class GNSSMeasurements {
  public:
    using VectorProvider = std::function<Vec3(Real)>;
    using CustomIonospherePlasmaDelayModel
        = std::function<Real(Real, const Vec3&, const Vec3&, GnssFreq)>;
    using BatchCustomIonospherePlasmaDelayModel = std::function<std::vector<std::vector<Real>>(
        const std::vector<Real>&, const std::vector<State>&,
        const std::vector<std::vector<GnssChannel>>&)>;

    GNSSMeasurements() = default;

    explicit GNSSMeasurements(Ptr<GnssConstellation> constellation);

    void SetConstellation(Ptr<GnssConstellation> constellation) { constellation_ = constellation; }

    Ptr<GnssConstellation> GetConstellation() const { return constellation_; }

    void SetFrequency(GnssFreq frequency) { frequency_ = frequency; }

    GnssFreq GetFrequency() const { return frequency_; }

    void SetOptions(const GnssMeasurementOptions& options) { options_ = options; }

    const GnssMeasurementOptions& GetOptions() const { return options_; }

    void SetSunPositionProvider(VectorProvider provider) { sun_position_provider_ = provider; }

    void SetBoresightTargetProvider(VectorProvider provider) {
      boresight_target_provider_ = provider;
    }

    void SetOccludingBodies(const std::vector<GnssOccludingBody>& bodies) {
      occluding_bodies_ = bodies;
    }

    void SetReceiverParams(const GnssReceiverParams& params) { rx_params_ = params; }

    void SetReceiverAntenna(const Antenna& antenna) { rx_antenna_ = antenna; }

    void SetCN0Threshold(Real cn0_threshold_dbhz) {
      options_.cn0_threshold_dbhz = cn0_threshold_dbhz;
    }

    void SetCustomIonospherePlasmaDelayModel(CustomIonospherePlasmaDelayModel model) {
      custom_ionosphere_plasma_delay_model_ = model;
    }

    void SetBatchCustomIonospherePlasmaDelayModel(BatchCustomIonospherePlasmaDelayModel model) {
      batch_custom_ionosphere_plasma_delay_model_ = model;
    }

    [[deprecated("Use SetCustomIonospherePlasmaDelayModel")]]
    void SetIonospherePlasmaDelayProvider(CustomIonospherePlasmaDelayModel provider) {
      SetCustomIonospherePlasmaDelayModel(provider);
    }

    [[deprecated("Use SetBatchCustomIonospherePlasmaDelayModel")]]
    void SetBatchIonospherePlasmaDelayProvider(BatchCustomIonospherePlasmaDelayModel provider) {
      SetBatchCustomIonospherePlasmaDelayModel(provider);
    }

    void SetIonospherePlasmaRayTraceOptions(const GnssIonospherePlasmaRayTraceOptions& options) {
      ionosphere_plasma_raytrace_options_ = options;
    }

    void ClearIonospherePlasmaRayTraceOptions() { ionosphere_plasma_raytrace_options_.reset(); }

    static bool ComputeVisibility(const Vec3& r1, const Vec3& r2, Real R_body,
                                  const Vec3& r_body = Vec3::Zero());

    std::vector<GnssChannel> BuildChannels(Real receive_time, const State& user_state) const;

    GNSSMeasurementsEpoch Compute(Real receive_time, const State& user_state,
                                  MatXd* H = nullptr) const;

    std::vector<GNSSMeasurementsEpoch> Precompute(const std::vector<Real>& receive_times,
                                                  const std::vector<State>& user_states,
                                                  bool compute_jacobians = false);

    const std::vector<GNSSMeasurementsEpoch>& GetPrecomputed() const { return precomputed_; }

    FilterMeasurementFunction CreateFunction(Real t) const;

  private:
    Ptr<GnssConstellation> constellation_;
    GnssFreq frequency_ = GnssFreq::L1;
    GnssMeasurementOptions options_;
    std::vector<GnssOccludingBody> occluding_bodies_;
    Antenna rx_antenna_;
    GnssReceiverParams rx_params_;
    VectorProvider sun_position_provider_;
    VectorProvider boresight_target_provider_;
    CustomIonospherePlasmaDelayModel custom_ionosphere_plasma_delay_model_;
    BatchCustomIonospherePlasmaDelayModel batch_custom_ionosphere_plasma_delay_model_;
    std::optional<GnssIonospherePlasmaRayTraceOptions> ionosphere_plasma_raytrace_options_;
    std::vector<GNSSMeasurementsEpoch> precomputed_;

    Vec3 SunPosition(Real t) const;

    Vec3 BoresightTarget(Real t) const;

    GnssChannel BuildChannelForPrn(int prn, Real receive_time, const State& user_state,
                                   bool compute_ionosphere_plasma_delay = true) const;

    std::vector<GnssChannel> BuildChannels(Real receive_time, const State& user_state,
                                           bool compute_ionosphere_plasma_delay) const;

    GNSSMeasurementsEpoch ComputeFromChannels(Real receive_time, const State& user_state,
                                              std::vector<GnssChannel> channels,
                                              MatXd* H = nullptr) const;

    Real ComputeShapiroDelay(Real receive_time, const Vec3& r_rx_eci, const Vec3& r_tx_eci) const;

    Real ComputeIonospherePlasmaDelay(Real receive_time, const Vec3& r_rx_eci, const Vec3& r_tx_eci,
                                      GnssFreq freq) const;

    Real ComputeIonospherePlasmaRayTraceDelay(Real receive_time, const Vec3& r_rx, const Vec3& r_tx,
                                              GnssFreq freq) const;

    Real ComputeCN0(const GnssChannel& channel, const Vec3& r_rx_eci, Real receive_time) const;

    Real ComputeSigmaRange(Real cn0_dbhz, GnssFreq freq) const;

    Real ComputeSigmaRangeRate(Real cn0_dbhz, GnssFreq freq) const;

    Real ComputeSigmaCarrierPhase(Real cn0_dbhz, GnssFreq freq) const;
  };

}  // namespace lupnt