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

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