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

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