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

Merge branch 'padre-add-antenna-config' into 'master'

Add Antenna field and unittest

See merge request RD/DP3!1220
parents 0a4074bd 919cc6d1
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.
Finish editing this message first!
Please register or to comment