RdAntennaChannelToStationConverter.cc
Go to the documentation of this file.
2 
3 #include <complex>
4 #include <cmath>
5 
6 #include <evt/ShowerRecData.h>
7 #include <evt/ShowerRRecData.h>
8 #include <evt/ShowerSRecData.h>
9 #include <evt/ShowerFRecData.h>
10 #include <revt/StationRecData.h>
11 
12 #include <evt/Event.h>
13 #include <revt/REvent.h>
14 #include <revt/Header.h>
15 #include <revt/Station.h>
16 #include <revt/Channel.h>
17 #include <evt/ShowerSimData.h>
18 
19 #include <utl/TraceAlgorithm.h>
20 #include <utl/ErrorLogger.h>
21 #include <utl/Reader.h>
22 #include <utl/config.h>
23 #include <utl/AugerUnits.h>
24 #include <utl/MathConstants.h>
25 #include <utl/CoordinateSystemPtr.h>
26 #include <utl/StringCompare.h>
27 #include <utl/RadioGeometryUtilities.h>
28 #include <utl/PhysicalFunctions.h>
29 
30 #include <fwk/MagneticFieldModel.h>
31 
32 #include <det/Detector.h>
33 
34 #include <rdet/RDetector.h>
35 #include <rdet/Station.h>
36 #include <rdet/Channel.h>
37 
38 // atmosphere
39 #include <atm/Atmosphere.h>
40 #include <atm/ProfileResult.h>
41 
42 #include <fwk/CentralConfig.h>
43 
44 #include <fevt/FEvent.h>
45 #include <fevt/Eye.h>
46 #include <fevt/EyeRecData.h>
47 
48 using namespace std;
49 using namespace utl;
50 using namespace fwk;
51 using namespace revt;
52 using namespace fevt;
53 
55 
57 {
58  Branch topBranch = CentralConfig::GetInstance()->GetTopBranch("RdAntennaChannelToStationConverter");
59  topBranch.GetChild("InfoLevel").GetData(fInfoLevel);
60  INFODebug("");
61 
62  string usedDirectionStr = topBranch.GetChild("UsedDirection").Get<string>();
63 
64  if (StringEquivalent(usedDirectionStr, "RdDirection")) {
65  fUsedDirection = eRdReconstruction;
66  } else if (StringEquivalent(usedDirectionStr, "SdReconstruction")) {
67  fUsedDirection = eSdReconstruction;
68  } else if (StringEquivalent(usedDirectionStr, "FdReconstruction")) {
69  fUsedDirection = eFdReconstruction;
70  } else if (StringEquivalent(usedDirectionStr, "McTruth")) {
71  fUsedDirection = eMcTruth;
72  } else if (StringEquivalent(usedDirectionStr, "IndividualPerStation")) {
73  fUsedDirection = eIndividualPerStation;
74  } else if (StringEquivalent(usedDirectionStr, "Reference")) {
75  fUsedDirection = eReference;
76  } else if (StringEquivalent(usedDirectionStr, "LineOfSightStationShowerMaximum")) {
77  fUsedDirection = eLineOfSightStationShowerMaximum;
78  }
79 
80  fDirectionOfXmax = topBranch.GetChild("DirectionOfXmax").Get<string>();
81  fXmaxEstimator = topBranch.GetChild("XmaxEstimator").Get<string>();
82 
83  topBranch.GetChild("CropFrequencies").GetData(fCropFreq);
84  topBranch.GetChild("ExtendLowerLimitPercentage").GetData(fLowSmear);
85  topBranch.GetChild("ExtendUpperLimitPercentage").GetData(fUpSmear);
86  topBranch.GetChild("ZeroLimit").GetData(fZeroLimit);
87  topBranch.GetChild("InterpolationMode").GetData(fInterpolationMode);
88 
89  topBranch.GetChild("rejectSaturatedStations").GetData(fRejectSaturatedStations);
90 
91  topBranch.GetChild("calculateWeightsFromExpectedEField").GetData(fCalculateWeightsFromExpectedEField);
92  topBranch.GetChild("calculateWeightsFromAntennaPattern").GetData(fCalculateWeightsFromAntennaPattern);
93  topBranch.GetChild("useUserDefinedWeight").GetData(fUseUserDefinedWeights);
94  ostringstream sstr;
95  if (fUseUserDefinedWeights) {
96  topBranch.GetChild("weight12").GetData(fWeight12);
97  topBranch.GetChild("weight13").GetData(fWeight13);
98  topBranch.GetChild("weight23").GetData(fWeight23);
99  sstr << "\n\tUser defined weights for the calculation of the electric field out of 3 channel"
100  << " will be used, weights are:"
101  << " w12: " << fWeight12 << ", w13: " << fWeight13 << ", w23: " << fWeight23;
102  INFOFinal(sstr);
103  }
104 
105  string whichEye = topBranch.GetChild("UsedEye").Get<string>();
106  if (whichEye == "CO")
107  fWhichEye = 4;
108  else if (whichEye == "HE")
109  fWhichEye = 5;
110  else if (whichEye == "HECO")
111  fWhichEye = 6;
112  else
113  ERROR("<UsedEye> has invalid config.");
114 
115  sstr.str("");
116  sstr << "\n\tUse direction : " << usedDirectionStr
117  << "\n\tUse interpolation method : " << fInterpolationMode
118  << "\n\tReject saturated stations : " << fRejectSaturatedStations << endl;
119  INFOFinal(sstr);
120 
121  if ((fCalculateWeightsFromAntennaPattern + fCalculateWeightsFromExpectedEField + fUseUserDefinedWeights) != 1) {
122  ERROR("Only one option to calculate weights must be chooses.");
123  return eFailure;
124  }
125 
126  return eSuccess;
127 }
128 
129 
131 RdAntennaChannelToStationConverter::Run(evt::Event& event)
132 {
133  // check if radio event information exists
134  if (!event.HasREvent()) {
135  ERROR("Event has no radio event!");
136  return eContinueLoop;
137  }
138 
139  ostringstream msg;
140 
141  // Declare some variable which will be used inside a loop to save time
142  double channelfrequency = 0.;
143  Vector3C V3CZero; // neutral 3d complex vector for initialisations
144 
145  // temporal dummy variable holding the result of the E-Field calculation.
146  // Later transferred to the station frequency spectrum
147  Vector3C efield;
148  pair<complex<double>, complex<double> > channel1Response;
149  pair<complex<double>, complex<double> > channel2Response;
150  V3CZero = complex<double>(0.0), complex<double>(0.0), complex<double>(0.0);
151  bool e1flag = true;
152  bool e2flag = true;
153 
154  // set the direction up with the fall-back values
155  const utl::CoordinateSystemPtr referenceCS = det::Detector::GetInstance()
156  .GetReferenceCoordinateSystem();
157 
158  double azimuth = 0.;
159  double zenith = 0.;
160  utl::Vector showerAxis;
161 
162  // Get direction to be used to apply the antenna pattern.
163  // If the function returns false, something went wrong.
164  if (!GetDirection(event, showerAxis, azimuth, zenith))
165  return eFailure;
166 
167  REvent& rEvent = event.GetREvent();
168  const rdet::RDetector& rDetector = det::Detector::GetInstance().GetRDetector();
169 
170  // counter. counts how many times the inversion of
171  // the linear equation system has failed
172  unsigned int inversionfail = 0;
173 
174  for (auto& station : rEvent.StationsRange()) {
175 
176  StationFrequencySpectrum& stationspectrum = station.GetStationFrequencySpectrum();
177 
178  // initialize station frequency spectrum
179  for (StationFrequencySpectrum::SizeType i = 0; i < stationspectrum.GetSize(); ++i) {
180  stationspectrum[i][0] = complex<double>(0.0, 0.0);
181  stationspectrum[i][1] = complex<double>(0.0, 0.0);
182  stationspectrum[i][2] = complex<double>(0.0, 0.0);
183  }
184 
185  // identify active channels
186  vector<revt::Channel> usableChannels;
187  for (auto& channel : station.ChannelsRange()){
188  if (channel.IsActive()) {
189  usableChannels.push_back(channel);
190 
191  if (channel.IsSaturated()) {
192  station.SetSaturated();
193  msg.str("");
194  msg << "detected saturated channel " << channel.GetId()
195  << " for station " << station.GetId() << ".";
196 
197  if(fRejectSaturatedStations) {
198  msg << " Rejecting station.";
199  station.SetRejectedReason(revt::eSaturated);
200  }
201 
202  INFOFinal(msg);
203  }
204  }
205  }
206 
207  // if station has no channels (i.e. no data) exclude it ( in general this should not happen)
208  if (usableChannels.size() == 0) {
209  msg.str("");
210  msg << "Station " << station.GetId()
211  << " is excluded as it does not have any active channels"
212  << " (actually this should never happen!)";
213  WARNING(msg);
214  station.SetExcludedReason(revt::eHardwareMismatch);
215  continue;
216  }
217 
218  // if individual directions for each station are used, get this direction now
219  if (fUsedDirection == eIndividualPerStation) {
220 
221  if (!station.HasRecData())
222  ERROR("Radio station does not have RecData, but it should have by now...");
223  const StationRecData& recStation = station.GetRecData();
224 
225  if (!recStation.HasParameter(eSignalArrivalDirectionX) ||
226  !recStation.HasParameter(eSignalArrivalDirectionY) ||
227  !recStation.HasParameter(eSignalArrivalDirectionZ)) {
228  msg.str("");
229  msg << "Module is configured to use individual signal arrival directions for each station, "
230  << "but no direction is set for station " << station.GetId() << ".";
231  ERROR(msg);
232  return eFailure;
233  } else {
234  azimuth = recStation.GetSignalArrivalAzimuth();
235  zenith = recStation.GetSignalArrivalZenith();
236  }
237  } else if (fUsedDirection == eLineOfSightStationShowerMaximum) {
238 
239  Point xMaxCore;
240  Vector xMaxAxis;
241 
242  if (fDirectionOfXmax == "MC") {
243  if (event.HasSimShower()) {
244  xMaxCore = event.GetSimShower().GetPosition();
245  xMaxAxis = -event.GetSimShower().GetDirection();
246  } else {
247  ERROR("Module is configured to use Monte Carlo truth direction, but event has no SimShower!");
248  return eFailure;
249  }
250  } else if (event.HasRecShower()) {
251  if (fDirectionOfXmax == "Sd") {
252  if (!event.GetRecShower().HasSRecShower()) {
253  ERROR("Module is configured to use SD direction, but event has no SRecShower!");
254  return eFailure;
255  } else {
256  xMaxCore = event.GetRecShower().GetSRecShower().GetCorePosition();
257  xMaxAxis = event.GetRecShower().GetSRecShower().GetAxis();
258  }
259  }
260  else if (fDirectionOfXmax == "Reference") {
261  evt::ShowerRRecData& rrecShower = event.GetRecShower().GetRRecShower();
262  xMaxAxis = rrecShower.GetReferenceAxis(event);
263  xMaxCore = rrecShower.GetReferenceCorePosition(event);
264  } else {
265  ERROR("The configured DirectionOfXmax was invalid.");
266  return eFailure;
267  }
268  } else {
269  ERROR("The configured DirectionOfXmax was invalid.");
270  return eFailure;
271  }
272 
273  // initiate atm + tables (which model is used is decieded in the config)
274  const atm::Atmosphere& theAtm = det::Detector::GetInstance().GetAtmosphere();
275  theAtm.InitSlantProfileModel(xMaxCore, xMaxAxis, 5 * g / cm2);
276  const atm::ProfileResult& distanceFromDepth = theAtm.EvaluateDistanceVsSlantDepth();
277 
278  // Estimate Xmax to estimate start value for dmax
279  const double showerXmaxGuess = GetXmaxEstimator(event);
280  if (isnan(showerXmaxGuess) || showerXmaxGuess / (g/cm2) <= 1) {
281  msg.str("");
282  msg << "Could not get xmax estimator \"" << fXmaxEstimator << "\": "
283  << showerXmaxGuess / (g / cm2);
284  WARNING(msg);
285  return eFailure;
286  }
287 
288  // distance to shower maximum, yep, the result is negative, convention thing...
289  const double xMaxDistance = abs(distanceFromDepth.Y(showerXmaxGuess));
290  const Point xMaxPos = xMaxCore + xMaxAxis * xMaxDistance;
291 
292  const rdet::Station& detStation = rDetector.GetStation(station);
293  const Point& stationPos = detStation.GetPosition();
294  const CoordinateSystemPtr stationCS =
296 
297  const Vector lineXmaxStation = xMaxPos - stationPos;
298  azimuth = lineXmaxStation.GetPhi(stationCS);
299  zenith = lineXmaxStation.GetTheta(stationCS);
300 
301  }
302 
303  // check for invalid values (NANs) - can happen when the direction reconstruction
304  // failed but the reconstruction is continued
305  if (std::isnan(azimuth) || std::isnan(zenith)) {
306  msg.str("");
307  msg << "Illegal azimuth / zenith: " << azimuth / degree << " / " << zenith / degree
308  << " deg. Check (reference) source / reconstruction.";
309  ERROR(msg);
310  return eContinueLoop;
311  }
312 
313  if (usableChannels.size() == 2) {
314  INFOIntermediate("Only two channels selected, setting weight12 to 1, the other weights to zero");
315  fWeight12 = 1.;
316  fWeight13 = 0;
317  fWeight23 = 0;
318  } else if (usableChannels.size() == 3) {
319 
320  // calculate weights out of expected efield vector
321  if (fCalculateWeightsFromExpectedEField) {
322  utl::Vector magneticFieldAxis =
323  event.GetRecShower().GetRRecShower().GetMagneticFieldVector();
325  showerAxis, magneticFieldAxis);
326 
327  msg.str("");
328  msg << "Shower axis is (" << showerAxis.GetR(referenceCS) << ", "
329  << showerAxis.GetTheta(referenceCS) << ", " << showerAxis.GetPhi(referenceCS) << ")";
330  INFODebug(msg);
331 
332  msg.str("");
333  msg << "Efield expectation is (" << efieldExpectation.GetR(referenceCS) << ", "
334  << efieldExpectation.GetTheta(referenceCS) << ", "
335  << efieldExpectation.GetPhi(referenceCS) << ")";
336  INFODebug(msg);
337 
338  const revt::Channel& channel1 = usableChannels.at(0);
339  const revt::Channel& channel2 = usableChannels.at(1);
340  const revt::Channel& channel3 = usableChannels.at(2);
341  const rdet::Channel& cDetChannel1 = rDetector.GetStation(station.GetId()).GetChannel(
342  channel1.GetId());
343  const rdet::Channel& cDetChannel2 = rDetector.GetStation(station.GetId()).GetChannel(
344  channel2.GetId());
345  const rdet::Channel& cDetChannel3 = rDetector.GetStation(station.GetId()).GetChannel(
346  channel3.GetId());
347 
348  utl::Vector orientationChannel1 = utl::Vector(1., cDetChannel1.GetOrientationZenith(),
349  cDetChannel1.GetOrientationAzimuth(),
350  referenceCS, utl::Vector::kSpherical);
351  utl::Vector orientationChannel2 = utl::Vector(1., cDetChannel2.GetOrientationZenith(),
352  cDetChannel2.GetOrientationAzimuth(),
353  referenceCS, utl::Vector::kSpherical);
354  utl::Vector orientationChannel3 = utl::Vector(1., cDetChannel3.GetOrientationZenith(),
355  cDetChannel3.GetOrientationAzimuth(),
356  referenceCS, utl::Vector::kSpherical);
357 
358  double weight1 = efieldExpectation * orientationChannel1;
359  double weight2 = efieldExpectation * orientationChannel2;
360  double weight3 = efieldExpectation * orientationChannel3;
361  msg.str("");
362  msg << "weight 1 = " << weight1 << " weight2 = " << weight2 << " weight3 = " << weight3;
363  INFODebug(msg);
364 
365  fWeight12 = sqrt(weight1 * weight1 + weight2 * weight2); // weight for channel 1 and 2
366  fWeight13 = sqrt(weight1 * weight1 + weight3 * weight3); // weight for channel 1 and 3
367  fWeight23 = sqrt(weight2 * weight2 + weight3 * weight3); // weight for channel 2 and 3
368 
369  msg.str("");
370  msg << "using expected e-field vector to calculate weights\n"
371  << "\tweight12 = " << fWeight12 << "\n"
372  << "\tweight23 = " << fWeight23 << "\n"
373  << "\tweight13 = " << fWeight13 << "";
374  INFOIntermediate(msg);
375 
376  } else if (!fUseUserDefinedWeights) {
377  ERROR("Calculation of weights for 3 usable channels is only supported, if "
378  "'CalculateWeightsFromExpectedEField' or 'fUseUserDefinedWeights' "
379  "is set in XML configuration.");
380  return eFailure;
381  }
382 
383  } else {
384  msg.str("");
385  msg << "Station " << station.GetId()
386  << " does not have exactly two or three active channels! Have you forgotten"
387  << " to include the RdChannelSelector?";
388  ERROR(msg);
390  "RdAntennaChannelToStationConverter does not support stations with"
391  " number of Channels != 2 or != 3.");
392  }
393 
394  for (int iPolarization = 0; iPolarization < 3; ++iPolarization) {
395  // stop loop if there is nothing to do
396  if (iPolarization == 0 && fWeight12 == 0) {
397  continue;
398  }
399  if (iPolarization == 1 && fWeight13 == 0) {
400  continue;
401  }
402  if (iPolarization == 2 && fWeight23 == 0) {
403  continue;
404  }
405 
406  short channelId1 = 0;
407  short channelId2 = 0;
408  if (iPolarization == 0) {
409  channelId1 = 0;
410  channelId2 = 1;
411  } else if (iPolarization == 1) {
412  channelId1 = 0;
413  channelId2 = 2;
414  } else if (iPolarization == 2) {
415  channelId1 = 1;
416  channelId2 = 2;
417  }
418 
419  const revt::Channel& channel1 = usableChannels.at(channelId1);
420  const revt::Channel& channel2 = usableChannels.at(channelId2);
421 
422  // reference to the first channel frequency spectrum for calculation
423  const ChannelFrequencySpectrum& channel1spectrum =
424  channel1.GetChannelFrequencySpectrum();
425  // reference to the second channel frequency spectrum for calculation
426  const ChannelFrequencySpectrum& channel2spectrum =
427  channel2.GetChannelFrequencySpectrum();
428 
429  // check if both channel frequency spectra have the same size and binning
430  // maybe we have to check CloseTo<...> for binning?
431  if ((channel1spectrum.GetSize() != channel2spectrum.GetSize())
432  || (channel1spectrum.GetBinning() != channel2spectrum.GetBinning())
433  || (channel1.GetNyquistZone() != channel2.GetNyquistZone())) {
434  if (fInfoLevel >= eInfoIntermediate) {
435  msg.str("");
436  msg << "The 2 channels of station " << channel1.GetStationId()
437  << " have different sizes, binnings or Nyquist zones!";
438  WARNING(msg);
439  }
440  return eFailure;
441  }
442 
443  // initialize empty station frequency spectrum
444  stationspectrum = StationFrequencySpectrum(channel1spectrum.GetSize(),
445  channel1spectrum.GetBinning(), V3CZero);
446 
447  // set the Nyquist zone of the station
448  station.SetNyquistZone(channel1.GetNyquistZone());
449 
450  // up to here only the station and the station frequency spectrum are initialized
451 
452  // access to the channel detector information of channel 1
453  const rdet::Channel& cDetChannel1 = rDetector.GetStation(station.GetId()).GetChannel(
454  channel1.GetId());
455  // access to the channel detector information of channel 2
456  const rdet::Channel& cDetChannel2 = rDetector.GetStation(station.GetId()).GetChannel(
457  channel2.GetId());
458 
459  // for later check if in frequency range
460  const double lowerfrequency = cDetChannel1.GetDesignLowerFreq() / 100 * (100 - fLowSmear);
461  // given percentage smaller than actual frequency limit is accepted
462  const double upperfrequency = cDetChannel1.GetDesignUpperFreq() / 100 * (100 + fUpSmear);
463  // given percentage bigger than actual frequency limit is accepted
464 
465  const bool zenithBelowHorizon = zenith < 90. * degree;
466 
467  if (!zenithBelowHorizon) {
468  msg.str("");
469  msg << "Station " << station.GetId()
470  << ": Zenith bigger than 90 degree, will use antenna response for 89 degree.";
471  WARNING(msg);
472  }
473 
474  // loop over the station frequency spectrum
475  // for every bin in the station frequency spectrum, the "real" e-Field is calculated
476  // from the channel frequency spectra
477  for (StationFrequencySpectrum::SizeType i = 0; i < stationspectrum.GetSize(); ++i) {
478 
479  // access to the frequency of the first channel at bin i
480  channelfrequency = channel1.GetFrequencyOfBin(i);
481  // needed for the call to the response function of the channel
482 
483  if ((!fCropFreq) || ((channelfrequency > lowerfrequency) && (channelfrequency < upperfrequency))) {
484 
485  // retrieval of the channel response
486  // (pair of complex number / for each E-Field component one)
487  // for channel 1
488  if (zenithBelowHorizon) {
489  channel1Response = cDetChannel1.GetElectricFieldResponse(zenith, azimuth,
490  channelfrequency,
491  fInterpolationMode);
492  // for channel 2
493  channel2Response = cDetChannel2.GetElectricFieldResponse(zenith, azimuth,
494  channelfrequency,
495  fInterpolationMode);
496  } else {
497  // for channel 1
498  channel1Response = cDetChannel1.GetElectricFieldResponse(89. * degree, azimuth,
499  channelfrequency,
500  fInterpolationMode);
501  // for channel 2
502  channel2Response = cDetChannel2.GetElectricFieldResponse(89. * degree, azimuth,
503  channelfrequency,
504  fInterpolationMode);
505  }
506 
507  stringstream sstr;
508  sstr.str("");
509  sstr << "electric field response for " << zenith / utl::deg << " , " << azimuth / utl::deg
510  << ", " << channelfrequency / utl::MHz << "), channel1 = "
511  << abs(channel1Response.first) << ", " << abs(channel1Response.second)
512  << " channel 2 = " << abs(channel2Response.first) << ", "
513  << abs(channel2Response.second);
514  INFODebug(sstr.str());
515 
516  // calculate weights out of channel response
517  if (fCalculateWeightsFromAntennaPattern) {
518  WARNING("This option <calculateWeightsFromAntennaPattern> is not implemented yet.");
519  }
520 
521  // calculation of the clear E-Field without the channel response
522  // we solve the linear equation system in two ways.
523  // first checking if one of the response values is close to 0.
524  // in this case one of the two equations can be solved for one component
525  // imediately, makeing it easier to solve the other one.
526  // additionally it is checked, if a division of 0 (near 0 values of
527  // the response) rises, and if that can be avoided by rearranging
528  // the other function, thus preventing the division.
529  // This is possible since the linear equation system can be solved
530  // by either rearranging the first equation and put it into the second
531  // OR by rearranging the second equation and put it into the first.
532  // Both are valid methods to solve the system.
533  //
534  // if the inversion is not possible, it is asumed, that the
535  // corresponding efield component is 0
536  // Keep this in mind:
537  // IT IS NOT NECESSARILY TRUE THAT THE E-FIELD COMPONENT IS 0
538  // JUST BECAUSE THERE WAS NO SOLUTION FOR THE LINEAR EQUATION SYSTEM
539 
540  // Initialisation of the E-Field values to be calculated
541  efield[0] = complex<double>(0.0, 0.0);
542  efield[1] = complex<double>(0.0, 0.0);
543 
544  // in a first step we try to solve the linear equation system by
545  // putting the rearranged first equation into the second
546  // if this fails we later try to put the second equation into
547  // the first one
548  //
549  // Flags, indicating if the first or the second efield component
550  // can be determined directly by the first equation
551  e1flag = true;
552  e2flag = true;
553  // flag indicating if the inversion of the equation system failed
554  // inversionflag = false;
555  // check if the first response of the first equation is near 0,
556  // and if so, calculate directly the result for the second
557  // efield component
558  if (abs(channel1Response.first) < fZeroLimit
559  && abs(channel1Response.second) > fZeroLimit) {
560  efield[1] = complex<double>(channel1spectrum[i] / channel1Response.second);
561  e2flag = false;
562  }
563  // check if the second response of the first equation is near 0,
564  // and if so, calculate directly the result for the first
565  // efield component
566  if (abs(channel1Response.second) < fZeroLimit
567  && abs(channel1Response.first) > fZeroLimit) {
568  efield[0] = complex<double>(channel1spectrum[i] / channel1Response.first);
569  e1flag = false;
570  }
571 
572  // checking if the second efield component was calculated directly,
573  // if not calculate it now
574  if (e2flag) {
575  // check if the divisor is near 0 to prevent a division by 0,
576  // if yes then raise a response warning, since the inversion
577  // is not possible
578  if (abs(
579  channel1Response.first * channel2Response.second
580  - channel1Response.second * channel2Response.first) < fZeroLimit) {
581  efield[1] = complex<double>(0.0, 0.0);
582  // count up the response warnings.
583  //inversionflag = true;
584  ++inversionfail;
585  }
586  // if no then calculate the second component of the efield
587  else {
588  efield[1] = complex<double>(
589  (channel1Response.first * channel2spectrum[i]
590  - channel1spectrum[i] * channel2Response.first)
591  / (channel1Response.first * channel2Response.second
592  - channel1Response.second * channel2Response.first));
593  }
594  }
595 
596  // checking if the first efield component was calculated directly,
597  // if not calculate it now
598  if (e1flag) {
599  // check if the divisor is near 0 to prevent a division by 0,
600  // if yes then raise a response warning, since the inversion
601  // is not possible
602  if (abs(channel1Response.first) < fZeroLimit) {
603  efield[0] = complex<double>(0.0, 0.0);
604  // count up the response warnings.
605  //inversionflag = true;
606  ++inversionfail;
607  }
608  // if no then calculate the first component of the efield
609  else {
610  efield[0] = complex<double>(
611  (channel1spectrum[i] / channel1Response.first)
612  - (channel1Response.second * efield[1] / channel1Response.first));
613  }
614  }
615 
616  // if the equation system was not solved up to now, due to a divison
617  // by 0 we have to try the second option: rearranging equation two and
618  // putting it into the first equation
619  if (inversionfail) {
620 
621  // check if the first response of the second equation is near 0,
622  // and if so, calculate directly the result for the second
623  // efield component
624  if (abs(channel2Response.first) < fZeroLimit
625  && abs(channel2Response.second) > fZeroLimit) {
626  efield[1] = complex<double>(channel2spectrum[i] / channel2Response.second);
627  e2flag = false;
628  }
629 
630  // check if the second response of the second equation is near 0,
631  // and if so, calculate directly the result for the second
632  // efield component
633  if (abs(channel2Response.second) < fZeroLimit
634  && abs(channel2Response.first) > fZeroLimit) {
635  efield[0] = complex<double>(channel2spectrum[i] / channel2Response.first);
636  e1flag = false;
637  }
638 
639  // checking if the second efield component was calculated directly,
640  // if not, calculate it now
641  if (e2flag) {
642  // check if the divisor is near 0 to prevent a division by 0,
643  // if yes then raise a response warning, since the inversion
644  // is not possible
645  if (abs(
646  channel2Response.first * channel1Response.second
647  - channel2Response.second * channel1Response.first) < fZeroLimit) {
648  efield[1] = complex<double>(0.0, 0.0);
649  // count up the response warnings.
650  ++inversionfail;
651  }
652  // if no then calculate the first component of the efield
653  else {
654  efield[1] = complex<double>(
655  (channel2Response.first * channel1spectrum[i]
656  - channel2spectrum[i] * channel1Response.first)
657  / (channel2Response.first * channel1Response.second
658  - channel2Response.second * channel1Response.first));
659  }
660  }
661  // checking if the first efield component was calculated directly,
662  // if not, calculate it now
663  if (e1flag) {
664  // check if the divisor is near 0 to prevent a division by 0,
665  // if yes then raise a response warning, since the inversion
666  // is not possible
667  if (abs(channel2Response.first) < fZeroLimit) {
668  efield[0] = complex<double>(0.0, 0.0);
669  // count up the response warnings.
670  ++inversionfail;
671  }
672  // if no then calculate the first component of the efield
673  else {
674  efield[0] = complex<double>(
675  (channel2spectrum[i] / channel2Response.first)
676  - (channel2Response.second * efield[1] / channel2Response.first));
677  }
678  }
679  }
680 
681  } else {
682  efield[0] = complex<double>(0.0, 0.0);
683  efield[1] = complex<double>(0.0, 0.0);
684  }
685 
686  // set the third component of the E-Field to 0. Just initialisation
687  efield[2] = complex<double>(0.0, 0.0);
688 
689  // rotation of the coordinate system from
690  // the shower coordinate system (as presented here) to the
691  // global coordinate system at the station
692 
693  // rotation around y-axis
694  efield = rotateYaxis(efield, zenith);
695  // rotation around z-axis
696  efield = rotateZaxis(efield, azimuth);
697 
698  // transfer of the calculated E-Field at bin (frequency) i
699  // to the station frequency spectrum at bin i.
700  double sumOfWeights = fWeight12 + fWeight13 + fWeight23;
701  if (iPolarization == 0) {
702  efield *= fWeight12 / sumOfWeights;
703  stationspectrum[i] += efield;
704  }
705  if (iPolarization == 1) {
706  efield *= fWeight13 / sumOfWeights;
707  stationspectrum[i] += efield;
708  }
709  if (iPolarization == 2) {
710  efield *= fWeight23 / sumOfWeights;
711  stationspectrum[i] += efield;
712  }
713  } // station frequency spectrum loop end
714  } // loop 3D-Antenna ends
715  } // station loop end
716 
717  // output of the response warnings during the processing of the run
718  if (inversionfail != 0) {
719  if (fInfoLevel >= eInfoIntermediate) {
720  msg << "Linear equation solver failed for " << inversionfail << " frequencies!\n"
721  << "This is in general no problem,"
722  << "if the linear equation system could be solved"
723  << "for at least some frequencies." << endl;
724  WARNING(msg);
725  }
726  msg.str("");
727  }
728 
729  return eSuccess;
730 }
731 
732 // comment by TH: It could make sense to move the rotation functionality into the SVector class
733 // (for the specialized case of 3 components) and also use it from inside the
734 // RdAntennaStationToChannelConverter module.
735 
736 // function to invert the transformation to the shower coordinate system.
737 // first part, rotation around y-axis.
738 // separated into two functions for better transparency and flexibility
739 utl::Vector3C RdAntennaChannelToStationConverter::rotateYaxis(utl::Vector3C vecC, double angle)
740 {
741  utl::Vector3C resC;
742 
743  // transformation matrix used with input vector
744  // canonical rotation matrix around the Y axis
745  resC[0] = complex<double>(vecC[0] * cos(angle) + vecC[2] * sin(angle));
746  resC[1] = complex<double>(vecC[1]);
747  resC[2] = complex<double>(vecC[2] * cos(angle) - vecC[0] * sin(angle));
748 
749  return resC;
750 }
751 
752 // function to invert the transformation to the shower coordinate system.
753 // second part, rotation around z-axis.
754 // separated into two functions for better transparency and flexibility
755 utl::Vector3C RdAntennaChannelToStationConverter::rotateZaxis(utl::Vector3C vecC, double angle)
756 {
757  utl::Vector3C resC;
758 
759  // transformation matrix used with input vector
760  // canonical rotation matrix around the X axis
761  resC[0] = complex<double>(vecC[0] * cos(angle) - vecC[1] * sin(angle));
762  resC[1] = complex<double>(vecC[0] * sin(angle) + vecC[1] * cos(angle));
763  resC[2] = complex<double>(vecC[2]);
764 
765  return resC;
766 
767 }
768 
769 VModule::ResultFlag RdAntennaChannelToStationConverter::Finish()
770 {
771  INFODebug("");
772  return eSuccess;
773 }
774 
775 bool RdAntennaChannelToStationConverter::GetDirection(
776  const evt::Event& event, utl::Vector& showerAxis, double& azimuth, double& zenith)
777  const
778 {
779  CoordinateSystemPtr coordinateOriginCS;
780  /* Yes... for the options "eReference", "eSdReconstruction",
781  "eFdReconstruction" the coordinate origin stored in the RRecShower
782  is used to calculate azimuth and zenith angle. If "eMcTruth" take the MC core.
783  If "eRdReconstruction" take azimuth and zenith stored in RRecShower. */
784  if (fUsedDirection == eReference || fUsedDirection == eSdReconstruction ||
785  fUsedDirection == eFdReconstruction || fUsedDirection == eLineOfSightStationShowerMaximum) {
786  coordinateOriginCS = fwk::LocalCoordinateSystem::Create(
788  }
789 
790  // check which direction is to be used (for eIndividualPerStation, look further down)
791  if (fUsedDirection == eIndividualPerStation) {
792  /* If individual stations are used, then take the shower axis from the radio reconstruction:
793  The shower axis will be needed to calculate the magnetic field expectation (Lorentz angle etc.) */
794  if (event.GetRecShower().HasRRecShower()) {
795  INFOIntermediate("Using shower axis from radio reconstruction, "
796  "but individual arrival directions for each station");
797  showerAxis = event.GetRecShower().GetRRecShower().GetAxis();
798  } else {
799  ERROR("Module is configured to use individual direction, which still take the overall "
800  " shower axis as RD direction, but event has no RRecShower!");
801  // This may not cause a eFailure. The shower Axis is only needed if number of channels != 2.
802  }
803  }
804  else if (fUsedDirection == eReference || fUsedDirection == eLineOfSightStationShowerMaximum) {
805  // See comments in xml.in
806  showerAxis = event.GetRecShower().GetRRecShower().GetReferenceAxis(event);
807  azimuth = showerAxis.GetPhi(coordinateOriginCS);
808  zenith = showerAxis.GetTheta(coordinateOriginCS);
809  }
810  else if (fUsedDirection == eMcTruth) {
811  if (event.HasSimShower()) {
812  const CoordinateSystemPtr localCS = event.GetSimShower().GetLocalCoordinateSystem();
813  showerAxis = -event.GetSimShower().GetDirection();
814  zenith = showerAxis.GetTheta(localCS);
815  azimuth = showerAxis.GetPhi(localCS);
816  } else {
817  ERROR("Module is configured to use Monte Carlo truth direction, but event has no SimShower!");
818  return false;
819  }
820  } else if (event.HasRecShower()) {
821  // which axis has to be used?
822  if (fUsedDirection == eSdReconstruction) {
823  INFOIntermediate("Using direction obtained from SD reconstruction");
824 
825  // checking if it has a SRecShower
826  if (!event.GetRecShower().HasSRecShower()) {
827  ERROR("Module is configured to use SD direction, but event has no SRecShower!");
828  return false;
829  } else { // get angles of the SD axis in the coordinate system valid at the RD Coordinate Origin
830  showerAxis = event.GetRecShower().GetSRecShower().GetAxis();
831  azimuth = showerAxis.GetPhi(coordinateOriginCS);
832  zenith = showerAxis.GetTheta(coordinateOriginCS);
833  }
834  }
835  else if (fUsedDirection == eRdReconstruction) {
836  if (event.GetRecShower().HasRRecShower()) {
837  INFOIntermediate("Using angles from radio reconstruction");
838  zenith = event.GetRecShower().GetRRecShower().GetZenith();
839  azimuth = event.GetRecShower().GetRRecShower().GetAzimuth();
840  showerAxis = event.GetRecShower().GetRRecShower().GetAxis();
841  } else {
842  ERROR("Module is configured to use RD direction, but event has no RRecShower!");
843  return false;
844  }
845  }
846  else if (fUsedDirection == eFdReconstruction) {
847  INFOIntermediate("Using direction obtained from FD reconstruction");
848 
849  //checking if it has a FRecShower
850  if (!event.HasFEvent()) {
851  ERROR("Module is configured to use FD direction, but event has no FEvent!");
852  return false;
853  } else {
854  const FEvent& fdEvent = event.GetFEvent();
855  for (FEvent::ConstEyeIterator eyeIter = fdEvent.EyesBegin(ComponentSelector::eHasData);
856  eyeIter != fdEvent.EyesEnd(ComponentSelector::eHasData); ++eyeIter) {
857  if (eyeIter->GetId() != fWhichEye) continue; // continue until selected eye
858  const fevt::Eye& eye = *eyeIter;
859  if(!eye.HasRecData() || !eye.GetRecData().HasFRecShower()) {
860  ERROR("Eye has no RecData or FRecShower but FD direction specified in the settings!");
861  return false;
862  } else {
863  // get angles of the FD axis in the coordinate system valid at the RD Coordinate Origin
864  showerAxis = eye.GetRecData().GetFRecShower().GetAxis();
865  azimuth = showerAxis.GetPhi(coordinateOriginCS);
866  zenith = showerAxis.GetTheta(coordinateOriginCS);
867  }
868  }
869  }
870  }
871  } else {
872  ERROR("No reconstructed shower found, but shower direction is required to apply antenna model! "
873  "Alternatively, individual signal arrival direction can be defined for each station, "
874  "when this feature is activated in the XML file.");
875  return false;
876  }
877 
878  return true;
879 }
880 
881 double
882 RdAntennaChannelToStationConverter::GetXmaxEstimator(const evt::Event& event)
883  const
884 {
885  double xmax = 0;
886 
887  if (fXmaxEstimator == "MC") {
888  if (!event.HasSimShower()) {
889  ERROR("Event has no sim shower but was requested.");
890  return nan("");
891  }
892  const evt::ShowerSimData& simShower = event.GetSimShower();
893  const auto& gh = simShower.GetGHParameters();
894  xmax = gh.GetXMax();
895  }
896  else {
897  if (!event.GetRecShower().HasSRecShower()) {
898  ERROR("Event has no SD rec shower. Can not estimate Xmax with utl::XmaxParam::Mean.");
899  return nan("");
900  }
901  const evt::ShowerSRecData& sdShower = event.GetRecShower().GetSRecShower();
902  const double crEnergy = sdShower.GetEnergy();
904 
905  if (fXmaxEstimator == "ParamWithSDEnergy-Sibyll2.3d")
907  else if (fXmaxEstimator == "ParamWithSDEnergy-EPOS-LHC")
909  else if (fXmaxEstimator == "ParamWithSDEnergy-QGSJETII-04")
911  else {
912  ERROR("Wrong xml configuration!");
913  return nan("");
914  }
915 
916  xmax = 0.5 * (XmaxParam::Mean(crEnergy, 1, hadModel) + XmaxParam::Mean(crEnergy, 56, hadModel));
917  }
918 
919  return xmax;
920 }
921 
922 }
Branch GetTopBranch() const
Definition: Branch.cc:63
double GetSignalArrivalZenith() const
returns the zenith angle of the signal arrival direction (perpendicular to wavefront) ...
Class to access station level reconstructed data.
Top of the interface to Atmosphere information.
utl::Point GetReferenceCorePosition(const Event &event) const
Returning the reference core position depending on the corresponding flag.
const double degree
Point object.
Definition: Point.h:32
double GetPhi(const CoordinateSystemPtr &coordinateSystem) const
azimuth (phi) angle in spherical and cylindrical coordinates
Definition: BasicVector.h:254
void InitSlantProfileModel(const utl::Point &core, const utl::Vector &dir, const double deltaX) const
int GetId() const
Return Id of the Channel.
double GetSignalArrivalAzimuth() const
returns the azimuth angle of the signal arrival direction (perpendicular to wavefront) ...
Detector description interface for Station-related data.
double GetDesignUpperFreq() const
Get design value of the freq-band.
constexpr double MHz
Definition: AugerUnits.h:159
bool HasRecData() const
Definition: FEvent/Eye.h:116
Fluorescence Detector Eye Event.
Definition: FEvent/Eye.h:29
Interface class to access to the SD Reconstruction of a Shower.
boost::filter_iterator< ComponentSelector, ConstAllEyeIterator > ConstEyeIterator
Definition: FEvent.h:56
const evt::VGaisserHillasParameter & GetGHParameters() const
Get the Gaisser-Hillas parameters of the shower.
bool HasRecShower() const
bool HasFEvent() const
Interface class to access to the Radio part of an event.
Definition: REvent.h:42
double GetR(const CoordinateSystemPtr &coordinateSystem) const
radius r in spherical coordinates coordinates (distance to origin)
Definition: BasicVector.h:257
double GetTheta(const CoordinateSystemPtr &coordinateSystem) const
zenith (theta) angle in spherical coordinates
Definition: BasicVector.h:248
ShowerRecData & GetRecShower()
Interface class to access to the RD Reconstruction of a Shower.
double GetDesignLowerFreq() const
Get design value of the freq-band.
bool HasSimShower() const
EyeIterator EyesEnd(const ComponentSelector::Status status)
Definition: FEvent.h:66
double GetBinning() const
size of one slot
Definition: Trace.h:138
const atm::ProfileResult & EvaluateDistanceVsSlantDepth() const
Table of distance as a function of slant depth.
HadronicInteractionModel
void Init()
Initialise the registry.
Detector description interface for Channel-related data.
double GetOrientationZenith() const
Get zenith-direction of Antenna for this Channel.
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
Definition: Branch.cc:211
bool HasREvent() const
int GetStationId() const
Return Id of the station to which this Channel belongs.
ShowerRRecData & GetRRecShower()
unsigned int GetNyquistZone() const
Get the Nyquist zone.
bool StringEquivalent(const std::string &a, const std::string &b, Predicate p)
Utility to compare strings for equivalence. It takes a predicate to determine the equivalence of indi...
Definition: StringCompare.h:38
double Y(const double x) const
Get the Y value (coordinate) for given X (ordinate)
Detector description interface for RDetector-related data.
Definition: RDetector.h:46
constexpr double deg
Definition: AugerUnits.h:140
Interface class to access Shower Simulated parameters.
Definition: ShowerSimData.h:49
T Get() const
Definition: Branch.h:271
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
#define INFOIntermediate(y)
Definition: VModule.h:162
Class representing a document branch.
Definition: Branch.h:107
utl::Point GetCoordinateOrigin() const
std::vector< T >::size_type SizeType
Definition: Trace.h:58
Class describing the Atmospheric profile.
Definition: ProfileResult.h:25
Static (small and dense) vector class.
Definition: SVector.h:33
double abs(const SVector< n, T > &v)
static utl::Vector GetLorentzVector(const utl::Vector &showeraxis, const utl::Vector &vMagField)
returns the Lorentz force vector normalized to length = 1 for maximal emission (showeraxis vertical t...
bool HasRRecShower() const
bool HasFRecShower() const
Check if reconstructed shower info has been allocated.
Definition: EyeRecData.h:330
evt::ShowerFRecData & GetFRecShower()
Reconstructed shower info for this eye.
Definition: EyeRecData.h:323
EyeIterator EyesBegin(const ComponentSelector::Status status)
Definition: FEvent.h:58
constexpr double g
Definition: AugerUnits.h:200
static const CSpherical kSpherical
Definition: BasicVector.h:335
SizeType GetSize() const
Definition: Trace.h:156
#define WARNING(message)
Macro for logging warning messages.
Definition: ErrorLogger.h:163
void GetData(bool &b) const
Overloads of the GetData member template function.
Definition: Branch.cc:644
Top of Fluorescence Detector event hierarchy.
Definition: FEvent.h:33
Base class for inconsistency/illogicality exceptions.
#define INFODebug(y)
Definition: VModule.h:163
double GetEnergy() const
ResultFlag
Flag returned by module methods to the RunController.
Definition: VModule.h:60
bool HasParameter(const Parameter i) const
Template class for a FADC data or calibrated data container. Use the typedefs (TraceD, TraceI, etc.) defined in Trace-fwd.h.
Definition: Trace-fwd.h:19
ChannelFrequencySpectrum & GetChannelFrequencySpectrum()
retrieve Channel Frequency Spectrum (write access, only use this if you intend to change the data) ...
utl::TraceV3C StationFrequencySpectrum
double GetFrequencyOfBin(const ChannelFrequencySpectrum::SizeType bin) const
Get the frequency corresponding to a bin of the frequency spectrum.
Vector object.
Definition: Vector.h:30
Class that holds the data associated to an individual radio channel.
utl::Vector GetReferenceAxis(const Event &event) const
Returning the referencedirection depending on the corresponding flag.
double Mean(const std::vector< double > &v)
Definition: Functions.h:31
utl::Point GetPosition() const
Tank position in Site Cartesian Coordinates.
#define INFOFinal(y)
Definition: VModule.h:161
static Policy::type Create(const utl::Point &theOrigin)
Create the standard local coordinate system for a Point.
std::pair< std::complex< double >, std::complex< double > > GetElectricFieldResponse(const double theta, const double phi, const double freq, std::string interpolationMode) const
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
const Station & GetStation(const int stationId) const
Get station by Station Id.
Definition: RDetector.cc:141
bool HasSRecShower() const
double GetOrientationAzimuth() const
Get azimuth-direction of Antenna for this Channel.
constexpr double cm2
Definition: AugerUnits.h:118
fevt::EyeRecData & GetRecData()
Reconstructed data for this eye.
Definition: FEvent/Eye.cc:130
const utl::Vector & GetAxis() const
Shower Axis as reconstructed by the FD or FD eye.

, generated on Tue Sep 26 2023.