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

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