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

Last change on this file since 17048 was 17030, checked in by tbretz, 11 years ago
Fixed a bug in the R8 service; service calibrated currents also during ramping; fixed Iapd for the faulty patches; avoid to start feedback when bias is in ramping; still some debugging output.
File size: 39.8 KB
Line 
1#include <valarray>
2#include <algorithm>
3
4#include "Dim.h"
5#include "Event.h"
6#include "Shell.h"
7#include "StateMachineDim.h"
8#include "Connection.h"
9#include "Configuration.h"
10#include "Console.h"
11#include "externals/PixelMap.h"
12
13#include "tools.h"
14
15#include "LocalControl.h"
16
17#include "HeadersFSC.h"
18#include "HeadersBIAS.h"
19#include "HeadersFeedback.h"
20
21#include "DimState.h"
22#include "DimDescriptionService.h"
23
24using namespace std;
25
26// ------------------------------------------------------------------------
27
28class StateMachineFeedback : public StateMachineDim
29{
30private:
31 PixelMap fMap;
32
33 bool fIsVerbose;
34 bool fEnableOldAlgorithm;
35
36 DimVersion fDim;
37
38 DimDescribedState fDimFSC;
39 DimDescribedState fDimBias;
40
41 DimDescribedService fDimCalibration;
42 DimDescribedService fDimCalibration2;
43 DimDescribedService fDimCalibrationR8;
44 DimDescribedService fDimCurrents;
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<uint16_t> fBiasDac; // Dac value corresponding to the voltage setting
55
56 vector<float> fCalibration;
57 vector<float> fCalibDeltaI;
58 vector<float> fCalibR8;
59
60 int64_t fCursorCur;
61
62 Time fTimeCalib;
63 Time fTimeTemp;
64
65 double fUserOffset;
66 double fTempOffset;
67 double fTemp;
68
69 uint16_t fCurrentRequestInterval;
70 uint16_t fNumCalibIgnore;
71 uint16_t fNumCalibRequests;
72 uint16_t fCalibStep;
73
74 // ============================= Handle Services ========================
75
76 int HandleBiasStateChange()
77 {
78 if (fDimBias.state()==BIAS::State::kVoltageOn && GetCurrentState()==Feedback::State::kCalibrating)
79 {
80 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS");
81 Info("Starting calibration step "+to_string(fCalibStep));
82 }
83
84 if (fDimBias.state()==BIAS::State::kVoltageOff && GetCurrentState()==Feedback::State::kInProgress)
85 return Feedback::State::kCalibrated;
86
87 return GetCurrentState();
88 }
89 // ============================= Handle Services ========================
90
91 bool CheckEventSize(size_t has, const char *name, size_t size)
92 {
93 if (has==size)
94 return true;
95
96 // Disconnected
97 if (has==0)
98 return false;
99
100 ostringstream msg;
101 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
102 Fatal(msg);
103 return false;
104 }
105
106 int HandleBiasNom(const EventImp &evt)
107 {
108 if (evt.GetSize()>=416*sizeof(float))
109 {
110 fVoltGapd.assign(evt.Ptr<float>(), evt.Ptr<float>()+416);
111 Info("Nominal bias voltages and calibration resistor received.");
112 }
113
114 return GetCurrentState();
115 }
116
117 int HandleBiasVoltage(const EventImp &evt)
118 {
119 if (evt.GetSize()>=416*sizeof(float))
120 fBiasVolt.assign(evt.Ptr<float>(), evt.Ptr<float>()+416);
121 return GetCurrentState();
122 }
123
124 int HandleBiasDac(const EventImp &evt)
125 {
126 if (evt.GetSize()>=416*sizeof(uint16_t))
127 fBiasDac.assign(evt.Ptr<uint16_t>(), evt.Ptr<uint16_t>()+416);
128 return GetCurrentState();
129 }
130
131 int HandleCameraTemp(const EventImp &evt)
132 {
133 if (!CheckEventSize(evt.GetSize(), "HandleCameraTemp", 60*sizeof(float)))
134 {
135 fTimeTemp = Time(Time::none);
136 return GetCurrentState();
137 }
138
139 const float *ptr = evt.Ptr<float>();
140
141 double avgt = 0;
142 int numt = 0;
143 for (int i=1; i<32; i++)
144 if (ptr[i]!=0)
145 {
146 avgt += ptr[i];
147 numt++;
148 }
149
150 if (numt==0)
151 {
152 Warn("Received sensor temperatures all invalid.");
153 return GetCurrentState();
154 }
155
156 avgt /= numt; // [deg C]
157
158 fTimeTemp = evt.GetTime();
159 fTempOffset = (avgt-25)*0.0561765; // [V] From Hamamatsu datasheet
160 fTemp = avgt;
161
162 return GetCurrentState();
163 }
164
165 pair<vector<float>, vector<float>> AverageCurrents(const int16_t *ptr, int n)
166 {
167 if (fCursorCur++>=0)
168 {
169 for (int i=0; i<BIAS::kNumChannels; i++)
170 {
171 fCurrentsAvg[i] += ptr[i];
172 fCurrentsRms[i] += ptr[i]*ptr[i];
173 }
174 }
175
176 if (fCursorCur<n)
177 return make_pair(vector<float>(), vector<float>());
178
179 const double conv = 5e-3/4096;
180
181 vector<float> rms(BIAS::kNumChannels);
182 vector<float> avg(BIAS::kNumChannels);
183 for (int i=0; i<BIAS::kNumChannels; i++)
184 {
185 avg[i] = double(fCurrentsAvg[i])/fCursorCur * conv;
186 rms[i] = double(fCurrentsRms[i])/fCursorCur * conv * conv;
187
188 rms[i] = sqrt(rms[i]-avg[i]*avg[i]);
189 }
190
191 return make_pair(avg, rms);
192 }
193
194 int HandleCalibration(const EventImp &evt)
195 {
196 if (fDimBias.state()!=BIAS::State::kVoltageOn)
197 return GetCurrentState();
198
199 const uint16_t dac = 256+512*fCalibStep; // Command value
200
201 // Only the channels which are no spare channels are ramped
202 if (std::count(fBiasDac.begin(), fBiasDac.end(), dac)!=320)
203 return GetCurrentState();
204
205 const auto rc = AverageCurrents(evt.Ptr<int16_t>(), fNumCalibRequests);
206 if (rc.first.size()==0)
207 {
208 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS");
209 return GetCurrentState();
210 }
211
212 const vector<float> &avg = rc.first;
213 const vector<float> &rms = rc.second;
214
215 // Current through resistor R8
216 fCalibCurrentMes[fCalibStep] = avg; // [A]
217 fCalibVoltage[fCalibStep] = fBiasVolt; // [V]
218
219 // ------------------------- Update calibration data --------------------
220
221 struct cal_data
222 {
223 uint32_t dac;
224 float U[416];
225 float Iavg[416];
226 float Irms[416];
227
228 cal_data() { memset(this, 0, sizeof(cal_data)); }
229 } __attribute__((__packed__));
230
231 cal_data cal;
232 cal.dac = dac;
233 memcpy(cal.U, fBiasVolt.data(), 416*sizeof(float));
234 memcpy(cal.Iavg, avg.data(), 416*sizeof(float));
235 memcpy(cal.Irms, rms.data(), 416*sizeof(float));
236
237 fDimCalibration2.setData(fCalibration);
238 fDimCalibration2.Update(fTimeCalib);
239
240 // -------------------- Start next calibration steo ---------------------
241
242 if (++fCalibStep<6)
243 {
244 fCursorCur = -fNumCalibIgnore;
245 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
246 fCurrentsRms.assign(BIAS::kNumChannels, 0);
247
248 Dim::SendCommandNB("BIAS_CONTROL/SET_GLOBAL_DAC", uint32_t(256+512*fCalibStep));
249
250 return GetCurrentState();
251 }
252
253 // --------------- Calculate old style calibration ----------------------
254
255 fCalibration.resize(BIAS::kNumChannels*4);
256
257 float *pavg = fCalibration.data();
258 float *prms = fCalibration.data()+BIAS::kNumChannels;
259 float *pres = fCalibration.data()+BIAS::kNumChannels*2;
260 float *pUmes = fCalibration.data()+BIAS::kNumChannels*3;
261
262 for (int i=0; i<BIAS::kNumChannels; i++)
263 {
264 const double I = fCalibCurrentMes[5][i]; // [A]
265 const double U = fBiasVolt[i]; // [V]
266
267 pavg[i] = I*1e6; // [uA]
268 prms[i] = rms[i]*1e6; // [uA]
269 pres[i] = U/I; // [Ohm]
270 pUmes[i] = U; // [V]
271 }
272
273 fDimCalibration.setData(fCalibration);
274 fDimCalibration.Update(fTimeCalib);
275
276 // -------------------- New style calibration --------------------------
277
278 fCalibDeltaI.resize(BIAS::kNumChannels);
279 fCalibR8.resize(BIAS::kNumChannels);
280
281 // Linear regression of the values at 256+512*N for N={ 3, 4, 5 }
282 for (int i=0; i<BIAS::kNumChannels; i++)
283 {
284 // x: Idac
285 // y: Iadc
286
287 double x = 0;
288 double y = 0;
289 double xx = 0;
290 double xy = 0;
291
292 const int beg = 3;
293 const int end = 5;
294 const int len = end-beg+1;
295
296 for (int j=beg; j<=end; j++)
297 {
298 const double Idac = (256+512*j)*1e-3/4096;
299
300 x += Idac;
301 xx += Idac*Idac;
302 y += fCalibCurrentMes[j][i];
303 xy += fCalibCurrentMes[j][i]*Idac;
304 }
305
306 const double m1 = xy - x*y / len;
307 const double m2 = xx - x*x / len;
308
309 const double m = m2==0 ? 0 : m1/m2;
310
311 const double t = (y - m*x) / len;
312
313 fCalibDeltaI[i] = t; // [A]
314 fCalibR8[i] = 100/m; // [Ohm]
315 }
316
317 vector<float> v;
318 v.reserve(BIAS::kNumChannels*2);
319 v.insert(v.end(), fCalibDeltaI.begin(), fCalibDeltaI.end());
320 v.insert(v.end(), fCalibR8.begin(), fCalibR8.end());
321
322 fDimCalibrationR8.setData(v);
323 fDimCalibrationR8.Update(fTimeCalib);
324
325 // ---------------------------------------------------------------------
326
327 Info("Calibration successfully done.");
328 Dim::SendCommandNB("BIAS_CONTROL/SET_ZERO_VOLTAGE");
329
330 return Feedback::State::kCalibrated;
331 }
332
333 int HandleBiasCurrent(const EventImp &evt)
334 {
335 if (!CheckEventSize(evt.GetSize(), "HandleBiasCurrent", BIAS::kNumChannels*sizeof(uint16_t)))
336 return Feedback::State::kConnected;
337
338 if (GetCurrentState()<Feedback::State::kCalibrating)
339 return GetCurrentState();
340
341 // ------------------------------- HandleCalibration -----------------------------------
342 if (GetCurrentState()==Feedback::State::kCalibrating)
343 return HandleCalibration(evt);
344
345 // ---------------------- Calibrated, WaitingForData, InProgress -----------------------
346
347 // We are waiting but no valid temperature yet, go on waiting
348 if (GetCurrentState()==Feedback::State::kWaitingForData &&
349 (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5)))
350 return GetCurrentState();
351
352 // We are already in progress but no valid temperature update anymore
353 if (GetCurrentState()==Feedback::State::kInProgress &&
354 (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5)))
355 {
356 Warn("Current control in progress, but last received temperature older than 5min... switching voltage off.");
357 Dim::SendCommandNB("BIAS_CONTROL/SET_ZERO_VOLTAGE");
358 return Feedback::State::kCalibrated;
359 }
360
361 // ---------------------- Calibrated, WaitingForData, InProgress -----------------------
362
363 const int Navg = fDimBias.state()!=BIAS::State::kVoltageOn ? 1 : 3;
364
365 const vector<float> &Imes = AverageCurrents(evt.Ptr<int16_t>(), Navg).first;
366 if (Imes.size()==0)
367 return GetCurrentState();
368
369 fCurrentsAvg.assign(416, 0);
370 fCurrentsRms.assign(416, 0);
371 fCursorCur = 0;
372
373 // -------------------------------------------------------------------------------------
374
375 // Nominal overvoltage (w.r.t. the bias setup values)
376 const double overvoltage = GetCurrentState()<Feedback::State::kWaitingForData ? 0 : fUserOffset;
377
378 double avg[2] = { 0, 0 };
379 double min[2] = { 90, 90 };
380 double max[2] = { -90, -90 };
381 int num[3] = { 0, 0, 0 };
382
383 vector<double> med[3];
384 med[0].resize(416);
385 med[1].resize(416);
386 med[2].resize(416);
387
388 struct dim_data
389 {
390 float I[416];
391 float Iavg;
392 float Irms;
393 float Imed;
394 float Idev;
395 uint32_t N;
396 float Tdiff;
397 float Uov[416];
398 float Unom;
399 float dUtemp;
400
401 dim_data() { memset(this, 0, sizeof(dim_data)); }
402 } __attribute__((__packed__));
403
404 dim_data data;
405
406 data.Unom = overvoltage;
407 data.dUtemp = fTempOffset;
408
409 vector<float> vec(416);
410
411 if (fEnableOldAlgorithm)
412 {
413 // Pixel 583: 5 31 == 191 (5) C2 B3 P3
414 // Pixel 830: 2 2 == 66 (4) C0 B8 P1
415 // Pixel 1401: 6 1 == 193 (5) C2 B4 P0
416
417 // 3900 Ohm/n + 1000 Ohm + 1100 Ohm (with n=4 or n=5)
418 const double R[2] = { 3075, 2870 };
419
420 const float *Iavg = fCalibration.data(); // Offset at U=fCalibrationOffset
421 const float *Ravg = fCalibration.data()+BIAS::kNumChannels*2; // Measured resistance
422
423 for (int i=0; i<320; i++)
424 {
425 const PixelMapEntry &hv = fMap.hv(i);
426 if (!hv)
427 continue;
428
429 // Average measured current
430 const double Im = Imes[i] * 1e6; // [uA]
431
432 // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
433 const int g = hv.group();
434
435 // Serial resistors in front of the G-APD
436 double Rg = R[g];
437
438 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
439 if (i==66) // Pixel 830(66)
440 Rg = 2400; // 2400 = (3/3900 + 1/390) + 1000 + 1100
441 if (i==191 || i==193) // Pixel 583(191) / Pixel 1401(193)
442 Rg = 2379; // 2379 = (4/3900 + 1/390) + 1000 + 1100
443
444 const double r = 1./(1./Rg + 1./Ravg[i]); // [Ohm]
445
446 // Offset induced by the voltage above the calibration point
447 const double Ubd = fVoltGapd[i] + fTempOffset;
448 const double U0 = Ubd + overvoltage - fCalibVoltage[5][i]; // appliedOffset-fCalibrationOffset;
449 const double dI = U0/Ravg[i]; // [V/Ohm]
450
451 // Offset at the calibration point (make sure that the calibration is
452 // valid (Im[i]>Iavg[i]) and we operate above the calibration point)
453 const double I = Im>Iavg[i] ? Im - Iavg[i] : 0; // [uA]
454
455 // Make sure that the averaged resistor is valid
456 const double dU = Ravg[i]>10000 ? r*(I*1e-6 - dI) : 0;
457
458 if (i==2)
459 cout << setprecision(4)<< dU << endl;;
460
461 vec[i] = Ubd + overvoltage + dU;
462
463 // Calculate statistics only for channels with a valid calibration
464 if (Iavg[i]>0)
465 {
466 med[g][num[g]] = dU;
467 avg[g] += dU;
468 num[g]++;
469
470 if (dU<min[g])
471 min[g] = dU;
472 if (dU>max[g])
473 max[g] = dU;
474
475 data.I[i] = Imes[i]*1e6 - fBiasVolt[i]/Ravg[i]*1e6;
476 data.I[i] /= hv.count();
477
478 if (i==66)
479 data.I[i] /= 1.3;
480 if (i==191 || i==193)
481 data.I[i] /= 1.4;
482
483 data.Iavg += data.I[i];
484 data.Irms += data.I[i]*data.I[i];
485
486 med[2][num[2]++] = data.I[i];
487 }
488 }
489 }
490 else
491 {
492 /* ================================= new ======================= */
493
494 for (int i=0; i<320/*BIAS::kNumChannels*/; i++)
495 {
496 const PixelMapEntry &hv = fMap.hv(i);
497 if (!hv)
498 continue;
499
500 // Number of G-APDs in this patch
501 const int N = hv.count();
502
503 // Average measured ADC value for this channel
504 const double adc = Imes[i]/* * (5e-3/4096)*/; // [A]
505
506 // Current through ~100 Ohm measurement resistor
507 const double I8 = (adc-fCalibDeltaI[i])*fCalibR8[i]/100;
508
509 // Applied voltage at calibration resistors, according to biasctrl
510 const double U9 = fBiasVolt[i];
511
512 // Current through calibration resistors (R9)
513 // This is uncalibrated, biut since the corresponding calibrated
514 // value I8 is subtracted, the difference should yield a correct value
515 const double I9 = fBiasDac[i] * (1e-3/4096);//U9/R9; [A]
516
517 // Current in R4/R5 branch
518 const double Iout = I8>I9 ? I8 - I9 : 0;
519
520 // Serial resistors (one 1kOhm at the output of the bias crate, one 1kOhm in the camera)
521 const double R4 = 2000;
522
523 // Serial resistor of the individual G-APDs
524 double R5 = 3900./N;
525
526 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
527 if (i==66) // Pixel 830(66)
528 R5 = 300; // 2400 = 1/(3/3900 + 1/390)
529 if (i==191 || i==193) // Pixel 583(191) / Pixel 1401(193)
530 R5 = 390/1.4; // 379 = 1/(4/3900 + 1/390)
531
532 // Total resistance of branch with diodes
533 const double R = R4+R5;
534
535 // For the patches with a broken resistor - ignoring the G-APD resistance -
536 // we get:
537 //
538 // I[R=3900] = Iout * 1/(10+(N-1)) = Iout /(N+9)
539 // I[R= 390] = Iout * (1 - 1/ (10+(N-1))) = Iout * (N+8)/(N+9)
540 //
541 // I[R=390] / I[R=3900] = N+8
542 //
543 // Udrp = Iout*3900/(N+9) + Iout*1000 + Iout*1000 = Iout * R
544
545 // Voltage drop in R4/R5 branch (for the G-APDs with correct resistor)
546 const double Udrp = R*Iout;
547
548 // Nominal breakdown voltage with correction for temperature dependence
549 const double Ubd = fVoltGapd[i] + fTempOffset;
550
551 // Current overvoltage (at a G-APD with the correct 3900 Ohm resistor)
552 const double Uov = U9-Udrp-Ubd>0 ? U9-Udrp-Ubd : 0;
553
554 // Iout linear with U9 above Ubd
555 //
556 // Rx = (U9-Ubd)/Iout
557 // I' = (U9'-Ubd) / Rx
558 // Udrp' = R*I'
559 // Uov = U9' - Udrp' - Ubd
560 // Uov = overvoltage
561 //
562 // overvoltage = U9' - Udrp' - Ubd
563 // overvoltage = U9' - R*I' - Ubd
564 // overvoltage = U9' - R*((U9'-Ubd)/Rx) - Ubd
565 // overvoltage = U9' - U9'*R/Rx + Ubd*R/Rx - Ubd
566 // overvoltage = U9'*(1 - R/Rx) + Ubd*R/Rx - Ubd
567 // overvoltage - Ubd*R/Rx +Ubd = U9'*(1 - R/Rx)
568 // U9' = [ overvoltage - Ubd*R/Rx +Ubd ] / (1 - R/Rx)
569 //
570
571 // The current through one G-APD is the sum divided by the number of G-APDs
572 // (assuming identical serial resistors)
573 double Iapd = Iout/N;
574
575 // In this and the previosu case we neglect the resistance of the G-APDs, but we can make an
576 // assumption: The differential resistance depends more on the NSB than on the PDE,
577 // thus it is at least comparable for all G-APDs in the patch. In addition, although the
578 // G-APD with the 390Ohm serial resistor has the wrong voltage applied, this does not
579 // significantly influences the ohmic resistor or the G-APD because the differential
580 // resistor is large enough that the increase of the overvoltage does not dramatically
581 // increase the current flow as compared to the total current flow.
582 if (i==66 || i==191 || i==193)
583 Iapd = Iout/(N+9); // Iapd = R5*Iout/3900;
584
585 // The differential resistance of the G-APD, i.e. the dependence of the
586 // current above the breakdown voltage, is given by
587 //const double Rapd = Uov/Iapd;
588 // This allows us to estimate the current Iov at the overvoltage we want to apply
589 //const double Iov = overvoltage/Rapd;
590
591 // Estimate set point for over-voltage (voltage drop at the target point)
592 //const double Uset = Ubd + overvoltage + R*Iov*N;
593 const double Uset = Uov<0.3 ? Ubd + overvoltage + Udrp : Ubd + overvoltage + Udrp*pow(overvoltage/Uov, 1.66);
594
595 // Voltage set point
596 vec[i] = Uset;
597
598 if (fDimBias.state()==BIAS::State::kVoltageOn && GetCurrentState()==Feedback::State::kInProgress &&
599 fabs(Uov-overvoltage)>0.033)
600 cout << setprecision(4) << setw(3) << i << ": Uov=" << Uov << " Udrp=" << Udrp << " Iapd=" << Iapd*1e6 << endl;
601
602
603 // Calculate statistics only for channels with a valid calibration
604 //if (Uov>0)
605 {
606 const int g = hv.group();
607
608 med[g][num[g]] = Uov;
609 avg[g] += Uov;
610 num[g]++;
611
612 if (Uov<min[g])
613 min[g] = Uov;
614 if (Uov>max[g])
615 max[g] = Uov;
616
617 const double iapd = Iapd*1e6; // A --> uA
618
619 data.I[i] = iapd;
620 data.Iavg += iapd;
621 data.Irms += iapd*iapd;
622
623 data.Uov[i] = Uov;
624
625 med[2][num[2]++] = iapd;
626 }
627 }
628 }
629
630 // ------------------------------- Update voltages ------------------------------------
631
632 if (GetCurrentState()!=Feedback::State::kCalibrated) // WaitingForData, InProgress
633 {
634 if (fDimBias.state()!=BIAS::State::kRamping)
635 {
636 DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_VOLTAGE",
637 vec.data(), BIAS::kNumChannels*sizeof(float));
638
639 ostringstream msg;
640 msg << setprecision(4) << "Sending new absolute offset: dU(" << fTemp << "degC)=" << fTempOffset << "V, Unom=" << overvoltage << "V, Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0);
641 Info(msg);
642 }
643 }
644 else
645 {
646 if (fDimBias.state()==BIAS::State::kVoltageOn)
647 {
648 ostringstream msg;
649 msg << setprecision(4) << "Current status: dU(" << fTemp << "degC)=" << fTempOffset << "V, Unom=" << overvoltage << "V, Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0);
650 Info(msg);
651 }
652
653 }
654
655 if (GetCurrentState()==Feedback::State::kInProgress &&
656 fDimBias.state()==BIAS::State::kRamping)
657 return GetCurrentState();
658
659 // --------------------------------- Console out --------------------------------------
660
661 if (num[0]>0 && num[1]>0 && fIsVerbose && !fDimBias.state()==BIAS::State::kRamping)
662 {
663 sort(med[0].begin(), med[0].begin()+num[0]);
664 sort(med[1].begin(), med[1].begin()+num[1]);
665
666 ostringstream msg;
667 msg << " Avg0=" << setw(7) << avg[0]/num[0] << " | Avg1=" << setw(7) << avg[1]/num[1];
668 Debug(msg);
669
670 msg.str("");
671 msg << " Med0=" << setw(7) << med[0][num[0]/2] << " | Med1=" << setw(7) << med[1][num[1]/2];
672 Debug(msg);
673
674 msg.str("");
675 msg << " Min0=" << setw(7) << min[0] << " | Min1=" << setw(7) << min[1];
676 Debug(msg);
677
678 msg.str("");
679 msg << " Max0=" << setw(7) << max[0] << " | Max1=" << setw(7) << max[1];
680 Debug(msg);
681 }
682
683 // ---------------------------- Calibrated Currents -----------------------------------
684
685 if (num[2]>0)
686 {
687 data.Iavg /= num[2];
688 data.Irms /= num[2];
689
690 data.N = num[2];
691 data.Irms = sqrt(data.Irms-data.Iavg*data.Iavg);
692
693 sort(med[2].data(), med[2].data()+num[2]);
694
695 data.Imed = num[2]%2 ? (med[2][num[2]/2-1]+med[2][num[2]/2])/2 : med[2][num[2]/2];
696
697 for (int i=0; i<num[2]; i++)
698 med[2][i] = fabs(med[2][i]-data.Imed);
699
700 sort(med[2].data(), med[2].data()+num[2]);
701
702 data.Idev = med[2][uint32_t(0.682689477208650697*num[2])];
703
704 data.Tdiff = evt.GetTime().UnixTime()-fTimeCalib.UnixTime();
705
706 // FIXME:
707 // + Current overvoltage
708 // + Temp offset
709 // + User offset
710 // + Command overvoltage
711 fDimCurrents.setQuality(GetCurrentState());
712 fDimCurrents.setData(&data, sizeof(dim_data));
713 fDimCurrents.Update(evt.GetTime());
714 }
715
716 return GetCurrentState()==Feedback::State::kCalibrated ? Feedback::State::kCalibrated : Feedback::State::kInProgress;
717 }
718
719 // ======================================================================
720
721 int Print() const
722 {
723 Out() << fDim << endl;
724 Out() << fDimFSC << endl;
725 Out() << fDimBias << endl;
726
727 return GetCurrentState();
728 }
729
730 int PrintCalibration()
731 {
732 /*
733 if (fCalibration.size()==0)
734 {
735 Out() << "No calibration performed so far." << endl;
736 return GetCurrentState();
737 }
738
739 const float *avg = fCalibration.data();
740 const float *rms = fCalibration.data()+BIAS::kNumChannels;
741 const float *res = fCalibration.data()+BIAS::kNumChannels*2;
742
743 Out() << "Average current at " << fCalibrationOffset << "V below G-APD operation voltage:\n";
744
745 for (int k=0; k<13; k++)
746 for (int j=0; j<8; j++)
747 {
748 Out() << setw(2) << k << "|" << setw(2) << j*4 << "|";
749 for (int i=0; i<4; i++)
750 Out() << Tools::Form(" %6.1f+-%4.1f", avg[k*32+j*4+i], rms[k*32+j*4+i]);
751 Out() << '\n';
752 }
753 Out() << '\n';
754
755 Out() << "Measured calibration resistor:\n";
756 for (int k=0; k<13; k++)
757 for (int j=0; j<4; j++)
758 {
759 Out() << setw(2) << k << "|" << setw(2) << j*8 << "|";
760 for (int i=0; i<8; i++)
761 Out() << Tools::Form(" %5.0f", res[k*32+j*8+i]);
762 Out() << '\n';
763 }
764
765 Out() << flush;
766 */
767 return GetCurrentState();
768 }
769
770 int EnableOldAlgorithm(const EventImp &evt)
771 {
772 if (!CheckEventSize(evt.GetSize(), "EnableOldAlgorithm", 1))
773 return kSM_FatalError;
774
775 fEnableOldAlgorithm = evt.GetBool();
776
777 return GetCurrentState();
778 }
779
780 int SetVerbosity(const EventImp &evt)
781 {
782 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
783 return kSM_FatalError;
784
785 fIsVerbose = evt.GetBool();
786
787 return GetCurrentState();
788 }
789
790 int SetCurrentRequestInterval(const EventImp &evt)
791 {
792 if (!CheckEventSize(evt.GetSize(), "SetCurrentRequestInterval", 2))
793 return kSM_FatalError;
794
795 fCurrentRequestInterval = evt.GetUShort();
796
797 Out() << "New current request interval: " << fCurrentRequestInterval << "ms" << endl;
798
799 return GetCurrentState();
800 }
801
802 int Calibrate()
803 {
804 if (fDimBias.state()!=BIAS::State::kVoltageOff)
805 {
806 Warn("Calibration can only be started when biasctrl is in state VoltageOff.");
807 return GetCurrentState();
808 }
809
810 Message("Starting calibration (ignore="+to_string(fNumCalibIgnore)+", N="+to_string(fNumCalibRequests)+")");
811
812 fCursorCur = -fNumCalibIgnore;
813 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
814 fCurrentsRms.assign(BIAS::kNumChannels, 0);
815
816 fBiasDac.assign(BIAS::kNumChannels, 0);
817
818 fCalibStep = 3;
819 fTimeCalib = Time();
820
821 Dim::SendCommandNB("BIAS_CONTROL/SET_GLOBAL_DAC", uint32_t(256+512*fCalibStep));
822
823 return Feedback::State::kCalibrating;
824 }
825
826 int Start(const EventImp &evt)
827 {
828 if (!CheckEventSize(evt.GetSize(), "Start", 4))
829 return kSM_FatalError;
830
831 if (fDimBias.state()==BIAS::State::kRamping)
832 {
833 Warn("Feedback can not be started when biasctrl is in state Ramping.");
834 return GetCurrentState();
835 }
836
837 fUserOffset = evt.GetFloat();
838
839 fCursorCur = 0;
840
841 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
842 fCurrentsRms.assign(BIAS::kNumChannels, 0);
843
844 ostringstream out;
845 out << "Starting feedback with an offset of " << fUserOffset << "V";
846 Message(out);
847
848 return Feedback::State::kWaitingForData;
849 }
850
851 int StopFeedback()
852 {
853 if (GetCurrentState()==Feedback::State::kCalibrating)
854 return Feedback::State::kConnected;
855
856 if (GetCurrentState()>Feedback::State::kCalibrated)
857 return Feedback::State::kCalibrated;
858
859 return GetCurrentState();
860 }
861
862 int Execute()
863 {
864 if (!fDim.online())
865 return Feedback::State::kDimNetworkNA;
866
867 const bool bias = fDimBias.state() >= BIAS::State::kConnecting;
868 const bool fsc = fDimFSC.state() >= FSC::State::kConnected;
869
870 // All subsystems are not connected
871 if (!bias && !fsc)
872 return Feedback::State::kDisconnected;
873
874 // Not all subsystems are yet connected
875 if (!bias || !fsc)
876 return Feedback::State::kConnecting;
877
878 if (GetCurrentState()<Feedback::State::kCalibrating)
879 return Feedback::State::kConnected;
880
881 if (GetCurrentState()==Feedback::State::kConnected)
882 return GetCurrentState();
883 if (GetCurrentState()==Feedback::State::kCalibrating)
884 return GetCurrentState();
885
886 // kCalibrated, kWaitingForData, kInProgress
887
888 if (fDimBias.state()==BIAS::State::kVoltageOn || (fDimBias.state()==BIAS::State::kVoltageOff && GetCurrentState()==Feedback::State::kWaitingForData))
889 {
890 static Time past;
891 if (fCurrentRequestInterval>0 && Time()-past>boost::posix_time::milliseconds(fCurrentRequestInterval))
892 {
893 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS");
894 past = Time();
895 }
896 }
897
898 return GetCurrentState();
899 }
900
901public:
902 StateMachineFeedback(ostream &out=cout) : StateMachineDim(out, "FEEDBACK"),
903 fIsVerbose(false), fEnableOldAlgorithm(true),
904 //---
905 fDimFSC("FSC_CONTROL"),
906 fDimBias("BIAS_CONTROL"),
907 //---
908 fDimCalibration("FEEDBACK/CALIBRATION", "F:416;F:416;F:416;F:416",
909 "Current offsets"
910 "|Avg[uA]:Average offset at dac=256+5*512"
911 "|Rms[uA]:Rms of Avg"
912 "|R[Ohm]:Measured calibration resistor"
913 "|U[V]:Corresponding voltage reported by biasctrl"),
914 fDimCalibration2("FEEDBACK/CALIBRATION_STEPS", "I:1;F:416;F:416;F:416",
915 "Calibration of the R8 resistor"
916 "|DAC[dac]:DAC setting"
917 "|U[V]:Corresponding voltages reported by biasctrl"
918 "|Iavg[uA]:Averaged measured current"
919 "|Irms[uA]:Rms measured current"),
920 fDimCalibrationR8("FEEDBACK/CALIBRATION_R8", "F:416;F:416",
921 "Calibration of R8"
922 "|DeltaI[uA]:Average offset"
923 "|R8[uA]:Measured effective resistor R8"),
924 fDimCurrents("FEEDBACK/CALIBRATED_CURRENTS", "F:416;F:1;F:1;F:1;F:1;I:1;F:1;F:416;F:1;F:1",
925 "Calibrated currents"
926 "|I[uA]:Calibrated currents per pixel"
927 "|I_avg[uA]:Average calibrated current (N channels)"
928 "|I_rms[uA]:Rms of calibrated current (N channels)"
929 "|I_med[uA]:Median calibrated current (N channels)"
930 "|I_dev[uA]:Deviation of calibrated current (N channels)"
931 "|N[uint16]:Number of valid values"
932 "|T_diff[s]:Time difference to calibration"
933 "|U_ov[V]:Calculated overvoltage"
934 "|U_nom[V]:Nominal overvoltage"
935 "|dU_temp[V]:Correction calculated from temperature"
936 ),
937 fCurrentRequestInterval(0),
938 fNumCalibIgnore(30),
939 fNumCalibRequests(300)
940 {
941 fDim.Subscribe(*this);
942 fDimFSC.Subscribe(*this);
943 fDimBias.Subscribe(*this);
944
945 fDimBias.SetCallback(bind(&StateMachineFeedback::HandleBiasStateChange, this));
946
947 Subscribe("BIAS_CONTROL/CURRENT")
948 (bind(&StateMachineFeedback::HandleBiasCurrent, this, placeholders::_1));
949 Subscribe("BIAS_CONTROL/VOLTAGE")
950 (bind(&StateMachineFeedback::HandleBiasVoltage, this, placeholders::_1));
951 Subscribe("BIAS_CONTROL/DAC")
952 (bind(&StateMachineFeedback::HandleBiasDac, this, placeholders::_1));
953 Subscribe("BIAS_CONTROL/NOMINAL")
954 (bind(&StateMachineFeedback::HandleBiasNom, this, placeholders::_1));
955 Subscribe("FSC_CONTROL/TEMPERATURE")
956 (bind(&StateMachineFeedback::HandleCameraTemp, this, placeholders::_1));
957
958 // State names
959 AddStateName(Feedback::State::kDimNetworkNA, "DimNetworkNotAvailable",
960 "The Dim DNS is not reachable.");
961
962 AddStateName(Feedback::State::kDisconnected, "Disconnected",
963 "The Dim DNS is reachable, but the required subsystems are not available.");
964 AddStateName(Feedback::State::kConnecting, "Connecting",
965 "Either biasctrl or fscctrl not connected.");
966 AddStateName(Feedback::State::kConnected, "Connected",
967 "biasctrl and fscctrl are available and connected with their hardware.");
968
969 AddStateName(Feedback::State::kCalibrating, "Calibrating",
970 "Bias crate calibrating in progress.");
971 AddStateName(Feedback::State::kCalibrated, "Calibrated",
972 "Bias crate calibrated.");
973
974 AddStateName(Feedback::State::kWaitingForData, "WaitingForData",
975 "Current control started, waiting for valid temperature and current data.");
976 AddStateName(Feedback::State::kInProgress, "InProgress",
977 "Current control in progress.");
978
979
980 /*
981 AddEvent("SET_CURRENT_REQUEST_INTERVAL")
982 (bind(&StateMachineFeedback::SetCurrentRequestInterval, this, placeholders::_1))
983 ("|interval[ms]:Interval between two current requests in modes which need that.");
984 */
985
986 AddEvent("CALIBRATE", Feedback::State::kConnected, Feedback::State::kCalibrated)
987 (bind(&StateMachineFeedback::Calibrate, this))
988 ("");
989
990 AddEvent("START", "F:1", Feedback::State::kCalibrated)
991 (bind(&StateMachineFeedback::Start, this, placeholders::_1))
992 ("Start the current/temperature control loop"
993 "|Uov[V]:Overvoltage to be applied (standard value is 1.1V)");
994
995 AddEvent("STOP")
996 (bind(&StateMachineFeedback::StopFeedback, this))
997 ("Stop any control loop");
998
999
1000 AddEvent("ENABLE_OLD_ALRGORITHM", "B:1", Feedback::State::kConnected, Feedback::State::kCalibrated)
1001 (bind(&StateMachineFeedback::EnableOldAlgorithm, this, placeholders::_1));
1002
1003
1004 AddEvent("PRINT")
1005 (bind(&StateMachineFeedback::Print, this))
1006 ("");
1007 AddEvent("PRINT_CALIBRATION")
1008 (bind(&StateMachineFeedback::PrintCalibration, this))
1009 ("");
1010
1011 // Verbosity commands
1012 AddEvent("SET_VERBOSE", "B:1")
1013 (bind(&StateMachineFeedback::SetVerbosity, this, placeholders::_1))
1014 ("set verbosity state"
1015 "|verbosity[bool]:disable or enable verbosity when calculating overvoltage");
1016 }
1017
1018 int EvalOptions(Configuration &conf)
1019 {
1020 fIsVerbose = !conf.Get<bool>("quiet");
1021
1022 if (!fMap.Read(conf.Get<string>("pixel-map-file")))
1023 {
1024 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
1025 return 1;
1026 }
1027
1028 fCurrentRequestInterval = conf.Get<uint16_t>("current-request-interval");
1029 fNumCalibIgnore = conf.Get<uint16_t>("num-calib-ignore");
1030 fNumCalibRequests = conf.Get<uint16_t>("num-calib-average");
1031
1032 return -1;
1033 }
1034};
1035
1036// ------------------------------------------------------------------------
1037
1038#include "Main.h"
1039
1040template<class T>
1041int RunShell(Configuration &conf)
1042{
1043 return Main::execute<T, StateMachineFeedback>(conf);
1044}
1045
1046void SetupConfiguration(Configuration &conf)
1047{
1048 po::options_description control("Feedback options");
1049 control.add_options()
1050 ("quiet,q", po_bool(true), "Disable printing more information on average overvoltagecontents of all received messages (except dynamic data) in clear text.")
1051 ("pixel-map-file", var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage.")
1052 ("current-request-interval", var<uint16_t>(1000), "Interval between two current requests.")
1053 ("num-calib-ignore", var<uint16_t>(30), "Number of current requests to be ignored before averaging")
1054 ("num-calib-average", var<uint16_t>(300), "Number of current requests to be averaged")
1055 ;
1056
1057 conf.AddOptions(control);
1058}
1059
1060/*
1061 Extract usage clause(s) [if any] for SYNOPSIS.
1062 Translators: "Usage" and "or" here are patterns (regular expressions) which
1063 are used to match the usage synopsis in program output. An example from cp
1064 (GNU coreutils) which contains both strings:
1065 Usage: cp [OPTION]... [-T] SOURCE DEST
1066 or: cp [OPTION]... SOURCE... DIRECTORY
1067 or: cp [OPTION]... -t DIRECTORY SOURCE...
1068 */
1069void PrintUsage()
1070{
1071 cout <<
1072 "The feedback control the BIAS voltages based on the calibration signal.\n"
1073 "\n"
1074 "The default is that the program is started without user intercation. "
1075 "All actions are supposed to arrive as DimCommands. Using the -c "
1076 "option, a local shell can be initialized. With h or help a short "
1077 "help message about the usuage can be brought to the screen.\n"
1078 "\n"
1079 "Usage: feedback [-c type] [OPTIONS]\n"
1080 " or: feedback [OPTIONS]\n";
1081 cout << endl;
1082}
1083
1084void PrintHelp()
1085{
1086 Main::PrintHelp<StateMachineFeedback>();
1087
1088 /* Additional help text which is printed after the configuration
1089 options goes here */
1090
1091 /*
1092 cout << "bla bla bla" << endl << endl;
1093 cout << endl;
1094 cout << "Environment:" << endl;
1095 cout << "environment" << endl;
1096 cout << endl;
1097 cout << "Examples:" << endl;
1098 cout << "test exam" << endl;
1099 cout << endl;
1100 cout << "Files:" << endl;
1101 cout << "files" << endl;
1102 cout << endl;
1103 */
1104}
1105
1106int main(int argc, const char* argv[])
1107{
1108 Configuration conf(argv[0]);
1109 conf.SetPrintUsage(PrintUsage);
1110 Main::SetupConfiguration(conf);
1111 SetupConfiguration(conf);
1112
1113 if (!conf.DoParse(argc, argv, PrintHelp))
1114 return 127;
1115
1116 //try
1117 {
1118 // No console access at all
1119 if (!conf.Has("console"))
1120 {
1121// if (conf.Get<bool>("no-dim"))
1122// return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
1123// else
1124 return RunShell<LocalStream>(conf);
1125 }
1126 // Cosole access w/ and w/o Dim
1127/* if (conf.Get<bool>("no-dim"))
1128 {
1129 if (conf.Get<int>("console")==0)
1130 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
1131 else
1132 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
1133 }
1134 else
1135*/ {
1136 if (conf.Get<int>("console")==0)
1137 return RunShell<LocalShell>(conf);
1138 else
1139 return RunShell<LocalConsole>(conf);
1140 }
1141 }
1142 /*catch (std::exception& e)
1143 {
1144 cerr << "Exception: " << e.what() << endl;
1145 return -1;
1146 }*/
1147
1148 return 0;
1149}
Note: See TracBrowser for help on using the repository browser.