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

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