Skip to content
Snippets Groups Projects
Commit 919cc6d1 authored by Mattia Mancini's avatar Mattia Mancini
Browse files

Add Antenna field and unittest

parent 0a4074bd
No related branches found
No related tags found
No related merge requests found
......@@ -700,6 +700,7 @@ if(BUILD_TESTING)
include(CTest)
set(TEST_FILENAMES
aartfaacreader/test/unit/tAntennaConfig.cc
antennaflagger/test/unit/tFlagger.cc
base/test/runtests.cc
base/test/unit/tAartfaacSubtableWriter.cc
......
// Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
// SPDX-License-Identifier: GPL-3.0-or-later
/// This class is responsible to parse the LOFAR antenna configuration file.
/// Such configuration contains the antenna positions for a specific aartfaac
/// configration but also the orientation matrix of the common reference field
/// This code was taken from
/// https://git.astron.nl/RD/aartfaac-tools/-/raw/master/lib/aartfaac2ms/antennaconfig.h?ref_type=heads
/// and adapted to fit into the DP3 codebase.
#ifndef AARTFAACREADER_ANTENNACONFIG_H_
#define AARTFAACREADER_ANTENNACONFIG_H_
#include <algorithm>
#include <array>
#include <cctype>
#include <fstream>
#include <locale>
#include <map>
#include <numeric>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <base/RcuMode.h>
#include <casacore/measures/Measures/MPosition.h>
namespace {
// trim from start (in place)
void LTrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
[](int ch) { return !std::isspace(ch); }));
}
// trim from end (in place)
void RTrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
[](int ch) { return !std::isspace(ch); })
.base(),
s.end());
}
// trim from both ends (in place)
void Trim(std::string &s) {
LTrim(s);
RTrim(s);
}
} // namespace
namespace dp3::aartfaacreader {
class AntennaConfig {
public:
explicit AntennaConfig(const char *filename) : file_(filename) {
ParseFile();
}
std::vector<casacore::MPosition> GetLBAPositions() const {
return GetPositions("LBA");
}
std::vector<casacore::MPosition> GetHBAPositions() const {
return GetPositions("HBA");
}
std::array<double, 9> GetLBAAxes() const {
return GetAxes("LBA_ROTATION_MATRIX");
}
std::array<double, 9> GetHBA0Axes() const {
return GetAxes("HBA0_ROTATION_MATRIX");
}
std::array<double, 9> GetHBA1Axes() const {
return GetAxes("HBA1_ROTATION_MATRIX");
}
std::array<double, 9> GetAxesFromMode(base::RcuMode mode) {
switch (mode.mode) {
case base::RcuMode::LBAInner10_90:
case base::RcuMode::LBAInner30_90:
case base::RcuMode::LBAOuter10_90:
case base::RcuMode::LBAOuter30_90:
return GetLBAAxes();
case base::RcuMode::HBA110_190:
case base::RcuMode::HBA170_230:
case base::RcuMode::HBA210_270:
return GetHBA0Axes();
break;
default:
throw std::runtime_error("Wrong RCU mode");
}
}
std::vector<casacore::MPosition> GetArrayFromMode(base::RcuMode mode) {
switch (mode.mode) {
case base::RcuMode::LBAInner10_90:
case base::RcuMode::LBAInner30_90:
case base::RcuMode::LBAOuter10_90:
case base::RcuMode::LBAOuter30_90:
return GetLBAPositions();
break;
case base::RcuMode::HBA110_190:
case base::RcuMode::HBA170_230:
case base::RcuMode::HBA210_270:
return GetHBAPositions();
default:
throw std::runtime_error("Wrong RCU mode");
}
}
private:
struct Array {
std::string name;
std::string band;
std::vector<double> data;
};
void ParseFile() {
Next();
struct Array a;
while (ReadArray(a.name, a.band, a.data)) {
std::string key;
if (a.band.empty())
key = a.name;
else
key = a.band + "_" + a.name;
values_.insert(std::make_pair(key, a));
}
}
const std::vector<double> &GetArray(const std::string &name) const {
return values_.find(name)->second.data;
}
std::vector<casacore::MPosition> GetPositions(
const std::string &arrayName) const {
const std::vector<double> &arr = GetArray(arrayName);
std::vector<casacore::MPosition> position;
for (size_t index = 0; index < arr.size(); index += 6) {
position.emplace_back(casacore::MPosition{
casacore::MVPosition{arr[index], arr[index + 1], arr[index + 2]},
casacore::MPosition::ITRF});
}
return position;
}
std::array<double, 9> GetAxes(const std::string &arrayName) const {
const std::vector<double> &arr = GetArray(arrayName);
if (arr.size() != 9)
throw std::runtime_error(
"The array for coordinate axes in the antenna "
"config file had an incorrect size");
std::array<double, 9> axes;
for (size_t index = 0; index < 9; ++index) axes[index] = arr[index];
return axes;
}
bool Next() {
if (line_.empty() || line_position_ >= line_.size()) {
do {
std::getline(file_, line_);
if (!file_) {
token_.clear();
line_.clear();
return false;
}
Trim(line_);
} while (line_.empty() || line_[0] == '#');
line_position_ = 0;
}
size_t pos = line_.find_first_of(" \t", line_position_);
if (pos == std::string::npos) {
token_ = line_.substr(line_position_);
line_.clear();
line_position_ = 0;
return true;
} else {
token_ = line_.substr(line_position_, pos - line_position_);
line_position_ = pos + 1;
while (line_position_ < line_.size() &&
(line_[line_position_] == ' ' || line_[line_position_] == '\t'))
++line_position_;
Trim(token_);
if (token_.empty())
return Next();
else
return true;
}
}
std::vector<int> ReadDimensions() {
std::vector<int> dimensions = {std::atoi(token_.c_str())};
do {
if (!Next())
throw std::runtime_error(
"Antenna config file has bad format: expected dimensions");
if (token_ == "x") {
if (!Next())
throw std::runtime_error(
"Antenna config file has bad format: "
"expected another dimension after x");
int dimension_value = std::atoi(token_.c_str());
dimensions.push_back(dimension_value);
} else if (token_ != "[") {
throw std::runtime_error("Antenna config file has bad format");
}
} while (token_ != "[");
return dimensions;
}
const std::vector<double> ReadData(const std::vector<int> &dimensions) {
int count = std::accumulate(dimensions.begin(), dimensions.end(), 1,
[](int a, int b) { return a * b; });
std::vector<double> values(count);
for (int i = 0; i != count; ++i) {
if (!Next()) {
throw std::runtime_error("Missing numbers");
}
values[i] = std::atof(token_.c_str());
}
Next(); // move TO ']'
return values;
}
bool ReadArray(std::string &name, std::string &band,
std::vector<double> &values) {
values.clear();
if (!std::isalpha(token_[0])) return false;
name = token_;
Next();
if (std::isalpha(token_[0])) {
band = token_;
Next();
} else {
band.clear();
}
std::vector<int> dimensions1 = ReadDimensions();
std::vector<double> data1 = ReadData(dimensions1);
if (Next() && token_[0] >= '0' && token_[0] <= '9') {
std::vector<int> dimensions2 = ReadDimensions();
std::vector<double> data2 = ReadData(dimensions2);
Next(); // skip ']'
values = std::move(data2);
for (size_t i = 0; i != values.size(); ++i)
values[i] += data1[i % data1.size()];
} else {
values = std::move(data1);
}
return true;
}
std::map<std::string, Array> values_;
std::ifstream file_;
std::string line_;
size_t line_position_;
std::string token_;
};
} // namespace dp3::aartfaacreader
#endif // AARTFAACREADER_ANTENNACONFIG_H_
#include <aartfaacreader/AntennaConfig.h>
#include <base/RcuMode.h>
#include <casacore/measures/Measures/MPosition.h>
#include <filesystem>
#include <fstream>
#include <array>
#include <boost/filesystem.hpp> // for the unique_path generation
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
namespace {
bool ComparePositions(const casacore::MPosition &left,
const casacore::MPosition &right,
const casacore::MPosition &reference) {
return left.getValue() == (right.getValue() + reference.getValue());
}
casacore::MPosition PositionFromITRFCoordinates(double x, double y, double z) {
return casacore::MPosition(casacore::MVPosition{x, y, z},
casacore::MPosition::ITRF);
}
void TestPosition(const casacore::MPosition &left,
const casacore::MPosition &right,
const casacore::MPosition &reference) {
BOOST_CHECK(ComparePositions(left, right, reference));
}
void TestArrayPositions(
const std::vector<casacore::MPosition> &positions,
const std::vector<casacore::MPosition> &expected_position,
const casacore::MPosition &reference_antenna) {
BOOST_CHECK_EQUAL(positions.size(), expected_position.size());
for (size_t i = 0; i < expected_position.size(); i++) {
TestPosition(positions[i], expected_position[i], reference_antenna);
}
}
const int kAxesDimension = 3 * 3;
void TestAxes(const std::array<double, kAxesDimension> &axes,
const std::array<double, kAxesDimension> &expected_axes) {
for (int i = 0; i < kAxesDimension; i++) {
BOOST_CHECK_EQUAL(axes[i], expected_axes[i]);
}
}
const std::string configuration_example =
"\
#\n\
# AntennaPositions for AARTFAAC-12 LBA_OUTER antennas\n\
# ITRF2005 target_date = 2015.5\n\
# Created: 2023-10-16 14:35:38\n\
#\n\
\n\
NORMAL_VECTOR LBA\n\
3 [ 0.598753 0.072099 0.797682 ]\n\
\n\
ROTATION_MATRIX LBA\n\
3 x 3 [\n\
-0.1195950000 -0.7919540000 0.5987530000 \n\
0.9928230000 -0.0954190000 0.0720990000 \n\
0.0000330000 0.6030780000 0.7976820000 \n\
]\n\
\n\
LBA\n\
3 [ 3826577.462000000 461022.624000000 5064892.526 ]\n\
4 x 2 x 3 [\n\
2.38300 -17.79500 -0.18000 2.38300 -17.79500 -0.18000\n\
0.95600 -20.19400 1.10800 0.95600 -20.19400 1.10800\n\
-10.83100 -14.47100 9.43800 -10.83100 -14.47100 9.43800\n\
-15.87100 0.64500 11.85500 -15.87100 0.64500 11.85500\n\
]\n\
\n\
HBA\n\
3 [ 3826577.462000000 461022.624000000 5064892.526 ]\n\
4 x 2 x 3 [\n\
-2.38300 -17.79500 -0.18000 -2.38300 -17.79500 -0.18000\n\
-0.95600 -20.19400 1.10800 -0.95600 -20.19400 1.10800\n\
+10.83100 -14.47100 9.43800 +10.83100 -14.47100 9.43800\n\
+15.87100 0.64500 11.85500 +15.87100 0.64500 11.85500\n\
]\n\
\n\
NORMAL_VECTOR HBA0\n\
3 [ 0.598753 0.072099 0.797682 ]\n\
\n\
ROTATION_MATRIX HBA0\n\
3 x 3 [\n\
-0.1195950000 -0.7919540000 0.5987530000 \n\
0.9928230000 -0.0954190000 0.0720990000 \n\
0.0000330000 0.6030780000 0.7976820000 \n\
]\n\
\n\
HBA0\n\
3 [ 0.000000000 0.000000000 0.000 ]\n\
\n\
NORMAL_VECTOR HBA1\n\
3 [ 0.598753 0.072099 0.797682 ]\n\
\n\
ROTATION_MATRIX HBA1\n\
3 x 3 [\n\
-0.1195950000 -0.7919540000 0.5987530000 \n\
0.9928230000 -0.0954190000 0.0720990000 \n\
0.0000330000 0.6030780000 0.7976820000 \n\
]\n\
\n\
HBA1\n\
3 [ 0.000000000 0.000000000 0.000 ]\n\
\n";
struct AntennaConfigFixture {
AntennaConfigFixture() {
const std::filesystem::path tmp_path =
std::filesystem::temp_directory_path() /
boost::filesystem::unique_path("tmp%%%%%%%.config").string();
path = tmp_path.string();
std::ofstream config;
config.open(path);
config << configuration_example;
config.close();
};
~AntennaConfigFixture() { std::filesystem::remove(path); };
std::string path;
};
const casacore::MPosition kLbaReference{PositionFromITRFCoordinates(
3826577.462000000, 461022.62400000, 5064892.526)};
const std::vector<casacore::MPosition> kLbaShifts = {
PositionFromITRFCoordinates(2.38300, -17.79500, -0.18000),
PositionFromITRFCoordinates(0.95600, -20.19400, 1.10800),
PositionFromITRFCoordinates(-10.83100, -14.47100, 9.43800),
PositionFromITRFCoordinates(-15.87100, 0.64500, 11.85500)};
const casacore::MPosition kHbaReference{PositionFromITRFCoordinates(
3826577.462000000, 461022.62400000, 5064892.526)};
const std::vector<casacore::MPosition> kHbaShifts = {
PositionFromITRFCoordinates(-2.38300, -17.79500, -0.18000),
PositionFromITRFCoordinates(-0.95600, -20.19400, 1.10800),
PositionFromITRFCoordinates(+10.83100, -14.47100, 9.43800),
PositionFromITRFCoordinates(+15.87100, 0.64500, 11.85500)};
const std::array<double, kAxesDimension> kLbaAxes{
-0.1195950000, -0.7919540000, 0.5987530000, 0.9928230000, -0.0954190000,
0.0720990000, 0.0000330000, 0.6030780000, 0.7976820000};
const std::array<double, kAxesDimension> kHba0Axes{
-0.1195950000, -0.7919540000, 0.5987530000, 0.9928230000, -0.0954190000,
0.0720990000, 0.0000330000, 0.6030780000, 0.7976820000};
const std::array<double, kAxesDimension> kHba1Axes{
-0.1195950000, -0.7919540000, 0.5987530000, 0.9928230000, -0.0954190000,
0.0720990000, 0.0000330000, 0.6030780000, 0.7976820000};
const int kFirstLbaMode = 1;
const int kLastLbaMode = 4;
const int kFirstHbaMode = 5;
const int kLastHbaMode = 7;
} // namespace
BOOST_AUTO_TEST_SUITE(aartfaacantennaconfig)
BOOST_FIXTURE_TEST_CASE(constructor_and_parsing, AntennaConfigFixture) {
BOOST_REQUIRE_NO_THROW(
dp3::aartfaacreader::AntennaConfig antennaConfig(path.c_str()));
};
BOOST_FIXTURE_TEST_CASE(get_lba_positions, AntennaConfigFixture) {
dp3::aartfaacreader::AntennaConfig antennaConfig(path.c_str());
std::vector<casacore::MPosition> positions = antennaConfig.GetLBAPositions();
TestArrayPositions(positions, kLbaShifts, kLbaReference);
}
BOOST_FIXTURE_TEST_CASE(get_hba_positions, AntennaConfigFixture) {
dp3::aartfaacreader::AntennaConfig antennaConfig(path.c_str());
std::vector<casacore::MPosition> positions = antennaConfig.GetHBAPositions();
TestArrayPositions(positions, kHbaShifts, kHbaReference);
}
BOOST_FIXTURE_TEST_CASE(get_lba_axes, AntennaConfigFixture) {
dp3::aartfaacreader::AntennaConfig antennaConfig(path.c_str());
std::array<double, kAxesDimension> lba_axes = antennaConfig.GetLBAAxes();
TestAxes(lba_axes, kLbaAxes);
}
BOOST_FIXTURE_TEST_CASE(get_hba0_axes, AntennaConfigFixture) {
dp3::aartfaacreader::AntennaConfig antennaConfig(path.c_str());
std::array<double, kAxesDimension> hba0_axes = antennaConfig.GetHBA0Axes();
TestAxes(hba0_axes, kHba0Axes);
}
BOOST_FIXTURE_TEST_CASE(get_hba1_axes, AntennaConfigFixture) {
dp3::aartfaacreader::AntennaConfig antennaConfig(path.c_str());
std::array<double, kAxesDimension> hba1_axes = antennaConfig.GetHBA1Axes();
TestAxes(hba1_axes, kHba1Axes);
}
BOOST_FIXTURE_TEST_CASE(get_axes_from_mode, AntennaConfigFixture) {
dp3::aartfaacreader::AntennaConfig antennaConfig(path.c_str());
for (int i = kFirstLbaMode; i <= kLastLbaMode; i++) {
TestAxes(antennaConfig.GetAxesFromMode(dp3::base::RcuMode::FromNumber(i)),
kLbaAxes);
}
for (int i = kFirstHbaMode; i <= kLastHbaMode; i++) {
TestAxes(antennaConfig.GetAxesFromMode(dp3::base::RcuMode::FromNumber(i)),
kHba0Axes);
}
BOOST_CHECK_THROW(
antennaConfig.GetAxesFromMode(dp3::base::RcuMode::FromNumber(0)),
std::runtime_error);
}
BOOST_FIXTURE_TEST_CASE(get_array_from_mode, AntennaConfigFixture) {
dp3::aartfaacreader::AntennaConfig antennaConfig(path.c_str());
for (int i = kFirstLbaMode; i <= kLastLbaMode; i++) {
TestArrayPositions(
antennaConfig.GetArrayFromMode(dp3::base::RcuMode::FromNumber(i)),
kLbaShifts, kLbaReference);
}
for (int i = kFirstHbaMode; i <= kLastHbaMode; i++) {
TestArrayPositions(
antennaConfig.GetArrayFromMode(dp3::base::RcuMode::FromNumber(i)),
kHbaShifts, kHbaReference);
}
BOOST_CHECK_THROW(
antennaConfig.GetArrayFromMode(dp3::base::RcuMode::FromNumber(0)),
std::runtime_error);
}
BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment