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

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