Line | Branch | Exec | Source |
---|---|---|---|
1 | // This file is part of INSTINCT, the INS Toolkit for Integrated | ||
2 | // Navigation Concepts and Training by the Institute of Navigation of | ||
3 | // the University of Stuttgart, Germany. | ||
4 | // | ||
5 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
6 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
7 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
8 | |||
9 | /// @file ObservationEstimator.hpp | ||
10 | /// @brief Calculates Observation estimates | ||
11 | /// @author T. Topp (topp@ins.uni-stuttgart.de) | ||
12 | /// @date 2023-12-21 | ||
13 | |||
14 | #pragma once | ||
15 | |||
16 | #include <algorithm> | ||
17 | #include <cmath> | ||
18 | #include <cstddef> | ||
19 | #include <cstdint> | ||
20 | #include <memory> | ||
21 | #include <unordered_map> | ||
22 | #include <utility> | ||
23 | #include <vector> | ||
24 | |||
25 | #include <imgui.h> | ||
26 | #include <application.h> | ||
27 | #include <imgui_stdlib.h> | ||
28 | #include "Navigation/GNSS/Core/Frequency.hpp" | ||
29 | #include "Navigation/GNSS/Core/SatelliteIdentifier.hpp" | ||
30 | #include "Navigation/GNSS/Positioning/AntexReader.hpp" | ||
31 | #include "Navigation/Transformations/Antenna.hpp" | ||
32 | #include "Navigation/Transformations/CoordinateFrames.hpp" | ||
33 | #include "NodeData/GNSS/GnssObs.hpp" | ||
34 | #include "internal/gui/widgets/imgui_ex.hpp" | ||
35 | #include "internal/gui/widgets/HelpMarker.hpp" | ||
36 | |||
37 | #include "Navigation/Atmosphere/Ionosphere/Ionosphere.hpp" | ||
38 | #include "Navigation/Atmosphere/Troposphere/Troposphere.hpp" | ||
39 | #include "Navigation/GNSS/Errors/MeasurementErrors.hpp" | ||
40 | #include "Navigation/GNSS/Positioning/Observation.hpp" | ||
41 | #include "Navigation/GNSS/Positioning/Receiver.hpp" | ||
42 | #include "Navigation/GNSS/Satellite/Ephemeris/GLONASSEphemeris.hpp" | ||
43 | |||
44 | #include "util/Container/UncertainValue.hpp" | ||
45 | #include "util/Json.hpp" | ||
46 | #include <Eigen/src/Core/MatrixBase.h> | ||
47 | #include <fmt/core.h> | ||
48 | #include <fmt/format.h> | ||
49 | |||
50 | namespace NAV | ||
51 | { | ||
52 | |||
53 | /// @brief Calculates Observation estimates | ||
54 | class ObservationEstimator | ||
55 | { | ||
56 | public: | ||
57 | /// @brief Constructor | ||
58 | /// @param[in] receiverCount Number of receivers | ||
59 | 119 | explicit ObservationEstimator(size_t receiverCount) | |
60 | 119 | : _receiverCount(receiverCount) | |
61 | { | ||
62 |
2/2✓ Branch 0 taken 119 times.
✓ Branch 1 taken 119 times.
|
238 | for (size_t i = 0; i < receiverCount; i++) |
63 | { | ||
64 |
3/8✓ Branch 2 taken 119 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 119 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 119 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
119 | _antenna.emplace(i, Antenna{}); |
65 | } | ||
66 | 119 | } | |
67 | |||
68 | /// @brief How the observation gets used. Influenced the measurement variance | ||
69 | enum ObservationDifference : uint8_t | ||
70 | { | ||
71 | NoDifference, ///< Estimation is not differenced | ||
72 | SingleDifference, ///< Single Difference | ||
73 | DoubleDifference, ///< Double Difference | ||
74 | }; | ||
75 | |||
76 | /// @brief Calculates the observation estimates | ||
77 | /// @param[in, out] observations List of GNSS observation data used for the calculation of this epoch | ||
78 | /// @param[in] ionosphericCorrections Ionospheric correction parameters collected from the Nav data | ||
79 | /// @param[in] receiver Receiver | ||
80 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
81 | /// @param[in] obsDiff Observation Difference type to estimate | ||
82 | template<typename ReceiverType> | ||
83 | 933 | void calcObservationEstimates(Observations& observations, | |
84 | const Receiver<ReceiverType>& receiver, | ||
85 | const std::shared_ptr<const IonosphericCorrections>& ionosphericCorrections, | ||
86 | [[maybe_unused]] const std::string& nameId, | ||
87 | ObservationDifference obsDiff) const | ||
88 | { | ||
89 | LOG_DATA("{}: Calculating all observation estimates:", nameId); | ||
90 |
2/2✓ Branch 7 taken 34903 times.
✓ Branch 8 taken 933 times.
|
35836 | for (auto& [satSigId, observation] : observations.signals) |
91 | { | ||
92 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | calcObservationEstimate(satSigId, observation, receiver, ionosphericCorrections, nameId, obsDiff); |
93 | } | ||
94 | 933 | } | |
95 | |||
96 | /// @brief Calculates the observation estimate for the given signal | ||
97 | /// @param[in] satSigId Satellite signal identifier | ||
98 | /// @param[in, out] observation GNSS observation data used for the calculation of this epoch | ||
99 | /// @param[in] ionosphericCorrections Ionospheric correction parameters collected from the Nav data | ||
100 | /// @param[in] receiver Receiver | ||
101 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
102 | /// @param[in] obsDiff Observation Difference type to estimate | ||
103 | template<typename ReceiverType> | ||
104 | 34903 | void calcObservationEstimate(const SatSigId& satSigId, | |
105 | Observations::SignalObservation& observation, | ||
106 | const Receiver<ReceiverType>& receiver, | ||
107 | const std::shared_ptr<const IonosphericCorrections>& ionosphericCorrections, | ||
108 | [[maybe_unused]] const std::string& nameId, | ||
109 | ObservationDifference obsDiff) const | ||
110 | { | ||
111 | LOG_DATA("{}: Calculating observation estimates for [{}]", nameId, satSigId); | ||
112 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | const Frequency freq = satSigId.freq(); |
113 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | const SatelliteSystem satSys = freq.getSatSys(); |
114 | |||
115 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | auto& recvObs = observation.recvObs.at(receiver.type); |
116 | |||
117 | 34903 | Eigen::Vector3d e_recvPosAPC = e_calcRecvPosAPC(freq, | |
118 | 34903 | receiver.type, | |
119 | 34903 | receiver.e_posMarker, | |
120 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | receiver.gnssObs, nameId); |
121 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | Eigen::Vector3d lla_recvPosAPC = trafo::ecef2lla_WGS84(e_recvPosAPC); |
122 | |||
123 |
1/2✓ Branch 2 taken 34903 times.
✗ Branch 3 not taken.
|
34903 | double elevation = recvObs->satElevation(e_recvPosAPC, lla_recvPosAPC); |
124 |
1/2✓ Branch 2 taken 34903 times.
✗ Branch 3 not taken.
|
34903 | double azimuth = recvObs->satAzimuth(e_recvPosAPC, lla_recvPosAPC); |
125 |
1/2✓ Branch 2 taken 34903 times.
✗ Branch 3 not taken.
|
34903 | Eigen::Vector3d e_pLOS = recvObs->e_pLOS(e_recvPosAPC); |
126 | |||
127 | // Receiver-Satellite Range [m] | ||
128 |
2/4✓ Branch 3 taken 34903 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 34903 times.
✗ Branch 7 not taken.
|
34903 | double rho_r_s = (recvObs->e_satPos() - e_recvPosAPC).norm(); |
129 | 34903 | recvObs->terms.rho_r_s = rho_r_s; | |
130 | // Troposphere | ||
131 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | auto tropo_r_s = calcTroposphericDelayAndMapping(receiver.gnssObs->insTime, lla_recvPosAPC, |
132 | 34903 | elevation, azimuth, _troposphereModels, nameId); | |
133 | 34903 | recvObs->terms.tropoZenithDelay = tropo_r_s; | |
134 | // Estimated troposphere propagation error [m] | ||
135 | 34903 | double dpsr_T_r_s = tropo_r_s.ZHD * tropo_r_s.zhdMappingFactor + tropo_r_s.ZWD * tropo_r_s.zwdMappingFactor; | |
136 | 34903 | recvObs->terms.dpsr_T_r_s = dpsr_T_r_s; | |
137 | // Estimated ionosphere propagation error [m] | ||
138 |
2/4✓ Branch 4 taken 34903 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 34903 times.
✗ Branch 8 not taken.
|
69806 | double dpsr_I_r_s = calcIonosphericDelay(static_cast<double>(receiver.gnssObs->insTime.toGPSweekTow().tow), |
139 | 34903 | freq, observation.freqNum(), lla_recvPosAPC, elevation, azimuth, | |
140 | 34903 | _ionosphereModel, ionosphericCorrections.get()); | |
141 | 34903 | recvObs->terms.dpsr_I_r_s = dpsr_I_r_s; | |
142 | // Sagnac correction [m] | ||
143 |
1/2✓ Branch 3 taken 34903 times.
✗ Branch 4 not taken.
|
34903 | double dpsr_ie_r_s = calcSagnacCorrection(e_recvPosAPC, recvObs->e_satPos()); |
144 | 34903 | recvObs->terms.dpsr_ie_r_s = dpsr_ie_r_s; | |
145 | |||
146 | // Earth's gravitational field causes relativistic signal delay due to space-time curvature (Shapiro effect) [s] | ||
147 | // double posNorm = recvObs->e_satPos().norm() + e_recvPosAPC.norm(); | ||
148 | // double dt_rel_stc = e_recvPosAPC.norm() > InsConst::WGS84::a / 2.0 ? 2.0 * InsConst::WGS84::MU / std::pow(InsConst::C, 3) * std::log((posNorm + rho_r_s) / (posNorm - rho_r_s)) | ||
149 | // : 0.0; | ||
150 | |||
151 |
1/2✓ Branch 2 taken 34903 times.
✗ Branch 3 not taken.
|
34903 | double cn0 = recvObs->gnssObsData().CN0.value_or(1.0); |
152 | |||
153 |
1/2✓ Branch 3 taken 34903 times.
✗ Branch 4 not taken.
|
34903 | double pcv = calcPCV(freq, receiver.gnssObs->insTime, elevation, std::nullopt, receiver.type, receiver.gnssObs, nameId); // TODO: use 'azimuth', but need antenna azimuth |
154 | |||
155 |
2/2✓ Branch 8 taken 68930 times.
✓ Branch 9 taken 34903 times.
|
103833 | for (auto& [obsType, obsData] : recvObs->obs) |
156 | { | ||
157 | LOG_DATA("{}: [{}][{:11}][{:5}] Observation estimate", nameId, satSigId, obsType, receiver.type); | ||
158 |
2/5✓ Branch 0 taken 34903 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34027 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
68930 | switch (obsType) |
159 | { | ||
160 | 34903 | case GnssObs::Pseudorange: | |
161 | 34903 | obsData.estimate = rho_r_s | |
162 | 34903 | + dpsr_ie_r_s | |
163 | 34903 | + dpsr_T_r_s | |
164 | 34903 | + dpsr_I_r_s | |
165 | 34903 | + InsConst::C | |
166 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | * (*receiver.recvClk.biasFor(satSys) - recvObs->satClock().bias |
167 | // + dt_rel_stc | ||
168 |
4/6✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16485 times.
✓ Branch 4 taken 18418 times.
✓ Branch 6 taken 16485 times.
✗ Branch 7 not taken.
|
34903 | + (receiver.interFrequencyBias.contains(freq) ? receiver.interFrequencyBias.at(freq).value : 0.0)) |
169 | 34903 | + pcv; | |
170 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | obsData.measVar = _gnssMeasurementErrorModel.psrMeasErrorVar(satSys, elevation, cn0); |
171 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4f} [m] Geometrical range", nameId, satSigId, obsType, receiver.type, rho_r_s); | ||
172 | LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Sagnac correction", nameId, satSigId, obsType, receiver.type, dpsr_ie_r_s); | ||
173 | if (dpsr_T_r_s != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Tropospheric delay", nameId, satSigId, obsType, receiver.type, dpsr_T_r_s); } | ||
174 | if (dpsr_I_r_s != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Ionospheric delay", nameId, satSigId, obsType, receiver.type, dpsr_I_r_s); } | ||
175 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | if (*receiver.recvClk.biasFor(satSys) != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Receiver clock bias", nameId, satSigId, obsType, receiver.type, InsConst::C * *receiver.recvClk.biasFor(satSys)); } |
176 | LOG_DATA("{}: [{}][{:11}][{:5}] - {:.4f} [m] Satellite clock bias", nameId, satSigId, obsType, receiver.type, InsConst::C * recvObs->satClock().bias); | ||
177 | // LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Shapiro effect", nameId, satSigId, obsType, receiver.type, InsConst::C * dt_rel_stc); | ||
178 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | if (receiver.interFrequencyBias.contains(freq)) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Inter-frequency bias", nameId, satSigId, obsType, receiver.type, InsConst::C * receiver.interFrequencyBias.at(freq).value); } |
179 | LOG_DATA("{}: [{}][{:11}][{:5}] = {:.4f} [m] Pseudorange estimate", nameId, satSigId, obsType, receiver.type, obsData.estimate); | ||
180 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4f} [m] Pseudorange measurement", nameId, satSigId, obsType, receiver.type, obsData.measurement); | ||
181 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4e} [m] Difference to measurement", nameId, satSigId, obsType, receiver.type, obsData.measurement - obsData.estimate); | ||
182 | 34903 | break; | |
183 | ✗ | case GnssObs::Carrier: | |
184 | ✗ | obsData.estimate = rho_r_s | |
185 | ✗ | + dpsr_ie_r_s | |
186 | ✗ | + dpsr_T_r_s | |
187 | ✗ | - dpsr_I_r_s | |
188 | ✗ | + InsConst::C | |
189 | ✗ | * (*receiver.recvClk.biasFor(satSys) - recvObs->satClock().bias | |
190 | // + dt_rel_stc | ||
191 | ) | ||
192 | ✗ | + pcv; | |
193 | ✗ | obsData.measVar = _gnssMeasurementErrorModel.carrierMeasErrorVar(satSys, elevation, cn0); | |
194 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4f} [m] Geometrical range", nameId, satSigId, obsType, receiver.type, rho_r_s); | ||
195 | LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Sagnac correction", nameId, satSigId, obsType, receiver.type, dpsr_ie_r_s); | ||
196 | if (dpsr_T_r_s != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Tropospheric delay", nameId, satSigId, obsType, receiver.type, dpsr_T_r_s); } | ||
197 | if (dpsr_I_r_s != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] - {:.4f} [m] Ionospheric delay", nameId, satSigId, obsType, receiver.type, dpsr_I_r_s); } | ||
198 | ✗ | if (*receiver.recvClk.biasFor(satSys) != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Receiver clock bias", nameId, satSigId, obsType, receiver.type, InsConst::C * *receiver.recvClk.biasFor(satSys)); } | |
199 | LOG_DATA("{}: [{}][{:11}][{:5}] - {:.4f} [m] Satellite clock bias", nameId, satSigId, obsType, receiver.type, InsConst::C * recvObs->satClock().bias); | ||
200 | // LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m] Shapiro effect", nameId, satSigId, obsType, receiver.type, InsConst::C * dt_rel_stc); | ||
201 | LOG_DATA("{}: [{}][{:11}][{:5}] = {:.4f} [m] Carrier-phase estimate", nameId, satSigId, obsType, receiver.type, obsData.estimate); | ||
202 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4f} [m] Carrier-phase measurement", nameId, satSigId, obsType, receiver.type, obsData.measurement); | ||
203 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4e} [m] Difference to measurement", nameId, satSigId, obsType, receiver.type, obsData.measurement - obsData.estimate); | ||
204 | ✗ | break; | |
205 | 34027 | case GnssObs::Doppler: | |
206 |
2/4✓ Branch 3 taken 34027 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 34027 times.
✗ Branch 7 not taken.
|
34027 | obsData.estimate = e_pLOS.dot(recvObs->e_satVel() - receiver.e_vel) |
207 |
1/2✓ Branch 5 taken 34027 times.
✗ Branch 6 not taken.
|
34027 | - calcSagnacRateCorrection(e_recvPosAPC, recvObs->e_satPos(), receiver.e_vel, recvObs->e_satVel()) |
208 | 34027 | + InsConst::C | |
209 |
1/2✓ Branch 1 taken 34027 times.
✗ Branch 2 not taken.
|
34027 | * (*receiver.recvClk.driftFor(satSys) |
210 | 34027 | - recvObs->satClock().drift); | |
211 |
1/2✓ Branch 2 taken 34027 times.
✗ Branch 3 not taken.
|
34027 | obsData.measVar = _gnssMeasurementErrorModel.psrRateMeasErrorVar(freq, observation.freqNum(), elevation, cn0); |
212 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4f} [m/s] ", nameId, satSigId, obsType, receiver.type, e_pLOS.dot(recvObs->e_satVel() - receiver.e_vel)); | ||
213 | LOG_DATA("{}: [{}][{:11}][{:5}] - {:.4f} [m/s] Sagnac rate correction", nameId, satSigId, obsType, receiver.type, calcSagnacRateCorrection(e_recvPosAPC, recvObs->e_satPos(), receiver.e_vel, recvObs->e_satVel())); | ||
214 |
1/2✓ Branch 1 taken 34027 times.
✗ Branch 2 not taken.
|
34027 | if (*receiver.recvClk.driftFor(satSys) != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m/s] Receiver clock drift", nameId, satSigId, obsType, receiver.type, InsConst::C * *receiver.recvClk.driftFor(satSys)); } |
215 | LOG_DATA("{}: [{}][{:11}][{:5}] - {:.4f} [m/s] Satellite clock drift", nameId, satSigId, obsType, receiver.type, InsConst::C * recvObs->satClock().drift); | ||
216 | LOG_DATA("{}: [{}][{:11}][{:5}] = {:.4f} [m/s] Doppler estimate", nameId, satSigId, obsType, receiver.type, obsData.estimate); | ||
217 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4f} [m/s] Doppler measurement", nameId, satSigId, obsType, receiver.type, obsData.measurement); | ||
218 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4e} [m/s] Difference to measurement", nameId, satSigId, obsType, receiver.type, obsData.measurement - obsData.estimate); | ||
219 | 34027 | break; | |
220 | ✗ | case GnssObs::ObservationType_COUNT: | |
221 | ✗ | break; | |
222 | } | ||
223 | LOG_DATA("{}: [{}][{:11}][{:5}] Observation error variance", nameId, satSigId, obsType, receiver.type); | ||
224 | LOG_DATA("{}: [{}][{:11}][{:5}] {:.4g} [{}] Measurement error variance", nameId, satSigId, obsType, receiver.type, obsData.measVar, | ||
225 | obsType == GnssObs::Doppler ? "m^2/s^2" : "m^2"); | ||
226 | |||
227 |
1/2✓ Branch 0 taken 68930 times.
✗ Branch 1 not taken.
|
68930 | if (obsDiff == NoDifference) |
228 | { | ||
229 |
3/4✓ Branch 0 taken 34027 times.
✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 34027 times.
|
68930 | if (obsType == GnssObs::Pseudorange || obsType == GnssObs::Carrier) |
230 | { | ||
231 |
1/2✓ Branch 3 taken 34903 times.
✗ Branch 4 not taken.
|
34903 | obsData.measVar += observation.navData()->calcSatellitePositionVariance() |
232 |
1/2✓ Branch 2 taken 34903 times.
✗ Branch 3 not taken.
|
34903 | + ionoErrorVar(dpsr_I_r_s, freq, observation.freqNum()) |
233 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | + tropoErrorVar(dpsr_T_r_s, elevation); |
234 | LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m^2] Satellite position variance", nameId, satSigId, obsType, receiver.type, observation.navData()->calcSatellitePositionVariance()); | ||
235 | if (dpsr_I_r_s != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m^2] Ionosphere variance", nameId, satSigId, obsType, receiver.type, ionoErrorVar(dpsr_I_r_s, freq, observation.freqNum())); } | ||
236 | if (dpsr_T_r_s != 0.0) { LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m^2] Troposphere variance", nameId, satSigId, obsType, receiver.type, tropoErrorVar(dpsr_T_r_s, elevation)); } | ||
237 | } | ||
238 |
2/2✓ Branch 0 taken 34903 times.
✓ Branch 1 taken 34027 times.
|
68930 | if (obsType == GnssObs::Pseudorange) |
239 | { | ||
240 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | obsData.measVar += _gnssMeasurementErrorModel.codeBiasErrorVar(); |
241 | LOG_DATA("{}: [{}][{:11}][{:5}] + {:.4f} [m^2] Code bias variance", nameId, satSigId, obsType, receiver.type, _gnssMeasurementErrorModel.codeBiasErrorVar()); | ||
242 | } | ||
243 | } | ||
244 | LOG_DATA("{}: [{}][{:11}][{:5}] = {:.4g} [{}] Observation error variance", nameId, satSigId, obsType, receiver.type, obsData.measVar, | ||
245 | obsType == GnssObs::Doppler ? "m^2/s^2" : "m^2"); | ||
246 | } | ||
247 | 34903 | } | |
248 | |||
249 | /// Satellite Info used for estimate calculation | ||
250 | struct SatelliteInfo | ||
251 | { | ||
252 | /// @brief Constructor | ||
253 | /// @param[in] e_satPos Satellite position in ECEF coordinates [m] | ||
254 | /// @param[in] e_satVel Satellite velocity in ECEF frame [m/s] | ||
255 | /// @param[in] satClockBias Satellite clock bias [s] | ||
256 | /// @param[in] satClockDrift Satellite clock drift [s/s] | ||
257 | SatelliteInfo(Eigen::Vector3d e_satPos, Eigen::Vector3d e_satVel, double satClockBias, double satClockDrift) | ||
258 | : e_satPos(std::move(e_satPos)), e_satVel(std::move(e_satVel)), satClockBias(satClockBias), satClockDrift(satClockDrift) {} | ||
259 | |||
260 | Eigen::Vector3d e_satPos; ///< Satellite position in ECEF coordinates [m] | ||
261 | Eigen::Vector3d e_satVel; ///< Satellite velocity in ECEF frame [m/s] | ||
262 | double satClockBias{}; ///< Satellite clock bias [s] | ||
263 | double satClockDrift{}; ///< Satellite clock drift [s/s] | ||
264 | }; | ||
265 | |||
266 | /// @brief Calculates the pseudorange estimate | ||
267 | /// @param[in] freq Signal frequency | ||
268 | /// @param[in] receiverType Receiver type to select the correct antenna | ||
269 | /// @param[in] e_recvPosAPC Receiver antenna phase center position in ECEF coordinates [m] | ||
270 | /// @param[in] recvClockBias Receiver clock bias [s] | ||
271 | /// @param[in] interFreqBias Receiver inter-frequency bias [s] | ||
272 | /// @param[in] dpsr_T_r_s Estimated troposphere propagation error [m] | ||
273 | /// @param[in] dpsr_I_r_s Estimated ionosphere propagation error [m] | ||
274 | /// @param[in] satInfo Satellite Information (pos, vel, clock) | ||
275 | /// @param[in] elevation Elevation of the satellite [rad] | ||
276 | /// @param[in] gnssObs GNSS observation | ||
277 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
278 | /// @return The estimate [m] and the position line-of-sight unit vector (used for the Jacobian) | ||
279 | template<typename Derived> | ||
280 | auto calcPseudorangeEstimate(Frequency freq, | ||
281 | size_t receiverType, | ||
282 | const Eigen::MatrixBase<Derived>& e_recvPosAPC, | ||
283 | const auto& recvClockBias, | ||
284 | const auto& interFreqBias, | ||
285 | const auto& dpsr_T_r_s, | ||
286 | const auto& dpsr_I_r_s, | ||
287 | const std::shared_ptr<const SatelliteInfo>& satInfo, | ||
288 | const auto& elevation, | ||
289 | const std::shared_ptr<const GnssObs>& gnssObs, | ||
290 | [[maybe_unused]] const std::string& nameId) const | ||
291 | { | ||
292 | // Receiver-Satellite Range [m] | ||
293 | auto rho_r_s = (satInfo->e_satPos - e_recvPosAPC).norm(); | ||
294 | |||
295 | // Sagnac correction [m] | ||
296 | auto dpsr_ie_r_s = calcSagnacCorrection(e_recvPosAPC, satInfo->e_satPos); | ||
297 | |||
298 | // Earth's gravitational field causes relativistic signal delay due to space-time curvature (Shapiro effect) [s] | ||
299 | // double posNorm = e_satPos.norm() + e_recvPosAPC.norm(); | ||
300 | // double dt_rel_stc = e_recvPosAPC.norm() > InsConst::WGS84::a / 2.0 ? 2.0 * InsConst::WGS84::MU / std::pow(InsConst::C, 3) * std::log((posNorm + rho_r_s) / (posNorm - rho_r_s)) | ||
301 | // : 0.0; | ||
302 | |||
303 | auto pcv = calcPCV(freq, gnssObs->insTime, elevation, std::nullopt, receiverType, gnssObs, nameId); // TODO: use 'azimuth', but need antenna azimuth | ||
304 | |||
305 | return rho_r_s | ||
306 | + dpsr_ie_r_s | ||
307 | + dpsr_T_r_s | ||
308 | + dpsr_I_r_s | ||
309 | + InsConst::C * (recvClockBias - satInfo->satClockBias /* + dt_rel_stc */ + interFreqBias) | ||
310 | + pcv; | ||
311 | } | ||
312 | |||
313 | /// @brief Calculates the carrier-phase estimate | ||
314 | /// @param[in] freq Signal frequency | ||
315 | /// @param[in] freqNum Frequency number. Only used for GLONASS G1 and G2 | ||
316 | /// @param[in] receiverType Receiver type to select the correct antenna | ||
317 | /// @param[in] e_recvPosMarker Receiver marker position in ECEF coordinates [m] | ||
318 | /// @param[in] recvClockBias Receiver clock bias [s] | ||
319 | /// @param[in] satInfo Satellite Information (pos, vel, clock) | ||
320 | /// @param[in] gnssObs GNSS observation | ||
321 | /// @param[in] ionosphericCorrections Ionospheric correction parameters collected from the Nav data | ||
322 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
323 | /// @return The estimate [m] and the position line-of-sight unit vector (used for the Jacobian) | ||
324 | template<typename Derived> | ||
325 | std::pair<double, Eigen::Vector3d> | ||
326 | calcCarrierEstimate(Frequency freq, | ||
327 | int8_t freqNum, | ||
328 | size_t receiverType, | ||
329 | const Eigen::MatrixBase<Derived>& e_recvPosMarker, | ||
330 | double recvClockBias, | ||
331 | const std::shared_ptr<const SatelliteInfo>& satInfo, | ||
332 | const std::shared_ptr<const GnssObs>& gnssObs, | ||
333 | const std::shared_ptr<const IonosphericCorrections>& ionosphericCorrections, | ||
334 | [[maybe_unused]] const std::string& nameId) const | ||
335 | { | ||
336 | Eigen::Vector3d e_recvPosAPC = e_calcRecvPosAPC(freq, receiverType, e_recvPosMarker, gnssObs, nameId); | ||
337 | Eigen::Vector3d lla_recvPosAPC = trafo::ecef2lla_WGS84(e_recvPosAPC); | ||
338 | |||
339 | Eigen::Vector3d e_pLOS = e_calcLineOfSightUnitVector(e_recvPosAPC, satInfo->e_satPos); | ||
340 | Eigen::Vector3d n_lineOfSightUnitVector = trafo::n_Quat_e(lla_recvPosAPC(0), lla_recvPosAPC(1)) * e_pLOS; | ||
341 | double elevation = calcSatElevation(n_lineOfSightUnitVector); | ||
342 | double azimuth = calcSatAzimuth(n_lineOfSightUnitVector); | ||
343 | |||
344 | // Receiver-Satellite Range [m] | ||
345 | double rho_r_s = (satInfo->e_satPos - e_recvPosAPC).norm(); | ||
346 | // Estimated troposphere propagation error [m] | ||
347 | double dpsr_T_r_s = calculateTroposphericDelay(lla_recvPosAPC, gnssObs, elevation, azimuth, nameId); | ||
348 | // Estimated ionosphere propagation error [m] | ||
349 | double dpsr_I_r_s = calculateIonosphericDelay(freq, freqNum, lla_recvPosAPC, gnssObs, ionosphericCorrections, elevation, azimuth); | ||
350 | // Sagnac correction [m] | ||
351 | double dpsr_ie_r_s = calcSagnacCorrection(e_recvPosAPC, satInfo->e_satPos); | ||
352 | |||
353 | // Earth's gravitational field causes relativistic signal delay due to space-time curvature (Shapiro effect) [s] | ||
354 | // double posNorm = e_satPos.norm() + e_recvPosAPC.norm(); | ||
355 | // double dt_rel_stc = e_recvPosAPC.norm() > InsConst::WGS84::a / 2.0 ? 2.0 * InsConst::WGS84::MU / std::pow(InsConst::C, 3) * std::log((posNorm + rho_r_s) / (posNorm - rho_r_s)) | ||
356 | // : 0.0; | ||
357 | |||
358 | auto pcv = calcPCV(freq, gnssObs->insTime, elevation, std::nullopt, receiverType, gnssObs, nameId); // TODO: use 'azimuth', but need antenna azimuth | ||
359 | |||
360 | double estimate = rho_r_s | ||
361 | + dpsr_ie_r_s | ||
362 | + dpsr_T_r_s | ||
363 | - dpsr_I_r_s | ||
364 | + InsConst::C * (recvClockBias - satInfo->satClockBias /* + dt_rel_stc */) | ||
365 | + pcv; | ||
366 | |||
367 | return { estimate, e_pLOS }; | ||
368 | } | ||
369 | |||
370 | /// @brief Calculates the doppler estimate | ||
371 | /// @param[in] freq Signal frequency | ||
372 | /// @param[in] receiverType Receiver type to select the correct antenna | ||
373 | /// @param[in] e_recvPosMarker Receiver marker position in ECEF coordinates [m] | ||
374 | /// @param[in] e_recvVel Receiver velocity in ECEF frame [m/s] | ||
375 | /// @param[in] recvClockDrift Receiver clock drift [s/s] | ||
376 | /// @param[in] satInfo Satellite Information (pos, vel, clock) | ||
377 | /// @param[in] gnssObs GNSS observation | ||
378 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
379 | /// @return The estimate [m/s] and the position line-of-sight unit vector (used for the Jacobian) | ||
380 | template<typename DerivedPos, typename DerivedVel> | ||
381 | auto calcDopplerEstimate(Frequency freq, | ||
382 | size_t receiverType, | ||
383 | const Eigen::MatrixBase<DerivedPos>& e_recvPosMarker, | ||
384 | const Eigen::MatrixBase<DerivedVel>& e_recvVel, | ||
385 | const auto& recvClockDrift, | ||
386 | const std::shared_ptr<const SatelliteInfo>& satInfo, | ||
387 | const std::shared_ptr<const GnssObs>& gnssObs, | ||
388 | [[maybe_unused]] const std::string& nameId) const | ||
389 | { | ||
390 | auto e_recvPosAPC = e_calcRecvPosAPC(freq, receiverType, e_recvPosMarker, gnssObs, nameId); | ||
391 | |||
392 | auto e_pLOS = e_calcLineOfSightUnitVector(e_recvPosAPC, satInfo->e_satPos); | ||
393 | |||
394 | return e_pLOS.dot(satInfo->e_satVel - e_recvVel) | ||
395 | - calcSagnacRateCorrection(e_recvPosAPC, satInfo->e_satPos, e_recvVel, satInfo->e_satVel) | ||
396 | + InsConst::C * (recvClockDrift - satInfo->satClockDrift); | ||
397 | } | ||
398 | |||
399 | /// @brief Calculates the observation variance | ||
400 | /// @param[in] freq Signal Frequency | ||
401 | /// @param[in] freqNum Frequency number. Only used for GLONASS G1 and G2 | ||
402 | /// @param[in] receiverType Receiver type to select the correct antenna | ||
403 | /// @param[in] obsType Observation type to calculate the variance for | ||
404 | /// @param[in] e_recvPosMarker Receiver marker position in ECEF coordinates [m] | ||
405 | /// @param[in] e_satPos Satellite position in ECEF coordinates [m] | ||
406 | /// @param[in] cn0 Carrier-to-Noise density [dBHz] | ||
407 | /// @param[in] gnssObs GNSS observation | ||
408 | /// @param[in] navData Navigation data including this satellite | ||
409 | /// @param[in] ionosphericCorrections Ionospheric correction parameters collected from the Nav data | ||
410 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
411 | /// @param[in] obsDiff Observation Difference type to estimate | ||
412 | /// @return Observation variance in [m] (pseudorange, carrier) or [m/s] (doppler) | ||
413 | double calcObservationVariance(Frequency freq, | ||
414 | int8_t freqNum, | ||
415 | size_t receiverType, | ||
416 | GnssObs::ObservationType obsType, | ||
417 | const Eigen::Vector3d& e_recvPosMarker, | ||
418 | const Eigen::Vector3d& e_satPos, | ||
419 | double cn0, | ||
420 | const std::shared_ptr<const GnssObs>& gnssObs, | ||
421 | const std::shared_ptr<const SatNavData>& navData, | ||
422 | const std::shared_ptr<const IonosphericCorrections>& ionosphericCorrections, | ||
423 | [[maybe_unused]] const std::string& nameId, | ||
424 | ObservationDifference obsDiff) const | ||
425 | { | ||
426 | const SatelliteSystem satSys = freq.getSatSys(); | ||
427 | |||
428 | Eigen::Vector3d e_recvPosAPC = e_calcRecvPosAPC(freq, receiverType, e_recvPosMarker, gnssObs, nameId); | ||
429 | Eigen::Vector3d lla_recvPosAPC = trafo::ecef2lla_WGS84(e_recvPosAPC); | ||
430 | |||
431 | Eigen::Vector3d e_pLOS = e_calcLineOfSightUnitVector(e_recvPosAPC, e_satPos); | ||
432 | Eigen::Vector3d n_lineOfSightUnitVector = trafo::n_Quat_e(lla_recvPosAPC(0), lla_recvPosAPC(1)) * e_pLOS; | ||
433 | double elevation = calcSatElevation(n_lineOfSightUnitVector); | ||
434 | double azimuth = calcSatAzimuth(n_lineOfSightUnitVector); | ||
435 | |||
436 | // Estimated troposphere propagation error [m] | ||
437 | double dpsr_T_r_s = calculateTroposphericDelay(lla_recvPosAPC, gnssObs, elevation, azimuth, nameId); | ||
438 | // Estimated ionosphere propagation error [m] | ||
439 | double dpsr_I_r_s = calculateIonosphericDelay(freq, freqNum, lla_recvPosAPC, gnssObs, ionosphericCorrections, elevation, azimuth); | ||
440 | |||
441 | double variance = 0.0; | ||
442 | |||
443 | switch (obsType) | ||
444 | { | ||
445 | case GnssObs::Pseudorange: | ||
446 | variance = _gnssMeasurementErrorModel.psrMeasErrorVar(satSys, elevation, cn0); | ||
447 | break; | ||
448 | case GnssObs::Carrier: | ||
449 | variance = _gnssMeasurementErrorModel.carrierMeasErrorVar(satSys, elevation, cn0); | ||
450 | break; | ||
451 | case GnssObs::Doppler: | ||
452 | variance = _gnssMeasurementErrorModel.psrRateMeasErrorVar(freq, freqNum, elevation, cn0); | ||
453 | break; | ||
454 | case GnssObs::ObservationType_COUNT: | ||
455 | break; | ||
456 | } | ||
457 | |||
458 | if (obsDiff == NoDifference) | ||
459 | { | ||
460 | if (obsType == GnssObs::Pseudorange || obsType == GnssObs::Carrier) | ||
461 | { | ||
462 | variance += navData->calcSatellitePositionVariance() | ||
463 | + ionoErrorVar(dpsr_I_r_s, freq, freqNum) | ||
464 | + tropoErrorVar(dpsr_T_r_s, elevation); | ||
465 | } | ||
466 | if (obsType == GnssObs::Pseudorange) | ||
467 | { | ||
468 | variance += _gnssMeasurementErrorModel.codeBiasErrorVar(); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | return variance; | ||
473 | } | ||
474 | |||
475 | /// @brief Calculates the antenna phase center position for the marker | ||
476 | /// @param[in] freq Signal frequency | ||
477 | /// @param[in] receiverType Receiver type to select the correct antenna | ||
478 | /// @param[in] e_recvPosMarker Receiver marker position in ECEF coordinates [m] | ||
479 | /// @param[in] gnssObs GNSS observation | ||
480 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
481 | template<typename Derived> | ||
482 | 34903 | [[nodiscard]] Eigen::Vector3<typename Derived::Scalar> e_calcRecvPosAPC(const Frequency& freq, | |
483 | size_t receiverType, | ||
484 | const Eigen::MatrixBase<Derived>& e_recvPosMarker, | ||
485 | const std::shared_ptr<const GnssObs>& gnssObs, | ||
486 | [[maybe_unused]] const std::string& nameId) const | ||
487 | { | ||
488 | 34903 | const Antenna& antenna = _antenna.at(receiverType); | |
489 | |||
490 | 34903 | auto e_recvPosAPC = trafo::e_posMarker2ARP(e_recvPosMarker, | |
491 | gnssObs, | ||
492 | 34903 | antenna.hen_delta); | |
493 |
1/2✓ Branch 0 taken 34903 times.
✗ Branch 1 not taken.
|
34903 | if (antenna.correctPCO) |
494 | { | ||
495 | 34903 | std::string antennaType; | |
496 |
3/6✓ Branch 0 taken 34903 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 34903 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 34903 times.
✗ Branch 7 not taken.
|
34903 | if (antenna.autoDetermine && gnssObs->receiverInfo) |
497 | { | ||
498 |
1/2✓ Branch 4 taken 34903 times.
✗ Branch 5 not taken.
|
34903 | antennaType = gnssObs->receiverInfo->get().antennaType; |
499 | } | ||
500 | ✗ | else if (!antenna.autoDetermine) | |
501 | { | ||
502 | ✗ | antennaType = antenna.name; | |
503 | } | ||
504 |
1/2✓ Branch 1 taken 34903 times.
✗ Branch 2 not taken.
|
34903 | e_recvPosAPC = trafo::e_posARP2APC(e_recvPosAPC, gnssObs, freq, antennaType, nameId); |
505 | 34903 | } | |
506 | |||
507 | 34903 | return e_recvPosAPC; | |
508 | } | ||
509 | |||
510 | /// @brief Calculates the phase center variation | ||
511 | /// @param[in] freq Signal frequency | ||
512 | /// @param[in] insTime Time to lookup the PCV pattern | ||
513 | /// @param[in] elevation Elevation angle in [rad] | ||
514 | /// @param[in] azimuth Azimuth in [rad] or nullopt to use the azimuth independent (NOAZI) | ||
515 | /// @param[in] receiverType Receiver type to select the correct antenna | ||
516 | /// @param[in] gnssObs GNSS observation | ||
517 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
518 | /// @return The PCV value in [m] | ||
519 | template<typename T> | ||
520 | 34903 | [[nodiscard]] T calcPCV(const Frequency& freq, | |
521 | const InsTime& insTime, | ||
522 | const T& elevation, | ||
523 | std::optional<double> azimuth, | ||
524 | size_t receiverType, | ||
525 | const std::shared_ptr<const GnssObs>& gnssObs, | ||
526 | [[maybe_unused]] const std::string& nameId) const | ||
527 | { | ||
528 | 34903 | if (std::isnan(elevation) | |
529 |
3/8✓ Branch 0 taken 34903 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 34903 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 34903 times.
|
34903 | || (azimuth.has_value() && std::isnan(*azimuth))) { return T(0.0); } |
530 | 34903 | const Antenna& antenna = _antenna.at(receiverType); | |
531 |
1/2✓ Branch 0 taken 34903 times.
✗ Branch 1 not taken.
|
34903 | if (antenna.correctPCV) |
532 | { | ||
533 | 34903 | std::string antennaType; | |
534 |
3/6✓ Branch 0 taken 34903 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 34903 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 34903 times.
✗ Branch 7 not taken.
|
34903 | if (antenna.autoDetermine && gnssObs->receiverInfo) |
535 | { | ||
536 |
1/2✓ Branch 4 taken 34903 times.
✗ Branch 5 not taken.
|
34903 | antennaType = gnssObs->receiverInfo->get().antennaType; |
537 | } | ||
538 | ✗ | else if (!antenna.autoDetermine) | |
539 | { | ||
540 | ✗ | antennaType = antenna.name; | |
541 | } | ||
542 |
1/2✓ Branch 3 taken 34903 times.
✗ Branch 4 not taken.
|
34903 | return AntexReader::Get().getAntennaPhaseCenterVariation(antennaType, |
543 | Frequency_(freq), | ||
544 | insTime, | ||
545 | elevation, | ||
546 | azimuth, | ||
547 | nameId) | ||
548 | 34903 | .value_or(T(0.0)); | |
549 | 34903 | } | |
550 | |||
551 | ✗ | return T(0.0); | |
552 | } | ||
553 | |||
554 | /// @brief Calculate the ionospheric delay with the model selected in the GUI | ||
555 | /// @param[in] freq Frequency of the signal | ||
556 | /// @param[in] freqNum Frequency number. Only used for GLONASS G1 and G2 | ||
557 | /// @param[in] lla_recvPosAPC Receiver antenna phase center position in LLA coordinates [rad, rad, m] | ||
558 | /// @param[in] gnssObs GNSS observation | ||
559 | /// @param[in] ionosphericCorrections Ionospheric correction parameters collected from the Nav data | ||
560 | /// @param[in] elevation Satellite elevation [rad] | ||
561 | /// @param[in] azimuth Satellite azimuth [rad] | ||
562 | /// @return Estimated ionosphere propagation error [m] | ||
563 | double calculateIonosphericDelay(const Frequency& freq, | ||
564 | int8_t freqNum, | ||
565 | const Eigen::Vector3d& lla_recvPosAPC, | ||
566 | const std::shared_ptr<const GnssObs>& gnssObs, | ||
567 | const std::shared_ptr<const IonosphericCorrections>& ionosphericCorrections, | ||
568 | double elevation, | ||
569 | double azimuth) const | ||
570 | { | ||
571 | return calcIonosphericDelay(static_cast<double>(gnssObs->insTime.toGPSweekTow().tow), | ||
572 | freq, freqNum, lla_recvPosAPC, | ||
573 | elevation, azimuth, | ||
574 | _ionosphereModel, ionosphericCorrections.get()); | ||
575 | } | ||
576 | |||
577 | /// @brief Calculate the tropospheric delay with the model selected in the GUI | ||
578 | /// @param[in] lla_recvPosAPC Receiver antenna phase center position in LLA coordinates [rad, rad, m] | ||
579 | /// @param[in] gnssObs GNSS observation | ||
580 | /// @param[in] elevation Satellite elevation [rad] | ||
581 | /// @param[in] azimuth Satellite azimuth [rad] | ||
582 | /// @param[in] nameId Name and Id of the node used for log messages only | ||
583 | /// @return Estimated troposphere propagation error [m] | ||
584 | double calculateTroposphericDelay(const Eigen::Vector3d& lla_recvPosAPC, | ||
585 | const std::shared_ptr<const GnssObs>& gnssObs, | ||
586 | double elevation, | ||
587 | double azimuth, | ||
588 | [[maybe_unused]] const std::string& nameId) const | ||
589 | { | ||
590 | auto tropo_r_s = calcTroposphericDelayAndMapping(gnssObs->insTime, lla_recvPosAPC, | ||
591 | elevation, azimuth, _troposphereModels, nameId); | ||
592 | |||
593 | return tropo_r_s.ZHD * tropo_r_s.zhdMappingFactor + tropo_r_s.ZWD * tropo_r_s.zwdMappingFactor; | ||
594 | } | ||
595 | |||
596 | /// @brief Shows the GUI input to select the options | ||
597 | /// @param[in] id Unique id for ImGui. | ||
598 | /// @param[in] itemWidth Width of the widgets | ||
599 | template<typename ReceiverType> | ||
600 | ✗ | bool ShowGuiWidgets(const char* id, float itemWidth) | |
601 | { | ||
602 | ✗ | bool changed = false; | |
603 | |||
604 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
605 | ✗ | if (ImGui::TreeNode(fmt::format("Compensation models##{}", id).c_str())) | |
606 | { | ||
607 | ✗ | ImGui::SetNextItemWidth(itemWidth - ImGui::GetStyle().IndentSpacing); | |
608 | ✗ | if (ComboIonosphereModel(fmt::format("Ionosphere Model##{}", id).c_str(), _ionosphereModel)) | |
609 | { | ||
610 | ✗ | LOG_DEBUG("{}: Ionosphere Model changed to {}", id, NAV::to_string(_ionosphereModel)); | |
611 | ✗ | changed = true; | |
612 | } | ||
613 | ✗ | if (ComboTroposphereModel(fmt::format("Troposphere Model##{}", id).c_str(), _troposphereModels, itemWidth - ImGui::GetStyle().IndentSpacing)) | |
614 | { | ||
615 | ✗ | changed = true; | |
616 | } | ||
617 | ✗ | ImGui::TreePop(); | |
618 | } | ||
619 | |||
620 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
621 | ✗ | if (ImGui::TreeNode(fmt::format("GNSS Measurement Error##{}", id).c_str())) | |
622 | { | ||
623 | ✗ | if (_gnssMeasurementErrorModel.ShowGuiWidgets(id, itemWidth - ImGui::GetStyle().IndentSpacing)) | |
624 | { | ||
625 | ✗ | LOG_DEBUG("{}: GNSS Measurement Error Model changed.", id); | |
626 | ✗ | changed = true; | |
627 | } | ||
628 | ✗ | ImGui::TreePop(); | |
629 | } | ||
630 | |||
631 | ✗ | if (ImGui::TreeNode(fmt::format("Antenna settings##{}", id).c_str())) | |
632 | { | ||
633 | ✗ | for (size_t i = 0; i < _receiverCount; ++i) | |
634 | { | ||
635 | ✗ | if (_receiverCount > 1) | |
636 | { | ||
637 | ✗ | ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); | |
638 | ✗ | if (!ImGui::TreeNode(fmt::format("{}", static_cast<ReceiverType>(i)).c_str())) { continue; } | |
639 | } | ||
640 | |||
641 | ✗ | if (ImGui::Checkbox(fmt::format("Correct Phase-center offset##antenna {} {}", id, i).c_str(), &_antenna.at(i).correctPCO)) | |
642 | { | ||
643 | ✗ | LOG_DEBUG("{}: Antenna {} correctPCO: {}", id, i, _antenna.at(i).correctPCO); | |
644 | ✗ | changed = true; | |
645 | } | ||
646 | ✗ | ImGui::SameLine(); | |
647 | ✗ | if (ImGui::Checkbox(fmt::format("Correct Phase-center variation##antenna {} {}", id, i).c_str(), &_antenna.at(i).correctPCV)) | |
648 | { | ||
649 | ✗ | LOG_DEBUG("{}: Antenna {} correctPCV: {}", id, i, _antenna.at(i).correctPCV); | |
650 | ✗ | changed = true; | |
651 | } | ||
652 | |||
653 | ✗ | if (!_antenna.at(i).correctPCO && !_antenna.at(i).correctPCV) { ImGui::BeginDisabled(); } | |
654 | ✗ | if (ImGui::Checkbox(fmt::format("Auto determine##antenna {} {}", id, i).c_str(), &_antenna.at(i).autoDetermine)) | |
655 | { | ||
656 | ✗ | LOG_DEBUG("{}: Antenna {} auto: {}", id, i, _antenna.at(i).autoDetermine); | |
657 | ✗ | changed = true; | |
658 | } | ||
659 | ✗ | ImGui::SameLine(); | |
660 | ✗ | if (_antenna.at(i).autoDetermine) { ImGui::BeginDisabled(); } | |
661 | ✗ | ImGui::SetNextItemWidth(itemWidth - (1.0F + static_cast<float>(_receiverCount > 1)) * ImGui::GetStyle().IndentSpacing); | |
662 | |||
663 | ✗ | if (ImGui::BeginCombo(fmt::format("Type##Combo {} {}", id, i).c_str(), _antenna.at(i).name.c_str())) | |
664 | { | ||
665 | ✗ | ImGui::PushFont(Application::MonoFont()); | |
666 | ✗ | if (ImGui::Selectable(fmt::format("##Selectable {} {}", id, i).c_str(), _antenna.at(i).name.empty())) | |
667 | { | ||
668 | ✗ | _antenna.at(i).name = ""; | |
669 | } | ||
670 | |||
671 | ✗ | for (const auto& iter : AntexReader::Get().antennas()) | |
672 | { | ||
673 | ✗ | const bool is_selected = (_antenna.at(i).name == iter); | |
674 | ✗ | if (ImGui::Selectable(iter.c_str(), is_selected)) | |
675 | { | ||
676 | ✗ | _antenna.at(i).name = iter; | |
677 | } | ||
678 | ✗ | if (is_selected) { ImGui::SetItemDefaultFocus(); } | |
679 | } | ||
680 | ✗ | ImGui::PopFont(); | |
681 | ✗ | ImGui::EndCombo(); | |
682 | } | ||
683 | ✗ | if (_antenna.at(i).autoDetermine) { ImGui::EndDisabled(); } | |
684 | ✗ | if (!_antenna.at(i).correctPCO && !_antenna.at(i).correctPCV) { ImGui::EndDisabled(); } | |
685 | ✗ | ImGui::SameLine(); | |
686 | ✗ | gui::widgets::HelpMarker("Used for lookup in ANTEX file."); | |
687 | |||
688 | ✗ | ImGui::SetNextItemWidth(itemWidth - (1.0F + static_cast<float>(_receiverCount > 1)) * ImGui::GetStyle().IndentSpacing); | |
689 | ✗ | if (ImGui::InputDouble3(fmt::format("Delta H/E/N [m]##{} {}", id, i).c_str(), _antenna.at(i).hen_delta.data(), "%.3f")) | |
690 | { | ||
691 | ✗ | LOG_DEBUG("{}: Antenna {} delta: {}", id, i, _antenna.at(i).hen_delta.transpose()); | |
692 | ✗ | changed = true; | |
693 | } | ||
694 | ✗ | ImGui::SameLine(); | |
695 | ✗ | gui::widgets::HelpMarker("- Antenna height: Height of the antenna reference point (ARP) above the marker\n" | |
696 | "- Horizontal eccentricity of ARP relative to the marker (east/north)"); | ||
697 | |||
698 | ✗ | if (_receiverCount > 1) { ImGui::TreePop(); } | |
699 | } | ||
700 | ✗ | ImGui::TreePop(); | |
701 | } | ||
702 | |||
703 | ✗ | return changed; | |
704 | } | ||
705 | |||
706 | /// @brief Get the Ionosphere Model selected | ||
707 | [[nodiscard]] const IonosphereModel& getIonosphereModel() const { return _ionosphereModel; } | ||
708 | /// @brief Get the Troposphere Model selection | ||
709 | [[nodiscard]] const TroposphereModelSelection& getTroposphereModels() const { return _troposphereModels; } | ||
710 | /// @brief Get the GNSS measurement error model | ||
711 | [[nodiscard]] const GnssMeasurementErrorModel& getGnssMeasurementErrorModel() const { return _gnssMeasurementErrorModel; } | ||
712 | |||
713 | private: | ||
714 | IonosphereModel _ionosphereModel = IonosphereModel::Klobuchar; ///< Ionosphere Model used for the calculation | ||
715 | TroposphereModelSelection _troposphereModels; ///< Troposphere Models used for the calculation | ||
716 | GnssMeasurementErrorModel _gnssMeasurementErrorModel; ///< GNSS measurement error model to use | ||
717 | |||
718 | size_t _receiverCount; ///< Number of receivers (used for GUI) | ||
719 | |||
720 | /// Antenna information | ||
721 | struct Antenna | ||
722 | { | ||
723 | bool correctPCO = true; ///< Correct phase center offset | ||
724 | bool correctPCV = true; ///< Correct phase center variation | ||
725 | bool autoDetermine = true; ///< Try determine antenna type from e.g. RINEX file | ||
726 | std::string name; ///< Type of the antenna for the Antex lookup | ||
727 | Eigen::Vector3d hen_delta = Eigen::Vector3d::Zero(); ///< Delta values of marker to antenna base | ||
728 | }; | ||
729 | |||
730 | std::unordered_map<size_t, Antenna> _antenna; ///< User antenna selection. Key is receiver type | ||
731 | |||
732 | /// @brief Converts the provided object into json | ||
733 | /// @param[out] j Json object which gets filled with the info | ||
734 | /// @param[in] obj Object to convert into json | ||
735 | ✗ | friend void to_json(json& j, const ObservationEstimator::Antenna& obj) | |
736 | { | ||
737 | ✗ | j = json{ | |
738 | ✗ | { "correctPCO", obj.correctPCO }, | |
739 | ✗ | { "correctPCV", obj.correctPCV }, | |
740 | ✗ | { "autoDetermine", obj.autoDetermine }, | |
741 | ✗ | { "name", obj.name }, | |
742 | ✗ | { "hen_delta", obj.hen_delta }, | |
743 | ✗ | }; | |
744 | ✗ | } | |
745 | /// @brief Converts the provided json object into a node object | ||
746 | /// @param[in] j Json object with the needed values | ||
747 | /// @param[out] obj Object to fill from the json | ||
748 | 6 | friend void from_json(const json& j, ObservationEstimator::Antenna& obj) | |
749 | { | ||
750 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if (j.contains("correctPCO")) { j.at("correctPCO").get_to(obj.correctPCO); } |
751 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if (j.contains("correctPCV")) { j.at("correctPCV").get_to(obj.correctPCV); } |
752 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | if (j.contains("autoDetermine")) { j.at("autoDetermine").get_to(obj.autoDetermine); } |
753 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | if (j.contains("name")) { j.at("name").get_to(obj.name); } |
754 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | if (j.contains("hen_delta")) { j.at("hen_delta").get_to(obj.hen_delta); } |
755 | 6 | } | |
756 | |||
757 | /// @brief Converts the provided object into json | ||
758 | /// @param[out] j Json object which gets filled with the info | ||
759 | /// @param[in] obj Object to convert into json | ||
760 | ✗ | friend void to_json(json& j, const ObservationEstimator& obj) | |
761 | { | ||
762 | ✗ | j = json{ | |
763 | ✗ | { "ionosphereModel", obj._ionosphereModel }, | |
764 | ✗ | { "troposphereModels", obj._troposphereModels }, | |
765 | ✗ | { "gnssMeasurementError", obj._gnssMeasurementErrorModel }, | |
766 | ✗ | { "antenna", obj._antenna }, | |
767 | ✗ | }; | |
768 | ✗ | } | |
769 | /// @brief Converts the provided json object into a node object | ||
770 | /// @param[in] j Json object with the needed values | ||
771 | /// @param[out] obj Object to fill from the json | ||
772 | 7 | friend void from_json(const json& j, ObservationEstimator& obj) | |
773 | { | ||
774 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | if (j.contains("ionosphereModel")) { j.at("ionosphereModel").get_to(obj._ionosphereModel); } |
775 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | if (j.contains("troposphereModels")) { j.at("troposphereModels").get_to(obj._troposphereModels); } |
776 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | if (j.contains("gnssMeasurementError")) { j.at("gnssMeasurementError").get_to(obj._gnssMeasurementErrorModel); } |
777 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 times.
|
7 | if (j.contains("antenna")) { j.at("antenna").get_to(obj._antenna); } |
778 | 7 | } | |
779 | }; | ||
780 | |||
781 | } // namespace NAV | ||
782 |