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

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