Program Listing for File antenna.cc¶
↰ Return to documentation for file (measurements/antenna.cc)
#include "lupnt/measurements/antenna.h"
#include <filesystem>
#include <iostream>
#include <sstream>
#include "lupnt/core/file.h"
#include "lupnt/numerics/interpolation.h"
#include "lupnt/numerics/math_utils.h"
namespace lupnt {
void Antenna::FormatAntennaPattern(std::vector<double>& phi, std::vector<double>& theta,
std::vector<std::vector<double>>& gain) {
// Format theta to [0, 360] and phi to [-180, 180]
if (theta.front() == 0 && theta.back() <= 360 && phi.back() <= 180) {
if (phi.back() >= 80 && phi.back() < 90) {
phi.push_back(90);
gain.push_back(gain.front());
}
if (phi.back() >= 170 && phi.back() < 180) {
phi.push_back(180);
gain.push_back(gain.front());
}
phi_max_ = phi.back();
if (theta.back() < 360) {
theta.push_back(360);
for (auto& g : gain) g.push_back(g.front());
}
} else if (theta.front() == -180 && theta.back() == 180 && phi.front() == 0
&& phi.back() == 180) {
std::vector<double> theta_tmp, g_tmp;
std::vector<std::vector<double>> gain_tmp;
for (size_t i = 0; i < phi.size(); i++) {
g_tmp.clear();
for (size_t j = 0; j < theta.size(); j++) {
if (theta[j] >= 0) { // [0, 180]
if (i == 0) theta_tmp.push_back(theta[j]);
g_tmp.push_back(gain[i][j]);
}
}
for (size_t j = 1; j < theta.size(); j++) {
if (theta[j] <= 0) { // (180, 360]
if (i == 0) theta_tmp.push_back(theta[j] + 360);
g_tmp.push_back(gain[i][j]);
}
}
gain_tmp.push_back(g_tmp);
}
phi_max_ = phi.back();
theta = theta_tmp;
gain = gain_tmp;
} else {
throw std::runtime_error("Invalid antenna pattern");
}
}
void Antenna::LoadAntennaPattern() {
// Check for omni-directional antenna
if (name_ == "") {
n_dim_ = 0;
return;
}
// File
std::filesystem::path filepath = GetFilePath(name_);
std::ifstream file = OpenFile<std::ifstream>(filepath);
// std::cout << "Loading antenna pattern from " + filepath.string() + " ..." << std::endl;
// Header
std::string line;
while (std::getline(file, line)) {
if (line.empty()) continue; // skip blank lines safely
if (line[0] != '%') break; // first non-comment line
}
// 2D antenna pattern
// ----------------------------------
// -50 0.0 az2 ... azM
// 0.0 gain1,1 gain1,2 ... gain1,M
// . .
// . .
// elN gainN,1 gainN,2 ... gainN,M
//
// 1D antenna pattern
// ----------------------------------
// 0.0 gain1
// . .
// . .
// angN gainN
// Read the first line
std::stringstream ss(line);
std::string token;
std::vector<double> tokens;
while (std::getline(ss, token, ',')) tokens.push_back(std::stod(token));
// Check if 1D or 2D
if (tokens.size() == 2) {
// 1D pattern
n_dim_ = 1;
std::vector<double> phi, gain;
while (std::getline(file, line)) {
ss = std::stringstream(line);
tokens.clear();
while (std::getline(ss, token, ',')) tokens.push_back(std::stod(token));
phi.push_back(tokens[0]);
gain.push_back(tokens[1]);
}
gain_ = MatXd::Zero(phi.size(), 1);
phi_ = VecXd::Zero(phi.size());
for (size_t i = 0; i < gain.size(); i++) {
gain_(i, 0) = gain[i];
phi_(i) = WrapToPi(phi[i] * RAD).val();
}
} else {
// 2D
n_dim_ = 2;
std::vector<double> phi, theta;
std::vector<std::vector<double>> gain;
for (size_t i = 1; i < tokens.size(); i++) theta.push_back(tokens[i]);
while (std::getline(file, line)) {
ss = std::stringstream(line);
tokens.clear();
while (std::getline(ss, token, ',')) tokens.push_back(std::stod(token));
phi.push_back(tokens[0]);
gain.push_back(std::vector<double>(tokens.begin() + 1, tokens.end()));
}
// Format theta to [0, 360] and phi to [-180, 180]
FormatAntennaPattern(phi, theta, gain);
// Pattern
gain_ = MatXd::Zero(phi.size(), theta.size());
phi_ = VecXd::Zero(phi.size());
theta_ = VecXd::Zero(theta.size());
for (size_t i = 0; i < gain.size(); i++) {
phi_(i) = phi[i];
for (size_t j = 0; j < gain[i].size(); j++) gain_(i, j) = gain[i][j];
}
for (size_t i = 0; i < theta.size(); i++) theta_(i) = theta[i];
}
file.close();
}
Real Antenna::ComputeGain(Real theta, Real phi) const {
Real gain = 0.0;
if (n_dim_ == 0) return gain; // Omni-directional antenna
theta = WrapToTwoPi(theta) * DEG;
phi = WrapToPi(phi) * DEG;
if (phi > phi_max_ || phi < -phi_max_) return NAN; // Minimum angle
if (phi < 0 && phi_(0) >= 0) phi = -phi; // Symmetry
// Interpolation
if (n_dim_ == 2) {
gain = LinearInterp2d(phi_, theta_, gain_, phi.val(), theta.val());
} else if (n_dim_ == 1) {
gain = LinearInterp1d(phi_, gain_, phi.val());
}
return gain;
}
VEC_IMP_REAL_REAL(Antenna::ComputeGain)
Real Antenna::ComputeGain(const Vec3& direction) const {
Vec3 dir = direction.normalized();
// Compute phiation and thetauth angles
Real phi = acos(dir.dot(Vec3::UnitZ()));
Real theta = atan2(dir.y(), dir.x());
return ComputeGain(theta, phi);
}
} // namespace lupnt