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

Last change on this file since 15490 was 15490, checked in by tbretz, 12 years ago
Let the event buffer store the index of the corresponding runCtrl entry.
File size: 61.3 KB
Line 
1#include <valarray>
2
3#include "Dim.h"
4#include "Event.h"
5#include "Shell.h"
6#include "StateMachineDim.h"
7#include "Connection.h"
8#include "Configuration.h"
9#include "Console.h"
10#include "Converter.h"
11#include "externals/PixelMap.h"
12
13#include "tools.h"
14
15#include "LocalControl.h"
16
17#include "HeadersFAD.h"
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 enum control_t
33 {
34 kIdle,
35 kTemp,
36 kFeedback,
37 kFeedbackGlobal,
38 kCurrents,
39 kCurrentsNew,
40 };
41
42 control_t fControlType;
43
44 PixelMap fMap;
45
46 DimVersion fDim;
47 DimDescribedState fDimFAD;
48 DimDescribedState fDimFSC;
49 DimDescribedState fDimBias;
50
51 DimDescribedService fDimReference;
52 DimDescribedService fDimDeviation;
53 DimDescribedService fDimCalibration;
54 DimDescribedService fDimCurrents;
55
56 vector<int64_t> fCurrentsAvg;
57 vector<int64_t> fCurrentsRms;
58
59 vector<float> fCalibration;
60 vector<float> fVoltGapd;
61 vector<float> fBiasVolt;
62
63 vector<vector<float>> fData;
64
65 int64_t fCursorCur;
66 uint64_t fCursorAmpl;
67 uint64_t fCursorTemp;
68
69 Time fBiasLast;
70 Time fStartTime;
71 Time fCalibTime;
72
73 valarray<double> fPV[3]; // Process variable (intgerated/averaged amplitudes)
74 valarray<double> fSP; // Set point (target amplitudes)
75
76 double fKp; // Proportional constant
77 double fKi; // Integral constant
78 double fKd; // Derivative constant
79 double fT; // Time constant (cycle time)
80 double fGain; // Gain (conversion from a DRS voltage deviation into a BIAS voltage change at G-APD reference voltage)
81
82 double fT21;
83
84 double fBiasOffset;
85 double fTempOffset;
86 double fCalibrationOffset;
87 double fAppliedOffset;
88
89 uint16_t fCurrentRequestInterval;
90 uint16_t fNumCalibIgnore;
91 uint16_t fNumCalibRequests;
92
93 bool fOutputEnabled;
94
95 int HandleCameraTemp(const EventImp &evt)
96 {
97 if (fControlType!=kTemp && fControlType!=kCurrents && fControlType!=kCurrentsNew)
98 return GetCurrentState();
99
100 if (evt.GetSize()!=60*sizeof(float))
101 return GetCurrentState();
102
103 const float *ptr = evt.Ptr<float>();
104
105 double avgt = 0;
106 int numt = 0;
107 for (int i=1; i<32; i++)
108 if (ptr[i]!=0)
109 {
110 avgt += ptr[i];
111 numt++;
112 }
113
114 if (numt==0)
115 {
116 Warn("Received sensor temperatures all invalid.");
117 return GetCurrentState();
118 }
119
120 avgt /= numt; // [deg C]
121
122 fTempOffset = (avgt-25)*4./70; // [V]
123
124 fCursorTemp++;
125
126 return fControlType==kCurrentsNew ? HandleCurrentControlNew() : HandleCurrentControl();
127 }
128
129 int HandleCurrentControlNew()
130 {
131 if (GetCurrentState()==Feedback::State::kCalibrating && fBiasOffset>fTempOffset-1.2)
132 {
133 fCursorTemp = 0;
134
135 ostringstream msg;
136 msg << " (applied calibration offset " << fBiasOffset << "V exceeds temperature correction " << fTempOffset << "V - 1.2V.";
137 Warn("Trying to calibrate above G-APD breakdown volatge!");
138 Warn(msg);
139 return GetCurrentState();
140 }
141
142 double avg[2] = { 0, 0 };
143 double min[2] = { 90, 90 };
144 double max[2] = { -90, -90 };
145 int num[2] = { 0, 0 };
146
147 vector<double> med[2];
148 med[0].resize(416);
149 med[1].resize(416);
150
151 const float *Ravg = fCalibration.data()+BIAS::kNumChannels*2; // Measured resistance
152
153 vector<float> vec(2*BIAS::kNumChannels+2);
154
155 vec[BIAS::kNumChannels*2] = fTempOffset;
156 vec[BIAS::kNumChannels*2+1] = fBiasOffset;
157
158 float *Uoff = vec.data()+BIAS::kNumChannels;
159
160 if (GetCurrentState()==Feedback::State::kCalibrating)
161 for (int i=0; i<BIAS::kNumChannels; i++)
162 Uoff[i] = fBiasOffset;
163 else
164 for (int i=0; i<BIAS::kNumChannels; i++)
165 Uoff[i] = fTempOffset+fBiasOffset;
166
167 if (fControlType==kCurrentsNew)
168 {
169 // Would be a devision by zero. We need informations first.
170 if (fCursorCur==0)
171 return GetCurrentState();
172
173 for (int i=0; i<BIAS::kNumChannels; i++)
174 {
175 const PixelMapEntry &hv = fMap.hv(i);
176 if (!hv)
177 continue;
178
179 // Nominal breakdown voltage (includes overvoltage already)
180 double Ubd = fVoltGapd[i];
181
182 // Nominal breakdown voltage excluding overvoltage of 1.1V
183 Ubd -= 1.1;
184
185 // Correct breakdown voltage for temperature dependence
186 Ubd += fTempOffset;
187
188 // Number of G-APDs in this patch
189 const int N = hv.group() ? 5 : 4;
190
191 // 100 Ohm measurement resistor for current measurement
192 const double R2 = 100;
193
194 // Serial resistors (one 1kOhm at the output of the bias crate, one 1kOhm in the camera)
195 const double R4 = 2000;
196
197 // Serial resistor of the individual G-APDs
198 double R5 = 3900/N;
199
200 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
201 if (i==66) // Pixel 830(66)
202 R5 = 300; // 2400 = 1/(3/3900 + 1/390)
203 if (i==191 || i==193) // Pixel 583(191) / Pixel 1401(193)
204 R5 = 390/1.4; // 379 = 1/(4/3900 + 1/390)
205
206 // Total resistance of branch with diode
207 const double R3 = R4+R5;
208
209 // Measured calibration resistor
210 const double R1 = Ravg[i] - R2;
211
212 // Voltage output of bias crate
213 const double Uout = fBiasVolt[i];
214
215 // Average current measured for this channel
216 const double Imes = double(fCurrentsAvg[i])/fCursorCur * (5000/4096.); // [uA]
217
218 // Voltage drop at measurement resistor R2 is define
219 // bythe measured current and the resistor
220 const double U2 = R2*Imes;
221
222 // The voltage seen by the calibration resistor R1 is defined by the
223 // bias crate output voltage minus the drop at the measurement resistor R2
224 const double U1 = Uout - U2;
225
226 // The current through the resistor R1 is defined
227 // by the applied voltage and the resistor
228 const double I1 = U1/R1;
229
230 // The current through the diode branch is the measured current
231 // minus the current through the calibration resistor R1
232 const double I3 = Imes - I1;
233
234 // The voltage drop in the diode branch (without the diode) is defined by the
235 // resistor and the current. It is 0 below the breakdown voltage of the G-APD
236 // is reached at the G-APD. This is the case when the output voltage minus
237 // the voltage drop at the calibration resistor reaches the breakdown voltage.
238 const double U3 = Uout-U2<Ubd ? 0 : R3*I3;
239
240 // Voltage drop at measurement resistor R2 and
241 // the total serial resistor R3 in the diode branch
242 const double Udrp = U2 + U3;
243
244 // Voltage finally at each G-APD (bias crate output voltage minus voltage drop)
245 const double Uapd = Uout - Udrp;
246
247 // The over-voltage seen by the G-APD (the voltage above the breakdown voltage) is
248 const double Uov = Uapd<Ubd ? 0 : Uapd - Ubd;
249
250 // The current through one G-APD is the sum divided by the number of G-APDs
251 // (assuming identical serial resistors)
252 double Iapd = I3/N;
253
254 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
255 // In this and the previosu case we neglect the resistance of the G-APDs, but we can make an
256 // assumption: The differential resistance depends more on the NSB than on the PDE,
257 // thus it is at least comparable for all G-APDs in the patch. In addition, although the
258 // G-APD with the 390Ohm serial resistor has the wrong voltage applied, this does not
259 // significantly influences the ohmic resistor or the G-APD because the differential
260 // resistor is large enough that the increase of the overvoltage does not dramatically
261 // increase the current flow as compared to the total current flow.
262 if (i==66)
263 Iapd *= 1.3;
264 if (i==191 || i==193)
265 Iapd *= 1.4;
266
267 // If the G-APD voltage is above the breakdown voltage we have the current through the
268 // G-APD and the over-voltage applied to the G-APD to calculate its differential resistor.
269 if (Uapd>Ubd)
270 {
271 // The differential resistance of the G-APD, i.e. the dependence of the
272 // current above the breakdown voltage, is given by
273 const double Rapd = Uov/Iapd;
274
275 // This allows us to estimate the current Iov at the overvoltage we want to apply
276 const double Iov = (1.1+fBiasOffset)/Rapd;
277
278 // This gives us an ohmic resistance Rov of the G-APD at the set-point
279 const double Rest = (Ubd+1.1+fBiasOffset)/Iov;
280
281 // This lets us estimate the total resistance Rtot of the circuit at the set-point
282 const double R3b = R4 + (R5+Rest)/N;
283 const double Rtot = R2 + 1/(1/R1 + 1/R3b);
284
285 // From this we can estimate the output voltage we need to get the
286 // over-voltage at the G-APD as anticipated
287 const double r = 1 + R3/R1 - (R2 + R3 + R3*R2/R1)/Rtot;
288 const double Uset = (Ubd+1.1+fBiasOffset)/r;
289
290 Uoff[i] = Uset - fVoltGapd[i];
291 }
292
293 // Calculate statistics only for channels with a valid calibration
294 if (Uov>0)
295 {
296 const int g = hv.group();
297
298 med[g][num[g]] = Uov;
299 avg[g] += Uov;
300 num[g]++;
301
302 if (Uov<min[g])
303 min[g] = Uov;
304 if (Uov>max[g])
305 max[g] = Uov;
306 }
307 }
308
309 sort(med[0].begin(), med[0].begin()+num[0]);
310 sort(med[1].begin(), med[1].begin()+num[1]);
311
312 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
313 fCursorCur = 0;
314 }
315
316 fDimDeviation.setQuality(fControlType);
317 fDimDeviation.Update(vec);
318
319 // Warning: Here it is assumed that the ramp up and down is done properly
320 // within the time between two new temperatures and that the calibration
321 // is finished within that time.
322 if (GetCurrentState()!=Feedback::State::kCalibrating ||
323 fDimBias.state()!=BIAS::State::kVoltageOff ||
324 fCursorTemp!=1 || !fOutputEnabled)
325 {
326 if (!fOutputEnabled || fDimBias.state()!=BIAS::State::kVoltageOn)
327 return GetCurrentState();
328
329 // Trigger calibration
330 if (GetCurrentState()==Feedback::State::kCalibrating && fCursorTemp==2)
331 {
332 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
333 return GetCurrentState();
334 }
335 }
336
337 ostringstream msg;
338 msg << setprecision(4) << "Sending new absolute offset (" << fAppliedOffset << "V+" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << "V) to biasctrl.";
339 Info(msg);
340
341 if (fControlType==kCurrents && num[0]>0 && num[1]>0)
342 {
343 msg.str("");
344 msg << " Avg0=" << setw(7) << avg[0]/num[0] << " | Avg1=" << setw(7) << avg[1]/num[1];
345 Debug(msg);
346
347 msg.str("");
348 msg << " Med0=" << setw(7) << med[0][num[0]/2] << " | Med1=" << setw(7) << med[1][num[1]/2];
349 Debug(msg);
350
351 msg.str("");
352 msg << " Min0=" << setw(7) << min[0] << " | Min1=" << setw(7) << min[1];
353 Debug(msg);
354
355 msg.str("");
356 msg << " Max0=" << setw(7) << max[0] << " | Max1=" << setw(7) << max[1];
357 Debug(msg);
358 }
359
360 DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_OFFSET",
361 vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float));
362
363 return GetCurrentState();
364 }
365
366 int HandleCurrentControl()
367 {
368 const double dUt = fTempOffset; // [V]
369
370 if (GetCurrentState()==Feedback::State::kCalibrating && fBiasOffset>dUt-1.2)
371 {
372 fCursorTemp = 0;
373
374 ostringstream msg;
375 msg << " (applied calibration offset " << fBiasOffset << "V exceeds temperature correction " << fTempOffset << "V - 1.2V.";
376 Warn("Trying to calibrate above G-APD breakdown volatge!");
377 Warn(msg);
378 return GetCurrentState();
379 }
380
381 // FIXME: If calibrating do not wait for the temperature!
382 fAppliedOffset = fBiasOffset;
383 if (GetCurrentState()!=Feedback::State::kCalibrating)
384 fAppliedOffset += dUt;
385
386 vector<float> vec(2*BIAS::kNumChannels+2);
387 for (int i=0; i<BIAS::kNumChannels; i++)
388 vec[i+BIAS::kNumChannels] = fAppliedOffset;
389
390 vec[BIAS::kNumChannels*2] = dUt;
391 vec[BIAS::kNumChannels*2+1] = fBiasOffset;
392
393 double avg[2] = { 0, 0 };
394 double min[2] = { 90, 90 };
395 double max[2] = { -90, -90 };
396 int num[2] = { 0, 0 };
397
398 vector<double> med[2];
399 med[0].resize(416);
400 med[1].resize(416);
401
402 if (fControlType==kCurrents)
403 {
404 if (fCursorCur==0)
405 {
406 //DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
407 return GetCurrentState();
408 }
409
410 // Pixel 583: 5 31 == 191 (5) C2 B3 P3
411 // Pixel 830: 2 2 == 66 (4) C0 B8 P1
412 // Pixel 1401: 6 1 == 193 (5) C2 B4 P0
413
414 // Convert from DAC counts to uA
415 const double conv = 5000./4096;
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 // U0 = fCalibrationOffset
424 // dT = fAppliedVoltage
425
426 // Ifeedback = Im[i] - (U[i]-U0)/Ravg[i] - Iavg[i];
427 // dUapplied[i] + dUneu[i] = R[g] * (Im[i] - (dUapplied[i]+dUneu[i]-U0+dT)/Ravg[i] - Iavg[i])
428
429 // The assumption here is that the offset calculated from the temperature
430 // does not significanly change within a single step
431
432 // dU[i] := dUtotal[i] = dUapplied[i] + dUneu[i]
433 // dU[i] / R[g] = Im[i] - (dU[i]+dT-U0)/Ravg[i] - Iavg[i]
434 // dU[i]/R[g] + dU[i]/Ravg[i] = Im[i] + U0/Ravg[i] - dT/Ravg[i] - Iavg[i]
435 // dU[i]*(1/R[g]+1/Ravg[i]) = Im[i] - Iavg[i] + U0/Ravg[i] - dT/Ravg[i]
436 // dU[i] = (Im[i] - Iavg[i] + U0/Ravg[i] - dT/Ravg[i]) / (1/R[g]+1/Ravg[i])
437 // dU[i] = { Im[i] - Iavg[i] + (U0-dT)/Ravg[i] } * r with r := 1 / (1/R[g]+1/Ravg[i])
438
439 const double U0 = fAppliedOffset-fCalibrationOffset;
440
441 for (int i=0; i<BIAS::kNumChannels; i++)
442 {
443 const PixelMapEntry &hv = fMap.hv(i);
444 if (!hv)
445 continue;
446
447 // Average measured current
448 const double Im = double(fCurrentsAvg[i])/fCursorCur * conv; // [uA]
449
450 // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch)
451 const int g = hv.group();
452
453 // Serial resistors in front of the G-APD
454 double Rg = R[g];
455
456 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
457 if (i==66) // Pixel 830(66)
458 Rg = 2400; // 2400 = (3/3900 + 1/390) + 1000 + 1100
459 if (i==191 || i==193) // Pixel 583(191) / Pixel 1401(193)
460 Rg = 2379; // 2379 = (4/3900 + 1/390) + 1000 + 1100
461
462 const double r = 1./(1./Rg + 1./Ravg[i]); // [Ohm]
463
464 // Offset induced by the voltage above the calibration point
465 const double dI = U0/Ravg[i]; // [V/Ohm]
466
467 // Offset at the calibration point (make sure that the calibration is
468 // valid (Im[i]>Iavg[i]) and we operate above the calibration point)
469 const double I = Im>Iavg[i] ? Im - Iavg[i] : 0; // [A]
470
471 // Make sure that the averaged resistor is valid
472 const double dU = Ravg[i]>10000 ? r*(I*1e-6 - dI) : 0;
473
474 vec[i+BIAS::kNumChannels] += dU;
475
476 // Angelegte Spannung: U0+dU
477 // Gemessener Strom: Im - Iavg
478 // Strom offset: (U0+dU) / Ravg
479 // Fliessender Strom: Im-Iavg - (U0+dU)/Ravg
480 // Korrektur: [ Im-Iavg - (U0+dU)/Ravg ] * Rg
481
482 // Aufgeloest nach dU: dU = ( Im-Iavg - dU/Ravg ) / ( 1/Rg + 1/Ravg )
483 // Equivalent zu: dU = ( I*Ravg - U0 ) / ( Ravg/Rg+1 )
484
485 // Calculate statistics only for channels with a valid calibration
486 if (Iavg[i]>0)
487 {
488 med[g][num[g]] = dU;
489 avg[g] += dU;
490 num[g]++;
491
492 if (dU<min[g])
493 min[g] = dU;
494 if (dU>max[g])
495 max[g] = dU;
496 }
497 }
498
499 sort(med[0].begin(), med[0].begin()+num[0]);
500 sort(med[1].begin(), med[1].begin()+num[1]);
501
502 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
503 fCursorCur = 0;
504 }
505
506 fDimDeviation.setQuality(fControlType);
507 fDimDeviation.Update(vec);
508
509 // Warning: Here it is assumed that the ramp up and down is done properly
510 // within the time between two new temperatures and that the calibration
511 // is finished within that time.
512 if (!(GetCurrentState()==Feedback::State::kCalibrating && fCursorTemp==1 && fOutputEnabled && fDimBias.state()==BIAS::State::kVoltageOff))
513 {
514 if (!fOutputEnabled || fDimBias.state()!=BIAS::State::kVoltageOn)
515 return GetCurrentState();
516
517 // Trigger calibration
518 if (GetCurrentState()==Feedback::State::kCalibrating && fCursorTemp==2)
519 {
520 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
521 return GetCurrentState();
522 }
523 }
524
525 ostringstream msg;
526 msg << setprecision(4) << "Sending new absolute offset (" << fAppliedOffset << "V+" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << "V) to biasctrl.";
527 Info(msg);
528
529 if (fControlType==kCurrents && num[0]>0 && num[1]>0)
530 {
531 msg.str("");
532 msg << " Avg0=" << setw(7) << avg[0]/num[0] << " | Avg1=" << setw(7) << avg[1]/num[1];
533 Debug(msg);
534
535 msg.str("");
536 msg << " Med0=" << setw(7) << med[0][num[0]/2] << " | Med1=" << setw(7) << med[1][num[1]/2];
537 Debug(msg);
538
539 msg.str("");
540 msg << " Min0=" << setw(7) << min[0] << " | Min1=" << setw(7) << min[1];
541 Debug(msg);
542
543 msg.str("");
544 msg << " Max0=" << setw(7) << max[0] << " | Max1=" << setw(7) << max[1];
545 Debug(msg);
546 }
547
548 DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_OFFSET",
549 vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float));
550
551 return GetCurrentState();
552 }
553
554 int AverageCurrents(const EventImp &evt)
555 {
556 if (evt.GetSize()!=BIAS::kNumChannels*sizeof(int16_t))
557 return -1;
558
559 if (fDimBias.state()!=BIAS::State::kVoltageOn)
560 return false;
561
562 if (fCursorCur++<0)
563 return true;
564
565 const int16_t *ptr = evt.Ptr<int16_t>();
566
567 for (int i=0; i<BIAS::kNumChannels; i++)
568 {
569 fCurrentsAvg[i] += ptr[i];
570 fCurrentsRms[i] += ptr[i]*ptr[i];
571 }
572
573 return true;
574 }
575
576
577 void HandleCalibration(const EventImp &evt)
578 {
579 const int rc = AverageCurrents(evt);
580 if (rc<0)
581 return;
582
583 if (fCursorCur<fNumCalibRequests)
584 {
585 if (fDimBias.state()==BIAS::State::kVoltageOn)
586 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
587 return;
588 }
589
590 if (rc==0)
591 return;
592
593 fCalibration.resize(BIAS::kNumChannels*3);
594
595 float *avg = fCalibration.data();
596 float *rms = fCalibration.data()+BIAS::kNumChannels;
597 float *res = fCalibration.data()+BIAS::kNumChannels*2;
598
599 const double conv = 5000./4096;
600
601 for (int i=0; i<BIAS::kNumChannels; i++)
602 {
603 const double I = double(fCurrentsAvg[i])/fCursorCur;
604
605 res[i] = (fVoltGapd[i]+fCalibrationOffset)/I / conv * 1e6;
606 avg[i] = conv * I;
607 rms[i] = conv * sqrt(double(fCurrentsRms[i])/fCursorCur-I*I);
608 }
609
610 fCalibTime = Time();
611
612 fDimCalibration.setData(fCalibration);
613 fDimCalibration.Update(fCalibTime);
614
615 fOutputEnabled = false;
616 fControlType = kIdle;
617
618 Info("Calibration successfully done.");
619
620 if (fDimBias.state()==BIAS::State::kVoltageOn)
621 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
622 }
623
624 void HandleFeedback(const EventImp &evt)
625 {
626 if (evt.GetSize()!=1440*sizeof(float))
627 return;
628
629 // -------- Check age of last stored event --------
630
631 const Time tm(evt.GetTime());
632
633 if (Time()-fBiasLast>boost::posix_time::seconds(30))
634 {
635 Warn("Last received event data older than 30s... resetting average calculation.");
636 ResetData();
637 }
638 fBiasLast = tm;
639
640 // -------- Store new event --------
641
642 fData[fCursorAmpl%fData.size()].assign(evt.Ptr<float>(), evt.Ptr<float>()+1440);
643 if (++fCursorAmpl<fData.size())
644 return;
645
646 // -------- Calculate statistics --------
647
648 valarray<double> med(1440);
649
650 for (int ch=0; ch<1440; ch++)
651 {
652 vector<float> arr(fData.size());
653 for (size_t i=0; i<fData.size(); i++)
654 arr[i] = fData[i][ch];
655
656 sort(arr.begin(), arr.end());
657
658 med[ch] = arr[arr.size()/2];
659 }
660
661 /*
662 vector<float> med(1440);
663 vector<float> rms(1440);
664 for (size_t i=0; i<fData.size(); i++)
665 {
666 if (fData[i].size()==0)
667 return;
668
669 for (int j=0; j<1440; j++)
670 {
671 med[j] += fData[i][j];
672 rms[j] += fData[i][j]*fData[i][j];
673 }
674 }
675 */
676
677 vector<double> avg(BIAS::kNumChannels);
678 vector<int> num(BIAS::kNumChannels);
679 for (int i=0; i<1440; i++)
680 {
681 const PixelMapEntry &ch = fMap.hw(i);
682
683 // FIXME: Add a consistency check if the median makes sense...
684 // FIXME: Add a consistency check to remove pixels with bright stars (median?)
685
686 avg[ch.hv()] += med[i];
687 num[ch.hv()]++;
688 }
689
690 for (int i=0; i<BIAS::kNumChannels; i++)
691 {
692 if (num[i])
693 avg[i] /= num[i];
694
695 }
696
697 // -------- Calculate correction --------
698
699 // http://bestune.50megs.com/typeABC.htm
700
701 // CO: Controller output
702 // PV: Process variable
703 // SP: Set point
704 // T: Sampling period (loop update period)
705 // e = SP - PV
706 //
707 // Kp : No units
708 // Ki : per seconds
709 // Kd : seconds
710
711 // CO(k)-CO(k-1) = - Kp[ PV(k) - PV(k-1) ] + Ki * T * (SP(k)-PV(k)) - Kd/T [ PV(k) - 2PV(k-1) + PV(k-2) ]
712
713 if (fCursorAmpl%fData.size()>0)
714 return;
715
716 // FIXME: Take out broken / dead boards.
717
718 const Time tm0 = Time();
719
720 /*const*/ double T21 = fT>0 ? fT : (tm0-fStartTime).total_microseconds()/1000000.;
721 const double T10 = fT21;
722 fT21 = T21;
723
724 fStartTime = tm0;
725
726 ostringstream out;
727 out << "New " << fData.size() << " event received: " << fCursorAmpl << " / " << setprecision(3) << T21 << "s";
728 Info(out);
729
730 if (fPV[0].size()==0)
731 {
732 fPV[0].resize(avg.size());
733 fPV[0] = valarray<double>(avg.data(), avg.size());
734 return;
735 }
736
737 if (fPV[1].size()==0)
738 {
739 fPV[1].resize(avg.size());
740 fPV[1] = valarray<double>(avg.data(), avg.size());
741 return;
742 }
743
744 if (fPV[2].size()==0)
745 {
746 fPV[2].resize(avg.size());
747 fPV[2] = valarray<double>(avg.data(), avg.size());
748 return;
749 }
750
751 fPV[0] = fPV[1];
752 fPV[1] = fPV[2];
753
754 fPV[2].resize(avg.size());
755 fPV[2] = valarray<double>(avg.data(), avg.size());
756
757 if (T10<=0 || T21<=0)
758 return;
759
760 //cout << "Calculating (" << fCursor << ":" << T21 << ")... " << endl;
761
762 // fKi[j] = response[j]*gain;
763 // Kp = 0;
764 // Kd = 0;
765
766 // => Kp = 0.01 * gain = 0.00005
767 // => Ki = 0.8 * gain/20s = 0.00025
768 // => Kd = 0.1 * gain/20s = 0.00003
769
770 /*
771 fKp = 0;
772 fKd = 0;
773 fKi = 0.00003*20;
774 T21 = 1;
775 */
776
777 //valarray<double> correction = - Kp*(PV[2] - PV[1]) + Ki * dT * (SP-PV[2]) - Kd/dT * (PV[2] - 2*PV[1] + PV[0]);
778 //valarray<double> correction =
779 // - Kp * (PV[2] - PV[1])
780 // + dT * Ki * (SP - PV[2])
781 // - Kd / dT * (PV[2] - 2*PV[1] + PV[0]);
782 //
783 // - (Kp+Kd/dT1) * (PV[2] - PV[1])
784 // + dT2 * Ki * (SP - PV[2])
785 // + Kd / dT1 * (PV[1] - PV[0]);
786 //
787 // - Kp * (PV[2] - PV[1])
788 // + Ki * (SP - PV[2])*dT
789 // - Kd * (PV[2] - PV[1])/dT
790 // + Kd * (PV[1] - PV[0])/dT;
791 //
792 //valarray<double> correction =
793 // - Kp*(PV[2] - PV[1]) + Ki * T21 * (SP-PV[2]) - Kd*(PV[2]-PV[1])/T21 - Kd*(PV[0]-PV[1])/T01;
794 const valarray<double> correction = 1./fGain/1000*
795 (
796 - (fKp+fKd/T21)*(fPV[2] - fPV[1])
797 + fKi*T21*(fSP-fPV[2])
798 + fKd/T10*(fPV[1]-fPV[0])
799 );
800
801 /*
802 integral = 0
803 start:
804 integral += (fSP - fPV[2])*dt
805
806 output = Kp*(fSP - fPV[2]) + Ki*integral - Kd*(fPV[2] - fPV[1])/dt
807
808 wait(dt)
809
810 goto start
811 */
812
813 vector<float> vec(2*BIAS::kNumChannels+2);
814 for (int i=0; i<BIAS::kNumChannels; i++)
815 vec[i] = fPV[2][i]-fSP[i];
816
817 for (int i=0; i<BIAS::kNumChannels; i++)
818 vec[i+BIAS::kNumChannels] = avg[i]<5*2.5 ? 0 : correction[i];
819
820 fDimDeviation.setQuality(fControlType);
821 fDimDeviation.Update(vec);
822
823 if (!fOutputEnabled || fDimBias.state()!=BIAS::State::kVoltageOn)
824 return;
825
826 Info("Sending new relative offset to biasctrl.");
827
828 DimClient::sendCommandNB("BIAS_CONTROL/INCREASE_ALL_CHANNELS_VOLTAGE",
829 vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float));
830 }
831
832 void HandleGlobalFeedback(const EventImp &evt)
833 {
834 if (evt.GetSize()!=1440*sizeof(float))
835 return;
836
837 // -------- Store new event --------
838
839 vector<float> arr(evt.Ptr<float>(), evt.Ptr<float>()+1440);
840
841 sort(arr.begin(), arr.end());
842
843 const float med = arr[arr.size()/2];
844
845 fData[fCursorAmpl%fData.size()].resize(1); //assign(&med, &med);
846 fData[fCursorAmpl%fData.size()][0] = med; //assign(&med, &med);
847
848 if (++fCursorAmpl<fData.size())
849 return;
850
851 // -------- Calculate statistics --------
852
853 double avg=0;
854 double rms=0;
855 for (size_t i=0; i<fData.size(); i++)
856 {
857 avg += fData[i][0];
858 rms += fData[i][0]*fData[i][0];
859 }
860
861 avg /= fData.size();
862 rms /= fData.size();
863
864 rms = sqrt(rms-avg*avg);
865
866 // -------- Calculate correction --------
867
868 if (fCursorAmpl%fData.size()!=0)
869 return;
870
871 Out() << "Amplitude: " << avg << " +- " << rms << endl;
872
873 // FIXME: Take out broken / dead boards.
874
875 /*
876 ostringstream out;
877 out << "New " << fData.size() << " event received: " << fCursor << " / " << setprecision(3) << T21 << "s";
878 Info(out);
879 */
880
881 if (fPV[0].size()==0)
882 {
883 fPV[0].resize(1);
884 fPV[0] = valarray<double>(&avg, 1);
885 return;
886 }
887
888 if (fPV[1].size()==0)
889 {
890 fPV[1].resize(1);
891 fPV[1] = valarray<double>(&avg, 1);
892 return;
893 }
894
895 if (fPV[2].size()==0)
896 {
897 fPV[2].resize(1);
898 fPV[2] = valarray<double>(&avg, 1);
899 return;
900 }
901
902 fPV[0] = fPV[1];
903 fPV[1] = fPV[2];
904
905 fPV[2].resize(1);
906 fPV[2] = valarray<double>(&avg, 1);
907
908 // ----- Calculate average currents -----
909
910 vector<float> A(BIAS::kNumChannels);
911 for (int i=0; i<BIAS::kNumChannels; i++)
912 A[i] = double(fCurrentsAvg[i]) / fCursorCur;
913
914 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
915 fCursorCur = 0;
916
917 // -------- Calculate correction --------
918
919 // correction = (fSP[0]-fPV[2])*fKi
920 /*
921 const double T21 = 1; // feedback is 1s
922 const double T10 = 1; // feedback is 20s
923
924 const valarray<double> correction = 1./fGain/1000*
925 (
926 - (fKp+fKd/T21)*(fPV[2] - fPV[1])
927 + fKi*T21*(fSP[0]-fPV[2])
928 + fKd/T10*(fPV[1]-fPV[0])
929 );
930 */
931
932 // pow of 1.6 comes from the non-linearity of the
933 // amplitude vs bias voltage
934 const valarray<double> correction = 1./fGain/1000*
935 (
936 //fKi*(pow(fSP[0], 1./1.6)-pow(fPV[2], 1./1.6))
937 fKi*(fSP[0]-fPV[2])
938 );
939
940 Out() << "Correction: " << correction[0] << "V (" << fSP[0] << ")" << endl;
941
942 const int nch = BIAS::kNumChannels;
943
944 // FIXME: Sanity check!
945
946 vector<float> vec;
947 vec.reserve(2*nch+2);
948 vec.insert(vec.begin(), nch, fPV[2][0]-fSP[0]);
949 vec.insert(vec.begin()+nch, nch, correction[0]);
950 vec.push_back(0);
951 vec.push_back(0);
952
953 fDimDeviation.setQuality(fControlType);
954 fDimDeviation.Update(vec);
955
956 if (!fOutputEnabled || fDimBias.state()!=BIAS::State::kVoltageOn)
957 return;
958
959 Info("Sending new global relative offset to biasctrl.");
960
961 DimClient::sendCommandNB("BIAS_CONTROL/INCREASE_ALL_CHANNELS_VOLTAGE",
962 vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float));
963 }
964
965 void HandleCalibrateCurrents(const EventImp &evt)
966 {
967 if (fBiasVolt.size()==0 || fCalibration.size()==0 || evt.GetSize()<416*sizeof(int16_t))
968 return;
969
970 struct dim_data {
971 float I[416];
972 float Iavg;
973 float Irms;
974 float Imed;
975 float Idev;
976 uint32_t N;
977 float Tdiff;
978
979 dim_data() { memset(this, 0, sizeof(dim_data)); }
980 } __attribute__((__packed__));
981
982 const int16_t *I = evt.Ptr<int16_t>();
983 const float *R = fCalibration.data()+BIAS::kNumChannels*2;
984 const float *U = fBiasVolt.data();
985
986 vector<float> med(416);
987 uint16_t cnt = 0;
988
989 double avg = 0;
990 double rms = 0;
991
992 dim_data data;
993 for (int i=0; i<416; i++)
994 {
995 const PixelMapEntry &hv = fMap.hv(i);
996 if (!hv)
997 continue;
998
999 if (R[i]<=0)
1000 continue;
1001
1002 data.I[i] = I[i]*5000./4096 - U[i]/R[i]*1e6;
1003 data.I[i] /= hv.group() ? 5 : 4;
1004
1005 avg += data.I[i];
1006 rms += data.I[i]*data.I[i];
1007
1008 if (i>=320)
1009 continue;
1010
1011 med[cnt++] = data.I[i];
1012 }
1013
1014 if (cnt==0)
1015 return;
1016
1017 avg /= cnt;
1018 rms /= cnt;
1019
1020 data.N = cnt;
1021 data.Iavg = avg;
1022 data.Irms = sqrt(rms-avg*avg);
1023
1024 sort(med.data(), med.data()+cnt);
1025
1026 data.Imed = cnt%2 ? (med[cnt/2-1]+med[cnt/2])/2 : med[cnt/2];
1027
1028 for (int i=0; i<cnt; i++)
1029 med[i] = fabs(med[i]-data.Imed);
1030
1031 sort(med.data(), med.data()+cnt);
1032
1033 data.Idev = med[uint32_t(0.682689477208650697*cnt)];
1034
1035 data.Tdiff = evt.GetTime().UnixTime()-fCalibTime.UnixTime();
1036
1037 fDimCurrents.setData(&data, sizeof(dim_data));
1038 fDimCurrents.Update(evt.GetTime());
1039 }
1040
1041 int HandleBiasCurrent(const EventImp &evt)
1042 {
1043 if (fControlType==kTemp && GetCurrentState()==Feedback::State::kCalibrating)
1044 HandleCalibration(evt);
1045
1046 if (fControlType==kFeedbackGlobal || fControlType==kCurrents || fControlType==kCurrentsNew)
1047 AverageCurrents(evt);
1048
1049 /*
1050 if (fControlType==kCurrents && fCursorTemp>0 && fCursorCur>0)
1051 {
1052 // fCursorTemp: 1 2 3 4 5 6 7 8
1053 // fCursor%x: 1 1 1 2 2 2 3 3 // 9 steps in ~15s
1054 //if (fCursorTemp<3 && fCursorCur%(fCursorTemp/3+1)==0)
1055 HandleCurrentControl();
1056 }*/
1057
1058 HandleCalibrateCurrents(evt);
1059
1060 return GetCurrentState();
1061 }
1062
1063 int HandleBiasData(const EventImp &evt)
1064 {
1065 if (fControlType==kFeedback)
1066 HandleFeedback(evt);
1067
1068 if (fControlType==kFeedbackGlobal)
1069 HandleGlobalFeedback(evt);
1070
1071 return GetCurrentState();
1072 }
1073
1074 int HandleBiasNom(const EventImp &evt)
1075 {
1076 if (evt.GetSize()>=416*sizeof(float))
1077 {
1078 fVoltGapd.assign(evt.Ptr<float>(), evt.Ptr<float>()+416);
1079 Info("Nominal bias voltages received.");
1080 }
1081
1082 return GetCurrentState();
1083 }
1084
1085 int HandleBiasVoltage(const EventImp &evt)
1086 {
1087 if (evt.GetSize()>=416*sizeof(float))
1088 fBiasVolt.assign(evt.Ptr<float>(), evt.Ptr<float>()+416);
1089 return GetCurrentState();
1090 }
1091
1092 bool CheckEventSize(size_t has, const char *name, size_t size)
1093 {
1094 if (has==size)
1095 return true;
1096
1097 ostringstream msg;
1098 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1099 Fatal(msg);
1100 return false;
1101 }
1102
1103 int Print() const
1104 {
1105 Out() << fDim << endl;
1106 Out() << fDimFAD << endl;
1107 Out() << fDimFSC << endl;
1108 Out() << fDimBias << endl;
1109
1110 return GetCurrentState();
1111 }
1112
1113 int PrintCalibration()
1114 {
1115 if (fCalibration.size()==0)
1116 {
1117 Out() << "No calibration performed so far." << endl;
1118 return GetCurrentState();
1119 }
1120
1121 const float *avg = fCalibration.data();
1122 const float *rms = fCalibration.data()+BIAS::kNumChannels;
1123 const float *res = fCalibration.data()+BIAS::kNumChannels*2;
1124
1125 Out() << "Average current at " << fCalibrationOffset << "V below G-APD operation voltage:\n";
1126
1127 for (int k=0; k<13; k++)
1128 for (int j=0; j<8; j++)
1129 {
1130 Out() << setw(2) << k << "|" << setw(2) << j*4 << "|";
1131 for (int i=0; i<4; i++)
1132 Out() << Tools::Form(" %6.1f+-%4.1f", avg[k*32+j*4+i], rms[k*32+j*4+i]);
1133 Out() << '\n';
1134 }
1135 Out() << '\n';
1136
1137 Out() << "Measured calibration resistor:\n";
1138 for (int k=0; k<13; k++)
1139 for (int j=0; j<4; j++)
1140 {
1141 Out() << setw(2) << k << "|" << setw(2) << j*8 << "|";
1142 for (int i=0; i<8; i++)
1143 Out() << Tools::Form(" %5.0f", res[k*32+j*8+i]);
1144 Out() << '\n';
1145 }
1146
1147 Out() << flush;
1148
1149 return GetCurrentState();
1150 }
1151
1152 void WarnState(bool needfsc, bool needfad)
1153 {
1154 const bool bias = fDimBias.state() >= BIAS::State::kConnecting;
1155 const bool fsc = fDimFSC.state() >= FSC::State::kConnected;
1156 const bool fad = fDimFAD.state() >= FAD::State::kConnected;
1157
1158 if (!bias)
1159 Warn("Bias control not yet ready.");
1160 if (needfsc && !fsc)
1161 Warn("FSC control not yet ready.");
1162 if (needfad && !fad)
1163 Warn("FAD control not yet ready.");
1164 }
1165
1166 int SetConstant(const EventImp &evt, int constant)
1167 {
1168 if (!CheckEventSize(evt.GetSize(), "SetConstant", 8))
1169 return kSM_FatalError;
1170
1171 switch (constant)
1172 {
1173 case 0: fKi = evt.GetDouble(); break;
1174 case 1: fKp = evt.GetDouble(); break;
1175 case 2: fKd = evt.GetDouble(); break;
1176 case 3: fT = evt.GetDouble(); break;
1177 case 4: fGain = evt.GetDouble(); break;
1178 default:
1179 Fatal("SetConstant got an unexpected constant id -- this is a program bug!");
1180 return kSM_FatalError;
1181 }
1182
1183 return GetCurrentState();
1184 }
1185
1186 int EnableOutput(const EventImp &evt)
1187 {
1188 if (!CheckEventSize(evt.GetSize(), "EnableOutput", 1))
1189 return kSM_FatalError;
1190
1191 fOutputEnabled = evt.GetBool();
1192
1193 if (fControlType==kCurrents || fControlType==kCurrentsNew)
1194 if (fCursorTemp>1)
1195 fCursorTemp = 1;
1196
1197 return GetCurrentState();
1198 }
1199
1200 void ResetData(int16_t n=-1)
1201 {
1202 fData.assign(n>0 ? n : fData.size(), vector<float>(0));
1203
1204 fCursorAmpl = 0;
1205 fCursorCur = 0;
1206 fCursorTemp = 0;
1207
1208 fStartTime = Time();
1209
1210 fSP = valarray<double>(0., BIAS::kNumChannels);
1211
1212 vector<float> vec(2*BIAS::kNumChannels+2, fBiasOffset);
1213 vec[2*BIAS::kNumChannels] = 0;
1214 fDimDeviation.setQuality(kIdle);
1215 fDimDeviation.Update(vec);
1216
1217 fPV[0].resize(0);
1218 fPV[1].resize(0);
1219 fPV[2].resize(0);
1220
1221 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
1222 fCurrentsRms.assign(BIAS::kNumChannels, 0);
1223
1224 if (fKp==0 && fKi==0 && fKd==0)
1225 Warn("Control loop parameters are all set to zero.");
1226 }
1227
1228 int StartFeedback(const EventImp &evt)
1229 {
1230 if (!CheckEventSize(evt.GetSize(), "StartFeedback", 2))
1231 return kSM_FatalError;
1232
1233 WarnState(false, true);
1234
1235 fBiasOffset = 0;
1236 ResetData(evt.GetShort());
1237
1238 fControlType = kFeedback;
1239
1240 return GetCurrentState();
1241 }
1242
1243 int StartFeedbackGlobal(const EventImp &evt)
1244 {
1245 if (!CheckEventSize(evt.GetSize(), "StartFeedbackGlobal", 2))
1246 return kSM_FatalError;
1247
1248 WarnState(false, true);
1249
1250 fBiasOffset = 0;
1251 ResetData(evt.GetShort());
1252
1253 fControlType = kFeedbackGlobal;
1254
1255 return GetCurrentState();
1256 }
1257
1258 int StartTempCtrl(const EventImp &evt)
1259 {
1260 if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))
1261 return kSM_FatalError;
1262
1263 WarnState(true, false);
1264
1265 fBiasOffset = evt.GetFloat();
1266 fControlType = kTemp;
1267
1268 ostringstream out;
1269 out << "Starting temperature feedback with an offset of " << fBiasOffset << "V";
1270 Message(out);
1271
1272 if (fDimBias.state()==BIAS::State::kVoltageOn)
1273 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
1274
1275 return GetCurrentState();
1276 }
1277
1278 int StartCurrentCtrl(const EventImp &evt)
1279 {
1280 if (!CheckEventSize(evt.GetSize(), "StartCurrentCtrl", 4))
1281 return kSM_FatalError;
1282
1283 if (fCalibration.size()==0)
1284 {
1285 Warn("Current control needs a bias crate calibration first... command ignored.");
1286 return GetCurrentState();
1287 }
1288
1289 WarnState(true, false);
1290
1291 fBiasOffset = evt.GetFloat();
1292 fTempOffset = -3;
1293 ResetData(0);
1294 fControlType = kCurrents;
1295
1296 ostringstream out;
1297 out << "Starting current/temp feedback with an offset of " << fBiasOffset << "V";
1298 Message(out);
1299
1300 return GetCurrentState();
1301 }
1302
1303 int StartNewCurrentCtrl(const EventImp &evt)
1304 {
1305 if (!CheckEventSize(evt.GetSize(), "StartNewCurrentCtrl", 4))
1306 return kSM_FatalError;
1307
1308 if (fCalibration.size()==0)
1309 {
1310 Warn("Current control needs a bias crate calibration first... command ignored.");
1311 return GetCurrentState();
1312 }
1313
1314 WarnState(true, false);
1315
1316 fBiasOffset = evt.GetFloat();
1317 fTempOffset = -3;
1318 ResetData(0);
1319 fControlType = kCurrentsNew;
1320
1321 ostringstream out;
1322 out << "Starting new current/temp feedback with an offset of " << fBiasOffset << "V";
1323 Message(out);
1324
1325 return GetCurrentState();
1326 }
1327
1328 int StopFeedback()
1329 {
1330 fControlType = kIdle;
1331
1332 return GetCurrentState();
1333 }
1334
1335 int StoreReference()
1336 {
1337 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())
1338 {
1339 Warn("No values in memory. Take enough events first!");
1340 return GetCurrentState();
1341 }
1342
1343 // FIXME: Check age
1344
1345 if (!fPV[1].size() && !fPV[2].size())
1346 fSP = fPV[0];
1347
1348 if (!fPV[2].size())
1349 fSP = fPV[1];
1350 else
1351 fSP = fPV[2];
1352
1353 vector<float> vec(BIAS::kNumChannels);
1354 for (int i=0; i<BIAS::kNumChannels; i++)
1355 vec[i] = fSP[i];
1356 fDimReference.Update(vec);
1357
1358 return GetCurrentState();
1359 }
1360
1361 int SetReference(const EventImp &evt)
1362 {
1363 if (!CheckEventSize(evt.GetSize(), "SetReference", 4))
1364 return kSM_FatalError;
1365
1366 const float val = evt.GetFloat();
1367 /*
1368 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())
1369 {
1370 Warn("No values in memory. Take enough events first!");
1371 return GetCurrentState();
1372 }*/
1373
1374 vector<float> vec(BIAS::kNumChannels);
1375 for (int i=0; i<BIAS::kNumChannels; i++)
1376 vec[i] = fSP[i] = val;
1377 fDimReference.Update(vec);
1378
1379 Out() << "New global reference value: " << val << "mV" << endl;
1380
1381 return GetCurrentState();
1382 }
1383
1384 int CalibrateCurrents()
1385 {
1386// if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))
1387// return kSM_FatalError;
1388
1389 if (fDimBias.state()==BIAS::State::kRamping)
1390 {
1391 Warn("Calibration cannot be started when biasctrl is in state Ramping.");
1392 return GetCurrentState();
1393 }
1394
1395 if (fVoltGapd.size()==0)
1396 {
1397 Error("No G-APD reference voltages received yet (BIAS_CONTROL/NOMINAL).");
1398 return GetCurrentState();
1399 }
1400
1401 WarnState(true, false);
1402
1403 ostringstream out;
1404 out << "Starting temperature feedback for calibration with an offset of " << fCalibrationOffset << "V";
1405 Message(out);
1406
1407 fBiasOffset = fCalibrationOffset;
1408 fControlType = kTemp;
1409 fCursorCur = -fNumCalibIgnore;
1410 fCursorTemp = 0;
1411 fCurrentsAvg.assign(BIAS::kNumChannels, 0);
1412 fCurrentsRms.assign(BIAS::kNumChannels, 0);
1413 fCalibration.resize(0);
1414 fStartTime = Time();
1415 fOutputEnabled = true;
1416
1417 return Feedback::State::kCalibrating;
1418 }
1419
1420 int SetCurrentRequestInterval(const EventImp &evt)
1421 {
1422 if (!CheckEventSize(evt.GetSize(), "SetCurrentRequestInterval", 2))
1423 return kSM_FatalError;
1424
1425 fCurrentRequestInterval = evt.GetUShort();
1426
1427 Out() << "New current request interval: " << fCurrentRequestInterval << "ms" << endl;
1428
1429 return GetCurrentState();
1430 }
1431
1432 int Execute()
1433 {
1434 // Dispatch (execute) at most one handler from the queue. In contrary
1435 // to run_one(), it doesn't wait until a handler is available
1436 // which can be dispatched, so poll_one() might return with 0
1437 // handlers dispatched. The handlers are always dispatched/executed
1438 // synchronously, i.e. within the call to poll_one()
1439 //poll_one();
1440
1441 if (!fDim.online())
1442 return Feedback::State::kDimNetworkNA;
1443
1444 const bool bias = fDimBias.state() >= BIAS::State::kConnecting;
1445 const bool fad = fDimFAD.state() >= FAD::State::kConnected;
1446 const bool fsc = fDimFSC.state() >= FSC::State::kConnected;
1447
1448 // All subsystems are not connected
1449 if (!bias && !fad && !fsc)
1450 return Feedback::State::kDisconnected;
1451
1452 // At least one subsystem apart from bias is connected
1453 if (bias && !fad && !fsc)
1454 return Feedback::State::kConnecting;
1455
1456/*
1457 // All subsystems are connected
1458 if (GetCurrentStatus()==Feedback::State::kConfiguringStep1)
1459 {
1460 if (fCursor<1)
1461 return Feedback::State::kConfiguringStep1;
1462
1463 if (fCursor==1)
1464 {
1465 fStartTime = Time();
1466 return Feedback::State::kConfiguringStep2;
1467 }
1468 }
1469 if (GetCurrentStatus()==Feedback::State::kConfiguringStep2)
1470 {
1471 if (fCursor==1)
1472 {
1473 if ((Time()-fStartTime).total_microseconds()/1000000.<1.5)
1474 return Feedback::State::kConfiguringStep2;
1475
1476 Dim::SendCommand("BIAS_CONTROL/REQUEST_STATUS");
1477 }
1478 if (fCursor==2)
1479 {
1480
1481 int n=0;
1482 double avg = 0;
1483 for (size_t i=0; i<fCurrents.size(); i++)
1484 if (fCurrents[i]>=0)
1485 {
1486 avg += fCurrents[i];
1487 n++;
1488 }
1489
1490 cout << avg/n << endl;
1491 }
1492 return Feedback::State::kConnected;
1493 }
1494 */
1495
1496 // Needs connection of FAD and BIAS
1497 if (bias && fad)
1498 {
1499 if (fControlType==kFeedback || fControlType==kFeedbackGlobal)
1500 return fOutputEnabled ? Feedback::State::kFeedbackCtrlRunning : Feedback::State::kFeedbackCtrlIdle;
1501 }
1502
1503 // Needs connection of FSC and BIAS
1504 if (bias && fsc)
1505 {
1506 if (fControlType==kTemp)
1507 {
1508 if (GetCurrentState()==Feedback::State::kCalibrating && fCursorCur<fNumCalibRequests)
1509 return GetCurrentState();
1510
1511 return fOutputEnabled ? Feedback::State::kTempCtrlRunning : Feedback::State::kTempCtrlIdle;
1512 }
1513 if (fControlType==kCurrents || fControlType==kCurrentsNew)
1514 {
1515 static Time past;
1516 if (fCurrentRequestInterval>0 && Time()-past>boost::posix_time::milliseconds(fCurrentRequestInterval))
1517 {
1518 if (fDimBias.state()==BIAS::State::kVoltageOn)
1519 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
1520 past = Time();
1521 }
1522
1523 return fOutputEnabled && fCursorTemp>0 ? Feedback::State::kCurrentCtrlRunning : Feedback::State::kCurrentCtrlIdle;
1524 }
1525 }
1526
1527 if (bias && fad && !fsc)
1528 return Feedback::State::kConnectedFAD;
1529
1530 if (bias && fsc && !fad)
1531 return Feedback::State::kConnectedFSC;
1532
1533 return Feedback::State::kConnected;
1534 }
1535
1536public:
1537 StateMachineFeedback(ostream &out=cout) : StateMachineDim(out, "FEEDBACK"),
1538 //---
1539 fDimFAD("FAD_CONTROL"),
1540 fDimFSC("FSC_CONTROL"),
1541 fDimBias("BIAS_CONTROL"),
1542 //---
1543 fDimReference("FEEDBACK/REFERENCE", "F:416",
1544 "Amplitude reference value(s)"
1545 "Vref[mV]:Amplitude reference"),
1546 fDimDeviation("FEEDBACK/DEVIATION", "F:416;F:416;F:1;F:1",
1547 "Control loop information"
1548 "|DeltaAmpl[mV]:Amplitude offset measures"
1549 "|DeltaBias[mV]:Correction value calculated"
1550 "|DeltaTemp[mV]:Correction calculated from temperature"
1551 "|DeltaUser[mV]:Additional offset specified by user"),
1552 fDimCalibration("FEEDBACK/CALIBRATION", "F:416;F:416;F:416",
1553 "Current offsets"
1554 "|Avg[uA]:Average offset"
1555 "|Rms[uA]:Rms of offset"
1556 "|R[Ohm]:Measured calibration resistor"),
1557 fDimCurrents("FEEDBACK/CALIBRATED_CURRENTS", "F:416;F:1;F:1;F:1;F:1;I:1;F:1",
1558 "Calibrated currents"
1559 "|I[uA]:Calibrated currents"
1560 "|I_avg[uA]:Average calibrated current (320 channels)"
1561 "|I_rms[uA]:Rms of calibrated current (320 channels)"
1562 "|I_med[uA]:Median calibrated current (320 channels)"
1563 "|I_dev[uA]:Deviation of calibrated current (320 channels)"
1564 "|N[uint16]:Number of valid values"
1565 "|T_diff[s]:Time difference to calibration"),
1566 fSP(BIAS::kNumChannels),
1567 fKp(0), fKi(0), fKd(0), fT(-1),
1568 fCalibrationOffset(-3),
1569 fCurrentRequestInterval(0),
1570 fNumCalibIgnore(30),
1571 fNumCalibRequests(300),
1572 fOutputEnabled(false)
1573 {
1574 // ba::io_service::work is a kind of keep_alive for the loop.
1575 // It prevents the io_service to go to stopped state, which
1576 // would prevent any consecutive calls to run()
1577 // or poll() to do nothing. reset() could also revoke to the
1578 // previous state but this might introduce some overhead of
1579 // deletion and creation of threads and more.
1580
1581 fDim.Subscribe(*this);
1582 fDimFAD.Subscribe(*this);
1583 fDimFSC.Subscribe(*this);
1584 fDimBias.Subscribe(*this);
1585
1586 Subscribe("BIAS_CONTROL/CURRENT")
1587 (bind(&StateMachineFeedback::HandleBiasCurrent, this, placeholders::_1));
1588 Subscribe("BIAS_CONTROL/VOLTAGE")
1589 (bind(&StateMachineFeedback::HandleBiasVoltage, this, placeholders::_1));
1590 Subscribe("BIAS_CONTROL/FEEDBACK_DATA")
1591 (bind(&StateMachineFeedback::HandleBiasData, this, placeholders::_1));
1592 Subscribe("BIAS_CONTROL/NOMINAL")
1593 (bind(&StateMachineFeedback::HandleBiasNom, this, placeholders::_1));
1594 Subscribe("FSC_CONTROL/TEMPERATURE")
1595 (bind(&StateMachineFeedback::HandleCameraTemp, this, placeholders::_1));
1596
1597 // State names
1598 AddStateName(Feedback::State::kDimNetworkNA, "DimNetworkNotAvailable",
1599 "The Dim DNS is not reachable.");
1600
1601 AddStateName(Feedback::State::kDisconnected, "Disconnected",
1602 "The Dim DNS is reachable, but the required subsystems are not available.");
1603
1604 AddStateName(Feedback::State::kConnecting, "Connecting",
1605 "Only biasctrl is available and connected with its hardware.");
1606
1607 AddStateName(Feedback::State::kConnectedFSC, "ConnectedFSC",
1608 "biasctrl and fscctrl are available and connected with their hardware.");
1609 AddStateName(Feedback::State::kConnectedFAD, "ConnectedFAD",
1610 "biasctrl and fadctrl are available and connected with their hardware.");
1611 AddStateName(Feedback::State::kConnected, "Connected",
1612 "biasctrl, fadctrl and fscctrl are available and connected with their hardware.");
1613
1614 AddStateName(Feedback::State::kFeedbackCtrlIdle, "FeedbackIdle",
1615 "Feedback control activated, but voltage output disabled.");
1616 AddStateName(Feedback::State::kTempCtrlIdle, "TempCtrlIdle",
1617 "Temperature control activated, but voltage output disabled.");
1618 AddStateName(Feedback::State::kCurrentCtrlIdle, "CurrentCtrlIdle",
1619 "Current control activated, but voltage output disabled.");
1620
1621 AddStateName(Feedback::State::kFeedbackCtrlRunning, "FeedbackControl",
1622 "Feedback control activated and voltage output enabled.");
1623 AddStateName(Feedback::State::kTempCtrlRunning, "TempControl",
1624 "Temperature control activated and voltage output enabled.");
1625 AddStateName(Feedback::State::kCurrentCtrlRunning, "CurrentControl",
1626 "Current/Temp control activated and voltage output enabled.");
1627 AddStateName(Feedback::State::kCalibrating, "Calibrating",
1628 "Calibrating current offsets.");
1629
1630 AddEvent("START_FEEDBACK_CONTROL", "S:1", Feedback::State::kConnectedFAD, Feedback::State::kConnected)
1631 (bind(&StateMachineFeedback::StartFeedback, this, placeholders::_1))
1632 ("Start the feedback control loop"
1633 "|Num[short]:Number of events 'medianed' to calculate the correction value");
1634
1635 AddEvent("START_GLOBAL_FEEDBACK", "S:1", Feedback::State::kConnectedFAD, Feedback::State::kConnected)
1636 (bind(&StateMachineFeedback::StartFeedbackGlobal, this, placeholders::_1))
1637 ("Start the global feedback control loop"
1638 "Num[short]:Number of events averaged to calculate the correction value");
1639
1640 AddEvent("START_TEMP_CONTROL", "F:1", Feedback::State::kConnectedFSC, Feedback::State::kConnected)
1641 (bind(&StateMachineFeedback::StartTempCtrl, this, placeholders::_1))
1642 ("Start the temperature control loop"
1643 "|offset[V]:Offset from the nominal temperature corrected value in Volts");
1644
1645 AddEvent("START_CURRENT_CONTROL", "F:1", Feedback::State::kConnectedFSC, Feedback::State::kConnected)
1646 (bind(&StateMachineFeedback::StartCurrentCtrl, this, placeholders::_1))
1647 ("Start the current/temperature control loop"
1648 "|offset[V]:Offset from the nominal current/temperature corrected value in Volts");
1649
1650 // Feedback::State::kTempCtrlIdle, Feedback::State::kFeedbackCtrlIdle, Feedback::State::kTempCtrlRunning, Feedback::State::kFeedbackCtrlRunning
1651 AddEvent("STOP")
1652 (bind(&StateMachineFeedback::StopFeedback, this))
1653 ("Stop any control loop");
1654
1655 AddEvent("ENABLE_OUTPUT", "B:1")//, Feedback::State::kIdle)
1656 (bind(&StateMachineFeedback::EnableOutput, this, placeholders::_1))
1657 ("Enable sending of correction values caluclated by the control loop to the biasctrl");
1658
1659 AddEvent("STORE_REFERENCE")//, Feedback::State::kIdle)
1660 (bind(&StateMachineFeedback::StoreReference, this))
1661 ("Store the last (averaged) value as new reference (for debug purpose only)");
1662
1663 AddEvent("SET_REFERENCE", "F:1")//, Feedback::State::kIdle)
1664 (bind(&StateMachineFeedback::SetReference, this, placeholders::_1))
1665 ("Set a new global reference value (for debug purpose only)");
1666
1667 AddEvent("SET_Ki", "D:1")//, Feedback::State::kIdle)
1668 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 0))
1669 ("Set integral constant Ki");
1670
1671 AddEvent("SET_Kp", "D:1")//, Feedback::State::kIdle)
1672 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 1))
1673 ("Set proportional constant Kp");
1674
1675 AddEvent("SET_Kd", "D:1")//, Feedback::State::kIdle)
1676 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 2))
1677 ("Set derivative constant Kd");
1678
1679 AddEvent("SET_T", "D:1")//, Feedback::State::kIdle)
1680 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 3))
1681 ("Set time-constant. (-1 to use the cycle time, i.e. the time for the last average cycle, instead)");
1682
1683 AddEvent("CALIBRATE_CURRENTS", Feedback::State::kConnectedFSC, Feedback::State::kConnected)//, Feedback::State::kIdle)
1684 (bind(&StateMachineFeedback::CalibrateCurrents, this))
1685 ("");
1686
1687 AddEvent("SET_CURRENT_REQUEST_INTERVAL", Feedback::State::kConnectedFSC, Feedback::State::kConnected)//, Feedback::State::kIdle)
1688 (bind(&StateMachineFeedback::SetCurrentRequestInterval, this, placeholders::_1))
1689 ("|interval[ms]:Interval between two current requests in modes which need that.");
1690
1691 // Verbosity commands
1692// AddEvent("SET_VERBOSE", "B:1")
1693// (bind(&StateMachineMCP::SetVerbosity, this, placeholders::_1))
1694// ("set verbosity state"
1695// "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
1696
1697 AddEvent("PRINT")
1698 (bind(&StateMachineFeedback::Print, this))
1699 ("");
1700
1701 AddEvent("PRINT_CALIBRATION")
1702 (bind(&StateMachineFeedback::PrintCalibration, this))
1703 ("");
1704 }
1705
1706 int EvalOptions(Configuration &conf)
1707 {
1708 if (!fMap.Read(conf.Get<string>("pixel-map-file")))
1709 {
1710 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
1711 return 1;
1712 }
1713
1714 fGain = 0.1; // V(Amplitude) / V(Bias)
1715
1716 // 148 -> 248
1717
1718 // 33 : 10s < 2%
1719 // 50 : 5s < 2%
1720 // 66 : 3s < 2%
1721 // 85 : 2s < 2%
1722
1723 fKp = 0;
1724 fKd = 0;
1725 fKi = 0.75;
1726 fT = 1;
1727
1728 // Is that independent of the aboslute real amplitude of
1729 // the light pulser?
1730
1731 ostringstream msg;
1732 msg << "Control loop parameters: ";
1733 msg << "Kp=" << fKp << ", Kd=" << fKd << ", Ki=" << fKi << ", ";
1734 if (fT>0)
1735 msg << fT;
1736 else
1737 msg << "<auto>";
1738 msg << ", Gain(DRS/BIAS)=" << fGain << "V/V";
1739
1740 Message(msg);
1741
1742 fCurrentRequestInterval = conf.Get<uint16_t>("current-request-interval");
1743 fNumCalibIgnore = conf.Get<uint16_t>("num-calib-ignore");
1744 fNumCalibRequests = conf.Get<uint16_t>("num-calib-average");
1745 fCalibrationOffset = conf.Get<float>("calibration-offset");
1746
1747 return -1;
1748 }
1749};
1750
1751// ------------------------------------------------------------------------
1752
1753#include "Main.h"
1754
1755template<class T>
1756int RunShell(Configuration &conf)
1757{
1758 return Main::execute<T, StateMachineFeedback>(conf);
1759}
1760
1761void SetupConfiguration(Configuration &conf)
1762{
1763 po::options_description control("Feedback options");
1764 control.add_options()
1765 ("pixel-map-file", var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage.")
1766 ("current-request-interval", var<uint16_t>(1000), "Interval between two current requests.")
1767 ("num-calib-ignore", var<uint16_t>(30), "Number of current requests to be ignored before averaging")
1768 ("num-calib-average", var<uint16_t>(300), "Number of current requests to be averaged")
1769 ("calibration-offset", var<float>(-3), "Absolute offset relative to the G-APD operation voltage when calibrating")
1770 ;
1771
1772 conf.AddOptions(control);
1773}
1774
1775/*
1776 Extract usage clause(s) [if any] for SYNOPSIS.
1777 Translators: "Usage" and "or" here are patterns (regular expressions) which
1778 are used to match the usage synopsis in program output. An example from cp
1779 (GNU coreutils) which contains both strings:
1780 Usage: cp [OPTION]... [-T] SOURCE DEST
1781 or: cp [OPTION]... SOURCE... DIRECTORY
1782 or: cp [OPTION]... -t DIRECTORY SOURCE...
1783 */
1784void PrintUsage()
1785{
1786 cout <<
1787 "The feedback control the BIAS voltages based on the calibration signal.\n"
1788 "\n"
1789 "The default is that the program is started without user intercation. "
1790 "All actions are supposed to arrive as DimCommands. Using the -c "
1791 "option, a local shell can be initialized. With h or help a short "
1792 "help message about the usuage can be brought to the screen.\n"
1793 "\n"
1794 "Usage: feedback [-c type] [OPTIONS]\n"
1795 " or: feedback [OPTIONS]\n";
1796 cout << endl;
1797}
1798
1799void PrintHelp()
1800{
1801 Main::PrintHelp<StateMachineFeedback>();
1802
1803 /* Additional help text which is printed after the configuration
1804 options goes here */
1805
1806 /*
1807 cout << "bla bla bla" << endl << endl;
1808 cout << endl;
1809 cout << "Environment:" << endl;
1810 cout << "environment" << endl;
1811 cout << endl;
1812 cout << "Examples:" << endl;
1813 cout << "test exam" << endl;
1814 cout << endl;
1815 cout << "Files:" << endl;
1816 cout << "files" << endl;
1817 cout << endl;
1818 */
1819}
1820
1821int main(int argc, const char* argv[])
1822{
1823 Configuration conf(argv[0]);
1824 conf.SetPrintUsage(PrintUsage);
1825 Main::SetupConfiguration(conf);
1826 SetupConfiguration(conf);
1827
1828 if (!conf.DoParse(argc, argv, PrintHelp))
1829 return 127;
1830
1831 //try
1832 {
1833 // No console access at all
1834 if (!conf.Has("console"))
1835 {
1836// if (conf.Get<bool>("no-dim"))
1837// return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
1838// else
1839 return RunShell<LocalStream>(conf);
1840 }
1841 // Cosole access w/ and w/o Dim
1842/* if (conf.Get<bool>("no-dim"))
1843 {
1844 if (conf.Get<int>("console")==0)
1845 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
1846 else
1847 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
1848 }
1849 else
1850*/ {
1851 if (conf.Get<int>("console")==0)
1852 return RunShell<LocalShell>(conf);
1853 else
1854 return RunShell<LocalConsole>(conf);
1855 }
1856 }
1857 /*catch (std::exception& e)
1858 {
1859 cerr << "Exception: " << e.what() << endl;
1860 return -1;
1861 }*/
1862
1863 return 0;
1864}
Note: See TracBrowser for help on using the repository browser.