source: trunk/FACT++/src/ratecontrol.cc@ 18414

Last change on this file since 18414 was 17950, checked in by tbretz, 10 years ago
Updated the threshold curve according to the latest measurement in the feedback paper. The +1 is to get ceil(t)
File size: 31.0 KB
Line 
1#include <valarray>
2
3#include "Dim.h"
4#include "Event.h"
5#include "Shell.h"
6#include "StateMachineDim.h"
7#include "Connection.h"
8#include "Configuration.h"
9#include "Console.h"
10#include "externals/PixelMap.h"
11
12#include "tools.h"
13
14#include "LocalControl.h"
15
16#include "HeadersFTM.h"
17#include "HeadersDrive.h"
18#include "HeadersRateScan.h"
19#include "HeadersRateControl.h"
20
21namespace ba = boost::asio;
22namespace bs = boost::system;
23namespace dummy = ba::placeholders;
24
25using namespace std;
26
27// ------------------------------------------------------------------------
28
29#include "DimDescriptionService.h"
30#include "DimState.h"
31
32// ------------------------------------------------------------------------
33
34class StateMachineRateControl : public StateMachineDim//, public DimInfoHandler
35{
36private:
37 struct config
38 {
39 uint16_t fCalibrationType;
40 uint16_t fTargetRate;
41 uint16_t fMinThreshold;
42 uint16_t fAverageTime;
43 uint16_t fRequiredEvents;
44 };
45
46 map<string, config> fRunTypes;
47
48 PixelMap fMap;
49
50 bool fPhysTriggerEnabled;
51 bool fTriggerOn;
52
53 vector<bool> fBlock;
54
55 DimVersion fDim;
56 DimDescribedState fDimFTM;
57 DimDescribedState fDimRS;
58 DimDescribedState fDimDrive;
59
60 DimDescribedService fDimThreshold;
61
62 float fTargetRate;
63 float fTriggerRate;
64
65 uint16_t fThresholdMin;
66 uint16_t fThresholdReference;
67
68 uint16_t fAverageTime;
69 uint16_t fRequiredEvents;
70
71 list<pair<Time,float>> fCurrentsMed;
72 list<pair<Time,float>> fCurrentsDev;
73 list<pair<Time,vector<float>>> fCurrentsVec;
74
75 bool fVerbose;
76 bool fCalibrateByCurrent;
77
78 uint64_t fCounter;
79
80 Time fCalibrationTimeStart;
81
82 bool CheckEventSize(const EventImp &evt, size_t size)
83 {
84 if (size_t(evt.GetSize())==size)
85 return true;
86
87 if (evt.GetSize()==0)
88 return false;
89
90 ostringstream msg;
91 msg << evt.GetName() << " - Received event has " << evt.GetSize() << " bytes, but expected " << size << ".";
92 Fatal(msg);
93 return false;
94 }
95
96 vector<uint32_t> fThresholds;
97
98 void PrintThresholds(const FTM::DimStaticData &sdata)
99 {
100 if (!fVerbose)
101 return;
102
103 if (fThresholds.empty())
104 return;
105
106 if (GetCurrentState()<=RateControl::State::kConnected)
107 return;
108
109 Out() << "Min. DAC=" << fThresholdMin << endl;
110
111 for (int j=0; j<10; j++)
112 {
113 for (int k=0; k<4; k++)
114 {
115 for (int i=0; i<4; i++)
116 {
117 const int p = i + k*4 + j*16;
118
119 if (fThresholds[p]!=fThresholdMin)
120 Out() << setw(3) << fThresholds[p];
121 else
122 Out() << " - ";
123
124 if (fThresholds[p]!=sdata.fThreshold[p])
125 Out() << "!";
126 else
127 Out() << " ";
128 }
129
130 Out() << " ";
131 }
132 Out() << endl;
133 }
134 Out() << endl;
135 }
136
137 // RETUNR VALUE
138 bool Step(int idx, float step)
139 {
140 uint32_t diff = fThresholds[idx]+int16_t(truncf(step));
141 if (diff<fThresholdMin)
142 diff=fThresholdMin;
143 if (diff>0xffff)
144 diff = 0xffff;
145
146 if (diff==fThresholds[idx])
147 return false;
148
149 if (fVerbose)
150 {
151 Out() << "Apply: Patch " << setw(3) << idx << " [" << idx/40 << "|" << (idx/4)%10 << "|" << idx%4 << "]";
152 Out() << (step>0 ? " += " : " -= ");
153 Out() << fabs(step) << " (old=" << fThresholds[idx] << ", new=" << diff << ")" << endl;
154 }
155
156 fThresholds[idx] = diff;
157 fBlock[idx/4] = true;
158
159 return true;
160 }
161
162 void ProcessPatches(const FTM::DimTriggerRates &sdata)
163 {
164
165 // Caluclate Median and deviation
166 vector<float> medb(sdata.fBoardRate, sdata.fBoardRate+40);
167 vector<float> medp(sdata.fPatchRate, sdata.fPatchRate+160);
168
169 sort(medb.begin(), medb.end());
170 sort(medp.begin(), medp.end());
171
172 vector<float> devb(40);
173 for (int i=0; i<40; i++)
174 devb[i] = fabs(sdata.fBoardRate[i]-medb[i]);
175
176 vector<float> devp(160);
177 for (int i=0; i<160; i++)
178 devp[i] = fabs(sdata.fPatchRate[i]-medp[i]);
179
180 sort(devb.begin(), devb.end());
181 sort(devp.begin(), devp.end());
182
183 const double mb = (medb[19]+medb[20])/2;
184 const double mp = (medp[79]+medp[80])/2;
185
186 const double db = devb[27];
187 const double dp = devp[109];
188
189 // If any is zero there is something wrong
190 if (mb==0 || mp==0 || db==0 || dp==0)
191 return;
192
193 if (fVerbose)
194 Out() << Tools::Form("Boards: Med=%3.1f +- %3.1f Hz Patches: Med=%3.1f +- %3.1f Hz", mb, db, mp, dp) << endl;
195
196 bool changed = false;
197
198 for (int i=0; i<40; i++)
199 {
200 if (fBlock[i])
201 {
202 fBlock[i] = false;
203 continue;
204 }
205
206 int maxi = -1;
207
208 const float dif = fabs(sdata.fBoardRate[i]-mb)/db;
209 if (dif>3)
210 {
211 if (fVerbose)
212 Out() << "Board " << setw(3) << i << ": " << dif << " dev away from med" << endl;
213
214 float max = sdata.fPatchRate[i*4];
215 maxi = 0;
216
217 for (int j=1; j<4; j++)
218 if (sdata.fPatchRate[i*4+j]>max)
219 {
220 max = sdata.fPatchRate[i*4+j];
221 maxi = j;
222 }
223 }
224
225 for (int j=0; j<4; j++)
226 {
227 // For the noise pixel correct down to median+3*deviation
228 if (maxi==j)
229 {
230 // This is the step which has to be performed to go from
231 // a NSB rate of sdata.fPatchRate[i*4+j]
232
233
234 const float step = (log10(sdata.fPatchRate[i*4+j])-log10(mp+3.5*dp))/0.039;
235 // * (dif-5)/dif
236 changed |= Step(i*4+j, step);
237 continue;
238 }
239
240 // For pixels below the median correct also back to median+3*deviation
241 if (sdata.fPatchRate[i*4+j]<mp)
242 {
243 const float step = (log10(sdata.fPatchRate[i*4+j])-log10(mp+3.5*dp))/0.039;
244 changed |= Step(i*4+j, step);
245 continue;
246 }
247
248 const float step = -1.5*(log10(mp+dp)-log10(mp))/0.039;
249 changed |= Step(i*4+j, step);
250 }
251 }
252
253 if (changed)
254 Dim::SendCommandNB("FTM_CONTROL/SET_SELECTED_THRESHOLDS", fThresholds);
255 }
256
257 int ProcessCamera(const FTM::DimTriggerRates &sdata)
258 {
259 if (fCounter++==0)
260 return GetCurrentState();
261
262 // Caluclate Median and deviation
263 vector<float> medb(sdata.fBoardRate, sdata.fBoardRate+40);
264
265 sort(medb.begin(), medb.end());
266
267 vector<float> devb(40);
268 for (int i=0; i<40; i++)
269 devb[i] = fabs(sdata.fBoardRate[i]-medb[i]);
270
271 sort(devb.begin(), devb.end());
272
273 double mb = (medb[19]+medb[20])/2;
274 double db = devb[27];
275
276 // If any is zero there is something wrong
277 if (mb==0 || db==0)
278 {
279 Warn("The median or the deviation of all board rates is zero... cannot calibrate.");
280 return GetCurrentState();
281 }
282
283 double avg = 0;
284 int num = 0;
285
286 for (int i=0; i<40; i++)
287 {
288 if ( fabs(sdata.fBoardRate[i]-mb)<2.5*db)
289 {
290 avg += sdata.fBoardRate[i];
291 num++;
292 }
293 }
294
295 fTriggerRate = avg/num * 40;
296
297 if (fVerbose)
298 {
299 Out() << "Board: Median=" << mb << " Dev=" << db << endl;
300 Out() << "Camera: " << fTriggerRate << " (" << sdata.fTriggerRate << ", n=" << num << ")" << endl;
301 Out() << "Target: " << fTargetRate << endl;
302 }
303
304 if (sdata.fTriggerRate<fTriggerRate)
305 fTriggerRate = sdata.fTriggerRate;
306
307 // ----------------------
308
309 /*
310 if (avg>0 && avg<fTargetRate)
311 {
312 // I am assuming here (and at other places) the the answer from the FTM when setting
313 // the new threshold always arrives faster than the next rate update.
314 fThresholdMin = fThresholds[0];
315 Out() << "Setting fThresholdMin to " << fThresholds[0] << endl;
316 }
317 */
318
319 if (fTriggerRate>0 && fTriggerRate<fTargetRate)
320 {
321 fThresholds.assign(160, fThresholdMin);
322
323 const RateControl::DimThreshold data = { fThresholdMin, fCalibrationTimeStart.Mjd(), Time().Mjd() };
324 fDimThreshold.setQuality(0);
325 fDimThreshold.Update(data);
326
327 ostringstream out;
328 out << setprecision(3);
329 out << "Measured rate " << fTriggerRate << "Hz below target rate " << fTargetRate << "... mininum threshold set to " << fThresholdMin;
330 Info(out);
331
332 fTriggerOn = false;
333 fPhysTriggerEnabled = false;
334 return RateControl::State::kGlobalThresholdSet;
335 }
336
337 // This is a step towards a threshold at which the NSB rate is equal the target rate
338 // +1 to avoid getting a step of 0
339 const float step = (log10(fTriggerRate)-log10(fTargetRate))/0.039 + 1;
340
341 const uint16_t diff = fThresholdMin+int16_t(truncf(step));
342 if (diff<=fThresholdMin)
343 {
344 const RateControl::DimThreshold data = { fThresholdMin, fCalibrationTimeStart.Mjd(), Time().Mjd() };
345 fDimThreshold.setQuality(1);
346 fDimThreshold.Update(data);
347
348 ostringstream out;
349 out << setprecision(3);
350 out << "Next step would be 0... mininum threshold set to " << fThresholdMin;
351 Info(out);
352
353 fTriggerOn = false;
354 fPhysTriggerEnabled = false;
355 return RateControl::State::kGlobalThresholdSet;
356 }
357
358 if (fVerbose)
359 {
360 //Out() << idx/40 << "|" << (idx/4)%10 << "|" << idx%4;
361 Out() << fThresholdMin;
362 Out() << (step>0 ? " += " : " -= ");
363 Out() << step << " (" << diff << ")" << endl;
364 }
365
366 const uint32_t val[2] = { -1, diff };
367 Dim::SendCommandNB("FTM_CONTROL/SET_THRESHOLD", val);
368
369 fThresholdMin = diff;
370
371 return GetCurrentState();
372 }
373
374 int HandleStaticData(const EventImp &evt)
375 {
376 if (!CheckEventSize(evt, sizeof(FTM::DimStaticData)))
377 return GetCurrentState();
378
379 const FTM::DimStaticData &sdata = *static_cast<const FTM::DimStaticData*>(evt.GetData());
380 fPhysTriggerEnabled = sdata.HasTrigger();
381 fTriggerOn = (evt.GetQoS()&FTM::kFtmStates)==FTM::kFtmRunning;
382
383 Out() << "\n" << evt.GetTime() << ": " << (bool)fTriggerOn << " " << (bool)fPhysTriggerEnabled << endl;
384 PrintThresholds(sdata);
385
386 if (GetCurrentState()==RateControl::State::kSettingGlobalThreshold && fCalibrateByCurrent)
387 {
388 if (fThresholds.empty())
389 return RateControl::State::kSettingGlobalThreshold;
390
391 if (!std::equal(sdata.fThreshold, sdata.fThreshold+160, fThresholds.begin()))
392 return RateControl::State::kSettingGlobalThreshold;
393
394 return RateControl::State::kGlobalThresholdSet;
395 }
396
397 fThresholds.assign(sdata.fThreshold, sdata.fThreshold+160);
398
399 return GetCurrentState();
400 }
401
402 int HandleTriggerRates(const EventImp &evt)
403 {
404 fTriggerOn = (evt.GetQoS()&FTM::kFtmStates)==FTM::kFtmRunning;
405
406 if (fThresholds.empty())
407 return GetCurrentState();
408
409 if (GetCurrentState()<=RateControl::State::kConnected ||
410 GetCurrentState()==RateControl::State::kGlobalThresholdSet)
411 return GetCurrentState();
412
413 if (!CheckEventSize(evt, sizeof(FTM::DimTriggerRates)))
414 return GetCurrentState();
415
416 const FTM::DimTriggerRates &sdata = *static_cast<const FTM::DimTriggerRates*>(evt.GetData());
417
418 if (GetCurrentState()==RateControl::State::kSettingGlobalThreshold && !fCalibrateByCurrent)
419 return ProcessCamera(sdata);
420
421 if (GetCurrentState()==RateControl::State::kInProgress)
422 ProcessPatches(sdata);
423
424 return GetCurrentState();
425 }
426
427 int HandleCalibratedCurrents(const EventImp &evt)
428 {
429 // Check if received event is valid
430 if (!CheckEventSize(evt, (2*416+8)*4))
431 return GetCurrentState();
432
433 // Record only currents when the drive is tracking to avoid
434 // bias from the movement
435 if (fDimDrive.state()<Drive::State::kTracking)
436 return GetCurrentState();
437
438 // Get time and median current (FIXME: check N?)
439 const Time &time = evt.GetTime();
440 const float med = evt.Get<float>(416*4+4+4);
441 const float dev = evt.Get<float>(416*4+4+4+4);
442 const float *cur = evt.Ptr<float>();
443
444 // Keep all median currents of the past 10 seconds
445 fCurrentsMed.emplace_back(time, med);
446 fCurrentsDev.emplace_back(time, dev);
447 fCurrentsVec.emplace_back(time, vector<float>(cur, cur+320));
448 while (!fCurrentsMed.empty())
449 {
450 if (time-fCurrentsMed.front().first<boost::posix_time::seconds(fAverageTime))
451 break;
452
453 fCurrentsMed.pop_front();
454 fCurrentsDev.pop_front();
455 fCurrentsVec.pop_front();
456 }
457
458 // If we are not doing a calibration no further action necessary
459 if (!fCalibrateByCurrent)
460 return GetCurrentState();
461
462 // We are not setting thresholds at all
463 if (GetCurrentState()!=RateControl::State::kSettingGlobalThreshold)
464 return GetCurrentState();
465
466 // Target thresholds have been assigned already
467 if (!fThresholds.empty())
468 return GetCurrentState();
469
470 // We want at least 8 values for averaging
471 if (fCurrentsMed.size()<fRequiredEvents)
472 return GetCurrentState();
473
474 // Calculate avera and rms of median
475 double avg = 0;
476 double rms = 0;
477 for (auto it=fCurrentsMed.begin(); it!=fCurrentsMed.end(); it++)
478 {
479 avg += it->second;
480 rms += it->second*it->second;
481 }
482 avg /= fCurrentsMed.size();
483 rms /= fCurrentsMed.size();
484 rms -= avg*avg;
485 rms = rms<0 ? 0 : sqrt(rms);
486
487 double avg_dev = 0;
488 for (auto it=fCurrentsDev.begin(); it!=fCurrentsDev.end(); it++)
489 avg_dev += it->second;
490 avg_dev /= fCurrentsMed.size();
491
492 // One could recalculate the median of all pixels including the
493 // correction for the three crazy pixels, but that is three out
494 // of 320. The effect on the median should be negligible anyhow.
495 vector<double> vec(160);
496 for (auto it=fCurrentsVec.begin(); it!=fCurrentsVec.end(); it++)
497 for (int i=0; i<320; i++)
498 {
499 const PixelMapEntry &hv = fMap.hv(i);
500 if (hv)
501 vec[hv.hw()/9] += it->second[i]*hv.count();
502 }
503
504 //fThresholdMin = max(uint16_t(36.0833*pow(avg, 0.638393)+184.037), fThresholdReference);
505 //fThresholdMin = max(uint16_t(42.4*pow(avg, 0.642)+182), fThresholdReference);
506 //fThresholdMin = max(uint16_t(41.6*pow(avg+1, 0.642)+175), fThresholdReference);
507 //fThresholdMin = max(uint16_t(42.3*pow(avg, 0.655)+190), fThresholdReference);
508 //fThresholdMin = max(uint16_t(46.6*pow(avg, 0.627)+187), fThresholdReference);
509 fThresholdMin = max(uint16_t(156.3*pow(avg, 0.3925)+1), fThresholdReference);
510 //fThresholdMin = max(uint16_t(41.6*pow(avg, 0.642)+175), fThresholdReference);
511 fThresholds.assign(160, fThresholdMin);
512
513 int counter = 1;
514
515 double avg2 = 0;
516 for (int i=0; i<160; i++)
517 {
518 vec[i] /= fCurrentsVec.size()*9;
519
520 avg2 += vec[i];
521
522 if (vec[i]>avg+3.5*avg_dev)
523 {
524 fThresholds[i] = max(uint16_t(40.5*pow(vec[i], 0.642)+164), fThresholdMin);
525
526 counter++;
527 }
528 }
529 avg2 /= 160;
530
531
532 Dim::SendCommandNB("FTM_CONTROL/SET_ALL_THRESHOLDS", fThresholds);
533
534
535 const RateControl::DimThreshold data = { fThresholdMin, fCalibrationTimeStart.Mjd(), Time().Mjd() };
536 fDimThreshold.setQuality(2);
537 fDimThreshold.Update(data);
538
539 //Info("Sent a total of "+to_string(counter)+" commands for threshold setting");
540
541 ostringstream out;
542 out << setprecision(3);
543 out << "Measured average current " << avg << "uA +- " << rms << "uA [N=" << fCurrentsMed.size() << "]... mininum threshold set to " << fThresholdMin;
544 Info(out);
545 Info("Set "+to_string(counter)+" individual thresholds.");
546
547 fTriggerOn = false;
548 fPhysTriggerEnabled = false;
549
550 return RateControl::State::kSettingGlobalThreshold;
551 }
552
553 int Calibrate()
554 {
555 if (!fPhysTriggerEnabled)
556 {
557 Info("Physics trigger not enabled... CALIBRATE command ignored.");
558
559 fTriggerOn = false;
560 fPhysTriggerEnabled = false;
561 return RateControl::State::kGlobalThresholdSet;
562 }
563
564 const int32_t val[2] = { -1, fThresholdReference };
565 Dim::SendCommandNB("FTM_CONTROL/SET_THRESHOLD", val);
566
567 fThresholds.assign(160, fThresholdReference);
568
569 fThresholdMin = fThresholdReference;
570 fTriggerRate = -1;
571 fCounter = 0;
572 fBlock.assign(160, false);
573
574 fCalibrateByCurrent = false;
575 fCalibrationTimeStart = Time();
576
577 ostringstream out;
578 out << "Rate calibration started at a threshold of " << fThresholdReference << " with a target rate of " << fTargetRate << " Hz";
579 Info(out);
580
581 return RateControl::State::kSettingGlobalThreshold;
582 }
583
584 int CalibrateByCurrent()
585 {
586 if (!fPhysTriggerEnabled)
587 {
588 Info("Physics trigger not enabled... CALIBRATE command ignored.");
589
590 fTriggerOn = false;
591 fPhysTriggerEnabled = false;
592 return RateControl::State::kGlobalThresholdSet;
593 }
594
595 if (fDimDrive.state()<Drive::State::kMoving)
596 Warn("Drive not even moving...");
597
598 fCounter = 0;
599 fCalibrateByCurrent = true;
600 fCalibrationTimeStart = Time();
601 fBlock.assign(160, false);
602
603 fThresholds.clear();
604
605 ostringstream out;
606 out << "Rate calibration by current with min. threshold of " << fThresholdReference << ".";
607 Info(out);
608
609 return RateControl::State::kSettingGlobalThreshold;
610 }
611
612 int CalibrateRun(const EventImp &evt)
613 {
614 const string name = evt.GetText();
615
616 auto it = fRunTypes.find(name);
617 if (it==fRunTypes.end())
618 {
619 Info("CalibrateRun - Run-type '"+name+"' not found... trying 'default'.");
620
621 it = fRunTypes.find("default");
622 if (it==fRunTypes.end())
623 {
624 Error("CalibrateRun - Run-type 'default' not found.");
625 return GetCurrentState();
626 }
627 }
628
629 const config &conf = it->second;
630
631 switch (conf.fCalibrationType)
632 {
633 case 0:
634 Info("No calibration requested.");
635 fTriggerOn = false;
636 fPhysTriggerEnabled = false;
637 return RateControl::State::kGlobalThresholdSet;
638 break;
639
640 case 1:
641 fThresholdReference = conf.fMinThreshold;
642 fTargetRate = conf.fTargetRate;
643 return Calibrate();
644
645 case 2:
646 fThresholdReference = conf.fMinThreshold;
647 fAverageTime = conf.fAverageTime;
648 fRequiredEvents = conf.fRequiredEvents;
649 return CalibrateByCurrent();
650 }
651
652 Error("CalibrateRun - Calibration type "+to_string(conf.fCalibrationType)+" unknown.");
653 return GetCurrentState();
654 }
655
656 int StopRC()
657 {
658 Info("Stop received.");
659 return RateControl::State::kConnected;
660 }
661
662 int SetMinThreshold(const EventImp &evt)
663 {
664 if (!CheckEventSize(evt, 4))
665 return kSM_FatalError;
666
667 // FIXME: Check missing
668
669 fThresholdReference = evt.GetUShort();
670
671 return GetCurrentState();
672 }
673
674 int SetTargetRate(const EventImp &evt)
675 {
676 if (!CheckEventSize(evt, 4))
677 return kSM_FatalError;
678
679 fTargetRate = evt.GetFloat();
680
681 return GetCurrentState();
682 }
683
684 int Print() const
685 {
686 Out() << fDim << endl;
687 Out() << fDimFTM << endl;
688 Out() << fDimRS << endl;
689 Out() << fDimDrive << endl;
690
691 return GetCurrentState();
692 }
693
694 int SetVerbosity(const EventImp &evt)
695 {
696 if (!CheckEventSize(evt, 1))
697 return kSM_FatalError;
698
699 fVerbose = evt.GetBool();
700
701 return GetCurrentState();
702 }
703
704 int Execute()
705 {
706 if (!fDim.online())
707 return RateControl::State::kDimNetworkNA;
708
709 // All subsystems are not connected
710 if (fDimFTM.state()<FTM::State::kConnected || fDimDrive.state()<Drive::State::kConnected)
711 return RateControl::State::kDisconnected;
712
713 // Do not allow any action while a ratescan is configured or in progress
714 if (fDimRS.state()>=RateScan::State::kConfiguring)
715 return RateControl::State::kConnected;
716
717 switch (GetCurrentState())
718 {
719 case RateControl::State::kSettingGlobalThreshold:
720 return RateControl::State::kSettingGlobalThreshold;
721
722 case RateControl::State::kGlobalThresholdSet:
723
724 // Wait for the trigger to get switched on before starting control loop
725 if (fTriggerOn && fPhysTriggerEnabled)
726 return RateControl::State::kInProgress;
727
728 return RateControl::State::kGlobalThresholdSet;
729
730 case RateControl::State::kInProgress:
731
732 // Go back to connected when the trigger has been switched off
733 if (!fTriggerOn || !fPhysTriggerEnabled)
734 return RateControl::State::kConnected;
735
736 return RateControl::State::kInProgress;
737 }
738
739 return RateControl::State::kConnected;
740 }
741
742public:
743 StateMachineRateControl(ostream &out=cout) : StateMachineDim(out, "RATE_CONTROL"),
744 fPhysTriggerEnabled(false), fTriggerOn(false), fBlock(40),
745 fDimFTM("FTM_CONTROL"),
746 fDimRS("RATE_SCAN"),
747 fDimDrive("DRIVE_CONTROL"),
748 fDimThreshold("RATE_CONTROL/THRESHOLD", "S:1;D:1;D:1",
749 "Resulting threshold after calibration"
750 "|threshold[dac]:Resulting threshold from calibration"
751 "|begin[mjd]:Start time of calibration"
752 "|end[mjd]:End time of calibration")
753 {
754 // ba::io_service::work is a kind of keep_alive for the loop.
755 // It prevents the io_service to go to stopped state, which
756 // would prevent any consecutive calls to run()
757 // or poll() to do nothing. reset() could also revoke to the
758 // previous state but this might introduce some overhead of
759 // deletion and creation of threads and more.
760
761 fDim.Subscribe(*this);
762 fDimFTM.Subscribe(*this);
763 fDimRS.Subscribe(*this);
764 fDimDrive.Subscribe(*this);
765
766 Subscribe("FTM_CONTROL/TRIGGER_RATES")
767 (bind(&StateMachineRateControl::HandleTriggerRates, this, placeholders::_1));
768 Subscribe("FTM_CONTROL/STATIC_DATA")
769 (bind(&StateMachineRateControl::HandleStaticData, this, placeholders::_1));
770 Subscribe("FEEDBACK/CALIBRATED_CURRENTS")
771 (bind(&StateMachineRateControl::HandleCalibratedCurrents, this, placeholders::_1));
772
773 // State names
774 AddStateName(RateControl::State::kDimNetworkNA, "DimNetworkNotAvailable",
775 "The Dim DNS is not reachable.");
776
777 AddStateName(RateControl::State::kDisconnected, "Disconnected",
778 "The Dim DNS is reachable, but the required subsystems are not available.");
779
780 AddStateName(RateControl::State::kConnected, "Connected",
781 "All needed subsystems are connected to their hardware, no action is performed.");
782
783 AddStateName(RateControl::State::kSettingGlobalThreshold, "Calibrating",
784 "A global minimum thrshold is currently determined.");
785
786 AddStateName(RateControl::State::kGlobalThresholdSet, "GlobalThresholdSet",
787 "A global threshold has ben set, waiting for the trigger to be switched on.");
788
789 AddStateName(RateControl::State::kInProgress, "InProgress",
790 "Rate control in progress.");
791
792 AddEvent("CALIBRATE")
793 (bind(&StateMachineRateControl::Calibrate, this))
794 ("Start a search for a reasonable minimum global threshold");
795
796 AddEvent("CALIBRATE_BY_CURRENT")
797 (bind(&StateMachineRateControl::CalibrateByCurrent, this))
798 ("Set the global threshold from the median current");
799
800 AddEvent("CALIBRATE_RUN", "C")
801 (bind(&StateMachineRateControl::CalibrateRun, this, placeholders::_1))
802 ("Start a threshold calibration as defined in the setup for this run-type, state change to InProgress is delayed until trigger enabled");
803
804 AddEvent("STOP", RateControl::State::kSettingGlobalThreshold, RateControl::State::kGlobalThresholdSet, RateControl::State::kInProgress)
805 (bind(&StateMachineRateControl::StopRC, this))
806 ("Stop a calibration or ratescan in progress");
807
808 AddEvent("SET_MIN_THRESHOLD", "I:1")
809 (bind(&StateMachineRateControl::SetMinThreshold, this, placeholders::_1))
810 ("Set a minimum threshold at which th rate control starts calibrating");
811
812 AddEvent("SET_TARGET_RATE", "F:1")
813 (bind(&StateMachineRateControl::SetTargetRate, this, placeholders::_1))
814 ("Set a target trigger rate for the calibration");
815
816 AddEvent("PRINT")
817 (bind(&StateMachineRateControl::Print, this))
818 ("Print current status");
819
820 AddEvent("SET_VERBOSE", "B")
821 (bind(&StateMachineRateControl::SetVerbosity, this, placeholders::_1))
822 ("set verbosity state"
823 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
824
825 }
826
827 bool GetConfig(Configuration &conf, const string &name, const string &sub, uint16_t &rc)
828 {
829 if (conf.HasDef(name, sub))
830 {
831 rc = conf.GetDef<uint16_t>(name, sub);
832 return true;
833 }
834
835 Error("Neither "+name+"default nor "+name+sub+" found.");
836 return false;
837 }
838
839 int EvalOptions(Configuration &conf)
840 {
841 fVerbose = !conf.Get<bool>("quiet");
842
843 if (!fMap.Read(conf.Get<string>("pixel-map-file")))
844 {
845 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
846 return 1;
847 }
848
849 fThresholdReference = 300;
850 fThresholdMin = 300;
851 fTargetRate = 75;
852
853 fAverageTime = 10;
854 fRequiredEvents = 8;
855
856 // ---------- Setup run types ---------
857 const vector<string> types = conf.Vec<string>("run-type");
858 if (types.empty())
859 Warn("No run-types defined.");
860 else
861 Message("Defining run-types");
862
863 for (auto it=types.begin(); it!=types.end(); it++)
864 {
865 Message(" -> "+ *it);
866
867 if (fRunTypes.count(*it)>0)
868 {
869 Error("Run-type "+*it+" defined twice.");
870 return 1;
871 }
872
873 config &c = fRunTypes[*it];
874 if (!GetConfig(conf, "calibration-type.", *it, c.fCalibrationType) ||
875 !GetConfig(conf, "target-rate.", *it, c.fTargetRate) ||
876 !GetConfig(conf, "min-threshold.", *it, c.fMinThreshold) ||
877 !GetConfig(conf, "average-time.", *it, c.fAverageTime) ||
878 !GetConfig(conf, "required-events.", *it, c.fRequiredEvents))
879 return 2;
880 }
881
882 return -1;
883 }
884};
885
886// ------------------------------------------------------------------------
887
888#include "Main.h"
889
890template<class T>
891int RunShell(Configuration &conf)
892{
893 return Main::execute<T, StateMachineRateControl>(conf);
894}
895
896void SetupConfiguration(Configuration &conf)
897{
898 po::options_description control("Rate control options");
899 control.add_options()
900 ("quiet,q", po_bool(), "Disable printing more informations during rate control.")
901 ("pixel-map-file", var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage.")
902 //("max-wait", var<uint16_t>(150), "The maximum number of seconds to wait to get the anticipated resolution for a point.")
903 // ("resolution", var<double>(0.05) , "The minimum resolution required for a single data point.")
904 ;
905
906 conf.AddOptions(control);
907
908 po::options_description runtype("Run type configuration");
909 runtype.add_options()
910 ("run-type", vars<string>(), "Name of run-types (replace the * in the following configuration by the case-sensitive names defined here)")
911 ("calibration-type.*", var<uint16_t>(), "Calibration type (0: none, 1: by rate, 2: by current)")
912 ("target-rate.*", var<uint16_t>(), "Target rate for calibration by rate")
913 ("min-threshold.*", var<uint16_t>(), "Minimum threshold which can be applied in a calibration")
914 ("average-time.*", var<uint16_t>(), "Time in seconds to average the currents for a calibration by current.")
915 ("required-events.*", var<uint16_t>(), "Number of required current events to start a calibration by current.");
916 ;
917
918 conf.AddOptions(runtype);
919}
920
921/*
922 Extract usage clause(s) [if any] for SYNOPSIS.
923 Translators: "Usage" and "or" here are patterns (regular expressions) which
924 are used to match the usage synopsis in program output. An example from cp
925 (GNU coreutils) which contains both strings:
926 Usage: cp [OPTION]... [-T] SOURCE DEST
927 or: cp [OPTION]... SOURCE... DIRECTORY
928 or: cp [OPTION]... -t DIRECTORY SOURCE...
929 */
930void PrintUsage()
931{
932 cout <<
933 "The ratecontrol program is a keep the rate reasonable low.\n"
934 "\n"
935 "Usage: ratecontrol [-c type] [OPTIONS]\n"
936 " or: ratecontrol [OPTIONS]\n";
937 cout << endl;
938}
939
940void PrintHelp()
941{
942 Main::PrintHelp<StateMachineRateControl>();
943
944 /* Additional help text which is printed after the configuration
945 options goes here */
946
947 /*
948 cout << "bla bla bla" << endl << endl;
949 cout << endl;
950 cout << "Environment:" << endl;
951 cout << "environment" << endl;
952 cout << endl;
953 cout << "Examples:" << endl;
954 cout << "test exam" << endl;
955 cout << endl;
956 cout << "Files:" << endl;
957 cout << "files" << endl;
958 cout << endl;
959 */
960}
961
962int main(int argc, const char* argv[])
963{
964 Configuration conf(argv[0]);
965 conf.SetPrintUsage(PrintUsage);
966 Main::SetupConfiguration(conf);
967 SetupConfiguration(conf);
968
969 if (!conf.DoParse(argc, argv, PrintHelp))
970 return 127;
971
972 if (!conf.Has("console"))
973 return RunShell<LocalStream>(conf);
974
975 if (conf.Get<int>("console")==0)
976 return RunShell<LocalShell>(conf);
977 else
978 return RunShell<LocalConsole>(conf);
979
980 return 0;
981}
Note: See TracBrowser for help on using the repository browser.