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

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