RdChannelLinearPredictorRFISuppressor.cc
Go to the documentation of this file.
1 
11 #include "LinearPredictor.h"
12 
13 #include <cmath>
14 #include <vector>
15 #include <algorithm>
16 
17 #include <fwk/CentralConfig.h>
18 
19 #include <utl/config.h>
20 #include <utl/ErrorLogger.h>
21 #include <utl/Reader.h>
22 #include <utl/HannWindow.h>
23 #include <utl/StringCompare.h>
24 #include <utl/AugerException.h>
25 
26 #include <evt/Event.h>
27 #include <evt/ShowerRRecDataQuantities.h>
28 #include <revt/StationRecData.h>
29 #include <revt/REvent.h>
30 #include <revt/Header.h>
31 #include <revt/Channel.h>
32 #include <revt/Station.h>
33 
34 
35 #define OUT(x) if ((x) <= fInfoLevel) cerr << " "
36 
37 using namespace revt;
38 using namespace fwk;
39 using namespace utl;
40 using namespace std;
41 
43 
44  string
45  SampleData::HelpOutput() {
46  ostringstream helpoutput;
47  helpoutput << "Useful values are:\n"
48  << " fBinning / ns = " << fBinning / nanosecond << "\n"
49  << "All following units are samples: \n"
50  << " fNumCoefficients = " << fNumCoefficients << "\n"
51  << " fDelayLine = " << fDelayLine << "\n"
52  << " fTrainRegionStart = " << fTrainRegionStart << "\n"
53  << " fTrainRegionStop = " << fTrainRegionStop << "\n"
54  << " fMinimumTrainRegionLength = " << fMinimumTrainRegionLength << "\n"
55  << " fTraceLength = " << fTraceLength << "\n"
56  << "Occupied samples are the samples that can't be used because they are "
57  "in/close to the SignalSearchWindow or the NoiseWindow \n"
58  << " fOccupiedRegion1Start = " << fOccupiedRegion1Start << "\n"
59  << " fOccupiedRegion1Stop = " << fOccupiedRegion1Stop << "\n"
60  << " fOccupiedRegion2Start = " << fOccupiedRegion2Start << "\n"
61  << " fOccupiedRegion2Stop = " << fOccupiedRegion2Stop << "\n"
62  << " fTrainRegionLength = " << fTrainRegionLength << "\n";
63  return helpoutput.str();
64  }
65 
66 
67  unsigned int
68  RdChannelLinearPredictorRFISuppressor::TranslateTimeToSample(double time, double binning, char const *name) {
69  const double floatSamples = time / binning;
70  const int signedintSamples = round(time/binning);
71  const unsigned int intSamples = signedintSamples > 0 ? abs(signedintSamples) : 0;
72  if ( fabs(floatSamples - intSamples) > 1E-10) {
73  ostringstream warning;
74  warning << "The configuration variable " << name << " with value "
75  << time / nanosecond << "ns doesn't seem to be a multiple of the time-binning ("
76  << binning / nanosecond << "ns) of this trace. Rounding to the nearest value of "
77  << intSamples << "samples. This is not necessarily a problem.";
78  INFOIntermediate(warning);
79  }
80  return intSamples;
81  }
82 
83 
84  bool
85  RdChannelLinearPredictorRFISuppressor::TestOverlap(int a1, int a2, int b1, int b2) {
86  // This method returns true if the region [a1...a2] and [b1...b2] overlap
87  if (a1 > b1 && a1 < b2)
88  return true;
89  if (a2 > b1 && a2 < b2)
90  return true;
91  if (b1 > a1 && b1 < a2)
92  return true;
93  if (b2 > a1 && b2 < a2)
94  return true;
95 
96  return false;
97  }
98 
101  {
102  INFO("RdChannelLinearPredictorRFISuppressor::Init()");
103 
104  // Read in the configurations of the xml file
105  Branch topBranch =
106  CentralConfig::GetInstance()->GetTopBranch("RdChannelLinearPredictorRFISuppressor");
107  topBranch.GetChild("SegmentLength").GetData(fSegmentLength);
108  topBranch.GetChild("DelayLine").GetData(fDelayLine);
109  topBranch.GetChild("TrainRegionStartOverride").GetData(fTrainRegionStartOverride);
110  topBranch.GetChild("TrainRegionStopOverride").GetData(fTrainRegionStopOverride);
111  topBranch.GetChild("MinimumTrainRegionLength").GetData(fMinimumTrainRegionLength);
112  topBranch.GetChild("WindowMargins").GetData(fWindowMargins);
113  topBranch.GetChild("FudgeFactor").GetData(fFudgeFactor);
114  topBranch.GetChild("NumChannels").GetData(fNumChannels);
115  topBranch.GetChild("InfoLevel").GetData(fInfoLevel);
116  topBranch.GetChild("Optimization").GetData(fOptimization);
117 
118  if ( !( fDelayLine + fSegmentLength < (fTrainRegionStopOverride - fTrainRegionStartOverride)*2 ) ) {
119  InvalidConfigurationException("The train-region must contain considerably more "
120  "samples than those that are spanned by the coefficients and the delay line.");
121  };
122 
123  return eSuccess;
124  }
125 
126  void
127  RdChannelLinearPredictorRFISuppressor::FillSampleDataWithRelevantValuesForFirstChannel(SampleData &samples, ChannelTimeSeries const &timeseries, Station const &station) {
128  // The struct SampleData is filled with the relevant values and the TrainRegion is determined
129  samples.fBinning = timeseries.GetBinning();
130  samples.fTraceLength = timeseries.GetSize();
131  samples.fNumCoefficients = TranslateTimeToSample(fSegmentLength, samples.fBinning, "SegmentLength");
132  samples.fDelayLine = TranslateTimeToSample(fDelayLine, samples.fBinning, "DelayLine");
133  samples.fTrainRegionStart = TranslateTimeToSample(fTrainRegionStartOverride, samples.fBinning, "RegionStart");
134  samples.fTrainRegionStop = TranslateTimeToSample(fTrainRegionStopOverride, samples.fBinning, "RegionStop");
135  samples.fMinimumTrainRegionLength = TranslateTimeToSample(fMinimumTrainRegionLength, samples.fBinning, "MinimumTrainRegionLength");
136  // If there is no override i.e. both sampleTrainRegionStart and sampleTrainRegionStop are zero
137  // this part of the code tries to find the best train region outside the SignalSearchWindow and
138  // the NoiseWindow
139  // This module works on the channel level but some information from the station is necessary to determine and check the right train-region
140  StationRecData const &recStation = station.GetRecData();
141  double NoiseWindowStart = recStation.GetParameter(eNoiseWindowStart) - fWindowMargins;
142  double NoiseWindowStop = recStation.GetParameter(eNoiseWindowStop) + fWindowMargins;
143  double SignalSearchWindowStart = recStation.GetParameter(eSignalSearchWindowStart) - fWindowMargins;
144  double SignalSearchWindowStop = recStation.GetParameter(eSignalSearchWindowStop) + fWindowMargins;
145  if ( NoiseWindowStart < SignalSearchWindowStart ) {
146  samples.fOccupiedRegion1Start = TranslateTimeToSample(NoiseWindowStart, samples.fBinning, "NoiseWindowStart");
147  samples.fOccupiedRegion1Stop = TranslateTimeToSample(NoiseWindowStop, samples.fBinning, "NoiseWindowStop");
148  samples.fOccupiedRegion2Start = TranslateTimeToSample(SignalSearchWindowStart, samples.fBinning, "SignalSearchWindowStart");
149  samples.fOccupiedRegion2Stop = TranslateTimeToSample(SignalSearchWindowStop, samples.fBinning, "SignalSearchWindowStop");
150  } else {
151  samples.fOccupiedRegion2Start = TranslateTimeToSample(NoiseWindowStart, samples.fBinning, "NoiseWindowStart");
152  samples.fOccupiedRegion2Stop = TranslateTimeToSample(NoiseWindowStop, samples.fBinning, "NoiseWindowStop");
153  samples.fOccupiedRegion1Start = TranslateTimeToSample(SignalSearchWindowStart, samples.fBinning, "SignalSearchWindowStart");
154  samples.fOccupiedRegion1Stop = TranslateTimeToSample(SignalSearchWindowStop, samples.fBinning, "SignalSearchWindowStop");
155  }
156 
157  // This code is written to automatically pick the best piece of available noise
158  if (samples.fTrainRegionStart == 0 and samples.fTrainRegionStop == 0) {
159  unsigned int Range1Start = timeseries.GetStart();
160  unsigned int Range1Stop = samples.fOccupiedRegion1Start;
161  unsigned int Range2Start = samples.fOccupiedRegion1Stop;
162  unsigned int Range2Stop = samples.fOccupiedRegion2Start;
163  unsigned int Range3Start = samples.fOccupiedRegion2Stop;
164  unsigned int Range3Stop = timeseries.GetStop() + 1;
165  int Range1Length = int(Range1Stop) - int(Range1Start); // negative values are possible but if all but the region that is chosen should definitiely not be negative
166  int Range2Length = int(Range2Stop) - int(Range2Start);
167  int Range3Length = int(Range3Stop) - int(Range3Start);
168  if ((Range1Length > Range2Length) && (Range1Length > Range3Length)) {
169  samples.fTrainRegionStart = Range1Start;
170  samples.fTrainRegionStop = Range1Stop;
171  } else if ((Range2Length > Range1Length) && (Range2Length > Range3Length)) {
172  samples.fTrainRegionStart = Range2Start;
173  samples.fTrainRegionStop = Range2Stop;
174  } else {
175  samples.fTrainRegionStart = Range3Start;
176  samples.fTrainRegionStop = Range3Stop;
177  }
178  }
179  samples.fTotalTraceLength = timeseries.GetStop() - timeseries.GetStart() + 1;
180  samples.fTrainRegionLength = samples.fTrainRegionStop - samples.fTrainRegionStart;
181  }
182 
183 
185  RdChannelLinearPredictorRFISuppressor::TestSampleDataForConsistency(SampleData &samples, ChannelTimeSeries const &timeseries) {
186  // Test for overlaps of the regions. No regions should overlap
187  if ( TestOverlap(samples.fTrainRegionStart, samples.fTrainRegionStop, samples.fOccupiedRegion1Start, samples.fOccupiedRegion1Stop)
188  || TestOverlap(samples.fTrainRegionStart, samples.fTrainRegionStop, samples.fOccupiedRegion2Start, samples.fOccupiedRegion2Stop)
189  ) {
190  ostringstream error;
191  error << "There is overlap of the TrainRegion with the SignalSearchWindow or the SignalNoiseWindow "
192  "Maybe you have set the train-region to a wrong manual value?). Here are some values to help you" << endl
193  << samples.HelpOutput() << endl;
194  ERROR(error.str());
195  return eFailure;
196  }
197  // Test that that the length of the Train region is larger or equal to the minimum specified length
198  if ( samples.fTrainRegionLength < int(samples.fMinimumTrainRegionLength) ) {
199  ostringstream error;
200  error << "The number of samples in the train region (" << samples.fTrainRegionLength <<
201  ") is lower than the minimum number of train-region samples (" <<
202  samples.fMinimumTrainRegionLength << "). Here are some values to help you:" << endl
203  << samples.HelpOutput() << endl;
204  ERROR(error.str());
205  return eFailure;
206  }
207  // Test that the train-region-start sample is in the valid part of the time-series
208  if ( samples.fTrainRegionStart < timeseries.GetStart() ) {
209  ostringstream error;
210  error << "Lowest sample of the noise region (" << samples.fTrainRegionStart <<
211  ") is lower than the lowest valid sample of the trace (" <<
212  timeseries.GetStart() << "). Here are some values to help you:" << endl
213  << samples.HelpOutput() << endl;
214  ERROR(error.str());
215  return eFailure;
216  }
217  // Test that the train-region-stop sample is in the valid part of the time-series
218  if ( samples.fTrainRegionStop > timeseries.GetStop() + 1 ) {
219  ostringstream error;
220  error << "Highest sample of the noise region (" << samples.fTrainRegionStop - 1 <<
221  ") is higher than the highest valid sample of the trace (" <<
222  timeseries.GetStop() << "). Here are some values to help you:" << endl
223  << samples.HelpOutput() << endl;
224  ERROR(error.str());
225  return eFailure;
226  }
227  // Test that the binning of the channels is the same
228  if ( samples.fBinning != timeseries.GetBinning() ) {
229  ERROR("Something bad is going on. The traces that I am asked to work with have "
230  "different binning. I can't work with that.");
231  return eFailure;
232  }
233  // Test that the length of the channels is the same (if not then a warning is emitted an attempt is made to continue)
234  if ( samples.fTraceLength != timeseries.GetSize() ) {
235  WARNING("Something bad might be going on. The traces of one station not have the "
236  "same length. I am adujsting the total length to the shortest trace.");
237  if ( timeseries.GetSize() < samples.fTraceLength ) {
238  samples.fTraceLength = timeseries.GetSize();
239  }
240  }
241  // Test that the the train region length is at least larger than the delay-line plus number of coefficients
242  // in general, for the algorithm to work well, it should be at least several times larger but if it is
243  // than this value then the algorithm would fail completely.
244  if ( !( int(samples.fDelayLine + samples.fNumCoefficients) < samples.fTrainRegionLength ) ) {
245  ostringstream error;
246  error << "This is not good: based on this sampling "
247  "the train-region does not contain more samples than those that are spanned "
248  "by the coefficients and the delay line. It would be impossible to calculate "
249  "the covariances and the coefficients. Here are some values to help you:" << endl
250  << samples.HelpOutput() << endl;
251  ERROR(error.str());
252  return eFailure;
253  }
254  return eSuccess;
255  }
256 
257 
259  RdChannelLinearPredictorRFISuppressor::Run(evt::Event& event)
260  {
261  INFO("RdChannelLinearPredictorRFISuppressor::Run()");
262 
263  // Check whether there are any events at all //
264  if(!event.HasREvent()) {
265  WARNING("No radio event found!");
266  return eContinueLoop;
267  }
268 
269  REvent& rEvent = event.GetREvent();
270 
271  // Initialization of the values such as the binning and the regions //
272  // loop through stations and for every station through every channel
273  for (auto& station : rEvent.StationsRange()) {
274 
275  unsigned int countActiveChannels = 0;
276  // Create a new struct instance of SampleData which will contain the relevant information
277  // about the samples
278  SampleData samples;
279 
280  for (auto& channel : station.ChannelsRange()) {
281 
282  if (!channel.IsActive())
283  continue;
284 
285  ChannelTimeSeries& timeseries(channel.GetChannelTimeSeries());
286  if (countActiveChannels == 0) {
287  // Most of the information about the time-series is extracted from a single channel
288  // for instance the binning must be the same for all channels, otherwise the predictor
289  // won't work
290  FillSampleDataWithRelevantValuesForFirstChannel(samples, timeseries, station);
291  }
292  // The values are determined from the first channel tested and for consistency
293  // for all channels here. If, for instance, the binning is not the same for all
294  // channels then an error is emitted here.
295  // In addition some other checks are done such as to test for acceptable size
296  // of the regions and to test that regions do not overlap
297  VModule::ResultFlag test_samples_result = TestSampleDataForConsistency(samples, timeseries);
298  if (test_samples_result != eSuccess) {
299  return test_samples_result;
300  }
301  ++countActiveChannels;
302  }
303  if ( countActiveChannels == 0 ) {
304  WARNING("No active channels were found. Skipping.");
305  return eContinueLoop;
306  }
307  if ( countActiveChannels != fNumChannels && fNumChannels != 0 ) {
308  ostringstream error;
309  error << "The value of fNumChannels (" << fNumChannels << ") is not the same as the number of active channels ("
310  << countActiveChannels << ")" << endl;
311  ERROR(error.str());
312  return eFailure;
313  }
314 
315  // Some useful Output //
316  OUT(2) << "--- Working on event with Id=" << rEvent.GetHeader().GetId()
317  << " and station with Id=" << station.GetId() << "---" << endl;
318  OUT(2) << "Setting up the linear predictor with:" << endl
319  << " - train region : [" << samples.fTrainRegionStart << ", " << samples.fTrainRegionStop << ") samples" << endl
320  << " - number of coefficients: " << samples.fNumCoefficients << " samples" << endl
321  << " - delay line : " << samples.fDelayLine << " samples" << endl
322  << " - total trace length : " << samples.fTotalTraceLength << " samples" << endl;
323  OUT(4) << samples.HelpOutput() << endl;
324 
325  // Here we apply the algorithm and use the LinearPredictor class //
326  // We prepare the noise-region and the predict-region of the traces and put them into c++ vectors.
327  unsigned int channelNum = 0;
328  vector< vector<double> > noiseRegion(countActiveChannels, vector<double>(samples.fTrainRegionLength));
329  vector< vector<double> > predictRegion(countActiveChannels, vector<double>(samples.fTraceLength));
330 
331  for (auto& channel : station.ChannelsRange()) {
332 
333  if (!channel.IsActive())
334  continue;
335 
336  ChannelTimeSeries &timeseries(channel.GetChannelTimeSeries());
337  for (unsigned int index = 0; int(index) < samples.fTrainRegionLength; ++index) {
338  noiseRegion[channelNum][index] = timeseries[index + samples.fTrainRegionStart];
339  }
340  for (unsigned int index = 0; index < samples.fTraceLength; ++index) {
341  predictRegion[channelNum][index] = timeseries[index];
342  }
343  ++channelNum;
344  }
345 
346  // Here we train the linear predictor with the noise-region and
347  // let it make a prediction based on the predict-region
348  LinearPredictor lp(samples.fNumCoefficients, samples.fDelayLine, fNumChannels, fOptimization);
349 
350  OUT(2) << "Training the linear predictor..." << endl;
351  lp.train(noiseRegion, fFudgeFactor);
352  vector< vector<double> > prediction(lp.predict(predictRegion));
353 
354  OUT(2) << "Subtracting the predicted signal from the traces..." << endl;
355  // We now have the predicted RFI values. These are subtracted from the
356  // trace to yield a trace that contains less RFI. (i.e. all 'predictable'
357  // RFI is removed)
358  channelNum = 0;
359  for (auto& channel : station.ChannelsRange()) {
360 
361  if (!channel.IsActive())
362  continue;
363 
364  ChannelTimeSeries &timeseries(channel.GetChannelTimeSeries());
365  for (unsigned int index = 0; index < samples.fTraceLength; ++index) {
366  if ( index < samples.fDelayLine + samples.fNumCoefficients + 1 ) {
367  timeseries[index] = 0;
368  } else {
369  timeseries[index] -= prediction[channelNum][index];
370  }
371  }
372  ++channelNum;
373  }
374  }
375  return eSuccess;
376  }
377 
379  RdChannelLinearPredictorRFISuppressor::Finish()
380  {
381  // Put any termination or cleanup code here.
382  // This method is called once at the end of the run.
383 
384  INFO("RdChannelLinearPredictorRFISuppressor::Finish()");
385 
386  return eSuccess;
387  }
388 
389 }
Branch GetTopBranch() const
Definition: Branch.cc:63
Class to access station level reconstructed data.
std::vector< std::vector< double > > predict(std::vector< std::vector< double > > const &channels) const
StationRecData & GetRecData()
Get station level reconstructed data.
SizeType GetStop() const
Get valid data stop bin.
Definition: Trace.h:148
Interface class to access to the Radio part of an event.
Definition: REvent.h:42
Base class for exceptions arising because configuration data are not valid.
double GetBinning() const
size of one slot
Definition: Trace.h:138
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
void Init()
Initialise the registry.
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
Definition: Branch.cc:211
bool HasREvent() const
#define INFOIntermediate(y)
Definition: VModule.h:162
Class representing a document branch.
Definition: Branch.h:107
class to hold data at the radio Station level.
constexpr double nanosecond
Definition: AugerUnits.h:143
double abs(const SVector< n, T > &v)
Header & GetHeader()
access to REvent Header
Definition: REvent.h:239
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
Linear Predictor. Trains on noise with RFI, predicts RFI given other noisy signal.
double GetParameter(const Parameter i) const
void train(std::vector< std::vector< double > > const &channels, double fudgeFactor)
ResultFlag
Flag returned by module methods to the RunController.
Definition: VModule.h:60
SizeType GetStart() const
Get valid data start bin.
Definition: Trace.h:142
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
int GetId() const
Definition: REvent/Header.h:21

, generated on Tue Sep 26 2023.