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

Last change on this file since 12991 was 12991, checked in by tbretz, 13 years ago
Added quality of service to Deviation
File size: 33.9 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,
57 kStateFeedbackCtrlIdle,
58 kStateTempCtrlRunning,
59 kStateFeedbackCtrlRunning,
60 kStateCalibrating,
61 };
62
63 enum control_t
64 {
65 kIdle,
66 kTemp,
67 kFeedback,
68 kFeedbackGlobal
69 };
70
71 control_t fControlType;
72
73 PixelMap fMap;
74
75 DimServiceInfoList fNetwork;
76
77 pair<Time, int> fStatusDim;
78 pair<Time, int> fStatusFAD;
79 pair<Time, int> fStatusFSC;
80 pair<Time, int> fStatusBias;
81
82 DimStampedInfo fDim;
83 DimStampedInfo fFAD;
84 DimStampedInfo fFSC;
85 DimStampedInfo fBias;
86 DimStampedInfo fBiasA;
87
88 DimStampedInfo fBiasData;
89 DimStampedInfo fCameraTemp;
90
91 DimDescribedService fDimReference;
92 DimDescribedService fDimDeviation;
93 DimDescribedService fDimCalibration;
94
95 vector<int64_t> fCurrentsAvg;
96 vector<int64_t> fCurrentsRms;
97
98 vector<float> fCalibration;
99
100 vector<vector<float>> fData;
101
102 uint64_t fCursor;
103
104 Time fBiasLast;
105 Time fStartTime;
106
107 valarray<double> fPV[3]; // Process variable (intgerated/averaged amplitudes)
108 valarray<double> fSP; // Set point (target amplitudes)
109
110 double fKp; // Proportional constant
111 double fKi; // Integral constant
112 double fKd; // Derivative constant
113 double fT; // Time constant (cycle time)
114 double fGain; // Gain (conversion from a DRS voltage deviation into a BIAS voltage change at G-APD reference voltage)
115
116 double fT21;
117
118 double fBiasOffset;
119
120 bool fOutputEnabled;
121
122 pair<Time, int> GetNewState(DimStampedInfo &info) const
123 {
124 const bool disconnected = info.getSize()==0;
125
126 // Make sure getTimestamp is called _before_ getTimestampMillisecs
127 const int tsec = info.getTimestamp();
128 const int tms = info.getTimestampMillisecs();
129
130 return make_pair(Time(tsec, tms*1000),
131 disconnected ? -2 : info.getQuality());
132 }
133
134 void HandleCameraTemp()
135 {
136 if (fCameraTemp.getSize()!=60*sizeof(float))
137 return;
138
139 const float *ptr = static_cast<float*>(fCameraTemp.getData());
140
141 double avg = 0;
142 int num = 0;
143 for (int i=1; i<32; i++)
144 if (ptr[i]!=0)
145 {
146 avg += ptr[i];
147 num++;
148 }
149
150 if (num==0)
151 return;
152
153 avg /= num;
154
155
156 const float diff = (avg-25)*4./70 + fBiasOffset;
157
158 vector<float> vec(2*BIAS::kNumChannels);
159 for (int i=0; i<BIAS::kNumChannels; i++)
160 vec[i+416] = diff;
161
162 if (fControlType!=kTemp)
163 return;
164
165 fDimDeviation.setQuality(fControlType);
166 fDimDeviation.Update(vec);
167
168 if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn)
169 return;
170
171 Info("Sending new absolute offset to biasctrl.");
172
173 DimClient::sendCommandNB((char*)"BIAS_CONTROL/SET_GAPD_REFERENCE_OFFSET",
174 (void*)&diff, sizeof(float));
175 }
176
177 void HandleCalibration()
178 {
179 if (fBiasA.getSize()!=416*sizeof(int16_t))
180 return;
181
182 if (fStatusBias.second==BIAS::kRamping)
183 return;
184
185 const int16_t *ptr = static_cast<int16_t*>(fBiasA.getData());
186
187 for (int i=0; i<416; i++)
188 if (ptr[i]>0)
189 {
190 fCurrentsAvg[i] += ptr[i];
191 fCurrentsRms[i] += ptr[i]*ptr[i];
192 }
193
194 if (++fCursor<100)
195 {
196 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
197 return;
198 }
199
200 fCalibration.resize(416*2);
201 for (int i=0; i<416; i++)
202 {
203 fCalibration[i] = double(fCurrentsAvg[i])/fCursor;
204 fCalibration[i+416] = sqrt(double(fCurrentsRms[i])/fCursor-fCalibration[i]*fCalibration[i]);
205 }
206
207 fDimCalibration.Update(fCalibration);
208
209 fOutputEnabled = false;
210 fControlType = kIdle;
211
212 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
213 }
214
215 void HandleFeedback()
216 {
217 if (fBiasData.getSize()!=1440*sizeof(float))
218 return;
219
220 // -------- Check age of last stored event --------
221
222 // Must be called in this order
223 const int tsec = fBiasData.getTimestamp();
224 const int tms = fBiasData.getTimestampMillisecs();
225
226 const Time tm(tsec, tms*1000);
227
228 if (Time()-fBiasLast>boost::posix_time::seconds(30))
229 {
230 Warn("Last received event data older than 30s... resetting average calculation.");
231 ResetData();
232 }
233 fBiasLast = tm;
234
235 // -------- Store new event --------
236
237 fData[fCursor%fData.size()].assign(reinterpret_cast<float*>(fBiasData.getData()),
238 reinterpret_cast<float*>(fBiasData.getData())+1440);
239
240 if (++fCursor<fData.size())
241 return;
242
243 // -------- Calculate statistics --------
244
245 valarray<double> med(1440);
246
247 for (int ch=0; ch<1440; ch++)
248 {
249 vector<float> arr(fData.size());
250 for (size_t i=0; i<fData.size(); i++)
251 arr[i] = fData[i][ch];
252
253 sort(arr.begin(), arr.end());
254
255 med[ch] = arr[arr.size()/2];
256 }
257
258 /*
259 vector<float> med(1440);
260 vector<float> rms(1440);
261 for (size_t i=0; i<fData.size(); i++)
262 {
263 if (fData[i].size()==0)
264 return;
265
266 for (int j=0; j<1440; j++)
267 {
268 med[j] += fData[i][j];
269 rms[j] += fData[i][j]*fData[i][j];
270 }
271 }
272 */
273
274 vector<double> avg(BIAS::kNumChannels);
275 vector<int> num(BIAS::kNumChannels);
276 for (int i=0; i<1440; i++)
277 {
278 const PixelMapEntry &ch = fMap.hw(i);
279
280 // FIXME: Add a consistency check if the median makes sense...
281 // FIXME: Add a consistency check to remove pixels with bright stars (median?)
282
283 avg[ch.hv()] += med[i];
284 num[ch.hv()]++;
285 }
286
287 for (int i=0; i<BIAS::kNumChannels; i++)
288 {
289 if (num[i])
290 avg[i] /= num[i];
291
292 }
293
294 // -------- Calculate correction --------
295
296 // http://bestune.50megs.com/typeABC.htm
297
298 // CO: Controller output
299 // PV: Process variable
300 // SP: Set point
301 // T: Sampling period (loop update period)
302 // e = SP - PV
303 //
304 // Kp : No units
305 // Ki : per seconds
306 // Kd : seconds
307
308 // 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) ]
309
310 if (fCursor%fData.size()>0)
311 return;
312
313 // FIXME: Take out broken / dead boards.
314
315 const Time tm0 = Time();
316
317 /*const*/ double T21 = fT>0 ? fT : (tm0-fStartTime).total_microseconds()/1000000.;
318 const double T10 = fT21;
319 fT21 = T21;
320
321 fStartTime = tm0;
322
323 ostringstream out;
324 out << "New " << fData.size() << " event received: " << fCursor << " / " << setprecision(3) << T21 << "s";
325 Info(out);
326
327 if (fPV[0].size()==0)
328 {
329 fPV[0].resize(avg.size());
330 fPV[0] = valarray<double>(avg.data(), avg.size());
331 return;
332 }
333
334 if (fPV[1].size()==0)
335 {
336 fPV[1].resize(avg.size());
337 fPV[1] = valarray<double>(avg.data(), avg.size());
338 return;
339 }
340
341 if (fPV[2].size()==0)
342 {
343 fPV[2].resize(avg.size());
344 fPV[2] = valarray<double>(avg.data(), avg.size());
345 return;
346 }
347
348 fPV[0] = fPV[1];
349 fPV[1] = fPV[2];
350
351 fPV[2].resize(avg.size());
352 fPV[2] = valarray<double>(avg.data(), avg.size());
353
354 if (T10<=0 || T21<=0)
355 return;
356
357 //cout << "Calculating (" << fCursor << ":" << T21 << ")... " << endl;
358
359 // fKi[j] = response[j]*gain;
360 // Kp = 0;
361 // Kd = 0;
362
363 // => Kp = 0.01 * gain = 0.00005
364 // => Ki = 0.8 * gain/20s = 0.00025
365 // => Kd = 0.1 * gain/20s = 0.00003
366
367 /*
368 fKp = 0;
369 fKd = 0;
370 fKi = 0.00003*20;
371 T21 = 1;
372 */
373
374 //valarray<double> correction = - Kp*(PV[2] - PV[1]) + Ki * dT * (SP-PV[2]) - Kd/dT * (PV[2] - 2*PV[1] + PV[0]);
375 //valarray<double> correction =
376 // - Kp * (PV[2] - PV[1])
377 // + dT * Ki * (SP - PV[2])
378 // - Kd / dT * (PV[2] - 2*PV[1] + PV[0]);
379 //
380 // - (Kp+Kd/dT1) * (PV[2] - PV[1])
381 // + dT2 * Ki * (SP - PV[2])
382 // + Kd / dT1 * (PV[1] - PV[0]);
383 //
384 // - Kp * (PV[2] - PV[1])
385 // + Ki * (SP - PV[2])*dT
386 // - Kd * (PV[2] - PV[1])/dT
387 // + Kd * (PV[1] - PV[0])/dT;
388 //
389 //valarray<double> correction =
390 // - Kp*(PV[2] - PV[1]) + Ki * T21 * (SP-PV[2]) - Kd*(PV[2]-PV[1])/T21 - Kd*(PV[0]-PV[1])/T01;
391 const valarray<double> correction = 1./fGain/1000*
392 (
393 - (fKp+fKd/T21)*(fPV[2] - fPV[1])
394 + fKi*T21*(fSP-fPV[2])
395 + fKd/T10*(fPV[1]-fPV[0])
396 );
397
398 /*
399 integral = 0
400 start:
401 integral += (fSP - fPV[2])*dt
402
403 output = Kp*(fSP - fPV[2]) + Ki*integral - Kd*(fPV[2] - fPV[1])/dt
404
405 wait(dt)
406
407 goto start
408 */
409
410 vector<float> vec(2*BIAS::kNumChannels);
411 for (int i=0; i<BIAS::kNumChannels; i++)
412 vec[i] = fPV[2][i]-fSP[i];
413
414 for (int i=0; i<BIAS::kNumChannels; i++)
415 vec[i+416] = avg[i]<5*2.5 ? 0 : correction[i];
416
417 fDimDeviation.setQuality(fControlType);
418 fDimDeviation.Update(vec);
419
420 if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn)
421 return;
422
423 Info("Sending new relative offset to biasctrl.");
424
425 DimClient::sendCommandNB((char*)"BIAS_CONTROL/ADD_REFERENCE_VOLTAGES",
426 (void*)(vec.data()+416), 416*sizeof(float));
427 }
428
429 void HandleGlobalFeedback()
430 {
431 if (fBiasData.getSize()!=1440*sizeof(float))
432 return;
433
434 // -------- Store new event --------
435
436 vector<float> arr(reinterpret_cast<float*>(fBiasData.getData()),
437 reinterpret_cast<float*>(fBiasData.getData())+1440);
438
439 sort(arr.begin(), arr.end());
440
441 const float med = arr[arr.size()/2];
442
443 fData[fCursor%fData.size()].resize(1); //assign(&med, &med);
444 fData[fCursor%fData.size()][0] = med; //assign(&med, &med);
445
446 if (++fCursor<fData.size())
447 return;
448
449 // -------- Calculate statistics --------
450
451 double avg=0;
452 double rms=0;
453 for (size_t i=0; i<fData.size(); i++)
454 {
455 avg += fData[i][0];
456 rms += fData[i][0]*fData[i][0];
457 }
458
459 avg /= fData.size();
460 rms /= fData.size();
461
462 rms = sqrt(rms-avg*avg);
463
464 // -------- Calculate correction --------
465
466 if (fCursor%fData.size()!=0)
467 return;
468
469 Out() << "Amplitude: " << avg << " +- " << rms << endl;
470
471 // FIXME: Take out broken / dead boards.
472
473 /*
474 ostringstream out;
475 out << "New " << fData.size() << " event received: " << fCursor << " / " << setprecision(3) << T21 << "s";
476 Info(out);
477 */
478
479 if (fPV[0].size()==0)
480 {
481 fPV[0].resize(1);
482 fPV[0] = valarray<double>(&avg, 1);
483 return;
484 }
485
486 if (fPV[1].size()==0)
487 {
488 fPV[1].resize(1);
489 fPV[1] = valarray<double>(&avg, 1);
490 return;
491 }
492
493 if (fPV[2].size()==0)
494 {
495 fPV[2].resize(1);
496 fPV[2] = valarray<double>(&avg, 1);
497 return;
498 }
499
500 fPV[0] = fPV[1];
501 fPV[1] = fPV[2];
502
503 fPV[2].resize(1);
504 fPV[2] = valarray<double>(&avg, 1);
505
506 const double T21 = 1; // feedback is 1s
507 const double T10 = 1; // feedback is 20s
508
509 // => Kp = 0.01 * gain = 0.00005
510 // => Ki = 0.8 * gain/20s = 0.00025
511 // => Kd = 0.1 * gain/20s = 0.00003
512
513 /*
514 fKp = 0;
515 fKd = 0;
516 fKi = 0.00003*20;
517 */
518
519 // correction = (fSP[0]-fPV[2])*fKi
520 const valarray<double> correction = 1./fGain/1000*
521 (
522 - (fKp+fKd/T21)*(fPV[2] - fPV[1])
523 + fKi*T21*(fSP[0]-fPV[2])
524 + fKd/T10*(fPV[1]-fPV[0])
525 );
526
527 Out() << "Correction: " << correction[0] << "V (" << fSP[0] << ")" << endl;
528
529 const int nch = BIAS::kNumChannels;
530
531 // FIXME: Sanity check!
532
533 vector<float> vec;
534 vec.reserve(2*nch);
535 vec.insert(vec.begin(), nch, fPV[2][0]-fSP[0]);
536 vec.insert(vec.begin()+nch, nch, correction[0]);
537
538 fDimDeviation.setQuality(fControlType);
539 fDimDeviation.Update(vec);
540
541 if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn)
542 return;
543
544 Info("Sending new global relative offset to biasctrl.");
545
546 DimClient::sendCommandNB((char*)"BIAS_CONTROL/ADD_REFERENCE_VOLTAGES",
547 (void*)(vec.data()+nch), nch*sizeof(float));
548 }
549
550 void infoHandler()
551 {
552 DimInfo *curr = getInfo(); // get current DimInfo address
553 if (!curr)
554 return;
555
556 if (curr==&fBias)
557 {
558 fStatusBias = GetNewState(fBias);
559 return;
560 }
561
562 if (curr==&fFAD)
563 {
564 fStatusFAD = GetNewState(fFAD);
565 return;
566 }
567
568 if (curr==&fFSC)
569 {
570 fStatusFSC = GetNewState(fFSC);
571 return;
572 }
573
574 if (curr==&fDim)
575 {
576 fStatusDim = GetNewState(fDim);
577 fStatusDim.second = curr->getSize()==4 ? curr->getInt() : 0;
578 return;
579 }
580
581 if (curr==&fCameraTemp)
582 HandleCameraTemp();
583
584 if (curr==&fBiasA && fControlType==kTemp && GetCurrentState()==kStateCalibrating)
585 HandleCalibration();
586
587 if (curr==&fBiasData && fControlType==kFeedback)
588 HandleFeedback();
589
590 if (curr==&fBiasData && fControlType==kFeedbackGlobal)
591 HandleGlobalFeedback();
592 }
593
594 bool CheckEventSize(size_t has, const char *name, size_t size)
595 {
596 if (has==size)
597 return true;
598
599 ostringstream msg;
600 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
601 Fatal(msg);
602 return false;
603 }
604
605 void PrintState(const pair<Time,int> &state, const char *server)
606 {
607 const State rc = fNetwork.GetState(server, state.second);
608
609 Out() << state.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
610 Out() << kBold << server << ": ";
611 Out() << rc.name << "[" << rc.index << "]";
612 Out() << kReset << " - " << kBlue << rc.comment << endl;
613 }
614
615 int Print()
616 {
617 Out() << fStatusDim.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
618 Out() << kBold << "DIM_DNS: ";
619 if (fStatusDim.second==0)
620 Out() << "Offline" << endl;
621 else
622 Out() << "V" << fStatusDim.second/100 << 'r' << fStatusDim.second%100 << endl;
623
624 PrintState(fStatusFAD, "FAD_CONTROL");
625 PrintState(fStatusFSC, "FSC_CONTROL");
626 PrintState(fStatusBias, "BIAS_CONTROL");
627
628 return GetCurrentState();
629 }
630
631 int PrintCalibration()
632 {
633 if (fCalibration.size()==0)
634 {
635 Out() << "No calibration performed so far." << endl;
636 return GetCurrentState();
637 }
638
639 for (int k=0; k<13; k++)
640 for (int j=0; j<8; j++)
641 {
642 Out() << setw(2) << k << "|" << setw(2) << j*4 << "|";
643 for (int i=0; i<4; i++)
644 Out() << Tools::Form(" %6.1f+-%4.1f", fCalibration[k*32+j*4+i], fCalibration[k*32+j*4+i+416]);
645 Out() << endl;
646 }
647
648 return GetCurrentState();
649 }
650
651 int SetConstant(const EventImp &evt, int constant)
652 {
653 if (!CheckEventSize(evt.GetSize(), "SetConstant", 8))
654 return kSM_FatalError;
655
656 switch (constant)
657 {
658 case 0: fKi = evt.GetDouble(); break;
659 case 1: fKp = evt.GetDouble(); break;
660 case 2: fKd = evt.GetDouble(); break;
661 case 3: fT = evt.GetDouble(); break;
662 case 4: fGain = evt.GetDouble(); break;
663 default:
664 Fatal("SetConstant got an unexpected constant id -- this is a program bug!");
665 return kSM_FatalError;
666 }
667
668 return GetCurrentState();
669 }
670
671 int EnableOutput(const EventImp &evt)
672 {
673 if (!CheckEventSize(evt.GetSize(), "EnableOutput", 1))
674 return kSM_FatalError;
675
676 fOutputEnabled = evt.GetBool();
677
678 return GetCurrentState();
679 }
680
681 void ResetData()
682 {
683 fData.clear();
684 fData.resize(500);
685 fCursor = 0;
686 fStartTime = Time();
687
688 fSP = valarray<double>(0., 416);
689
690 vector<float> vec(2*BIAS::kNumChannels);
691 fDimDeviation.setQuality(kIdle);
692 fDimDeviation.Update(vec);
693
694 fPV[0].resize(0);
695 fPV[1].resize(0);
696 fPV[2].resize(0);
697
698 if (fKp==0 && fKi==0 && fKd==0)
699 Warn("Control loop parameters are all set to zero.");
700 }
701
702 int StartFeedback()
703 {
704 ResetData();
705
706 fControlType = kFeedback;
707
708 return GetCurrentState();
709 }
710
711 int StartFeedbackGlobal()
712 {
713 ResetData();
714 fData.resize(5);
715
716 fControlType = kFeedbackGlobal;
717
718 return GetCurrentState();
719 }
720
721 int StartTempCtrl(const EventImp &evt)
722 {
723 if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))
724 return kSM_FatalError;
725
726 fBiasOffset = evt.GetFloat();
727 fControlType = kTemp;
728
729 ostringstream out;
730 out << "Starting temperature feedback with an offset of " << fBiasOffset << "V";
731 Message(out);
732
733 return GetCurrentState();
734 }
735
736 int StopFeedback()
737 {
738 fControlType = kIdle;
739
740 return GetCurrentState();
741 }
742
743 int StoreReference()
744 {
745 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())
746 {
747 Warn("No values in memory. Take enough events first!");
748 return GetCurrentState();
749 }
750
751 // FIXME: Check age
752
753 if (!fPV[1].size() && !fPV[2].size())
754 fSP = fPV[0];
755
756 if (!fPV[2].size())
757 fSP = fPV[1];
758 else
759 fSP = fPV[2];
760
761 vector<float> vec(BIAS::kNumChannels);
762 for (int i=0; i<BIAS::kNumChannels; i++)
763 vec[i] = fSP[i];
764 fDimReference.Update(vec);
765
766 return GetCurrentState();
767 }
768
769 int SetReference(const EventImp &evt)
770 {
771 if (!CheckEventSize(evt.GetSize(), "SetReference", 4))
772 return kSM_FatalError;
773
774 const float val = evt.GetFloat();
775 /*
776 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())
777 {
778 Warn("No values in memory. Take enough events first!");
779 return GetCurrentState();
780 }*/
781
782 vector<float> vec(BIAS::kNumChannels);
783 for (int i=0; i<BIAS::kNumChannels; i++)
784 vec[i] = fSP[i] = val;
785 fDimReference.Update(vec);
786
787 Out() << "New global reference value: " << val << "mV" << endl;
788
789 return GetCurrentState();
790 }
791
792 int CalibrateCurrents()
793 {
794// if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))
795// return kSM_FatalError;
796
797 ostringstream out;
798 out << "Starting temperature feedback with an offset of -2V";
799 Message(out);
800
801 fBiasOffset = -2;
802 fControlType = kTemp;
803 fCursor = 0;
804 fCurrentsAvg.assign(416, 0);
805 fCurrentsRms.assign(416, 0);
806 fCalibration.resize(0);
807 fStartTime = Time();
808 fOutputEnabled = true;
809
810 return kStateCalibrating;
811 }
812
813 int Execute()
814 {
815 // Dispatch (execute) at most one handler from the queue. In contrary
816 // to run_one(), it doesn't wait until a handler is available
817 // which can be dispatched, so poll_one() might return with 0
818 // handlers dispatched. The handlers are always dispatched/executed
819 // synchronously, i.e. within the call to poll_one()
820 //poll_one();
821
822 if (fStatusDim.second==0)
823 return kStateDimNetworkNA;
824
825 const bool bias = fStatusBias.second >= BIAS::kConnecting;
826 const bool fad = fStatusFAD.second >= FAD::kConnected;
827 const bool fsc = fStatusFSC.second >= 2;
828
829 // All subsystems are not connected
830 if (!bias && !fad && !fsc)
831 return kStateDisconnected;
832
833 // At least one subsystem apart from bias is connected
834 if (bias && !fad && !fsc)
835 return kStateConnecting;
836
837/*
838 // All subsystems are connected
839 if (GetCurrentStatus()==kStateConfiguringStep1)
840 {
841 if (fCursor<1)
842 return kStateConfiguringStep1;
843
844 if (fCursor==1)
845 {
846 fStartTime = Time();
847 return kStateConfiguringStep2;
848 }
849 }
850 if (GetCurrentStatus()==kStateConfiguringStep2)
851 {
852 if (fCursor==1)
853 {
854 if ((Time()-fStartTime).total_microseconds()/1000000.<1.5)
855 return kStateConfiguringStep2;
856
857 Dim::SendCommand("BIAS_CONTROL/REQUEST_STATUS");
858 }
859 if (fCursor==2)
860 {
861
862 int n=0;
863 double avg = 0;
864 for (size_t i=0; i<fCurrents.size(); i++)
865 if (fCurrents[i]>=0)
866 {
867 avg += fCurrents[i];
868 n++;
869 }
870
871 cout << avg/n << endl;
872 }
873 return kStateConnected;
874 }
875 */
876
877 // Needs connection of FAD and BIAS
878 if (bias && fad)
879 {
880 if (fControlType==kFeedback || fControlType==kFeedbackGlobal)
881 return fOutputEnabled ? kStateFeedbackCtrlRunning : kStateFeedbackCtrlIdle;
882 }
883
884 // Needs connection of FSC and BIAS
885 if (bias && fsc)
886 {
887 if (fControlType==kTemp)
888 {
889 if (GetCurrentState()==kStateCalibrating && fCursor<100)
890 return GetCurrentState();
891
892 return fOutputEnabled ? kStateTempCtrlRunning : kStateTempCtrlIdle;
893 }
894 }
895
896 if (bias && fad && !fsc)
897 return kStateConnectedFAD;
898
899 if (bias && fsc && !fad)
900 return kStateConnectedFSC;
901
902 return kStateConnected;
903 }
904
905public:
906 StateMachineFeedback(ostream &out=cout) : StateMachineDim(out, "FEEDBACK"),
907 fStatusDim(make_pair(Time(), -2)),
908 fStatusFAD(make_pair(Time(), -2)),
909 fStatusBias(make_pair(Time(), -2)),
910 fDim("DIS_DNS/VERSION_NUMBER", (void*)NULL, 0, this),
911 fFAD("FAD_CONTROL/STATE", (void*)NULL, 0, this),
912 fFSC("FSC_CONTROL/STATE", (void*)NULL, 0, this),
913 fBias("BIAS_CONTROL/STATE", (void*)NULL, 0, this),
914 fBiasA("BIAS_CONTROL/CURRENT", (void*)NULL, 0, this),
915 fBiasData("FAD_CONTROL/FEEDBACK_DATA", (void*)NULL, 0, this),
916 fCameraTemp("FSC_CONTROL/TEMPERATURE", (void*)NULL, 0, this),
917 fDimReference("FEEDBACK/REFERENCE", "F:416",
918 "Amplitude reference value(s)"
919 "Vref[mV]:Amplitude reference"),
920 fDimDeviation("FEEDBACK/DEVIATION", "F:416;F:416",
921 "Control loop information"
922 "|DeltaAmpl[mV]:Amplitude offset measures"
923 "|DeltaBias[mV]:Correction value calculated"),
924 fDimCalibration("FEEDBACK/CALIBRATION", "F:416;F:416",
925 "Current offsets"
926 "|Avg[nA]:Average offset"
927 "|Rms[nA]:Rms of offset"),
928 fSP(416),
929 fKp(0), fKi(0), fKd(0), fT(-1), fOutputEnabled(false)
930 {
931 // ba::io_service::work is a kind of keep_alive for the loop.
932 // It prevents the io_service to go to stopped state, which
933 // would prevent any consecutive calls to run()
934 // or poll() to do nothing. reset() could also revoke to the
935 // previous state but this might introduce some overhead of
936 // deletion and creation of threads and more.
937
938// fSP.resize(416);
939
940 // State names
941 AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable",
942 "The Dim DNS is not reachable.");
943
944 AddStateName(kStateDisconnected, "Disconnected",
945 "The Dim DNS is reachable, but the required subsystems are not available.");
946
947 AddStateName(kStateConnecting, "Connecting",
948 "Only biasctrl is available and connected with its hardware.");
949
950 AddStateName(kStateConnectedFSC, "ConnectedFSC",
951 "biasctrl and fscctrl are available and connected with their hardware.");
952 AddStateName(kStateConnectedFAD, "ConnectedFAD",
953 "biasctrl and fadctrl are available and connected with their hardware.");
954 AddStateName(kStateConnected, "Connected",
955 "biasctrl, fadctrl and fscctrl are available and connected with their hardware.");
956
957 AddStateName(kStateFeedbackCtrlIdle, "FeedbackIdle",
958 "Feedback control activated, but voltage output disabled.");
959
960 AddStateName(kStateTempCtrlIdle, "FeedbackIdle",
961 "Temperature control activated, but voltage output disabled.");
962
963 AddStateName(kStateFeedbackCtrlRunning, "FeedbackControl",
964 "Feedback control activated and voltage output enabled.");
965
966 AddStateName(kStateTempCtrlRunning, "TempControl",
967 "Temperature control activated and voltage output enabled.");
968
969 AddStateName(kStateCalibrating, "Calibrating",
970 "Calibrating current offsets.");
971
972 AddEvent("START_FEEDBACK_CONTROL", kStateConnectedFAD, kStateConnected)
973 (bind(&StateMachineFeedback::StartFeedback, this))
974 ("Start the feedback control loop");
975
976 AddEvent("START_GLOBAL_FEEDBACK", kStateConnectedFAD, kStateConnected)
977 (bind(&StateMachineFeedback::StartFeedbackGlobal, this))
978 ("Start the global feedback control loop");
979
980 AddEvent("START_TEMP_CONTROL", "F:1", kStateConnectedFSC, kStateConnected)
981 (bind(&StateMachineFeedback::StartTempCtrl, this, placeholders::_1))
982 ("Start the temperature control loop"
983 "|offset[V]:Offset from the nominal temperature corrected value in Volts");
984
985 // kStateTempCtrlIdle, kStateFeedbackCtrlIdle, kStateTempCtrlRunning, kStateFeedbackCtrlRunning
986 AddEvent("STOP")
987 (bind(&StateMachineFeedback::StopFeedback, this))
988 ("Stop any control loop");
989
990 AddEvent("ENABLE_OUTPUT", "B:1")//, kStateIdle)
991 (bind(&StateMachineFeedback::EnableOutput, this, placeholders::_1))
992 ("Enable sending of correction values caluclated by the control loop to the biasctrl");
993
994 AddEvent("STORE_REFERENCE")//, kStateIdle)
995 (bind(&StateMachineFeedback::StoreReference, this))
996 ("Store the last (averaged) value as new reference (for debug purpose only)");
997
998 AddEvent("SET_REFERENCE", "F:1")//, kStateIdle)
999 (bind(&StateMachineFeedback::SetReference, this, placeholders::_1))
1000 ("Set a new global reference value (for debug purpose only)");
1001
1002 AddEvent("SET_Ki", "D:1")//, kStateIdle)
1003 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 0))
1004 ("Set integral constant Ki");
1005
1006 AddEvent("SET_Kp", "D:1")//, kStateIdle)
1007 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 1))
1008 ("Set proportional constant Kp");
1009
1010 AddEvent("SET_Kd", "D:1")//, kStateIdle)
1011 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 2))
1012 ("Set derivative constant Kd");
1013
1014 AddEvent("SET_T", "D:1")//, kStateIdle)
1015 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 3))
1016 ("Set time-constant. (-1 to use the cycle time, i.e. the time for the last average cycle, instead)");
1017
1018 AddEvent("CALIBRATE_CURRENTS", kStateConnectedFSC, kStateConnected)//, kStateIdle)
1019 (bind(&StateMachineFeedback::CalibrateCurrents, this))
1020 ("");
1021
1022 // Verbosity commands
1023// AddEvent("SET_VERBOSE", "B:1")
1024// (bind(&StateMachineMCP::SetVerbosity, this, placeholders::_1))
1025// ("set verbosity state"
1026// "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
1027
1028 AddEvent("PRINT")
1029 (bind(&StateMachineFeedback::Print, this))
1030 ("");
1031
1032 AddEvent("PRINT_CALIBRATION")
1033 (bind(&StateMachineFeedback::PrintCalibration, this))
1034 ("");
1035 }
1036
1037 int EvalOptions(Configuration &conf)
1038 {
1039 if (!fMap.Read(conf.Get<string>("pixel-map-file")))
1040 {
1041 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
1042 return 1;
1043 }
1044
1045 fGain = 0.1; // V(Amplitude) / V(Bias)
1046
1047 // 148 -> 248
1048
1049 // 33 : 10s < 2%
1050 // 50 : 5s < 2%
1051 // 66 : 3s < 2%
1052 // 85 : 2s < 2%
1053
1054 fKp = 0;
1055 fKd = 0;
1056 fKi = 0.66;
1057 fT = 1;
1058
1059 // Is that independent of the aboslute real amplitude of
1060 // the light pulser?
1061
1062 ostringstream msg;
1063 msg << "Control loop parameters: ";
1064 msg << "Kp=" << fKp << ", Kd=" << fKd << ", Ki=" << fKi << ", ";
1065 if (fT>0)
1066 msg << fT;
1067 else
1068 msg << "<auto>";
1069 msg << ", Gain(DRS/BIAS)=" << fGain << "V/V";
1070
1071 Message(msg);
1072
1073 return -1;
1074 }
1075};
1076
1077// ------------------------------------------------------------------------
1078
1079#include "Main.h"
1080
1081template<class T>
1082int RunShell(Configuration &conf)
1083{
1084 return Main::execute<T, StateMachineFeedback>(conf);
1085}
1086
1087void SetupConfiguration(Configuration &conf)
1088{
1089 po::options_description control("Feedback options");
1090 control.add_options()
1091 ("pixel-map-file", var<string>("FACTmapV5a.txt"), "Pixel mapping file. Used here to get the default reference voltage.")
1092 ;
1093
1094 conf.AddOptions(control);
1095}
1096
1097/*
1098 Extract usage clause(s) [if any] for SYNOPSIS.
1099 Translators: "Usage" and "or" here are patterns (regular expressions) which
1100 are used to match the usage synopsis in program output. An example from cp
1101 (GNU coreutils) which contains both strings:
1102 Usage: cp [OPTION]... [-T] SOURCE DEST
1103 or: cp [OPTION]... SOURCE... DIRECTORY
1104 or: cp [OPTION]... -t DIRECTORY SOURCE...
1105 */
1106void PrintUsage()
1107{
1108 cout <<
1109 "The feedback control the BIAS voltages based on the calibration signal.\n"
1110 "\n"
1111 "The default is that the program is started without user intercation. "
1112 "All actions are supposed to arrive as DimCommands. Using the -c "
1113 "option, a local shell can be initialized. With h or help a short "
1114 "help message about the usuage can be brought to the screen.\n"
1115 "\n"
1116 "Usage: feedback [-c type] [OPTIONS]\n"
1117 " or: feedback [OPTIONS]\n";
1118 cout << endl;
1119}
1120
1121void PrintHelp()
1122{
1123 Main::PrintHelp<StateMachineFeedback>();
1124
1125 /* Additional help text which is printed after the configuration
1126 options goes here */
1127
1128 /*
1129 cout << "bla bla bla" << endl << endl;
1130 cout << endl;
1131 cout << "Environment:" << endl;
1132 cout << "environment" << endl;
1133 cout << endl;
1134 cout << "Examples:" << endl;
1135 cout << "test exam" << endl;
1136 cout << endl;
1137 cout << "Files:" << endl;
1138 cout << "files" << endl;
1139 cout << endl;
1140 */
1141}
1142
1143int main(int argc, const char* argv[])
1144{
1145 Configuration conf(argv[0]);
1146 conf.SetPrintUsage(PrintUsage);
1147 Main::SetupConfiguration(conf);
1148 SetupConfiguration(conf);
1149
1150 if (!conf.DoParse(argc, argv, PrintHelp))
1151 return -1;
1152
1153 //try
1154 {
1155 // No console access at all
1156 if (!conf.Has("console"))
1157 {
1158// if (conf.Get<bool>("no-dim"))
1159// return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
1160// else
1161 return RunShell<LocalStream>(conf);
1162 }
1163 // Cosole access w/ and w/o Dim
1164/* if (conf.Get<bool>("no-dim"))
1165 {
1166 if (conf.Get<int>("console")==0)
1167 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
1168 else
1169 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
1170 }
1171 else
1172*/ {
1173 if (conf.Get<int>("console")==0)
1174 return RunShell<LocalShell>(conf);
1175 else
1176 return RunShell<LocalConsole>(conf);
1177 }
1178 }
1179 /*catch (std::exception& e)
1180 {
1181 cerr << "Exception: " << e.what() << endl;
1182 return -1;
1183 }*/
1184
1185 return 0;
1186}
Note: See TracBrowser for help on using the repository browser.