source: trunk/FACT++/src/feedback.cc@ 18501

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