109 LOG_DEBUG(
"Reading ANTEX files started...");
112 if (!std::filesystem::exists(path))
114 LOG_WARN(
"Not reading ANTEX files because path does not exist: {}", path);
117 for (
const auto& entry : std::filesystem::directory_iterator(path))
119 if (entry.path().extension() !=
".atx") {
continue; }
120 LOG_DEBUG(
"Reading {}...", entry.path().string());
122 std::ifstream fs(entry.path());
125 LOG_ERROR(
"Could not read ANTEX file: {}", entry.path().string());
129 auto extHeaderLabel = [](
const std::string& line) {
134 size_t lineNumber = 0;
136 bool antennaStarted =
false;
138 std::vector<NAV::AntexReader::Antenna::AntennaInfo>::iterator antInfo;
139 std::string antennaType;
144 const double azimuthStart = 0.0;
145 const double azimuthEnd =
deg2rad(360.0);
146 double azimuthDelta = 0.0;
147 double zenithStart = 0.0;
148 double zenithEnd = 0.0;
149 double zenithDelta = 0.0;
150#if LOG_LEVEL <= LOG_LEVEL_DATA
151 bool patternLogging =
false;
153 while (std::getline(fs, line) && !fs.eof())
156 auto label = extHeaderLabel(line);
157 if (label ==
"START OF ANTENNA") { antennaStarted =
true; }
158 else if (label ==
"END OF ANTENNA")
160 antennaStarted =
false;
172 else if (antennaStarted)
174 if (label ==
"TYPE / SERIAL NO")
178 if (!serialNumber.empty()) { antennaType +=
":" + serialNumber; }
184 else if (label ==
"METH / BY / # / DATE")
187 auto strDate = line.substr(50, 10);
188 auto day = std::stoi(strDate.substr(0, 2));
189 auto strMon = strDate.substr(3, 3);
191 if (strMon ==
"JAN") { mon = 1; }
192 if (strMon ==
"FEB") { mon = 2; }
193 if (strMon ==
"MAR") { mon = 3; }
194 if (strMon ==
"APR") { mon = 4; }
195 if (strMon ==
"MAY") { mon = 5; }
196 if (strMon ==
"JUN") { mon = 6; }
197 if (strMon ==
"JUL") { mon = 7; }
198 if (strMon ==
"AUG") { mon = 8; }
199 if (strMon ==
"SEP") { mon = 9; }
200 if (strMon ==
"OCT") { mon = 10; }
201 if (strMon ==
"NOV") { mon = 11; }
202 if (strMon ==
"DEZ") { mon = 12; }
203 auto year = 2000 + std::stoi(strDate.substr(7, 2));
206 else if (label ==
"DAZI")
216 azimuthDelta =
deg2rad(std::stod(line.substr(2, 6)));
218 else if (label ==
"ZEN1 / ZEN2 / DZEN")
230 zenithStart =
deg2rad(std::stod(line.substr(2, 6)));
231 zenithEnd =
deg2rad(std::stod(line.substr(8, 6)));
232 zenithDelta =
deg2rad(std::stod(line.substr(14, 6)));
234 else if (label ==
"VALID FROM")
236 validFrom =
InsTime(std::stoi(line.substr(0, 6)),
237 std::stoi(line.substr(6, 6)),
238 std::stoi(line.substr(12, 6)),
239 std::stoi(line.substr(18, 6)),
240 std::stoi(line.substr(24, 6)),
241 std::stod(line.substr(30, 13)),
244 else if (label ==
"VALID UNTIL")
246 validUntil =
InsTime(std::stoi(line.substr(0, 6)),
247 std::stoi(line.substr(6, 6)),
248 std::stoi(line.substr(12, 6)),
249 std::stoi(line.substr(18, 6)),
250 std::stoi(line.substr(24, 6)),
251 std::stod(line.substr(30, 13)),
254 else if (label ==
"START OF FREQUENCY")
259 LOG_WARN(
" AntexReader: Invalid frequency [{}] in line {} of file '{}'", line.substr(3, 3), lineNumber, entry.path());
263#if LOG_LEVEL <= LOG_LEVEL_DATA
264 patternLogging =
true;
268 return antInfo.from == validFrom && antInfo.until == validUntil;
272 antenna->
antennaInfo.emplace_back(date, validFrom, validUntil);
290 else if (label ==
"END OF FREQUENCY") { frequency =
Freq_None; }
293 if (label ==
"NORTH / EAST / UP")
295 antInfo->freqInformation[frequency].phaseCenterOffsetToARP = Eigen::Vector3d(
str::stod(line.substr(0, 10), 0.0),
299 LOG_DATA(
" Adding antenna '{}' [{}]{} phaseCenterOffsetToARP: {}", antennaType,
Frequency(frequency),
301 antInfo->freqInformation.at(frequency).phaseCenterOffsetToARP.transpose());
303 else if (line.substr(3, 5) ==
"NOAZI")
312 Eigen::Matrix2Xd& pattern = antInfo->freqInformation.at(frequency).patternAzimuthIndependent;
313 pattern = Eigen::Matrix2Xd::Zero(2,
static_cast<int>(std::round(zenithEnd / zenithDelta)) + 1);
314 pattern.row(0).setLinSpaced(zenithStart, zenithEnd);
315 for (
int c = 0; c < pattern.cols(); c++)
317 pattern(1, c) = std::stod(line.substr((
static_cast<size_t>(c) + 1) * 8, 8)) * 1e-3;
319 LOG_DATA(
" Adding antenna '{}' [{}] NOAZI pattern", antennaType,
Frequency(frequency));
321 else if (azimuthDelta > 0.0)
331 Eigen::MatrixXd& pattern = antInfo->freqInformation.at(frequency).pattern;
333 if (pattern.cols() == 0)
335 pattern = Eigen::MatrixXd::Zero(
static_cast<int>(std::round(azimuthEnd / azimuthDelta)) + 2,
336 static_cast<int>(std::round(zenithEnd / zenithDelta)) + 2);
337 pattern.row(0).rightCols(pattern.cols() - 1).setLinSpaced(zenithStart, zenithEnd);
338 pattern.col(0).bottomRows(pattern.rows() - 1).setLinSpaced(azimuthStart, azimuthEnd);
340 double azimuth =
deg2rad(std::stod(line.substr(0, 8)));
341 int r =
static_cast<int>(std::round((azimuth - azimuthStart) / azimuthDelta)) + 1;
342 for (
int c = 0; c < pattern.cols() - 1; c++)
344 pattern(r, c + 1) = std::stod(line.substr((
static_cast<size_t>(c) + 1) * 8, 8)) * 1e-3;
347#if LOG_LEVEL <= LOG_LEVEL_DATA
350 LOG_DATA(
" Adding antenna '{}' [{}] azimuth dependent pattern", antennaType,
Frequency(frequency));
351 patternLogging =
false;
359 LOG_DEBUG(
"Reading ANTEX file finished.");
396 const T& elevation, std::optional<double> azimuth,
397 [[maybe_unused]]
const std::string& nameId)
const
400 if (!antInfo.has_value()) {
return std::nullopt; }
402 auto zenith =
deg2rad(90.0) - elevation;
404 if (zenith < antInfo->get().zenithStart || zenith > antInfo->get().zenithEnd
405 || (azimuth && (azimuth < antInfo->get().azimuthStart || azimuth > antInfo->get().azimuthEnd)))
407 LOG_DATA(
"{}: The zenith or azimuth provided are outside the pattern in the ANTEX file", nameId);
411 if (!antFreqInfo.has_value()) {
return std::nullopt; }
413 if (!azimuth.has_value())
415 const Eigen::Matrix2Xd& pattern = antFreqInfo->get().patternAzimuthIndependent;
416 Eigen::Index zenithLoc = -1;
417 if (zenith == pattern(0, 0)) { zenithLoc = 1; }
418 else if (zenith == pattern(0, Eigen::last)) { zenithLoc = pattern.cols() - 1; }
421 zenithLoc = std::distance(
422 pattern.row(0).begin(),
423 std::upper_bound(pattern.row(0).begin(),
424 pattern.row(0).end(),
426 if (zenithLoc == 0) { zenithLoc++; }
428 Eigen::Index uLoc = zenithLoc - 1;
429 double a = pattern(0, uLoc);
430 double b = pattern(0, zenithLoc);
431 auto t = (zenith - a) / (b - a);
433 return std::lerp(pattern(1, uLoc), pattern(1, zenithLoc), t);
436 const Eigen::MatrixXd& pattern = antFreqInfo->get().pattern;
437 Eigen::Index zenithLoc = -1;
438 Eigen::Index azimuthLoc = -1;
439 if (zenith == pattern(0, 1)) { zenithLoc = 2; }
440 else if (zenith == pattern(0, Eigen::last)) { zenithLoc = pattern.cols() - 1; }
443 zenithLoc = std::distance(
444 pattern.row(0).rightCols(pattern.cols() - 1).begin(),
445 std::upper_bound(pattern.row(0).rightCols(pattern.cols() - 1).begin(),
446 pattern.row(0).rightCols(pattern.cols() - 1).end(),
448 if (zenithLoc != pattern.cols() - 1) { zenithLoc++; }
450 if (*azimuth == pattern(1, 0)) { azimuthLoc = 2; }
451 else if (*azimuth == pattern(Eigen::last, 0)) { azimuthLoc = pattern.rows() - 1; }
454 azimuthLoc = std::distance(
455 pattern.col(0).bottomRows(pattern.rows() - 1).begin(),
456 std::upper_bound(pattern.col(0).bottomRows(pattern.rows() - 1).begin(),
457 pattern.col(0).bottomRows(pattern.rows() - 1).end(),
459 if (azimuthLoc != pattern.rows() - 1) { azimuthLoc++; }
462 Eigen::Index uZenithLoc = zenithLoc - 1;
463 auto za = pattern(0, uZenithLoc);
464 auto zb = pattern(0, zenithLoc);
465 auto zt = (zenith - za) / (zb - za);
467 Eigen::Index uAzimuthLoc = azimuthLoc - 1;
468 auto aa = pattern(uAzimuthLoc, 0);
469 auto ab = pattern(azimuthLoc, 0);
470 auto at = (*azimuth - aa) / (ab - aa);
473 pattern(uAzimuthLoc, uZenithLoc), pattern(azimuthLoc, uZenithLoc),
474 pattern(uAzimuthLoc, zenithLoc), pattern(azimuthLoc, zenithLoc));