AntennaType.cc
Go to the documentation of this file.
1 #include <rdet/AntennaType.h>
2 #include <det/VManager.h>
3 #include <det/Detector.h>
4 
5 #include <utl/AugerException.h>
6 
7 #include <cmath>
8 #include <sstream>
9 
10 using namespace det;
11 using namespace std;
12 using namespace utl;
13 
14 
15 namespace rdet {
16 
17  std::map<std::string, AntennaPattern> AntennaType::fgAntennaPattern; // global variable that holds the antenna pattern
18 
19  AntennaType::AntennaType(const string& antennaType)
20  {
21  fAntennaType = antennaType;
22  }
23 
24 
25  void
26  AntennaType::BufferAntennaPattern()
27  {
28  if (fgAntennaPattern.find(fAntennaType) == fgAntennaPattern.end()) { // check if AntennaPattern has been buffered before
29  AntennaPattern antennaPattern;
30  std::map<ResponseKey, VectorEffectiveLength> antennaResponse;
31  // User Information which a lot is going to appear on the screen:
32  ostringstream message;
33  message << "Buffering Antenna Pattern for " << fAntennaType;
34  INFO(message);
35 
36  utl::Validated<vector<string>> frequenciesS; //fetch the frequencies as string
37  GetAntennaData(frequenciesS, "frequency"); //cause they are used as identifier
38 
39  utl::Validated<vector<double>> frequencies; //fetch the frequencies as double
40  GetAntennaData(frequencies, "frequency"); //-- don't want to cheat the unit system ;)
41  antennaPattern.frequency_lower_bound = frequencies.Get().front();
42  antennaPattern.frequency_upper_bound = frequencies.Get().back();
43  antennaPattern.frequencies = frequencies.Get();
44  antennaPattern.nFrequency = antennaPattern.frequencies.size();
45  message.str("");
46  message << "buffering frequencies:";
47  for (std::vector<double>::iterator it = antennaPattern.frequencies.begin();
48  it != antennaPattern.frequencies.end(); ++it) {
49  message << ' ' << *it / utl::MHz;
50  }
51  message << " MHz";
52  INFO(message);
53 
54  // import zenith angles
55  /* the import of the zenith angles is a complicated because in the antennamodel xml file the phi
56  * and theta list define pairs. Thus, the theta list contains all theta angles nPhi times.
57  * With the following algorithm the number of theta samples is determined and a std::vector
58  * containing all theta angles is filled.
59  * */
60  utl::Validated<vector<double>> vTheta; //fetch the according theta angles
61  GetAntennaData(vTheta, "theta");
62  antennaPattern.theta_lower_bound = vTheta.Get().front();
63  antennaPattern.theta_upper_bound = vTheta.Get().back();
64  unsigned int nTheta = 0;
65  bool first_iteration = true;
66  double theta_0 = vTheta.Get().at(0);
67  for (unsigned int i = 0; i < vTheta.Get().size(); ++i) {
68  const double theta = vTheta.Get().at(i);
69  if (first_iteration && theta == theta_0 && i) {
70  nTheta = i;
71  first_iteration = false;
72  }
73  if (first_iteration) {
74  antennaPattern.thetaAngles.push_back(theta);
75  } else { // check with remaining theta angles if theta angles were imported correctly
76  if (antennaPattern.thetaAngles.at(i % nTheta) != theta) {
77  ERROR("error in importing zenith angles");
78  throw utl::AugerException("error in importing zenith angles");
79  }
80  }
81  }
82  antennaPattern.nTheta = nTheta;
83 
84  message.str("");
85  message << "buffering theta angles:";
86  for (std::vector<double>::iterator it = antennaPattern.thetaAngles.begin();
87  it != antennaPattern.thetaAngles.end(); ++it) {
88  message << ' ' << *it / utl::degree;
89  }
90  message << " degree";
91  INFO(message);
92 
93  // determine nPhi
94  /* The size of the phi and theta list of the antenna-model xml file is the product of the
95  * number of theta samples and the number of phi samples. Thus, knowing nTheta, nPhi can be
96  * calculated.
97  * */
98  const int nTotal = vTheta.Get().size();
99  antennaPattern.nPhi = nTotal / nTheta;
100  if (antennaPattern.nPhi * antennaPattern.nTheta != nTotal) {
101  ERROR("error in determining number of azimuth samples");
102  throw utl::AugerException("error in determining number of azimuth samples");
103  }
104  message.str("");
105  message << "nPhi has been determined to " << antennaPattern.nPhi
106  << "\t nTheta has been determined to " << antennaPattern.nTheta;
107  INFO(message);
108 
109  // read in phi angles
110  utl::Validated<vector<double>> vPhi; //fetch the phi angles where the spheres
111  GetAntennaData(vPhi, "phi"); //have been simulated
112  antennaPattern.phi_lower_bound = vPhi.Get().front();
113  antennaPattern.phi_upper_bound = vPhi.Get().back();
114  for (int iPhi = 0; iPhi < antennaPattern.nPhi; ++iPhi)
115  antennaPattern.phiAngles.push_back(vPhi.Get().at(iPhi * antennaPattern.nTheta));
116 
117  message.str("");
118  message << "buffering phi angles:";
119  for (std::vector<double>::iterator it = antennaPattern.phiAngles.begin();
120  it != antennaPattern.phiAngles.end(); ++it) {
121  message << ' ' << *it / utl::degree;
122  }
123  message << " degree";
124  INFO(message);
125 
126  for (unsigned int iFrequency = 0; iFrequency < frequenciesS.Get().size(); ++iFrequency) {
127  utl::Validated<vector<double>> sphereEAHTheta_amp; //...
128  utl::Validated<vector<double>> sphereEAHTheta_phase; //fetching the
129  utl::Validated<vector<double>> sphereEAHPhi_amp; //sphere data with
130  utl::Validated<vector<double>> sphereEAHPhi_phase; //frequency string as id
131  GetAntennaData(sphereEAHTheta_amp, "EAHTheta_amp", frequenciesS.Get().at(iFrequency));
132  GetAntennaData(sphereEAHTheta_phase, "EAHTheta_phase", frequenciesS.Get().at(iFrequency));
133  GetAntennaData(sphereEAHPhi_amp, "EAHPhi_amp", frequenciesS.Get().at(iFrequency));
134  GetAntennaData(sphereEAHPhi_phase, "EAHPhi_phase", frequenciesS.Get().at(iFrequency));
135 
136  for (int iPhi = 0; iPhi < antennaPattern.nPhi; ++iPhi) {
137  const double current_phi = antennaPattern.phiAngles.at(iPhi);
138  //message.str("");
139  //message << "buffering antenna pattern for phi = " << current_phi / utl::degree;
140  //INFO(message.str().c_str());
141  for (int iTheta = 0; iTheta < antennaPattern.nTheta; ++iTheta) {
142  // check if we are still at the correct phi angle
143  //message.str("");
144  //message << "theta = " << fThetaAngles.at(iTheta) / utl::degree;
145  //message << "\t(index = " << iPhi * fNTheta + iTheta << " phi = "
146  // << phi.Get().at(iPhi * fNTheta + iTheta) << " theta = "
147  // << theta.Get().at(iPhi * fNTheta + iTheta) << ")";
148  //INFO(message);
149  if (current_phi != vPhi.Get().at(iPhi * antennaPattern.nTheta + iTheta)) {
150  throw utl::AugerException("phi angle has changed during theta loop");
151  }
152 
153  const float Phi_amp = sphereEAHPhi_amp.Get().at(iPhi * antennaPattern.nTheta + iTheta);
154  const float Phi_phase = sphereEAHPhi_phase.Get().at(iPhi * antennaPattern.nTheta + iTheta);
155  const float Theta_amp = sphereEAHTheta_amp.Get().at(iPhi * antennaPattern.nTheta + iTheta);
156  const float Theta_phase = sphereEAHTheta_phase.Get().at(iPhi * antennaPattern.nTheta + iTheta);
157 
159  vel.Phi = std::polar(Phi_amp, Phi_phase);
160  vel.Theta = std::polar(Theta_amp, Theta_phase);
161  ResponseKey key(iFrequency, iTheta, iPhi);
162  //cout << "(" << iFrequency << ", " << iTheta << ", " << iPhi << ") = " << VEL.Phi_amp
163  // << ", " << VEL.Phi_phase << ", " << VEL.Theta_amp << ", " << VEL.Theta_phase << endl;
164  antennaResponse[key] = vel;
165  }
166  }
167  }
168  antennaPattern.AntennaResponse = antennaResponse;
169 
170  // calculate integrated antenna response
171  for (unsigned int iFrequency = 0; iFrequency < frequenciesS.Get().size(); ++iFrequency) {
172  const double integratedVEL =
173  CalculateIntegratedEffectiveAntennaHeight(antennaResponse, iFrequency, antennaPattern.nTheta, antennaPattern.nPhi);
174  antennaPattern.meanTransfer.push_back(integratedVEL);
175  }
176 
177  //utl::Validated<vector<double>> MeanTransfer; // fetch the mean transfer
178  //GetAntennaData(MeanTransfer, "MeanTransfer");
179  //antennaPattern.meanTransfer = MeanTransfer.Get();
180 
181  fgAntennaPattern[fAntennaType] = antennaPattern;
182  message.str("");
183  message << "finished buffering antenna " << fAntennaType;
184  INFO(message);
185  }
186  }
187 
188 
189  std::pair<std::complex<double>, std::complex<double>>
190  AntennaType::GetElectricFieldResponse(const double theta, const double phi, const double freq, const std::string& interpolationMode)
191  {
192  //The antenna patterns go from 0 -> 360 deg in azimuth. Make sure we are in that range
193  double matchedPhi = phi;
194  while (matchedPhi < 0)
195  matchedPhi += 360. * degree;
196  while (matchedPhi >= 360. * degree)
197  matchedPhi -= 360. * degree;
198 
199  if (interpolationMode == "Lookup") {
200  return GetElectricFieldResponse_lookup(theta, matchedPhi, freq);
201  } else if (interpolationMode == "LinearInterpolation") {
202  return GetElectricFieldResponse_LinearInterpolation(theta, matchedPhi, freq);
203  } else {
204  ostringstream message;
205  message << "interpolation mode " << interpolationMode << " unknown";
206  throw utl::AugerException(message.str().c_str());
207  }
208  }
209 
210 
211  std::pair<std::complex<double>, std::complex<double>>
212  AntennaType::GetComplexRepresentationOfVectorEffectiveLength(const VectorEffectiveLength& vel)
213  {
214  return std::pair<std::complex<double>, std::complex<double>>(vel.Theta, vel.Phi);
215  }
216 
217 
218  double
219  AntennaType::InterpolateLinear(const double x, const double x_low, const double x_up,
220  const double y_low, const double y_up)
221  {
222  if (x_low == x_up)
223  return y_low;
224 
225  const double result = y_low + (y_up - y_low) * (x - x_low) / (x_up - x_low);
226  if (std::isnan(result)) {
227  ostringstream msg;
228  msg << "linear interpolation results in NaN:\n"
229  "\tx = " << x << "\n"
230  "\tx_low = " << x_low << "\n"
231  "\tx_up = " << x_up << "\n"
232  "\ty_low = " << y_low << "\n"
233  "\ty_up = " << y_up << "\n"
234  "\tresult = " << result << '\n';
235  throw AugerException(msg.str());
236  }
237  return result;
238  }
239 
240 
241  std::complex<float>
242  AntennaType::InterpolateLinear(const float x, const float x_low, const float x_up,
243  const std::complex<float>& y_low, const std::complex<float>& y_up)
244  {
245  if (x_low == x_up)
246  return y_low;
247 
248  const std::complex<float> result = y_low + (y_up - y_low) * ((x - x_low) / (x_up - x_low));
249 
250  if (std::isnan(std::abs(result))) {
251  ostringstream msg;
252  msg << "linear interpolation results in NaN:\n"
253  "\tx = " << x << "\n"
254  "\tx_low = " << x_low << "\n"
255  "\tx_up = " << x_up << "\n"
256  "\ty_low = " << y_low << "\n"
257  "\ty_up = " << y_up << "\n"
258  "\tresult = " << result << '\n';
259  throw AugerException(msg.str());
260  }
261 
262  return result;
263  }
264 
265 
267  AntennaType::InterpolateLinear(const double x, const double x_low, const double x_up,
268  const VectorEffectiveLength& vel_low, const VectorEffectiveLength& vel_up)
269  {
270  const auto theta = InterpolateLinear(x, x_low, x_up, vel_low.Theta, vel_up.Theta);
271  const auto phi = InterpolateLinear(x, x_low, x_up, vel_low.Phi, vel_up.Phi);
272  return VectorEffectiveLength{theta, phi};
273  }
274 
275 
276  std::pair<std::complex<double>, std::complex<double>>
277  AntennaType::GetElectricFieldResponse_lookup(const double theta, const double phi, const double freq)
278  {
279  BufferAntennaPattern();
280  const double theta_lower_bound = fgAntennaPattern[fAntennaType].theta_lower_bound;
281  const double theta_upper_bound = fgAntennaPattern[fAntennaType].theta_upper_bound;
282  const double phi_lower_bound = fgAntennaPattern[fAntennaType].phi_lower_bound;
283  const double phi_upper_bound = fgAntennaPattern[fAntennaType].phi_upper_bound;
284  const double frequency_lower_bound = fgAntennaPattern[fAntennaType].frequency_lower_bound;
285  const double frequency_upper_bound = fgAntennaPattern[fAntennaType].frequency_upper_bound;
286  const int nTheta = fgAntennaPattern[fAntennaType].nTheta;
287  const int nPhi = fgAntennaPattern[fAntennaType].nPhi;
288  const int nFrequency = fgAntennaPattern[fAntennaType].nFrequency;
289 
290  const int iTheta =
291  int(round((theta - theta_lower_bound) / (theta_upper_bound - theta_lower_bound) * (nTheta - 1)));
292  const int iPhi = int(round((phi - phi_lower_bound) / (phi_upper_bound - phi_lower_bound) * (nPhi - 1)));
293  const int iFrequency =
294  int(round( (freq - frequency_lower_bound) / (frequency_upper_bound - frequency_lower_bound) * (nFrequency - 1)));
295  ResponseKey key(iFrequency, iTheta, iPhi);
296  VectorEffectiveLength& vel = fgAntennaPattern[fAntennaType].AntennaResponse.at(key);
297  //cout << "f=" << freq / utl::MHz << "MHz theta=" << theta / utl::deg << " phi=" << phi / utl::deg
298  // << " :" << VEL.Phi_amp << ", " << VEL.Phi_phase / utl::deg << ", " << VEL.Theta_amp << ", "
299  // << VEL.Theta_phase / utl::deg << endl;
300  return GetComplexRepresentationOfVectorEffectiveLength(vel);
301  }
302 
303 
304  std::pair<std::complex<double>, std::complex<double>>
305  AntennaType::GetElectricFieldResponse_LinearInterpolation(const double theta, const double phi, const double freq)
306  {
307  BufferAntennaPattern();
308 
309  const double theta_lower_bound = fgAntennaPattern[fAntennaType].theta_lower_bound;
310  const double theta_upper_bound = fgAntennaPattern[fAntennaType].theta_upper_bound;
311  const double phi_lower_bound = fgAntennaPattern[fAntennaType].phi_lower_bound;
312  const double phi_upper_bound = fgAntennaPattern[fAntennaType].phi_upper_bound;
313  const double frequency_lower_bound = fgAntennaPattern[fAntennaType].frequency_lower_bound;
314  const double frequency_upper_bound = fgAntennaPattern[fAntennaType].frequency_upper_bound;
315 
316  if ((freq < frequency_lower_bound || freq > frequency_upper_bound) ||
317  (phi < phi_lower_bound || phi > phi_upper_bound) ||
318  (theta < theta_lower_bound || theta > theta_upper_bound)) {
319  ostringstream msg;
320  msg << "frequency (" << freq / utl::MHz << "MHz) or zenith (" << theta / utl::degree
321  << "deg) or azimuth (" << phi / utl::degree
322  << "deg) angle requested that is not contained in the antenna pattern, returning 0";
323  WARNING(msg);
324  return std::pair<std::complex<double>, std::complex<double>>(std::complex<double>(0, 0),
325  std::complex<double>(0, 0));
326  }
327 
328  const int nTheta = fgAntennaPattern[fAntennaType].nTheta;
329  const int nPhi = fgAntennaPattern[fAntennaType].nPhi;
330  const int nFrequency = fgAntennaPattern[fAntennaType].nFrequency;
331  const std::vector<double>& frequencies = fgAntennaPattern[fAntennaType].frequencies;
332  const std::vector<double>& thetaAngles = fgAntennaPattern[fAntennaType].thetaAngles;
333  const std::vector<double>& phiAngles = fgAntennaPattern[fAntennaType].phiAngles;
334 
335  const int iTheta_lower =
336  int(floor((theta - theta_lower_bound) / (theta_upper_bound - theta_lower_bound) * (nTheta - 1)));
337  const double theta_lower = thetaAngles.at(iTheta_lower);
338  const int iTheta_upper =
339  int(ceil((theta - theta_lower_bound) / (theta_upper_bound - theta_lower_bound) * (nTheta - 1)));
340  const double theta_upper = thetaAngles.at(iTheta_upper);
341  const int iPhi_lower =
342  int(floor((phi - phi_lower_bound) / (phi_upper_bound - phi_lower_bound) * (nPhi - 1)));
343  const double phi_lower = phiAngles.at(iPhi_lower);
344  const int iPhi_upper =
345  int(ceil((phi - phi_lower_bound) / (phi_upper_bound - phi_lower_bound) * (nPhi - 1)));
346  const double phi_upper = phiAngles.at(iPhi_upper);
347  const int iFrequency_lower =
348  int(floor((freq - frequency_lower_bound) / (frequency_upper_bound - frequency_lower_bound) * (nFrequency - 1)));
349  const double frequency_lower = frequencies.at(iFrequency_lower);
350  const int iFrequency_upper =
351  int( ceil((freq - frequency_lower_bound) / (frequency_upper_bound - frequency_lower_bound) * (nFrequency - 1)));
352  const double frequency_upper = frequencies.at(iFrequency_upper);
353  const std::map<ResponseKey, VectorEffectiveLength>& antennaResponse =
354  fgAntennaPattern[fAntennaType].AntennaResponse;
355 
356  // lower frequency bound
357  // theta low
358  const VectorEffectiveLength vel_freq_low_theta_low =
359  InterpolateLinear(
360  phi, phi_lower, phi_upper,
361  antennaResponse.at(ResponseKey(iFrequency_lower, iTheta_lower, iPhi_lower)),
362  antennaResponse.at(ResponseKey(iFrequency_lower, iTheta_lower, iPhi_upper))
363  );
364 
365  // theta up
366  const VectorEffectiveLength vel_freq_low_theta_up =
367  InterpolateLinear(
368  phi, phi_lower, phi_upper,
369  antennaResponse.at(ResponseKey(iFrequency_lower, iTheta_upper, iPhi_lower)),
370  antennaResponse.at(ResponseKey(iFrequency_lower, iTheta_upper, iPhi_upper))
371  );
372 
373  const VectorEffectiveLength vel_freq_low =
374  InterpolateLinear(theta, theta_lower, theta_upper, vel_freq_low_theta_low, vel_freq_low_theta_up);
375 
376  // upper frequency bound
377  // theta low
378  const VectorEffectiveLength vel_freq_up_theta_low =
379  InterpolateLinear(
380  phi, phi_lower, phi_upper,
381  antennaResponse.at(ResponseKey(iFrequency_upper, iTheta_lower, iPhi_lower)),
382  antennaResponse.at(ResponseKey(iFrequency_upper, iTheta_lower, iPhi_upper))
383  );
384 
385  // theta up
386  const VectorEffectiveLength vel_freq_up_theta_up =
387  InterpolateLinear(
388  phi, phi_lower, phi_upper,
389  antennaResponse.at(ResponseKey(iFrequency_upper, iTheta_upper, iPhi_lower)),
390  antennaResponse.at(ResponseKey(iFrequency_upper, iTheta_upper, iPhi_upper))
391  );
392 
393  const VectorEffectiveLength vel_freq_up =
394  InterpolateLinear(theta, theta_lower, theta_upper, vel_freq_up_theta_low, vel_freq_up_theta_up);
395 
396  const VectorEffectiveLength vel =
397  InterpolateLinear(freq, frequency_lower, frequency_upper, vel_freq_low, vel_freq_up);
398  //cout << "f=" << freq / utl::MHz << "MHz theta=" << theta / utl::deg << " phi=" << phi / utl::deg
399  // << " :" << VEL.Phi_amp << ", " << VEL.Phi_phase / utl::deg << ", " << VEL.Theta_amp << ", "
400  // << VEL.Theta_phase / utl::deg << endl;
401  return GetComplexRepresentationOfVectorEffectiveLength(vel);
402  }
403 
404 
405  double
406  AntennaType::GetIntegratedEffectiveAntennaHeight(const double freq)
407  {
408  BufferAntennaPattern();
409  const double frequency_lower_bound = fgAntennaPattern[fAntennaType].frequency_lower_bound;
410  const double frequency_upper_bound = fgAntennaPattern[fAntennaType].frequency_upper_bound;
411  //ostringstream msg;
412  if (freq < frequency_lower_bound || freq > frequency_upper_bound) {
413  //msg << "frequency (" << freq / utl::MHz
414  // << "MHz) requested that is not contained in the antenna pattern, returning 0";
415  //WARNING(msg);
416  return 0;
417  }
418 
419  const int nFrequency = fgAntennaPattern[fAntennaType].nFrequency;
420  const std::vector<double>& frequencies = fgAntennaPattern[fAntennaType].frequencies;
421 
422  const int iFrequency_lower =
423  int(floor((freq - frequency_lower_bound) / (frequency_upper_bound - frequency_lower_bound) * (nFrequency - 1)));
424  const double frequency_lower = frequencies.at(iFrequency_lower);
425  const int iFrequency_upper =
426  int(ceil((freq - frequency_lower_bound) / (frequency_upper_bound - frequency_lower_bound) * (nFrequency - 1)));
427  const double frequency_upper = frequencies.at(iFrequency_upper);
428 
429  //msg.str("");
430  //msg << "requesting frequency " << freq / utl::MHz << " MHz -> frequency bin " << iFrequency_lower
431  // << " and " << iFrequency_upper;
432  //INFO(msg.str());
433 
434  return InterpolateLinear(freq, frequency_lower, frequency_upper,
435  fgAntennaPattern[fAntennaType].meanTransfer.at(iFrequency_lower),
436  fgAntennaPattern[fAntennaType].meanTransfer.at(iFrequency_upper));
437  }
438 
439 
440  double
441  AntennaType::CalculateIntegratedEffectiveAntennaHeight(const std::map<ResponseKey, VectorEffectiveLength>& antennaResponse,
442  const int iFreq, const int nTheta, const int nPhi)
443  {
444  double integratedVEL = 0;
445  for (int iTheta = 0; iTheta < nTheta; ++iTheta) {
446  for (int iPhi = 0; iPhi < nPhi; ++iPhi) {
447  VectorEffectiveLength vel = antennaResponse.at(ResponseKey(iFreq, iTheta, iPhi));
448  integratedVEL += sqrt(std::norm(vel.Phi) + std::norm(vel.Theta));
449  }
450  }
451  integratedVEL *= 2 * M_PI / (nTheta * nPhi * sqrt(2));
452  return integratedVEL;
453  }
454 
455 
456  void
457  AntennaType::NotFoundAndExit(const string& msg)
458  const
459  {
460  ostringstream err;
461  err << "Did not find requested component: " << msg;
462  ERROR(err);
463  exit(EXIT_FAILURE);
464  }
465 }
const double degree
std::complex< float > Theta
Definition: AntennaType.h:32
Base class for all exceptions used in the auger offline code.
constexpr double MHz
Definition: AugerUnits.h:159
std::map< ResponseKey, VectorEffectiveLength > AntennaResponse
Definition: AntennaType.h:51
int freq
Definition: dump1090.h:244
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
int exit
Definition: dump1090.h:237
Wrapper class for initially unset data.
Definition: ResponseMap.h:17
std::vector< double > meanTransfer
Definition: AntennaType.h:50
double abs(const SVector< n, T > &v)
const Data result[]
constexpr double degree
#define WARNING(message)
Macro for logging warning messages.
Definition: ErrorLogger.h:163
std::vector< double > frequencies
Definition: AntennaType.h:47
double frequency_upper_bound
Definition: AntennaType.h:46
boost::tuple< int, int, int > ResponseKey
Definition: AntennaType.h:17
double norm(utl::Vector x)
std::complex< float > Phi
Definition: AntennaType.h:33
std::vector< double > thetaAngles
Definition: AntennaType.h:48
std::vector< double > phiAngles
Definition: AntennaType.h:49
double frequency_lower_bound
Definition: AntennaType.h:45
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165

, generated on Tue Sep 26 2023.