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

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