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

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