INSTINCT Code Coverage Report


Directory: src/
File: Navigation/GNSS/Positioning/AntexReader.hpp
Date: 2025-07-19 10:51:51
Exec Total Coverage
Lines: 235 243 96.7%
Functions: 14 14 100.0%
Branches: 326 554 58.8%

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 AntexReader.hpp
10 /// @brief ANTEX file reader
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @date 2024-05-19
13
14 #pragma once
15
16 #include <algorithm>
17 #include <cstddef>
18 #include <filesystem>
19 #include <fstream>
20 #include <mutex>
21 #include <set>
22 #include <optional>
23 #include <string>
24 #include <unordered_map>
25 #include <unordered_set>
26 #include <utility>
27 #include <vector>
28 #include "Navigation/GNSS/Core/Frequency.hpp"
29 #include "Navigation/GNSS/Core/SatelliteSystem.hpp"
30 #include "Navigation/Time/InsTime.hpp"
31 #include "Navigation/Time/TimeSystem.hpp"
32 #include "Navigation/Transformations/Units.hpp"
33 #include "internal/FlowManager.hpp"
34 #include "util/Eigen.hpp"
35 #include "util/Logger.hpp"
36 #include "util/Container/Pair.hpp"
37 #include "util/StringUtil.hpp"
38 #include <Eigen/src/Core/Matrix.h>
39 #include <fmt/core.h>
40
41 namespace NAV
42 {
43
44 /// @brief ANTEX file reader
45 class AntexReader
46 {
47 public:
48 /// Antenna frequency dependant information
49 struct AntennaFreqInfo
50 {
51 /// Eccentricities of the mean antenna phase center relative to the antenna reference point (ARP). North, east and up component in [m]
52 Eigen::Vector3d phaseCenterOffsetToARP = Eigen::Vector3d::Zero();
53 /// Phase center variation pattern independent from azimuth [mm]
54 /// - First row is zenith angle in [rad]
55 /// - Second row is the values
56 Eigen::Matrix2Xd patternAzimuthIndependent;
57 /// Phase center variation [mm]
58 /// - First row is zenith angle in [rad]
59 /// - First column is the azimuth angle in [rad]
60 Eigen::MatrixXd pattern;
61 };
62
63 /// Antenna information
64 struct Antenna
65 {
66 /// Antenna info
67 struct AntennaInfo
68 {
69 /// @brief Constructor
70 /// @param[in] date Date of measurement
71 /// @param[in] from Valid from
72 /// @param[in] until Valid until
73 74772 AntennaInfo(const InsTime& date, const InsTime& from, const InsTime& until)
74 74772 : date(date), from(from), until(until) {}
75
76 InsTime date; ///< Date of measurement
77 InsTime from; ///< Valid from
78 InsTime until; ///< Valid until
79
80 double zenithStart = 0.0; ///< Zenith start of the phase center variation pattern in [rad]
81 double zenithEnd = 0.0; ///< Zenith end of the phase center variation pattern in [rad]
82 double zenithDelta = 0.0; ///< Zenith delta of the phase center variation pattern in [rad]
83 double azimuthStart = 0.0; ///< Azimuth start of the phase center variation pattern in [rad]
84 double azimuthEnd = 0.0; ///< Azimuth end of the phase center variation pattern in [rad]
85 double azimuthDelta = 0.0; ///< Azimuth delta of the phase center variation pattern in [rad]
86
87 /// Frequency dependant information
88 std::unordered_map<Frequency_, AntennaFreqInfo> freqInformation;
89 };
90 /// Serial number
91 std::string serialNumber;
92
93 /// Antenna Information
94 std::vector<AntennaInfo> antennaInfo;
95 };
96
97 /// @brief Get the static Instance of the reader
98 70094 static AntexReader& Get()
99 {
100
3/4
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 70001 times.
✓ Branch 3 taken 93 times.
✗ Branch 4 not taken.
70094 static AntexReader self;
101 70094 return self;
102 }
103
104 /// @brief Initialize from ANTEX file
105 120 void initialize()
106 {
107
2/2
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 93 times.
120 if (!_antennas.empty()) { return; }
108
109
2/4
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 93 times.
✗ Branch 6 not taken.
93 LOG_DEBUG("Reading ANTEX files started...");
110
111
7/14
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 93 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 93 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 93 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 93 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 93 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 93 times.
✗ Branch 20 not taken.
93 auto path = flow::GetProgramRootPath() / "resources" / "gnss" / "antex";
112
2/4
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 93 times.
93 if (!std::filesystem::exists(path))
113 {
114 LOG_WARN("Not reading ANTEX files because path does not exist: {}", path);
115 return;
116 }
117
4/6
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 11 taken 465 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 465 times.
✓ Branch 15 taken 93 times.
558 for (const auto& entry : std::filesystem::directory_iterator(path))
118 {
119
4/6
✓ Branch 1 taken 465 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 465 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 186 times.
✓ Branch 11 taken 279 times.
465 if (entry.path().extension() != ".atx") { continue; }
120
3/6
✓ Branch 1 taken 279 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 279 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 279 times.
✗ Branch 10 not taken.
279 LOG_DEBUG("Reading {}...", entry.path().string());
121
122
1/2
✓ Branch 2 taken 279 times.
✗ Branch 3 not taken.
279 std::ifstream fs(entry.path());
123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 279 times.
279 if (!fs.good())
124 {
125 LOG_ERROR("Could not read ANTEX file: {}", entry.path().string());
126 return;
127 }
128
129 25789179 auto extHeaderLabel = [](const std::string& line) {
130
1/2
✓ Branch 2 taken 25789179 times.
✗ Branch 3 not taken.
25789179 return str::trim_copy(std::string_view(line).substr(60, 20));
131 };
132
133 279 std::string line;
134 279 size_t lineNumber = 0;
135
136 279 bool antennaStarted = false;
137 279 Antenna* antenna = nullptr;
138 279 std::vector<NAV::AntexReader::Antenna::AntennaInfo>::iterator antInfo;
139 279 std::string antennaType;
140 279 InsTime date;
141 279 InsTime validFrom;
142 279 InsTime validUntil;
143 279 Frequency_ frequency = Freq_None;
144 279 const double azimuthStart = 0.0;
145 279 const double azimuthEnd = deg2rad(360.0);
146 279 double azimuthDelta = 0.0;
147 279 double zenithStart = 0.0;
148 279 double zenithEnd = 0.0;
149 279 double zenithDelta = 0.0;
150 #if LOG_LEVEL <= LOG_LEVEL_DATA
151 bool patternLogging = false;
152 #endif
153
7/10
✓ Branch 1 taken 25789458 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25789458 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 25789179 times.
✓ Branch 7 taken 279 times.
✓ Branch 9 taken 25789179 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 25789179 times.
✓ Branch 12 taken 279 times.
25789458 while (std::getline(fs, line) && !fs.eof())
154 {
155 25789179 lineNumber++;
156
1/2
✓ Branch 1 taken 25789179 times.
✗ Branch 2 not taken.
25789179 auto label = extHeaderLabel(line);
157
2/2
✓ Branch 2 taken 74958 times.
✓ Branch 3 taken 25714221 times.
25789179 if (label == "START OF ANTENNA") { antennaStarted = true; }
158
2/2
✓ Branch 2 taken 74958 times.
✓ Branch 3 taken 25639263 times.
25714221 else if (label == "END OF ANTENNA")
159 {
160 74958 antennaStarted = false;
161 74958 antenna = nullptr;
162 74958 antInfo = {};
163 74958 antennaType.clear();
164 74958 date.reset();
165 74958 validFrom.reset();
166 74958 validUntil.reset();
167 74958 azimuthDelta = 0.0;
168 74958 zenithStart = 0.0;
169 74958 zenithEnd = 0.0;
170 74958 zenithDelta = 0.0;
171 }
172
2/2
✓ Branch 0 taken 25600203 times.
✓ Branch 1 taken 39060 times.
25639263 else if (antennaStarted)
173 {
174
2/2
✓ Branch 2 taken 74958 times.
✓ Branch 3 taken 25525245 times.
25600203 if (label == "TYPE / SERIAL NO")
175 {
176
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74958 times.
✗ Branch 5 not taken.
74958 antennaType = str::trim_copy(line.substr(0, 20));
177
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74958 times.
✗ Branch 5 not taken.
74958 std::string serialNumber = str::trim_copy(line.substr(20, 20));
178
4/6
✓ Branch 1 taken 33852 times.
✓ Branch 2 taken 41106 times.
✓ Branch 4 taken 33852 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 33852 times.
✗ Branch 8 not taken.
74958 if (!serialNumber.empty()) { antennaType += ":" + serialNumber; }
179
180
1/2
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
74958 antenna = &_antennas[antennaType];
181
1/2
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
74958 antenna->serialNumber = serialNumber;
182
1/2
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
74958 _antennaNames.insert(antennaType);
183 74958 }
184
2/2
✓ Branch 2 taken 74958 times.
✓ Branch 3 taken 25450287 times.
25525245 else if (label == "METH / BY / # / DATE")
185 {
186 // " COD/ESA 0 29-JAN-17 METH / BY / # / DATE"
187
1/2
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
74958 auto strDate = line.substr(50, 10);
188
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74958 times.
✗ Branch 5 not taken.
74958 auto day = std::stoi(strDate.substr(0, 2));
189
1/2
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
74958 auto strMon = strDate.substr(3, 3);
190 74958 int mon = 0;
191
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35526 times.
✓ Branch 4 taken 39432 times.
74958 if (strMon == "JAN") { mon = 1; }
192
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2232 times.
✓ Branch 4 taken 72726 times.
74958 if (strMon == "FEB") { mon = 2; }
193
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5952 times.
✓ Branch 4 taken 69006 times.
74958 if (strMon == "MAR") { mon = 3; }
194
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3534 times.
✓ Branch 4 taken 71424 times.
74958 if (strMon == "APR") { mon = 4; }
195
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5115 times.
✓ Branch 4 taken 69843 times.
74958 if (strMon == "MAY") { mon = 5; }
196
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7347 times.
✓ Branch 4 taken 67611 times.
74958 if (strMon == "JUN") { mon = 6; }
197
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1767 times.
✓ Branch 4 taken 73191 times.
74958 if (strMon == "JUL") { mon = 7; }
198
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 558 times.
✓ Branch 4 taken 74400 times.
74958 if (strMon == "AUG") { mon = 8; }
199
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4836 times.
✓ Branch 4 taken 70122 times.
74958 if (strMon == "SEP") { mon = 9; }
200
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2511 times.
✓ Branch 4 taken 72447 times.
74958 if (strMon == "OCT") { mon = 10; }
201
3/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2511 times.
✓ Branch 4 taken 72447 times.
74958 if (strMon == "NOV") { mon = 11; }
202
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 74958 times.
74958 if (strMon == "DEZ") { mon = 12; }
203
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74958 times.
✗ Branch 5 not taken.
74958 auto year = 2000 + std::stoi(strDate.substr(7, 2));
204
1/2
✓ Branch 2 taken 74958 times.
✗ Branch 3 not taken.
74958 date = InsTime(year, mon, day, 0, 0, 0.0, GPST);
205 74958 }
206
2/2
✓ Branch 2 taken 74958 times.
✓ Branch 3 taken 25375329 times.
25450287 else if (label == "DAZI")
207 {
208 // Increment of the azimuth: 2X,F6.1,52X
209 // 0 to 360 with increment 'DAZI' (in degrees).
210 // 360 degrees have to be divisible by 'DAZI'.
211 // Common value for 'DAZI': 5.0
212 // For non-azimuth-dependent phase center
213 // variations '0.0' has to be specified.
214 //
215 // 5.0 DAZI
216
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74958 times.
✗ Branch 5 not taken.
74958 azimuthDelta = deg2rad(std::stod(line.substr(2, 6)));
217 }
218
2/2
✓ Branch 2 taken 74958 times.
✓ Branch 3 taken 25300371 times.
25375329 else if (label == "ZEN1 / ZEN2 / DZEN")
219 {
220 // Receiver antenna: 2X,3F6.1,40X
221 // Definition of the grid in zenith angle:
222 // Zenith distance 'ZEN1' to 'ZEN2' with increment 'DZEN' (in degrees).
223 // 'DZEN' has to be > 0.0.
224 // 'ZEN1' and 'ZEN2' always have to be multiples of 'DZEN'.
225 // 'ZEN2' always has to be greater than 'ZEN1'.
226 // Common value for 'DZEN': 5.0
227 // Example: ' 0.0 90.0 5.0'
228 //
229 // 0.0 90.0 5.0 ZEN1 / ZEN2 / DZEN
230
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74958 times.
✗ Branch 5 not taken.
74958 zenithStart = deg2rad(std::stod(line.substr(2, 6)));
231
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74958 times.
✗ Branch 5 not taken.
74958 zenithEnd = deg2rad(std::stod(line.substr(8, 6)));
232
2/4
✓ Branch 1 taken 74958 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74958 times.
✗ Branch 5 not taken.
74958 zenithDelta = deg2rad(std::stod(line.substr(14, 6)));
233 }
234
2/2
✓ Branch 2 taken 33852 times.
✓ Branch 3 taken 25266519 times.
25300371 else if (label == "VALID FROM")
235 {
236
7/14
✓ Branch 1 taken 33852 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 33852 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 33852 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 33852 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 33852 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 33852 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 33852 times.
✗ Branch 20 not taken.
135408 validFrom = InsTime(std::stoi(line.substr(0, 6)),
237
1/2
✓ Branch 1 taken 33852 times.
✗ Branch 2 not taken.
67704 std::stoi(line.substr(6, 6)),
238
1/2
✓ Branch 1 taken 33852 times.
✗ Branch 2 not taken.
67704 std::stoi(line.substr(12, 6)),
239
1/2
✓ Branch 1 taken 33852 times.
✗ Branch 2 not taken.
67704 std::stoi(line.substr(18, 6)),
240 33852 std::stoi(line.substr(24, 6)),
241
3/6
✓ Branch 1 taken 33852 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 33852 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 33852 times.
✗ Branch 8 not taken.
67704 std::stod(line.substr(30, 13)),
242 33852 GPST);
243 }
244
2/2
✓ Branch 2 taken 19251 times.
✓ Branch 3 taken 25247268 times.
25266519 else if (label == "VALID UNTIL")
245 {
246
7/14
✓ Branch 1 taken 19251 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19251 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 19251 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 19251 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 19251 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 19251 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 19251 times.
✗ Branch 20 not taken.
77004 validUntil = InsTime(std::stoi(line.substr(0, 6)),
247
1/2
✓ Branch 1 taken 19251 times.
✗ Branch 2 not taken.
38502 std::stoi(line.substr(6, 6)),
248
1/2
✓ Branch 1 taken 19251 times.
✗ Branch 2 not taken.
38502 std::stoi(line.substr(12, 6)),
249
1/2
✓ Branch 1 taken 19251 times.
✗ Branch 2 not taken.
38502 std::stoi(line.substr(18, 6)),
250 19251 std::stoi(line.substr(24, 6)),
251
3/6
✓ Branch 1 taken 19251 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19251 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 19251 times.
✗ Branch 8 not taken.
38502 std::stod(line.substr(30, 13)),
252 19251 GPST);
253 }
254
2/2
✓ Branch 2 taken 411339 times.
✓ Branch 3 taken 24835929 times.
25247268 else if (label == "START OF FREQUENCY")
255 {
256
2/4
✓ Branch 1 taken 411339 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 411339 times.
✗ Branch 5 not taken.
411339 frequency = Frequency_(Frequency::fromString(line.substr(3, 3)));
257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 411339 times.
411339 if (frequency == Freq_None)
258 {
259 LOG_WARN(" AntexReader: Invalid frequency [{}] in line {} of file '{}'", line.substr(3, 3), lineNumber, entry.path());
260 continue;
261 }
262
263 #if LOG_LEVEL <= LOG_LEVEL_DATA
264 patternLogging = true;
265 #endif
266
267
1/2
✓ Branch 1 taken 411339 times.
✗ Branch 2 not taken.
411339 antInfo = std::ranges::find_if(antenna->antennaInfo, [&](const Antenna::AntennaInfo& antInfo) {
268
3/4
✓ Branch 1 taken 336567 times.
✓ Branch 2 taken 35433 times.
✓ Branch 4 taken 336567 times.
✗ Branch 5 not taken.
372000 return antInfo.from == validFrom && antInfo.until == validUntil;
269 });
270
2/2
✓ Branch 2 taken 74772 times.
✓ Branch 3 taken 336567 times.
411339 if (antInfo == antenna->antennaInfo.end())
271 {
272
1/2
✓ Branch 1 taken 74772 times.
✗ Branch 2 not taken.
74772 antenna->antennaInfo.emplace_back(date, validFrom, validUntil);
273 74772 antInfo = antenna->antennaInfo.end() - 1;
274 }
275
276 411339 antInfo->zenithStart = zenithStart;
277 411339 antInfo->zenithEnd = zenithEnd;
278 411339 antInfo->zenithDelta = zenithDelta;
279 411339 antInfo->azimuthStart = azimuthStart;
280 411339 antInfo->azimuthEnd = azimuthEnd;
281 411339 antInfo->azimuthDelta = azimuthDelta;
282
283
6/10
✓ Branch 2 taken 411339 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4650 times.
✓ Branch 5 taken 406689 times.
✓ Branch 8 taken 4650 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 4650 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 411339 times.
411339 if (antInfo->freqInformation.contains(frequency) && antInfo->date > date)
284 {
285 LOG_TRACE(" Antenna '{}' [{}]{} already exists.", antennaType, Frequency(frequency),
286 !validFrom.empty() || !validUntil.empty() ? fmt::format(" (valid [{}] - [{}])", validFrom.toYMDHMS(GPST), validUntil.toYMDHMS(GPST)) : "");
287 frequency = Freq_None;
288 }
289 }
290
2/2
✓ Branch 2 taken 411339 times.
✓ Branch 3 taken 24424590 times.
24835929 else if (label == "END OF FREQUENCY") { frequency = Freq_None; }
291
2/2
✓ Branch 0 taken 24011856 times.
✓ Branch 1 taken 412734 times.
24424590 else if (frequency != Freq_None)
292 {
293
2/2
✓ Branch 2 taken 411339 times.
✓ Branch 3 taken 23600517 times.
24011856 if (label == "NORTH / EAST / UP")
294 {
295
2/4
✓ Branch 2 taken 411339 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 411339 times.
✗ Branch 7 not taken.
1234017 antInfo->freqInformation[frequency].phaseCenterOffsetToARP = Eigen::Vector3d(str::stod(line.substr(0, 10), 0.0),
296
1/2
✓ Branch 2 taken 411339 times.
✗ Branch 3 not taken.
822678 str::stod(line.substr(10, 10), 0.0),
297
1/2
✓ Branch 2 taken 411339 times.
✗ Branch 3 not taken.
822678 str::stod(line.substr(20, 10), 0.0))
298
3/6
✓ Branch 1 taken 411339 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 411339 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 411339 times.
✗ Branch 8 not taken.
1645356 * 1e-3;
299 LOG_DATA(" Adding antenna '{}' [{}]{} phaseCenterOffsetToARP: {}", antennaType, Frequency(frequency),
300 !validFrom.empty() || !validUntil.empty() ? fmt::format(" (valid [{}] - [{}])", validFrom.toYMDHMS(GPST), validUntil.toYMDHMS(GPST)) : "",
301 antInfo->freqInformation.at(frequency).phaseCenterOffsetToARP.transpose());
302 }
303
4/6
✓ Branch 1 taken 23600517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23600517 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 411339 times.
✓ Branch 8 taken 23189178 times.
23600517 else if (line.substr(3, 5) == "NOAZI")
304 {
305 // (Values of a non- | The flag 'NOAZI' denotes a non-azimuth- | 3X,A5,mF8.2
306 // azimuth-dependent | dependent pattern that has to be |
307 // pattern) | specified in any case (also if 'DAZI' > |
308 // | 0.0). |
309 // | Phase pattern values in millimeters from |
310 // | 'ZEN1' to 'ZEN2' (with increment 'DZEN').|
311 // | All values on one line. |
312
1/2
✓ Branch 2 taken 411339 times.
✗ Branch 3 not taken.
411339 Eigen::Matrix2Xd& pattern = antInfo->freqInformation.at(frequency).patternAzimuthIndependent;
313
2/4
✓ Branch 1 taken 411339 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 411339 times.
✗ Branch 5 not taken.
411339 pattern = Eigen::Matrix2Xd::Zero(2, static_cast<int>(std::round(zenithEnd / zenithDelta)) + 1);
314
2/4
✓ Branch 1 taken 411339 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 411339 times.
✗ Branch 5 not taken.
411339 pattern.row(0).setLinSpaced(zenithStart, zenithEnd);
315
2/2
✓ Branch 1 taken 7810884 times.
✓ Branch 2 taken 411339 times.
8222223 for (int c = 0; c < pattern.cols(); c++)
316 {
317
3/6
✓ Branch 1 taken 7810884 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7810884 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7810884 times.
✗ Branch 8 not taken.
7810884 pattern(1, c) = std::stod(line.substr((static_cast<size_t>(c) + 1) * 8, 8)) * 1e-3;
318 }
319 LOG_DATA(" Adding antenna '{}' [{}] NOAZI pattern", antennaType, Frequency(frequency));
320 }
321
1/2
✓ Branch 0 taken 23189178 times.
✗ Branch 1 not taken.
23189178 else if (azimuthDelta > 0.0)
322 {
323 // (Values of an | The azimuth-dependent pattern has to be | F8.1,mF8.2
324 // azimuth-dependent | specified, if 'DAZI' > 0.0. The first |
325 // pattern) | value in each line denotes the azimuth |
326 // | angle followed by the phase pattern |
327 // | values in millimeters from 'ZEN1' to |
328 // | 'ZEN2' (with increment 'DZEN'). |
329 // | All values of one azimuth angle on one |
330 // | line. |
331
1/2
✓ Branch 2 taken 23189178 times.
✗ Branch 3 not taken.
23189178 Eigen::MatrixXd& pattern = antInfo->freqInformation.at(frequency).pattern;
332
333
2/2
✓ Branch 1 taken 310992 times.
✓ Branch 2 taken 22878186 times.
23189178 if (pattern.cols() == 0)
334 {
335 pattern = Eigen::MatrixXd::Zero(static_cast<int>(std::round(azimuthEnd / azimuthDelta)) + 2,
336
2/4
✓ Branch 1 taken 310992 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 310992 times.
✗ Branch 5 not taken.
310992 static_cast<int>(std::round(zenithEnd / zenithDelta)) + 2);
337
3/6
✓ Branch 1 taken 310992 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 310992 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 310992 times.
✗ Branch 9 not taken.
310992 pattern.row(0).rightCols(pattern.cols() - 1).setLinSpaced(zenithStart, zenithEnd);
338
3/6
✓ Branch 1 taken 310992 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 310992 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 310992 times.
✗ Branch 9 not taken.
310992 pattern.col(0).bottomRows(pattern.rows() - 1).setLinSpaced(azimuthStart, azimuthEnd);
339 }
340
2/4
✓ Branch 1 taken 23189178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23189178 times.
✗ Branch 5 not taken.
23189178 double azimuth = deg2rad(std::stod(line.substr(0, 8)));
341 23189178 int r = static_cast<int>(std::round((azimuth - azimuthStart) / azimuthDelta)) + 1;
342
2/2
✓ Branch 1 taken 460604634 times.
✓ Branch 2 taken 23189178 times.
483793812 for (int c = 0; c < pattern.cols() - 1; c++)
343 {
344
3/6
✓ Branch 1 taken 460604634 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 460604634 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 460604634 times.
✗ Branch 8 not taken.
460604634 pattern(r, c + 1) = std::stod(line.substr((static_cast<size_t>(c) + 1) * 8, 8)) * 1e-3;
345 }
346
347 #if LOG_LEVEL <= LOG_LEVEL_DATA
348 if (patternLogging)
349 {
350 LOG_DATA(" Adding antenna '{}' [{}] azimuth dependent pattern", antennaType, Frequency(frequency));
351 patternLogging = false;
352 }
353 #endif
354 }
355 }
356 }
357 }
358 372 }
359
2/4
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 93 times.
✗ Branch 6 not taken.
93 LOG_DEBUG("Reading ANTEX file finished.");
360 93 }
361
362 /// @brief Reset the temporary variables
363 113 void reset()
364 {
365 113 _notFoundAnt.clear();
366 113 _notFoundFreq.clear();
367 113 }
368
369 /// @brief Get the Antenna Phase Center Offset To ARP if it is found in the ANTEX file
370 /// @param[in] antennaType Antenna Type
371 /// @param[in] freq Frequency
372 /// @param[in] insTime Time
373 /// @param[in] nameId NameId of the calling node for Log output
374 /// @return Phase center offset in north, east, up components in [m]
375 34913 std::optional<Eigen::Vector3d> getAntennaPhaseCenterOffsetToARP(const std::string& antennaType, Frequency_ freq, const InsTime& insTime,
376 [[maybe_unused]] const std::string& nameId) const
377 {
378
3/4
✓ Branch 1 taken 34913 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 34905 times.
34913 if (auto antFreqInfo = getAntennaFrequencyInfo(antennaType, freq, insTime, nameId))
379 {
380
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 return antFreqInfo->get().phaseCenterOffsetToARP;
381 }
382
383 34905 return std::nullopt;
384 }
385
386 /// @brief Gets the phase center variation for given elevation and azimuth
387 /// @param[in] antennaType Antenna Type
388 /// @param[in] freq Frequency
389 /// @param[in] insTime Time
390 /// @param[in] elevation Elevation angle in [rad]
391 /// @param[in] azimuth Azimuth in [rad] or nullopt to use the azimuth independent (NOAZI)
392 /// @param[in] nameId NameId of the calling node for Log output
393 /// @return The interpolated phase center variation in [m]
394 template<typename T>
395 34922 std::optional<T> getAntennaPhaseCenterVariation(const std::string& antennaType, Frequency_ freq, const InsTime& insTime,
396 const T& elevation, std::optional<double> azimuth,
397 [[maybe_unused]] const std::string& nameId) const
398 {
399
1/2
✓ Branch 1 taken 34922 times.
✗ Branch 2 not taken.
34922 auto antInfo = getAntennaInfo(antennaType, insTime, nameId);
400
2/2
✓ Branch 1 taken 34903 times.
✓ Branch 2 taken 19 times.
34922 if (!antInfo.has_value()) { return std::nullopt; }
401
402 19 auto zenith = deg2rad(90.0) - elevation;
403
404
2/2
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 2 times.
36 if (zenith < antInfo->get().zenithStart || zenith > antInfo->get().zenithEnd
405
10/10
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 4 times.
✓ Branch 9 taken 10 times.
✓ Branch 10 taken 1 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 9 times.
✓ Branch 17 taken 6 times.
✓ Branch 18 taken 13 times.
36 || (azimuth && (azimuth < antInfo->get().azimuthStart || azimuth > antInfo->get().azimuthEnd)))
406 {
407 LOG_DATA("{}: The zenith or azimuth provided are outside the pattern in the ANTEX file", nameId);
408 6 return std::nullopt;
409 }
410
1/2
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
13 auto antFreqInfo = getAntennaFrequencyInfo(antInfo->get(), antennaType, freq, nameId);
411
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if (!antFreqInfo.has_value()) { return std::nullopt; }
412
413
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
13 if (!azimuth.has_value())
414 {
415 4 const Eigen::Matrix2Xd& pattern = antFreqInfo->get().patternAzimuthIndependent;
416 4 Eigen::Index zenithLoc = -1;
417
3/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 3 times.
4 if (zenith == pattern(0, 0)) { zenithLoc = 1; }
418
3/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
3 else if (zenith == pattern(0, Eigen::last)) { zenithLoc = pattern.cols() - 1; }
419 else
420 {
421
4/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
6 zenithLoc = std::distance(
422
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 pattern.row(0).begin(),
423
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::upper_bound(pattern.row(0).begin(),
424
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 pattern.row(0).end(),
425 zenith));
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (zenithLoc == 0) { zenithLoc++; }
427 }
428 4 Eigen::Index uLoc = zenithLoc - 1;
429
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 double a = pattern(0, uLoc);
430
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 double b = pattern(0, zenithLoc);
431 4 auto t = (zenith - a) / (b - a);
432
433
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 return std::lerp(pattern(1, uLoc), pattern(1, zenithLoc), t);
434 }
435
436 9 const Eigen::MatrixXd& pattern = antFreqInfo->get().pattern;
437 9 Eigen::Index zenithLoc = -1;
438 9 Eigen::Index azimuthLoc = -1;
439
3/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 7 times.
9 if (zenith == pattern(0, 1)) { zenithLoc = 2; }
440
3/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 5 times.
7 else if (zenith == pattern(0, Eigen::last)) { zenithLoc = pattern.cols() - 1; }
441 else
442 {
443
4/8
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
15 zenithLoc = std::distance(
444
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
5 pattern.row(0).rightCols(pattern.cols() - 1).begin(),
445
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
5 std::upper_bound(pattern.row(0).rightCols(pattern.cols() - 1).begin(),
446
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
5 pattern.row(0).rightCols(pattern.cols() - 1).end(),
447 zenith));
448
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 if (zenithLoc != pattern.cols() - 1) { zenithLoc++; }
449 }
450
3/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 6 times.
9 if (*azimuth == pattern(1, 0)) { azimuthLoc = 2; }
451
3/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 else if (*azimuth == pattern(Eigen::last, 0)) { azimuthLoc = pattern.rows() - 1; }
452 else
453 {
454
4/8
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
12 azimuthLoc = std::distance(
455
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
4 pattern.col(0).bottomRows(pattern.rows() - 1).begin(),
456
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
4 std::upper_bound(pattern.col(0).bottomRows(pattern.rows() - 1).begin(),
457
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
4 pattern.col(0).bottomRows(pattern.rows() - 1).end(),
458 4 *azimuth));
459
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (azimuthLoc != pattern.rows() - 1) { azimuthLoc++; }
460 }
461
462 9 Eigen::Index uZenithLoc = zenithLoc - 1;
463
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 auto za = pattern(0, uZenithLoc);
464
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 auto zb = pattern(0, zenithLoc);
465 9 auto zt = (zenith - za) / (zb - za);
466
467 9 Eigen::Index uAzimuthLoc = azimuthLoc - 1;
468
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 auto aa = pattern(uAzimuthLoc, 0);
469
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 auto ab = pattern(azimuthLoc, 0);
470 9 auto at = (*azimuth - aa) / (ab - aa);
471
472 18 auto v = math::bilinearInterpolation(at, zt,
473
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 pattern(uAzimuthLoc, uZenithLoc), pattern(azimuthLoc, uZenithLoc),
474
3/6
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
9 pattern(uAzimuthLoc, zenithLoc), pattern(azimuthLoc, zenithLoc));
475
476 9 return v;
477 }
478
479 /// Antennas read from the ANTEX files
480 1 const std::set<std::string>& antennas() const
481 {
482 1 return _antennaNames;
483 };
484
485 private:
486 /// @brief Constructor
487 93 AntexReader() = default;
488
489 /// Antennas read from the ANTEX files
490 std::unordered_map<std::string, Antenna> _antennas;
491
492 /// Ordered names of all antennas
493 std::set<std::string> _antennaNames;
494
495 /// List of Antennas not found to emit a warning
496 mutable std::unordered_set<std::string> _notFoundAnt;
497
498 /// List of Frequencies not found to emit a warning
499 mutable std::unordered_set<std::pair<std::string, Frequency_>> _notFoundFreq;
500
501 /// @brief Get the antenna info object
502 /// @param[in] antennaType Antenna Type
503 /// @param[in] insTime Time
504 /// @param[in] nameId NameId of the calling node for Log output
505 69855 std::optional<std::reference_wrapper<const Antenna::AntennaInfo>> getAntennaInfo(const std::string& antennaType, const InsTime& insTime,
506 [[maybe_unused]] const std::string& nameId) const
507 {
508
3/4
✓ Branch 1 taken 69855 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 69807 times.
✓ Branch 4 taken 48 times.
69855 if (!_antennas.contains(antennaType))
509 {
510
3/4
✓ Branch 1 taken 69807 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 69799 times.
69807 if (!_notFoundAnt.contains(antennaType))
511 {
512
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
8 LOG_WARN("{}: Antenna type '{}' is not found in the ANTEX files.",
513 nameId, antennaType);
514
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 _notFoundAnt.insert(antennaType);
515 }
516 69807 return std::nullopt;
517 }
518
519
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 const auto& antenna = _antennas.at(antennaType);
520
521 48 auto antInfo = antenna.antennaInfo.cend();
522
2/2
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 4 times.
48 if (antenna.antennaInfo.size() == 1) // One element only, so take it
523 {
524 44 antInfo = antenna.antennaInfo.begin();
525 }
526 else // No element is not possible, so more than one here, so search for time
527 {
528
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 antInfo = std::ranges::find_if(antenna.antennaInfo, [&](const Antenna::AntennaInfo& antInfo) {
529
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7 return (antInfo.from.empty() && antInfo.until.empty())
530
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
7 || (antInfo.from.empty() && insTime <= antInfo.until)
531
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
7 || (antInfo.until.empty() && antInfo.from <= insTime)
532
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 4 times.
14 || (antInfo.from <= insTime && insTime <= antInfo.until);
533 });
534 }
535
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 47 times.
48 if (antInfo == antenna.antennaInfo.end()) // None matching, so take last
536 {
537 1 antInfo = antenna.antennaInfo.cend() - 1;
538 }
539
540 48 return *antInfo;
541 }
542
543 /// @brief Get the antenna frequency info object
544 /// @param[in] antennaType Antenna Type
545 /// @param[in] freq Frequency
546 /// @param[in] insTime Time
547 /// @param[in] nameId NameId of the calling node for Log output
548 34932 std::optional<std::reference_wrapper<const AntennaFreqInfo>> getAntennaFrequencyInfo(const std::string& antennaType, Frequency_ freq,
549 const InsTime& insTime,
550 const std::string& nameId) const
551 {
552
3/4
✓ Branch 1 taken 34932 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✓ Branch 5 taken 34904 times.
34932 if (auto antInfo = getAntennaInfo(antennaType, insTime, nameId))
553 {
554
1/2
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
28 return getAntennaFrequencyInfo(antInfo->get(), antennaType, freq, nameId);
555 }
556
557 34904 return std::nullopt;
558 }
559
560 /// @brief Get the antenna frequency info object
561 /// @param[in] antInfo Antenna Info object
562 /// @param[in] antennaType Antenna Type
563 /// @param[in] freq Frequency
564 /// @param[in] nameId NameId of the calling node for Log output
565 41 std::optional<std::reference_wrapper<const AntennaFreqInfo>> getAntennaFrequencyInfo(const Antenna::AntennaInfo& antInfo,
566 const std::string& antennaType, Frequency_ freq,
567 [[maybe_unused]] const std::string& nameId) const
568 {
569
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 1 times.
41 if (antInfo.freqInformation.contains(freq))
570 {
571
1/2
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
40 return antInfo.freqInformation.at(freq);
572 }
573
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 if (!_notFoundFreq.contains(std::make_pair(antennaType, freq)))
574 {
575
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 LOG_WARN("{}: Antenna type '{}' is does not have frequency [{}] in the ANTEX file."
576 " Please provide a new ANTEX file under 'resources/gnss/antex' or consider not using the frequency, as this can introduce height errors of several centimeter.",
577 nameId, antennaType, Frequency(freq));
578
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 _notFoundFreq.insert(std::make_pair(antennaType, freq));
579 }
580
581 1 return std::nullopt;
582 }
583 };
584
585 } // namespace NAV
586