source: branches/FACT++_lidctrl_usb/src/feedback.cc@ 19582

Last change on this file since 19582 was 18607, checked in by tbretz, 8 years ago
WORKAROUND CH272: Removed the shortcut channel from the list of treated channels.
File size: 51.7 KB
Line 
1#include <valarray>
2#include <algorithm>
3
4#include "Dim.h"
5#include "Event.h"
6#include "Shell.h"
7#include "StateMachineDim.h"
8#include "Connection.h"
9#include "Configuration.h"
10#include "Console.h"
11#include "externals/PixelMap.h"
12
13#include "tools.h"
14
15#include "LocalControl.h"
16
17#include "HeadersFSC.h"
18#include "HeadersBIAS.h"
19#include "HeadersFeedback.h"
20
21#include "DimState.h"
22#include "DimDescriptionService.h"
23
24using namespace std;
25
26// ------------------------------------------------------------------------
27
28class StateMachineFeedback : public StateMachineDim
29{
30private:
31 PixelMap fMap;
32
33 bool fIsVerbose;
34
35 DimVersion fDim;
36
37 DimDescribedState fDimFSC;
38 DimDescribedState fDimBias;
39
40 DimDescribedService fDimCalibration;
41 DimDescribedService fDimCalibration2;
42 DimDescribedService fDimCalibrationR8;
43 DimDescribedService fDimCurrents;
44 DimDescribedService fDimOffsets;
45
46 vector<float> fCalibCurrentMes[6]; // Measured calibration current at six different levels
47 vector<float> fCalibVoltage[6]; // Corresponding voltage as reported by biasctrl
48
49 vector<int64_t> fCurrentsAvg;
50 vector<int64_t> fCurrentsRms;
51
52 vector<float> fVoltGapd; // Nominal breakdown voltage + 1.1V
53 vector<float> fBiasVolt; // Output voltage as reported by bias crate (voltage between R10 and R8)
54 vector<float> fBiasR9; //
55 vector<uint16_t> fBiasDac; // Dac value corresponding to the voltage setting
56
57 vector<float> fCalibration;
58 vector<float> fCalibDeltaI;
59 vector<float> fCalibR8;
60
61 int64_t fCursorCur;
62
63 Time fTimeCalib;
64 Time fTimeTemp;
65 Time fTimeCritical;
66
67 double fUserOffset;
68 double fVoltageReduction;
69 vector<double> fTempOffset;
70 float fTempOffsetAvg;
71 float fTempOffsetRms;
72 double fTempCoefficient;
73 double fTemp;
74
75 vector<double> fVoltOffset;
76
77 uint16_t fMoonMode;
78
79 uint16_t fCurrentRequestInterval;
80 uint16_t fNumCalibIgnore;
81 uint16_t fNumCalibRequests;
82 uint16_t fCalibStep;
83
84 uint16_t fTimeoutCritical;
85
86 // ============================= Handle Services ========================
87
88 int HandleBiasStateChange()
89 {
90 if (fDimBias.state()==BIAS::State::kVoltageOn && GetCurrentState()==Feedback::State::kCalibrating)
91 {
92 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS");
93 Info("Starting calibration step "+to_string(fCalibStep));
94 }
95
96 if (fDimBias.state()==BIAS::State::kVoltageOff && GetCurrentState()>=Feedback::State::kInProgress)
97 return Feedback::State::kCalibrated;
98
99 return GetCurrentState();
100 }
101 // ============================= Handle Services ========================
102
103 bool CheckEventSize(size_t has, const char *name, size_t size)
104 {
105 if (has==size)
106 return true;
107
108 // Disconnected
109 if (has==0)
110 return false;
111
112 ostringstream msg;
113 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
114 Fatal(msg);
115 return false;
116 }
117
118 int HandleBiasNom(const EventImp &evt)
119 {
120 if (evt.GetSize()>=BIAS::kNumChannels*sizeof(float))
121 {
122 fVoltGapd.assign(evt.Ptr<float>(), evt.Ptr<float>()+BIAS::kNumChannels);
123 fBiasR9.assign(evt.Ptr<float>()+2*BIAS::kNumChannels, evt.Ptr<float>()+3*BIAS::kNumChannels);
124
125 for (int i=0; i<320; i++)
126 fVoltGapd[i] += 1.1;
127
128 Info("Nominal bias voltages and calibration resistor received.");
129 }
130
131 return GetCurrentState();
132 }
133
134 int HandleBiasVoltage(const EventImp &evt)
135 {
136 if (evt.GetSize()>=BIAS::kNumChannels*sizeof(float))
137 fBiasVolt.assign(evt.Ptr<float>(), evt.Ptr<float>()+BIAS::kNumChannels);
138 return GetCurrentState();
139 }
140
141 int HandleBiasDac(const EventImp &evt)
142 {
143 if (evt.GetSize()>=BIAS::kNumChannels*sizeof(uint16_t))
144 fBiasDac.assign(evt.Ptr<uint16_t>(), evt.Ptr<uint16_t>()+BIAS::kNumChannels);
145 return GetCurrentState();
146 }
147
148 int HandleCameraTemp(const EventImp &evt)
149 {
150 if (!CheckEventSize(evt.GetSize(), "HandleCameraTemp", 323*sizeof(float)))
151 {
152 fTimeTemp = Time(Time::none);
153 return GetCurrentState();
154 }
155
156 //fTempOffset = (avgt-25)*0.0561765; // [V] From Hamamatsu datasheet
157 //fTempOffset = (avgt-25)*0.05678; // [V] From Hamamatsu datasheet plus our own measurement (gein vs. temperature)
158
159 const float *ptr = evt.Ptr<float>(4);
160
161 fTimeTemp = evt.GetTime();
162 fTemp = evt.Get<float>(321*4);
163
164 fTempOffsetAvg = (fTemp-25)*fTempCoefficient;
165 fTempOffsetRms = evt.Get<float>(322*4)*fTempCoefficient;
166
167 fTempOffset.resize(320);
168 for (int i=0; i<320; i++)
169 fTempOffset[i] = (ptr[i]-25)*fTempCoefficient;
170
171 return GetCurrentState();
172 }
173
174 pair<vector<float>, vector<float>> AverageCurrents(const int16_t *ptr, int n)
175 {
176 if (fCursorCur++>=0)
177 {
178 for (int i=0; i<BIAS::kNumChannels; i++)
179 {
180 fCurrentsAvg[i] += ptr[i];
181 fCurrentsRms[i] += ptr[i]*ptr[i];
182 }
183 }
184
185 if (fCursorCur<n)
186 return make_pair(vector<float>(), vector<float>());
187
188 const double conv = 5e-3/4096;
189
190 vector<float> rms(BIAS::kNumChannels);
191 vector<float> avg(BIAS::kNumChannels);
192 for (int i=0; i<BIAS::kNumChannels; i++)
193 {
194 avg[i] = double(fCurrentsAvg[i])/fCursorCur * conv;
195 rms[i] = double(fCurrentsRms[i])/fCursorCur * conv * conv;
196 rms[i] -= avg[i]*avg[i];
197 rms[i] = rms[i]<0 ? 0 : sqrt(rms[i]);
198 }
199
200 return make_pair(avg, rms);
201 }
202
203 int HandleCalibration(const EventImp &evt)
204 {
205 if (fDimBias.state()!=BIAS::State::kVoltageOn)
206 return GetCurrentState();
207
208 const uint16_t dac = 256+512*fCalibStep; // Command value
209
210 // Only the channels which are no spare channels are ramped
211 // Due to the shortcut, only 319 channels are ramped, so only
212 // 320 and not 319 are expected to have the correct day setting
213 if (std::count(fBiasDac.begin(), fBiasDac.end(), dac)!=319/*320*/)
214 return GetCurrentState();
215
216 const auto rc = AverageCurrents(evt.Ptr<int16_t>(), fNumCalibRequests);
217 if (rc.first.size()==0)
218 {
219 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS");
220 return GetCurrentState();
221 }
222
223 const vector<float> &avg = rc.first;
224 const vector<float> &rms = rc.second;
225
226 // Current through resistor R8
227 fCalibCurrentMes[fCalibStep] = avg; // [A]
228 fCalibVoltage[fCalibStep] = fBiasVolt; // [V]
229
230 // ------------------------- Update calibration data --------------------
231
232 struct cal_data
233 {
234 uint32_t dac;
235 float U[BIAS::kNumChannels];
236 float Iavg[BIAS::kNumChannels];
237 float Irms[BIAS::kNumChannels];
238
239 cal_data() { memset(this, 0, sizeof(cal_data)); }
240 } __attribute__((__packed__));
241
242 cal_data cal;
243 cal.dac = dac;
244 memcpy(cal.U, fBiasVolt.data(), BIAS::kNumChannels*sizeof(float));
245 memcpy(cal.Iavg, avg.data(), BIAS::kNumChannels*sizeof(float));
246 memcpy(cal.Irms, rms.data(), BIAS::kNumChannels*sizeof(float));
247
248 fDimCalibration2.setData(cal);
249 fDimCalibration2.Update(fTimeCalib);
250
251 // -------------------- Start next calibration steo ---------------------
252
253 if (++fCalibStep<6)
254 {
255 fCursorCur = -fNumCalibIgnore;
256 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
257 fCurrentsRms.assign(BIAS::kNumChannels, 0);
258
259 // Ramp all channels to the calibration setting except the one
260 // with a shortcut
261 vector<uint16_t> vec(BIAS::kNumChannels, uint16_t(256+512*fCalibStep));
262 vec[272] = 0;
263 Dim::SendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_DAC", vec);
264
265 //Dim::SendCommandNB("BIAS_CONTROL/SET_GLOBAL_DAC", uint16_t(256+512*fCalibStep));
266
267 return GetCurrentState();
268 }
269
270 // --------------- Calculate old style calibration ----------------------
271
272 fCalibration.resize(BIAS::kNumChannels*4);
273
274 float *pavg = fCalibration.data();
275 float *prms = fCalibration.data()+BIAS::kNumChannels;
276 float *pres = fCalibration.data()+BIAS::kNumChannels*2;
277 float *pUmes = fCalibration.data()+BIAS::kNumChannels*3;
278
279 for (int i=0; i<BIAS::kNumChannels; i++)
280 {
281 const double I = fCalibCurrentMes[5][i]; // [A]
282 const double U = fBiasVolt[i]; // [V]
283
284 pavg[i] = I*1e6; // [uA]
285 prms[i] = rms[i]*1e6; // [uA]
286 pres[i] = U/I; // [Ohm]
287 pUmes[i] = U; // [V]
288 }
289
290 fDimCalibration.setData(fCalibration);
291 fDimCalibration.Update(fTimeCalib);
292
293 // -------------------- New style calibration --------------------------
294
295 fCalibDeltaI.resize(BIAS::kNumChannels);
296 fCalibR8.resize(BIAS::kNumChannels);
297
298 // Linear regression of the values at 256+512*N for N={ 3, 4, 5 }
299 for (int i=0; i<BIAS::kNumChannels; i++)
300 {
301 // x: Idac
302 // y: Iadc
303
304 double x = 0;
305 double y = 0;
306 double xx = 0;
307 double xy = 0;
308
309 const int beg = 3;
310 const int end = 5;
311 const int len = end-beg+1;
312
313 for (int j=beg; j<=end; j++)
314 {
315 const double Idac = (256+512*j)*1e-3/4096;
316
317 x += Idac;
318 xx += Idac*Idac;
319 y += fCalibCurrentMes[j][i];
320 xy += fCalibCurrentMes[j][i]*Idac;
321 }
322
323 const double m1 = xy - x*y / len;
324 const double m2 = xx - x*x / len;
325
326 const double m = m2==0 ? 0 : m1/m2;
327
328 const double t = (y - m*x) / len;
329
330 fCalibDeltaI[i] = t; // [A]
331 fCalibR8[i] = 100/m; // [Ohm]
332 }
333
334 vector<float> v;
335 v.reserve(BIAS::kNumChannels*2);
336 v.insert(v.end(), fCalibDeltaI.begin(), fCalibDeltaI.end());
337 v.insert(v.end(), fCalibR8.begin(), fCalibR8.end());
338
339 fDimCalibrationR8.setData(v);
340 fDimCalibrationR8.Update(fTimeCalib);
341
342 // ---------------------------------------------------------------------
343
344 Info("Calibration successfully done.");
345 Dim::SendCommandNB("BIAS_CONTROL/SET_ZERO_VOLTAGE");
346
347 return Feedback::State::kCalibrated;
348 }
349
350 int CheckLimits(const float *I)
351 {
352 const float fAbsoluteMedianCurrentLimit = 85;
353 const float fRelativePixelCurrentLimit3 = 20;
354 const float fRelativePixelCurrentLimit0 = 45;
355
356 const float fAbsolutePixelCurrentLimit3 = fAbsoluteMedianCurrentLimit + fRelativePixelCurrentLimit3;
357 const float fAbsolutePixelCurrentLimit0 = fAbsoluteMedianCurrentLimit + fRelativePixelCurrentLimit0;
358
359 const float fRelativeCurrentLimitWarning = 10;//10;
360 const float fRelativeCurrentLimitCritical = 15;//20;
361 const float fRelativeCurrentLimitShutdown = 25;
362
363 fTimeoutCritical = 3000; // 5s
364
365 // Copy the calibrated currents
366 vector<float> v(I, I+320);
367
368 // Exclude the crazy patches (that's currently the best which could be done)
369 v[66] = 0;
370 v[191] = 0;
371 v[193] = 0;
372
373 sort(v.begin(), v.end());
374
375 const float &imax0 = v[319];
376 const float &imax3 = v[316];
377 const float &imed = v[161];
378
379 const bool shutdown =
380 imed >fAbsoluteMedianCurrentLimit+fRelativeCurrentLimitShutdown ||
381 imax3>fAbsolutePixelCurrentLimit3+fRelativeCurrentLimitShutdown ||
382 imax0>fAbsolutePixelCurrentLimit0+fRelativeCurrentLimitShutdown;
383
384 const bool critical =
385 imed >fAbsoluteMedianCurrentLimit+fRelativeCurrentLimitCritical ||
386 imax3>fAbsolutePixelCurrentLimit3+fRelativeCurrentLimitCritical ||
387 imax0>fAbsolutePixelCurrentLimit0+fRelativeCurrentLimitCritical;
388
389 const bool warning =
390 imed >fAbsoluteMedianCurrentLimit+fRelativeCurrentLimitWarning ||
391 imax3>fAbsolutePixelCurrentLimit3+fRelativeCurrentLimitWarning ||
392 imax0>fAbsolutePixelCurrentLimit0+fRelativeCurrentLimitWarning;
393
394 bool standby = GetCurrentState()==Feedback::State::kOnStandby;
395
396 if (standby)
397 {
398 // On Standby
399 if (fVoltageReduction==0 &&
400 imed <fAbsoluteMedianCurrentLimit &&
401 imax3<fAbsolutePixelCurrentLimit3 &&
402 imax0<fAbsolutePixelCurrentLimit0)
403 {
404 // Currents are back at nominal value and currents are again
405 // below the current limit, switching back to standard operation.
406 return Feedback::State::kInProgress;
407 }
408 }
409
410 // Shutdown level
411 if (!standby && shutdown)
412 {
413 // Currents exceed the shutdown limit, operation is switched
414 // immediately to voltage reduced operation
415
416 // Just in case (FIXME: Is that really the right location?)
417 Dim::SendCommandNB("FAD_CONTROL/CLOSE_ALL_OPEN_FILES");
418
419 Error("Current limit for shutdown exceeded.... switching to standby mode.");
420
421 standby = true;
422 }
423
424 // Critical level
425 if (!standby && critical)
426 {
427 // This is a state transition from InProgress or Warning to Critical.
428 // Keep the transition time.
429 if (GetCurrentState()==Feedback::State::kInProgress || GetCurrentState()==Feedback::State::kWarning)
430 {
431 Info("Critical current limit exceeded.... waiting for "+to_string(fTimeoutCritical)+" ms.");
432 fTimeCritical = Time();
433 }
434
435 // Critical is only allowed for fTimeoutCritical milliseconds.
436 // After this time, the operation is changed to reduced voltage.
437 if (Time()<fTimeCritical+boost::posix_time::milliseconds(fTimeoutCritical))
438 return Feedback::State::kCritical;
439
440 // Just in case (FIXME: Is that really the right location?)
441 Dim::SendCommandNB("FAD_CONTROL/CLOSE_ALL_OPEN_FILES");
442
443 // Currents in critical state
444 Warn("Critical current limit exceeded timeout.... switching to standby mode.");
445
446 standby = true;
447 }
448
449 // Warning level (is just informational)
450 if (!standby && warning)
451 return Feedback::State::kWarning;
452
453 // keep voltage
454 return standby ? Feedback::State::kOnStandby : Feedback::State::kInProgress;
455 }
456
457 int HandleBiasCurrent(const EventImp &evt)
458 {
459 if (!CheckEventSize(evt.GetSize(), "HandleBiasCurrent", BIAS::kNumChannels*sizeof(uint16_t)))
460 return Feedback::State::kConnected;
461
462 if (GetCurrentState()<Feedback::State::kCalibrating)
463 return GetCurrentState();
464
465 // ------------------------------- HandleCalibration -----------------------------------
466 if (GetCurrentState()==Feedback::State::kCalibrating)
467 return HandleCalibration(evt);
468
469 // ---------------------- Calibrated, WaitingForData, InProgress -----------------------
470
471 // We are waiting but no valid temperature yet, go on waiting
472 if (GetCurrentState()==Feedback::State::kWaitingForData &&
473 (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5)))
474 return GetCurrentState();
475
476 // We are waiting but biasctrl is still in ramping (this might
477 // be the case if the feedback was started with a new overvoltage
478 // while the last ramping command was still in progress)
479 if (GetCurrentState()==Feedback::State::kWaitingForData &&
480 fDimBias.state()==BIAS::State::kRamping)
481 return GetCurrentState();
482
483 // We are already in progress but no valid temperature update anymore
484 if (GetCurrentState()>=Feedback::State::kInProgress &&
485 (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5)))
486 {
487 Warn("Current control in progress, but last received temperature older than 5min... switching voltage off.");
488 Dim::SendCommandNB("BIAS_CONTROL/SET_ZERO_VOLTAGE");
489 return Feedback::State::kCalibrated;
490 }
491
492 // ---------------------- Calibrated, WaitingForData, InProgress -----------------------
493
494 const int Navg = fDimBias.state()!=BIAS::State::kVoltageOn ? 1 : 3;
495
496 const vector<float> &Imes = AverageCurrents(evt.Ptr<int16_t>(), Navg).first;
497 if (Imes.size()==0)
498 return GetCurrentState();
499
500 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
501 fCurrentsRms.assign(BIAS::kNumChannels, 0);
502 fCursorCur = 0;
503
504 // -------------------------------------------------------------------------------------
505 // Inner patches to be blocked (operated below the operation voltage) in moon mode
506
507 static const array<int, 14> inner0 =
508 {{
509 62, 63, 130, 131, 132, 133, 134,
510 135, 222, 223, 292, 293, 294, 295,
511 }};
512
513 static const array<int, 23> inner1 =
514 {{
515 58, 59, 60, 61, 129, 138, 139, 140, 141, 142, 143, 218,
516 219, 220, 221, 290, 291, 298, 299, 300, 301, 302, 303,
517 }};
518
519 static const array<int, 43> inner2 =
520 {{
521 42, 43, 44, 45, 55, 56, 57, 70, 71, 78, 79,
522 96, 97, 98, 99, 102, 103, 128, 136, 137, 159, 202,
523 203, 204, 205, 214, 216, 217, 228, 230, 231, 256, 257,
524 258, 259, 262, 263, 288, 289, 296, 297, 310, 318
525 }};
526
527 // -------------------------------------------------------------------------------------
528
529 // Nominal overvoltage (w.r.t. the bias setup values)
530 const double voltageoffset = GetCurrentState()<Feedback::State::kWaitingForData ? 0 : fUserOffset;
531
532 double avg[2] = { 0, 0 };
533 double min[2] = { 90, 90 };
534 double max[2] = { -90, -90 };
535 int num[3] = { 0, 0, 0 };
536
537 vector<double> med[3];
538 med[0].resize(BIAS::kNumChannels);
539 med[1].resize(BIAS::kNumChannels);
540 med[2].resize(BIAS::kNumChannels);
541
542 struct dim_data
543 {
544 float I[BIAS::kNumChannels];
545 float Iavg;
546 float Irms;
547 float Imed;
548 float Idev;
549 uint32_t N;
550 float Tdiff;
551 float Uov[BIAS::kNumChannels];
552 float Unom;
553 float dUtemp;
554
555 dim_data() { memset(this, 0, sizeof(dim_data)); }
556 } __attribute__((__packed__));
557
558 int Ndev[3] = { 0, 0, 0 };
559
560 dim_data data;
561
562 data.Unom = voltageoffset;
563 data.dUtemp = fTempOffsetAvg;
564
565 vector<float> vec(BIAS::kNumChannels);
566
567 // ================================= old =======================
568 // Pixel 583: 5 31 == 191 (5) C2 B3 P3
569 // Pixel 830: 2 2 == 66 (4) C0 B8 P1
570 // Pixel 1401: 6 1 == 193 (5) C2 B4 P0
571
572 double UdrpAvg = 0;
573 double UdrpRms = 0;
574
575 for (int i=0; i<320/*BIAS::kNumChannels*/; i++)
576 {
577 const PixelMapEntry &hv = fMap.hv(i);
578 if (!hv)
579 continue;
580
581 // Check if this is a blocked channel
582 // 272 is the one with the shortcut
583 const bool blocked =
584 (fMoonMode>0 && std::find(inner0.begin(), inner0.end(), i)!=inner0.end()) ||
585 (fMoonMode>1 && std::find(inner1.begin(), inner1.end(), i)!=inner1.end()) ||
586 (fMoonMode>2 && std::find(inner2.begin(), inner2.end(), i)!=inner2.end()) ||
587 i==272;
588
589 // Number of G-APDs in this patch
590 const int N = hv.count();
591
592 // Average measured ADC value for this channel
593 // FIXME: This is a workaround for the problem with the
594 // readout of bias voltage channel 263
595 const double adc = Imes[i]/* * (5e-3/4096)*/; // [A]
596
597 // Current through ~100 Ohm measurement resistor
598 //const double I8 = (adc-fCalibDeltaI[i])*fCalibR8[i]/100;
599 const double I8 = adc-fCalibDeltaI[i];
600
601 // Current through calibration resistors (R9)
602 // This is uncalibrated, but since the corresponding calibrated
603 // value I8 is subtracted, the difference should yield a correct value
604 const double I9 = fBiasDac[i] * (1e-3/4096);//U9/R9; [A]
605
606 // Current in R4/R5 branch
607 //const double Iout = I8 - I9;//I8>I9 ? I8 - I9 : 0;
608 const double Iout = I8 - I9*100/fCalibR8[i];//I8>I9 ? I8 - I9 : 0;
609
610 // Applied voltage at calibration resistors, according to biasctrl
611 const double U9 = fBiasVolt[i];
612
613 // new I8 - I9*100/fCalibR8 100
614 // change = --- = ---------------------- = -------- = 0.8
615 // old I8*fCalibR8/100 - I9 fCalibR8
616
617 // Serial resistors (one 1kOhm at the output of the bias crate, one 1kOhm in the camera)
618 const double R4 = 2000;
619
620 // Serial resistor of the individual G-APDs plus 50 Ohm termination
621 double R5 = 3900./N + 50;
622
623 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
624 if (i==66 || i==193) // Pixel 830(66) / Pixel 583(191)
625 R5 = 1./((N-1)/3900.+1/1000.);
626 if (i==191) // Pixel 1399(193)
627 R5 = 1./((N-1)/3900.+1/390.);
628 if (i==17 || i==206) // dead pixel 923(80) / dead pixel 424(927)
629 R5 = 3900./(N-1); // cannot identify third dead pixel in light-pulser data
630
631 // The measurement resistor
632 const double R8 = 0;
633
634 // Total resistance of branch with diodes (R4+R5)
635 // Assuming that the voltage output of the OpAMP is linear
636 // with the DAC setting and not the voltage at R9, the
637 // additional voltage drop at R8 must be taken into account
638 const double R = R4 + R5 + R8;
639
640 // For the patches with a broken resistor - ignoring the G-APD resistance -
641 // we get:
642 //
643 // I[R=3900] = Iout * 1/(10+(N-1)) = Iout /(N+9)
644 // I[R= 390] = Iout * (1 - 1/(10+(N-1))) = Iout * (N+8)/(N+9)
645 //
646 // I[R=390] / I[R=3900] = N+8
647 //
648 // Udrp = Iout*3900/(N+9) + Iout*1000 + Iout*1000 = Iout * R
649
650 // Voltage drop in R4/R5 branch (for the G-APDs with correct resistor)
651 // The voltage drop should not be <0, otherwise an unphysical value
652 // would be amplified when Uset is calculated.
653 const double Udrp = Iout<0 ? 0 : R*Iout;
654
655 // Nominal operation voltage with correction for temperature dependence
656 const double Uop = fVoltGapd[i] + fVoltOffset[i] + fTempOffset[i]
657 + (blocked ? -5 : 0);
658
659 // Current overvoltage (at a G-APD with the correct 3900 Ohm resistor)
660 // expressed w.r.t. to the operation voltage
661 const double Uov = (U9-Udrp)-Uop>-1.4 ? (U9-Udrp)-Uop : -1.4;
662
663 // The current through one G-APD is the sum divided by the number of G-APDs
664 // (assuming identical serial resistors)
665 double Iapd = Iout/N;
666
667 // Rtot = Uapd/Iout
668 // Ich = Uapd/Rch = (Rtot*Iout) / Rch = Rtot/Rch * Iout
669 //
670 // Rtot = 3900/N
671 // Rch = 3900
672 //
673 // Rtot = 1./((N-1)/3900 + 1/X) X=390 or X=1000
674 // Rch = 3900
675 //
676 // Rtot/Rch = 1/((N-1)/3900 + 1/X)/3900
677 // Rtot/Rch = 1/( [ X*(N-1) + 3900 ] / [ 3900 * X ])/3900
678 // Rtot/Rch = X/( [ X*(N-1)/3900 + 1 ] )/3900
679 // Rtot/Rch = X/( [ X*(N-1) + 3900 ] )
680 // Rtot/Rch = 1/( [ (N-1) + 3900/X ] )
681 //
682 // Rtot/Rch[390Ohm] = 1/( [ N + 9.0 ] )
683 // Rtot/Rch[1000Ohm] = 1/( [ N + 2.9 ] )
684 //
685 // In this and the previosu case we neglect the resistance of the G-APDs, but we can make an
686 // assumption: The differential resistance depends more on the NSB than on the PDE,
687 // thus it is at least comparable for all G-APDs in the patch. In addition, although the
688 // G-APD with the 390Ohm serial resistor has the wrong voltage applied, this does not
689 // significantly influences the ohmic resistor or the G-APD because the differential
690 // resistor is large enough that the increase of the overvoltage does not dramatically
691 // increase the current flow as compared to the total current flow.
692 if (i==66 || i==193) // Iout/13 15.8 / Iout/14 16.8
693 Iapd = Iout/(N+2.9);
694 if (i==191) // Iout/7.9 38.3
695 Iapd = Iout/(N+9);
696 if (i==17 || i==206)
697 Iapd = Iout/(N-1);
698
699 // The differential resistance of the G-APD, i.e. the dependence of the
700 // current above the breakdown voltage, is given by
701 //const double Rapd = Uov/Iapd;
702 // This allows us to estimate the current Iov at the overvoltage we want to apply
703 //const double Iov = overvoltage/Rapd;
704
705 // Estimate set point for over-voltage (voltage drop at the target point)
706 // This estimation is based on the linear increase of the
707 // gain with voltage and the increase of the crosstalk with
708 // voltage, as measured with the overvoltage-tests (OVTEST)
709 /*
710 Uov+0.44<0.022 ?
711 Ubd + overvoltage + Udrp*exp(0.6*(overvoltage-Uov))*pow((overvoltage+0.44), 0.6) :
712 Ubd + overvoltage + Udrp*exp(0.6*(overvoltage-Uov))*pow((overvoltage+0.44)/(Uov+0.44), 0.6);
713 */
714 const double Uset =
715 Uov+1.4<0.022 ?
716 Uop + voltageoffset + Udrp*exp(0.6*(voltageoffset-Uov))*pow((voltageoffset+1.4), 0.6) :
717 Uop + voltageoffset + Udrp*exp(0.6*(voltageoffset-Uov))*pow((voltageoffset+1.4)/(Uov+1.4), 0.6);
718
719 if (fabs(voltageoffset-Uov)>0.033)
720 Ndev[0]++;
721 if (fabs(voltageoffset-Uov)>0.022)
722 Ndev[1]++;
723 if (fabs(voltageoffset-Uov)>0.011)
724 Ndev[2]++;
725
726 // Voltage set point
727 vec[i] = Uset;
728
729 const double iapd = Iapd*1e6; // A --> uA
730
731 data.I[i] = iapd;
732 data.Uov[i] = Uov;
733
734 if (!blocked)
735 {
736 const int g = hv.group();
737
738 med[g][num[g]] = Uov;
739 avg[g] += Uov;
740 num[g]++;
741
742 if (Uov<min[g])
743 min[g] = Uov;
744 if (Uov>max[g])
745 max[g] = Uov;
746
747 data.Iavg += iapd;
748 data.Irms += iapd*iapd;
749
750 med[2][num[2]++] = iapd;
751
752 UdrpAvg += Udrp;
753 UdrpRms += Udrp*Udrp;
754 }
755 }
756
757
758 // ---------------------------- Calculate statistics ----------------------------------
759
760 // average and rms
761 data.Iavg /= num[2];
762 data.Irms /= num[2];
763 data.Irms -= data.Iavg*data.Iavg;
764
765 data.N = num[2];
766 data.Irms = data.Irms<0 ? 0: sqrt(data.Irms);
767
768 // median
769 sort(med[2].data(), med[2].data()+num[2]);
770
771 data.Imed = num[2]%2 ? med[2][num[2]/2] : (med[2][num[2]/2-1]+med[2][num[2]/2])/2;
772
773 // deviation
774 for (int i=0; i<num[2]; i++)
775 med[2][i] = fabs(med[2][i]-data.Imed);
776
777 sort(med[2].data(), med[2].data()+num[2]);
778
779 data.Idev = med[2][uint32_t(0.682689477208650697*num[2])];
780
781 // time difference to calibration
782 data.Tdiff = evt.GetTime().UnixTime()-fTimeCalib.UnixTime();
783
784 // Average overvoltage
785 const double Uov = (avg[0]+avg[1])/(num[0]+num[1]);
786
787 // ------------------------------- Update voltages ------------------------------------
788
789 int newstate = GetCurrentState();
790
791 if (GetCurrentState()!=Feedback::State::kCalibrated) // WaitingForData, OnStandby, InProgress, kWarning, kCritical
792 {
793 if (fDimBias.state()!=BIAS::State::kRamping)
794 {
795 newstate = CheckLimits(data.I);
796
797 // standby and change reduction level of voltage
798 if (newstate==Feedback::State::kOnStandby)
799 {
800 // Calculate average applied overvoltage and estimate an offset
801 // to reach fAbsoluteMedianCurrentLimit
802 float fAbsoluteMedianCurrentLimit = 85;
803 const double deltaU = (Uov+1.4)*(1-pow(fAbsoluteMedianCurrentLimit/data.Imed, 1./1.7));
804
805 if (fVoltageReduction+deltaU<0.033)
806 fVoltageReduction = 0;
807 else
808 {
809 fVoltageReduction += deltaU;
810
811 for (int i=0; i<320; i++)
812 vec[i] -= fVoltageReduction;
813 }
814 }
815
816 // FIXME: What if the brightest pixel gets too bright???
817 // FIXME: What if fVolatgeReduction > U1.4V?
818
819 // set voltage in 262 -> current in 262/263
820 vec[263] = vec[262]-fVoltGapd[262]+fVoltGapd[263];
821
822 // Do not ramp the channel with a shortcut
823 vec[272] = 0;
824
825// if (fDimBias.state()!=BIAS::State::kRamping)
826// {
827 DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_VOLTAGE",
828 vec.data(), BIAS::kNumChannels*sizeof(float));
829
830 UdrpAvg /= 320;
831 UdrpRms /= 320;
832 UdrpRms -= UdrpAvg*UdrpAvg;
833 UdrpRms = UdrpRms<0 ? 0 : sqrt(UdrpRms);
834
835 ostringstream msg;
836 msg << fixed;
837 msg << setprecision(2) << "dU(" << fTemp << "degC)="
838 << setprecision(3) << fTempOffsetAvg << "V+-" << fTempOffsetRms << " Udrp="
839 << UdrpAvg << "V+-" << UdrpRms;
840 msg.unsetf(ios_base::floatfield);
841
842 if (fVoltageReduction==0)
843 msg << " Unom=" << voltageoffset << "V";
844 else
845 msg << " Ured=" << fVoltageReduction << "V";
846
847 msg << " Uov=" << Uov;
848 msg << " Imed=" << data.Imed << "uA [N=" << Ndev[0] << "/" << Ndev[1] << "/" << Ndev[2] << "]";
849 Info(msg);
850 }
851 }
852 else
853 {
854 if (fDimBias.state()==BIAS::State::kVoltageOn)
855 {
856 ostringstream msg;
857 msg << setprecision(4) << "Current status: dU(" << fTemp << "degC)=" << fTempOffsetAvg << "V+-" << fTempOffsetRms << ", Unom=" << voltageoffset << "V, Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << " [N=" << Ndev[0] << "/" << Ndev[1] << "/" << Ndev[2] << "]";
858 Info(msg);
859 }
860 }
861
862 //if (GetCurrentState()>=Feedback::State::kOnStandby &&
863 // fDimBias.state()==BIAS::State::kRamping)
864 // return newstate;
865
866 // --------------------------------- Console out --------------------------------------
867
868 if (fIsVerbose && fDimBias.state()!=BIAS::State::kRamping)
869 {
870 sort(med[0].begin(), med[0].begin()+num[0]);
871 sort(med[1].begin(), med[1].begin()+num[1]);
872
873 ostringstream msg;
874 msg << " Avg0=" << setw(7) << avg[0]/num[0] << " | Avg1=" << setw(7) << avg[1]/num[1];
875 Debug(msg);
876
877 msg.str("");
878 msg << " Med0=" << setw(7) << med[0][num[0]/2] << " | Med1=" << setw(7) << med[1][num[1]/2];
879 Debug(msg);
880
881 msg.str("");
882 msg << " Min0=" << setw(7) << min[0] << " | Min1=" << setw(7) << min[1];
883 Debug(msg);
884
885 msg.str("");
886 msg << " Max0=" << setw(7) << max[0] << " | Max1=" << setw(7) << max[1];
887 Debug(msg);
888 }
889
890 // ---------------------------- Calibrated Currents -----------------------------------
891
892 // FIXME:
893 // + Current overvoltage
894 // + Temp offset
895 // + User offset
896 // + Command overvoltage
897 fDimCurrents.setQuality(GetCurrentState());
898 fDimCurrents.setData(&data, sizeof(dim_data));
899 fDimCurrents.Update(evt.GetTime());
900
901 // FIXME: To be checked
902 return GetCurrentState()==Feedback::State::kCalibrated ? Feedback::State::kCalibrated : newstate;
903 }
904
905 // ======================================================================
906
907 int Print() const
908 {
909 Out() << fDim << endl;
910 Out() << fDimFSC << endl;
911 Out() << fDimBias << endl;
912
913 return GetCurrentState();
914 }
915
916 int PrintCalibration()
917 {
918 /*
919 if (fCalibration.size()==0)
920 {
921 Out() << "No calibration performed so far." << endl;
922 return GetCurrentState();
923 }
924
925 const float *avg = fCalibration.data();
926 const float *rms = fCalibration.data()+BIAS::kNumChannels;
927 const float *res = fCalibration.data()+BIAS::kNumChannels*2;
928
929 Out() << "Average current at " << fCalibrationOffset << "V below G-APD operation voltage:\n";
930
931 for (int k=0; k<13; k++)
932 for (int j=0; j<8; j++)
933 {
934 Out() << setw(2) << k << "|" << setw(2) << j*4 << "|";
935 for (int i=0; i<4; i++)
936 Out() << Tools::Form(" %6.1f+-%4.1f", avg[k*32+j*4+i], rms[k*32+j*4+i]);
937 Out() << '\n';
938 }
939 Out() << '\n';
940
941 Out() << "Measured calibration resistor:\n";
942 for (int k=0; k<13; k++)
943 for (int j=0; j<4; j++)
944 {
945 Out() << setw(2) << k << "|" << setw(2) << j*8 << "|";
946 for (int i=0; i<8; i++)
947 Out() << Tools::Form(" %5.0f", res[k*32+j*8+i]);
948 Out() << '\n';
949 }
950
951 Out() << flush;
952 */
953 return GetCurrentState();
954 }
955
956 int SetVerbosity(const EventImp &evt)
957 {
958 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
959 return kSM_FatalError;
960
961 fIsVerbose = evt.GetBool();
962
963 return GetCurrentState();
964 }
965
966 int SetCurrentRequestInterval(const EventImp &evt)
967 {
968 if (!CheckEventSize(evt.GetSize(), "SetCurrentRequestInterval", 2))
969 return kSM_FatalError;
970
971 fCurrentRequestInterval = evt.GetUShort();
972
973 Info("New current request interval: "+to_string(fCurrentRequestInterval)+"ms");
974
975 return GetCurrentState();
976 }
977
978 int SetMoonMode(const EventImp &evt)
979 {
980 if (!CheckEventSize(evt.GetSize(), "SetMoonMode", 2))
981 return kSM_FatalError;
982
983 fMoonMode = evt.GetUShort();
984 if (fMoonMode>3)
985 fMoonMode=3;
986
987 Info("New moon mode: "+to_string(fMoonMode));
988
989 return GetCurrentState();
990 }
991
992 int Calibrate()
993 {
994 if (fDimBias.state()!=BIAS::State::kVoltageOff)
995 {
996 Warn("Calibration can only be started when biasctrl is in state VoltageOff.");
997 return GetCurrentState();
998 }
999
1000 Message("Starting calibration (ignore="+to_string(fNumCalibIgnore)+", N="+to_string(fNumCalibRequests)+")");
1001
1002 fCursorCur = -fNumCalibIgnore;
1003 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
1004 fCurrentsRms.assign(BIAS::kNumChannels, 0);
1005
1006 fBiasDac.assign(BIAS::kNumChannels, 0);
1007
1008 fCalibStep = 3;
1009 fTimeCalib = Time();
1010
1011 // Ramp all channels to the calibration setting except the one
1012 // with a shortcut
1013 vector<uint16_t> vec(BIAS::kNumChannels, uint16_t(256+512*fCalibStep));
1014 vec[272] = 0;
1015 Dim::SendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_DAC", vec);
1016
1017 //Dim::SendCommandNB("BIAS_CONTROL/SET_GLOBAL_DAC", uint16_t(256+512*fCalibStep));
1018
1019 return Feedback::State::kCalibrating;
1020 }
1021
1022 int Start(const EventImp &evt)
1023 {
1024 if (!CheckEventSize(evt.GetSize(), "Start", 4))
1025 return kSM_FatalError;
1026
1027 /*
1028 if (fDimBias.state()==BIAS::State::kRamping)
1029 {
1030 Warn("Feedback can not be started when biasctrl is in state Ramping.");
1031 return GetCurrentState();
1032 }*/
1033
1034 fUserOffset = evt.GetFloat()-1.1;
1035 fVoltageReduction = 0;
1036
1037 fCursorCur = 0;
1038
1039 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
1040 fCurrentsRms.assign(BIAS::kNumChannels, 0);
1041
1042 ostringstream out;
1043 out << "Starting feedback with an offset of " << fUserOffset << "V";
1044 Message(out);
1045
1046 if (fMoonMode>0)
1047 Message("Moon mode "+to_string(fMoonMode)+" turned on.");
1048
1049 return Feedback::State::kWaitingForData;
1050 }
1051
1052 int StopFeedback()
1053 {
1054 if (GetCurrentState()==Feedback::State::kCalibrating)
1055 return Feedback::State::kConnected;
1056
1057 if (GetCurrentState()>Feedback::State::kCalibrated)
1058 return Feedback::State::kCalibrated;
1059
1060 return GetCurrentState();
1061 }
1062
1063 bool LoadOffsets(const string &file)
1064 {
1065 vector<double> data(BIAS::kNumChannels);
1066
1067 ifstream fin(file);
1068
1069 int cnt = 0;
1070 while (fin && cnt<320)
1071 fin >> data[cnt++];
1072
1073 if (cnt!=320)
1074 {
1075 Error("Reading offsets from "+file+" failed [N="+to_string(cnt-1)+"]");
1076 return false;
1077 }
1078
1079 fVoltOffset = data;
1080
1081 fDimOffsets.Update(fVoltOffset);
1082
1083 Info("New voltage offsets loaded from "+file);
1084 return true;
1085
1086 }
1087
1088 int LoadOffset(const EventImp &evt)
1089 {
1090 LoadOffsets(evt.GetText());
1091 return GetCurrentState();
1092 }
1093
1094 int ResetOffset()
1095 {
1096 fVoltOffset.assign(BIAS::kNumChannels, 0);
1097
1098 fDimOffsets.Update(fVoltOffset);
1099
1100 Info("Voltage offsets resetted.");
1101 return GetCurrentState();
1102 }
1103
1104 int SaveCalibration()
1105 {
1106 ofstream fout("feedback-calib.bin");
1107
1108 double mjd = fTimeCalib.Mjd();
1109 fout.write((char*)&mjd, sizeof(double));
1110 fout.write((char*)fCalibDeltaI.data(), BIAS::kNumChannels*sizeof(float));
1111 fout.write((char*)fCalibR8.data(), BIAS::kNumChannels*sizeof(float));
1112
1113 return GetCurrentState();
1114 }
1115
1116 int LoadCalibration()
1117 {
1118 ifstream fin("feedback-calib.bin");
1119
1120 double mjd;
1121
1122 vector<float> di(BIAS::kNumChannels);
1123 vector<float> r8(BIAS::kNumChannels);
1124
1125 fin.read((char*)&mjd, sizeof(double));
1126 fin.read((char*)di.data(), BIAS::kNumChannels*sizeof(float));
1127 fin.read((char*)r8.data(), BIAS::kNumChannels*sizeof(float));
1128
1129 if (!fin)
1130 {
1131 Warn("Reading of calibration failed.");
1132 return GetCurrentState();
1133 }
1134
1135 fTimeCalib.Mjd(mjd);
1136 fCalibDeltaI = di;
1137 fCalibR8 = r8;
1138
1139 return Feedback::State::kCalibrated;
1140 }
1141
1142
1143
1144 int Execute()
1145 {
1146 if (!fDim.online())
1147 return Feedback::State::kDimNetworkNA;
1148
1149 const bool bias = fDimBias.state() >= BIAS::State::kConnecting;
1150 const bool fsc = fDimFSC.state() >= FSC::State::kConnected;
1151
1152 // All subsystems are not connected
1153 if (!bias && !fsc)
1154 return Feedback::State::kDisconnected;
1155
1156 // Not all subsystems are yet connected
1157 if (!bias || !fsc)
1158 return Feedback::State::kConnecting;
1159
1160 if (GetCurrentState()<Feedback::State::kCalibrating)
1161 return Feedback::State::kConnected;
1162
1163 if (GetCurrentState()==Feedback::State::kConnected)
1164 return GetCurrentState();
1165 if (GetCurrentState()==Feedback::State::kCalibrating)
1166 return GetCurrentState();
1167
1168 // kCalibrated, kWaitingForData, kInProgress
1169
1170 if (fDimBias.state()==BIAS::State::kVoltageOn || (fDimBias.state()==BIAS::State::kVoltageOff && GetCurrentState()==Feedback::State::kWaitingForData))
1171 {
1172 static Time past;
1173 if (fCurrentRequestInterval>0 && Time()-past>boost::posix_time::milliseconds(fCurrentRequestInterval))
1174 {
1175 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS");
1176 past = Time();
1177 }
1178 }
1179
1180 return GetCurrentState();
1181 }
1182
1183public:
1184 StateMachineFeedback(ostream &out=cout) : StateMachineDim(out, "FEEDBACK"),
1185 fIsVerbose(false),
1186 //---
1187 fDimFSC("FSC_CONTROL"),
1188 fDimBias("BIAS_CONTROL"),
1189 //---
1190 fDimCalibration("FEEDBACK/CALIBRATION", "F:416;F:416;F:416;F:416",
1191 "Current offsets"
1192 "|Avg[uA]:Average offset at dac=256+5*512"
1193 "|Rms[uA]:Rms of Avg"
1194 "|R[Ohm]:Measured calibration resistor"
1195 "|U[V]:Corresponding voltage reported by biasctrl"),
1196 fDimCalibration2("FEEDBACK/CALIBRATION_STEPS", "I:1;F:416;F:416;F:416",
1197 "Calibration of the R8 resistor"
1198 "|DAC[dac]:DAC setting"
1199 "|U[V]:Corresponding voltages reported by biasctrl"
1200 "|Iavg[uA]:Averaged measured current"
1201 "|Irms[uA]:Rms measured current"),
1202 fDimCalibrationR8("FEEDBACK/CALIBRATION_R8", "F:416;F:416",
1203 "Calibration of R8"
1204 "|DeltaI[uA]:Average offset"
1205 "|R8[Ohm]:Measured effective resistor R8"),
1206 fDimCurrents("FEEDBACK/CALIBRATED_CURRENTS", "F:416;F:1;F:1;F:1;F:1;I:1;F:1;F:416;F:1;F:1",
1207 "Calibrated currents"
1208 "|I[uA]:Calibrated currents per pixel"
1209 "|I_avg[uA]:Average calibrated current (N channels)"
1210 "|I_rms[uA]:Rms of calibrated current (N channels)"
1211 "|I_med[uA]:Median calibrated current (N channels)"
1212 "|I_dev[uA]:Deviation of calibrated current (N channels)"
1213 "|N[uint16]:Number of valid values"
1214 "|T_diff[s]:Time difference to calibration"
1215 "|U_ov[V]:Calculated overvoltage w.r.t. operation voltage"
1216 "|U_nom[V]:Nominal overvoltage w.r.t. operation voltage"
1217 "|dU_temp[V]:Correction calculated from temperature"
1218 ),
1219 fDimOffsets("FEEDBACK/OFFSETS", "F:416",
1220 "Offsets operation voltages"
1221 "|U[V]:Offset per bias channels"),
1222 fVoltOffset(BIAS::kNumChannels),
1223 fMoonMode(0),
1224 fCurrentRequestInterval(0),
1225 fNumCalibIgnore(30),
1226 fNumCalibRequests(300)
1227 {
1228 fDim.Subscribe(*this);
1229 fDimFSC.Subscribe(*this);
1230 fDimBias.Subscribe(*this);
1231
1232 fDimBias.SetCallback(bind(&StateMachineFeedback::HandleBiasStateChange, this));
1233
1234 Subscribe("BIAS_CONTROL/CURRENT")
1235 (bind(&StateMachineFeedback::HandleBiasCurrent, this, placeholders::_1));
1236 Subscribe("BIAS_CONTROL/VOLTAGE")
1237 (bind(&StateMachineFeedback::HandleBiasVoltage, this, placeholders::_1));
1238 Subscribe("BIAS_CONTROL/DAC")
1239 (bind(&StateMachineFeedback::HandleBiasDac, this, placeholders::_1));
1240 Subscribe("BIAS_CONTROL/NOMINAL")
1241 (bind(&StateMachineFeedback::HandleBiasNom, this, placeholders::_1));
1242 Subscribe("FSC_CONTROL/BIAS_TEMP")
1243 (bind(&StateMachineFeedback::HandleCameraTemp, this, placeholders::_1));
1244
1245 // State names
1246 AddStateName(Feedback::State::kDimNetworkNA, "DimNetworkNotAvailable",
1247 "The Dim DNS is not reachable.");
1248
1249 AddStateName(Feedback::State::kDisconnected, "Disconnected",
1250 "The Dim DNS is reachable, but the required subsystems are not available.");
1251 AddStateName(Feedback::State::kConnecting, "Connecting",
1252 "Either biasctrl or fscctrl not connected.");
1253 AddStateName(Feedback::State::kConnected, "Connected",
1254 "biasctrl and fscctrl are available and connected with their hardware.");
1255
1256 AddStateName(Feedback::State::kCalibrating, "Calibrating",
1257 "Bias crate calibrating in progress.");
1258 AddStateName(Feedback::State::kCalibrated, "Calibrated",
1259 "Bias crate calibrated.");
1260
1261 AddStateName(Feedback::State::kWaitingForData, "WaitingForData",
1262 "Current control started, waiting for valid temperature and current data.");
1263
1264 AddStateName(Feedback::State::kOnStandby, "OnStandby",
1265 "Current control in progress but with limited voltage.");
1266 AddStateName(Feedback::State::kInProgress, "InProgress",
1267 "Current control in progress.");
1268 AddStateName(Feedback::State::kWarning, "Warning",
1269 "Current control in progress but current warning level exceeded.");
1270 AddStateName(Feedback::State::kCritical, "Critical",
1271 "Current control in progress but critical current limit exceeded.");
1272
1273
1274 /*
1275 AddEvent("SET_CURRENT_REQUEST_INTERVAL")
1276 (bind(&StateMachineFeedback::SetCurrentRequestInterval, this, placeholders::_1))
1277 ("|interval[ms]:Interval between two current requests in modes which need that.");
1278 */
1279
1280 AddEvent("CALIBRATE", Feedback::State::kConnected, Feedback::State::kCalibrated)
1281 (bind(&StateMachineFeedback::Calibrate, this))
1282 ("");
1283
1284 AddEvent("START", "F:1", Feedback::State::kCalibrated)
1285 (bind(&StateMachineFeedback::Start, this, placeholders::_1))
1286 ("Start the current/temperature control loop"
1287 "|Uov[V]:Overvoltage to be applied (standard value is 1.1V)");
1288
1289 AddEvent("STOP")
1290 (bind(&StateMachineFeedback::StopFeedback, this))
1291 ("Stop any control loop");
1292
1293 AddEvent("LOAD_OFFSETS", "C", Feedback::State::kConnected, Feedback::State::kCalibrated)
1294 (bind(&StateMachineFeedback::LoadOffset, this, placeholders::_1))
1295 ("");
1296 AddEvent("RESET_OFFSETS", Feedback::State::kConnected, Feedback::State::kCalibrated)
1297 (bind(&StateMachineFeedback::ResetOffset, this))
1298 ("");
1299
1300
1301 AddEvent("SAVE_CALIBRATION", Feedback::State::kCalibrated)
1302 (bind(&StateMachineFeedback::SaveCalibration, this))
1303 ("");
1304 AddEvent("LOAD_CALIBRATION", Feedback::State::kConnected)
1305 (bind(&StateMachineFeedback::LoadCalibration, this))
1306 ("");
1307
1308 AddEvent("SET_MOON_MODE", "S:1", Feedback::State::kConnected, Feedback::State::kCalibrated)
1309 (bind(&StateMachineFeedback::SetMoonMode, this, placeholders::_1))
1310 ("Operate central pixels at 5V below nominal voltage. 0:off, 1:minimal, 2:medium, 3:maximum size.");
1311
1312
1313 AddEvent("PRINT")
1314 (bind(&StateMachineFeedback::Print, this))
1315 ("");
1316 AddEvent("PRINT_CALIBRATION")
1317 (bind(&StateMachineFeedback::PrintCalibration, this))
1318 ("");
1319
1320 // Verbosity commands
1321 AddEvent("SET_VERBOSE", "B:1")
1322 (bind(&StateMachineFeedback::SetVerbosity, this, placeholders::_1))
1323 ("set verbosity state"
1324 "|verbosity[bool]:disable or enable verbosity when calculating overvoltage");
1325 }
1326
1327 int EvalOptions(Configuration &conf)
1328 {
1329 fIsVerbose = !conf.Get<bool>("quiet");
1330
1331 if (!fMap.Read(conf.Get<string>("pixel-map-file")))
1332 {
1333 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
1334 return 1;
1335 }
1336
1337 fCurrentRequestInterval = conf.Get<uint16_t>("current-request-interval");
1338 fNumCalibIgnore = conf.Get<uint16_t>("num-calib-ignore");
1339 fNumCalibRequests = conf.Get<uint16_t>("num-calib-average");
1340 fTempCoefficient = conf.Get<double>("temp-coefficient");
1341
1342 if (conf.Has("offset-file"))
1343 LoadOffsets(conf.Get<string>("offset-file"));
1344
1345 return -1;
1346 }
1347};
1348
1349// ------------------------------------------------------------------------
1350
1351#include "Main.h"
1352
1353template<class T>
1354int RunShell(Configuration &conf)
1355{
1356 return Main::execute<T, StateMachineFeedback>(conf);
1357}
1358
1359void SetupConfiguration(Configuration &conf)
1360{
1361 po::options_description control("Feedback options");
1362 control.add_options()
1363 ("quiet,q", po_bool(true), "Disable printing more information on average overvoltagecontents of all received messages (except dynamic data) in clear text.")
1364 ("pixel-map-file", var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage.")
1365 ("current-request-interval", var<uint16_t>(1000), "Interval between two current requests.")
1366 ("num-calib-ignore", var<uint16_t>(30), "Number of current requests to be ignored before averaging")
1367 ("num-calib-average", var<uint16_t>(300), "Number of current requests to be averaged")
1368 ("temp-coefficient", var<double>()->required(), "Temp. coefficient [V/K]")
1369 ("offset-file", var<string>(), "File with operation voltage offsets")
1370 ;
1371
1372 conf.AddOptions(control);
1373}
1374
1375/*
1376 Extract usage clause(s) [if any] for SYNOPSIS.
1377 Translators: "Usage" and "or" here are patterns (regular expressions) which
1378 are used to match the usage synopsis in program output. An example from cp
1379 (GNU coreutils) which contains both strings:
1380 Usage: cp [OPTION]... [-T] SOURCE DEST
1381 or: cp [OPTION]... SOURCE... DIRECTORY
1382 or: cp [OPTION]... -t DIRECTORY SOURCE...
1383 */
1384void PrintUsage()
1385{
1386 cout <<
1387 "The feedback control the BIAS voltages based on the calibration signal.\n"
1388 "\n"
1389 "The default is that the program is started without user intercation. "
1390 "All actions are supposed to arrive as DimCommands. Using the -c "
1391 "option, a local shell can be initialized. With h or help a short "
1392 "help message about the usuage can be brought to the screen.\n"
1393 "\n"
1394 "Usage: feedback [-c type] [OPTIONS]\n"
1395 " or: feedback [OPTIONS]\n";
1396 cout << endl;
1397}
1398
1399void PrintHelp()
1400{
1401 Main::PrintHelp<StateMachineFeedback>();
1402
1403 /* Additional help text which is printed after the configuration
1404 options goes here */
1405
1406 /*
1407 cout << "bla bla bla" << endl << endl;
1408 cout << endl;
1409 cout << "Environment:" << endl;
1410 cout << "environment" << endl;
1411 cout << endl;
1412 cout << "Examples:" << endl;
1413 cout << "test exam" << endl;
1414 cout << endl;
1415 cout << "Files:" << endl;
1416 cout << "files" << endl;
1417 cout << endl;
1418 */
1419}
1420
1421int main(int argc, const char* argv[])
1422{
1423 Configuration conf(argv[0]);
1424 conf.SetPrintUsage(PrintUsage);
1425 Main::SetupConfiguration(conf);
1426 SetupConfiguration(conf);
1427
1428 if (!conf.DoParse(argc, argv, PrintHelp))
1429 return 127;
1430
1431 //try
1432 {
1433 // No console access at all
1434 if (!conf.Has("console"))
1435 {
1436// if (conf.Get<bool>("no-dim"))
1437// return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
1438// else
1439 return RunShell<LocalStream>(conf);
1440 }
1441 // Cosole access w/ and w/o Dim
1442/* if (conf.Get<bool>("no-dim"))
1443 {
1444 if (conf.Get<int>("console")==0)
1445 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
1446 else
1447 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
1448 }
1449 else
1450*/ {
1451 if (conf.Get<int>("console")==0)
1452 return RunShell<LocalShell>(conf);
1453 else
1454 return RunShell<LocalConsole>(conf);
1455 }
1456 }
1457 /*catch (std::exception& e)
1458 {
1459 cerr << "Exception: " << e.what() << endl;
1460 return -1;
1461 }*/
1462
1463 return 0;
1464}
Note: See TracBrowser for help on using the repository browser.