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

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