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

Last change on this file since 19457 was 19457, checked in by tbretz, 8 weeks ago
The prefix was missing for the offset-file.
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 "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.GetPrefixedString("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            if (!LoadOffsets(conf.GetPrefixedString("offset-file")))
1344                return 2;
1345
1346        return -1;
1347    }
1348};
1349
1350// ------------------------------------------------------------------------
1351
1352#include "Main.h"
1353
1354template<class T>
1355int RunShell(Configuration &conf)
1356{
1357    return Main::execute<T, StateMachineFeedback>(conf);
1358}
1359
1360void SetupConfiguration(Configuration &conf)
1361{
1362    po::options_description control("Feedback options");
1363    control.add_options()
1364        ("quiet,q", po_bool(true), "Disable printing more information on average overvoltagecontents of all received messages (except dynamic data) in clear text.")
1365        ("pixel-map-file",      var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage.")
1366        ("current-request-interval",  var<uint16_t>(1000), "Interval between two current requests.")
1367        ("num-calib-ignore",    var<uint16_t>(30), "Number of current requests to be ignored before averaging")
1368        ("num-calib-average",   var<uint16_t>(300), "Number of current requests to be averaged")
1369        ("temp-coefficient",    var<double>()->required(), "Temp. coefficient [V/K]")
1370        ("offset-file",         var<string>(), "File with operation voltage offsets")
1371        ;
1372
1373    conf.AddOptions(control);
1374}
1375
1376/*
1377 Extract usage clause(s) [if any] for SYNOPSIS.
1378 Translators: "Usage" and "or" here are patterns (regular expressions) which
1379 are used to match the usage synopsis in program output.  An example from cp
1380 (GNU coreutils) which contains both strings:
1381  Usage: cp [OPTION]... [-T] SOURCE DEST
1382    or:  cp [OPTION]... SOURCE... DIRECTORY
1383    or:  cp [OPTION]... -t DIRECTORY SOURCE...
1384 */
1385void PrintUsage()
1386{
1387    cout <<
1388        "The feedback control the BIAS voltages based on the calibration signal.\n"
1389        "\n"
1390        "The default is that the program is started without user intercation. "
1391        "All actions are supposed to arrive as DimCommands. Using the -c "
1392        "option, a local shell can be initialized. With h or help a short "
1393        "help message about the usuage can be brought to the screen.\n"
1394        "\n"
1395        "Usage: feedback [-c type] [OPTIONS]\n"
1396        "  or:  feedback [OPTIONS]\n";
1397    cout << endl;
1398}
1399
1400void PrintHelp()
1401{
1402    Main::PrintHelp<StateMachineFeedback>();
1403
1404    /* Additional help text which is printed after the configuration
1405     options goes here */
1406
1407    /*
1408     cout << "bla bla bla" << endl << endl;
1409     cout << endl;
1410     cout << "Environment:" << endl;
1411     cout << "environment" << endl;
1412     cout << endl;
1413     cout << "Examples:" << endl;
1414     cout << "test exam" << endl;
1415     cout << endl;
1416     cout << "Files:" << endl;
1417     cout << "files" << endl;
1418     cout << endl;
1419     */
1420}
1421
1422int main(int argc, const char* argv[])
1423{
1424    Configuration conf(argv[0]);
1425    conf.SetPrintUsage(PrintUsage);
1426    Main::SetupConfiguration(conf);
1427    SetupConfiguration(conf);
1428
1429    if (!conf.DoParse(argc, argv, PrintHelp))
1430        return 127;
1431
1432    //try
1433    {
1434        // No console access at all
1435        if (!conf.Has("console"))
1436        {
1437//            if (conf.Get<bool>("no-dim"))
1438//                return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
1439//            else
1440                return RunShell<LocalStream>(conf);
1441        }
1442        // Cosole access w/ and w/o Dim
1443/*        if (conf.Get<bool>("no-dim"))
1444        {
1445            if (conf.Get<int>("console")==0)
1446                return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
1447            else
1448                return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
1449        }
1450        else
1451*/        {
1452            if (conf.Get<int>("console")==0)
1453                return RunShell<LocalShell>(conf);
1454            else
1455                return RunShell<LocalConsole>(conf);
1456        }
1457    }
1458    /*catch (std::exception& e)
1459    {
1460        cerr << "Exception: " << e.what() << endl;
1461        return -1;
1462    }*/
1463
1464    return 0;
1465}
Note: See TracBrowser for help on using the repository browser.