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

Last change on this file since 13036 was 12994, checked in by tbretz, 13 years ago
Added the number of events to interpolate to the feedback commands; implemented an exponent of 1./1.6 for the global feedback
File size: 34.6 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 // => Kp = 0.01 * gain = 0.00005
507 // => Ki = 0.8 * gain/20s = 0.00025
508 // => Kd = 0.1 * gain/20s = 0.00003
509
510 /*
511 fKp = 0;
512 fKd = 0;
513 fKi = 0.00003*20;
514 */
515
516 // correction = (fSP[0]-fPV[2])*fKi
517 /*
518 const double T21 = 1; // feedback is 1s
519 const double T10 = 1; // feedback is 20s
520
521 const valarray<double> correction = 1./fGain/1000*
522 (
523 - (fKp+fKd/T21)*(fPV[2] - fPV[1])
524 + fKi*T21*(fSP[0]-fPV[2])
525 + fKd/T10*(fPV[1]-fPV[0])
526 );
527 */
528
529 // pow of 1.6 comes from the non-linearity of the
530 // amplitude vs bias voltage
531 const valarray<double> correction = 1./fGain/1000*
532 (
533 fKi*(pow(fSP[0], 1./1.6)-pow(fPV[2], 1./1.6))
534 );
535
536 Out() << "Correction: " << correction[0] << "V (" << fSP[0] << ")" << endl;
537
538 const int nch = BIAS::kNumChannels;
539
540 // FIXME: Sanity check!
541
542 vector<float> vec;
543 vec.reserve(2*nch);
544 vec.insert(vec.begin(), nch, fPV[2][0]-fSP[0]);
545 vec.insert(vec.begin()+nch, nch, correction[0]);
546
547 fDimDeviation.setQuality(fControlType);
548 fDimDeviation.Update(vec);
549
550 if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn)
551 return;
552
553 Info("Sending new global relative offset to biasctrl.");
554
555 DimClient::sendCommandNB((char*)"BIAS_CONTROL/ADD_REFERENCE_VOLTAGES",
556 (void*)(vec.data()+nch), nch*sizeof(float));
557 }
558
559 void infoHandler()
560 {
561 DimInfo *curr = getInfo(); // get current DimInfo address
562 if (!curr)
563 return;
564
565 if (curr==&fBias)
566 {
567 fStatusBias = GetNewState(fBias);
568 return;
569 }
570
571 if (curr==&fFAD)
572 {
573 fStatusFAD = GetNewState(fFAD);
574 return;
575 }
576
577 if (curr==&fFSC)
578 {
579 fStatusFSC = GetNewState(fFSC);
580 return;
581 }
582
583 if (curr==&fDim)
584 {
585 fStatusDim = GetNewState(fDim);
586 fStatusDim.second = curr->getSize()==4 ? curr->getInt() : 0;
587 return;
588 }
589
590 if (curr==&fCameraTemp)
591 HandleCameraTemp();
592
593 if (curr==&fBiasA && fControlType==kTemp && GetCurrentState()==kStateCalibrating)
594 HandleCalibration();
595
596 if (curr==&fBiasData && fControlType==kFeedback)
597 HandleFeedback();
598
599 if (curr==&fBiasData && fControlType==kFeedbackGlobal)
600 HandleGlobalFeedback();
601 }
602
603 bool CheckEventSize(size_t has, const char *name, size_t size)
604 {
605 if (has==size)
606 return true;
607
608 ostringstream msg;
609 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
610 Fatal(msg);
611 return false;
612 }
613
614 void PrintState(const pair<Time,int> &state, const char *server)
615 {
616 const State rc = fNetwork.GetState(server, state.second);
617
618 Out() << state.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
619 Out() << kBold << server << ": ";
620 Out() << rc.name << "[" << rc.index << "]";
621 Out() << kReset << " - " << kBlue << rc.comment << endl;
622 }
623
624 int Print()
625 {
626 Out() << fStatusDim.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
627 Out() << kBold << "DIM_DNS: ";
628 if (fStatusDim.second==0)
629 Out() << "Offline" << endl;
630 else
631 Out() << "V" << fStatusDim.second/100 << 'r' << fStatusDim.second%100 << endl;
632
633 PrintState(fStatusFAD, "FAD_CONTROL");
634 PrintState(fStatusFSC, "FSC_CONTROL");
635 PrintState(fStatusBias, "BIAS_CONTROL");
636
637 return GetCurrentState();
638 }
639
640 int PrintCalibration()
641 {
642 if (fCalibration.size()==0)
643 {
644 Out() << "No calibration performed so far." << endl;
645 return GetCurrentState();
646 }
647
648 for (int k=0; k<13; k++)
649 for (int j=0; j<8; j++)
650 {
651 Out() << setw(2) << k << "|" << setw(2) << j*4 << "|";
652 for (int i=0; i<4; i++)
653 Out() << Tools::Form(" %6.1f+-%4.1f", fCalibration[k*32+j*4+i], fCalibration[k*32+j*4+i+416]);
654 Out() << endl;
655 }
656
657 return GetCurrentState();
658 }
659
660 int SetConstant(const EventImp &evt, int constant)
661 {
662 if (!CheckEventSize(evt.GetSize(), "SetConstant", 8))
663 return kSM_FatalError;
664
665 switch (constant)
666 {
667 case 0: fKi = evt.GetDouble(); break;
668 case 1: fKp = evt.GetDouble(); break;
669 case 2: fKd = evt.GetDouble(); break;
670 case 3: fT = evt.GetDouble(); break;
671 case 4: fGain = evt.GetDouble(); break;
672 default:
673 Fatal("SetConstant got an unexpected constant id -- this is a program bug!");
674 return kSM_FatalError;
675 }
676
677 return GetCurrentState();
678 }
679
680 int EnableOutput(const EventImp &evt)
681 {
682 if (!CheckEventSize(evt.GetSize(), "EnableOutput", 1))
683 return kSM_FatalError;
684
685 fOutputEnabled = evt.GetBool();
686
687 return GetCurrentState();
688 }
689
690 void ResetData(int16_t n=-1)
691 {
692 fData.assign(n>0 ? n : fData.size(), vector<float>(0));
693
694 fCursor = 0;
695 fStartTime = Time();
696
697 fSP = valarray<double>(0., 416);
698
699 vector<float> vec(2*BIAS::kNumChannels);
700 fDimDeviation.setQuality(kIdle);
701 fDimDeviation.Update(vec);
702
703 fPV[0].resize(0);
704 fPV[1].resize(0);
705 fPV[2].resize(0);
706
707 if (fKp==0 && fKi==0 && fKd==0)
708 Warn("Control loop parameters are all set to zero.");
709 }
710
711 int StartFeedback(const EventImp &evt)
712 {
713 if (!CheckEventSize(evt.GetSize(), "StartFeedback", 2))
714 return kSM_FatalError;
715
716 ResetData(evt.GetShort());
717
718 fControlType = kFeedback;
719
720 return GetCurrentState();
721 }
722
723 int StartFeedbackGlobal(const EventImp &evt)
724 {
725 if (!CheckEventSize(evt.GetSize(), "StartFeedbackGlobal", 2))
726 return kSM_FatalError;
727
728 ResetData(evt.GetShort());
729
730 fControlType = kFeedbackGlobal;
731
732 return GetCurrentState();
733 }
734
735 int StartTempCtrl(const EventImp &evt)
736 {
737 if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))
738 return kSM_FatalError;
739
740 fBiasOffset = evt.GetFloat();
741 fControlType = kTemp;
742
743 ostringstream out;
744 out << "Starting temperature feedback with an offset of " << fBiasOffset << "V";
745 Message(out);
746
747 return GetCurrentState();
748 }
749
750 int StopFeedback()
751 {
752 fControlType = kIdle;
753
754 return GetCurrentState();
755 }
756
757 int StoreReference()
758 {
759 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())
760 {
761 Warn("No values in memory. Take enough events first!");
762 return GetCurrentState();
763 }
764
765 // FIXME: Check age
766
767 if (!fPV[1].size() && !fPV[2].size())
768 fSP = fPV[0];
769
770 if (!fPV[2].size())
771 fSP = fPV[1];
772 else
773 fSP = fPV[2];
774
775 vector<float> vec(BIAS::kNumChannels);
776 for (int i=0; i<BIAS::kNumChannels; i++)
777 vec[i] = fSP[i];
778 fDimReference.Update(vec);
779
780 return GetCurrentState();
781 }
782
783 int SetReference(const EventImp &evt)
784 {
785 if (!CheckEventSize(evt.GetSize(), "SetReference", 4))
786 return kSM_FatalError;
787
788 const float val = evt.GetFloat();
789 /*
790 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())
791 {
792 Warn("No values in memory. Take enough events first!");
793 return GetCurrentState();
794 }*/
795
796 vector<float> vec(BIAS::kNumChannels);
797 for (int i=0; i<BIAS::kNumChannels; i++)
798 vec[i] = fSP[i] = val;
799 fDimReference.Update(vec);
800
801 Out() << "New global reference value: " << val << "mV" << endl;
802
803 return GetCurrentState();
804 }
805
806 int CalibrateCurrents()
807 {
808// if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))
809// return kSM_FatalError;
810
811 ostringstream out;
812 out << "Starting temperature feedback with an offset of -2V";
813 Message(out);
814
815 fBiasOffset = -2;
816 fControlType = kTemp;
817 fCursor = 0;
818 fCurrentsAvg.assign(416, 0);
819 fCurrentsRms.assign(416, 0);
820 fCalibration.resize(0);
821 fStartTime = Time();
822 fOutputEnabled = true;
823
824 return kStateCalibrating;
825 }
826
827 int Execute()
828 {
829 // Dispatch (execute) at most one handler from the queue. In contrary
830 // to run_one(), it doesn't wait until a handler is available
831 // which can be dispatched, so poll_one() might return with 0
832 // handlers dispatched. The handlers are always dispatched/executed
833 // synchronously, i.e. within the call to poll_one()
834 //poll_one();
835
836 if (fStatusDim.second==0)
837 return kStateDimNetworkNA;
838
839 const bool bias = fStatusBias.second >= BIAS::kConnecting;
840 const bool fad = fStatusFAD.second >= FAD::kConnected;
841 const bool fsc = fStatusFSC.second >= 2;
842
843 // All subsystems are not connected
844 if (!bias && !fad && !fsc)
845 return kStateDisconnected;
846
847 // At least one subsystem apart from bias is connected
848 if (bias && !fad && !fsc)
849 return kStateConnecting;
850
851/*
852 // All subsystems are connected
853 if (GetCurrentStatus()==kStateConfiguringStep1)
854 {
855 if (fCursor<1)
856 return kStateConfiguringStep1;
857
858 if (fCursor==1)
859 {
860 fStartTime = Time();
861 return kStateConfiguringStep2;
862 }
863 }
864 if (GetCurrentStatus()==kStateConfiguringStep2)
865 {
866 if (fCursor==1)
867 {
868 if ((Time()-fStartTime).total_microseconds()/1000000.<1.5)
869 return kStateConfiguringStep2;
870
871 Dim::SendCommand("BIAS_CONTROL/REQUEST_STATUS");
872 }
873 if (fCursor==2)
874 {
875
876 int n=0;
877 double avg = 0;
878 for (size_t i=0; i<fCurrents.size(); i++)
879 if (fCurrents[i]>=0)
880 {
881 avg += fCurrents[i];
882 n++;
883 }
884
885 cout << avg/n << endl;
886 }
887 return kStateConnected;
888 }
889 */
890
891 // Needs connection of FAD and BIAS
892 if (bias && fad)
893 {
894 if (fControlType==kFeedback || fControlType==kFeedbackGlobal)
895 return fOutputEnabled ? kStateFeedbackCtrlRunning : kStateFeedbackCtrlIdle;
896 }
897
898 // Needs connection of FSC and BIAS
899 if (bias && fsc)
900 {
901 if (fControlType==kTemp)
902 {
903 if (GetCurrentState()==kStateCalibrating && fCursor<100)
904 return GetCurrentState();
905
906 return fOutputEnabled ? kStateTempCtrlRunning : kStateTempCtrlIdle;
907 }
908 }
909
910 if (bias && fad && !fsc)
911 return kStateConnectedFAD;
912
913 if (bias && fsc && !fad)
914 return kStateConnectedFSC;
915
916 return kStateConnected;
917 }
918
919public:
920 StateMachineFeedback(ostream &out=cout) : StateMachineDim(out, "FEEDBACK"),
921 fStatusDim(make_pair(Time(), -2)),
922 fStatusFAD(make_pair(Time(), -2)),
923 fStatusBias(make_pair(Time(), -2)),
924 fDim("DIS_DNS/VERSION_NUMBER", (void*)NULL, 0, this),
925 fFAD("FAD_CONTROL/STATE", (void*)NULL, 0, this),
926 fFSC("FSC_CONTROL/STATE", (void*)NULL, 0, this),
927 fBias("BIAS_CONTROL/STATE", (void*)NULL, 0, this),
928 fBiasA("BIAS_CONTROL/CURRENT", (void*)NULL, 0, this),
929 fBiasData("FAD_CONTROL/FEEDBACK_DATA", (void*)NULL, 0, this),
930 fCameraTemp("FSC_CONTROL/TEMPERATURE", (void*)NULL, 0, this),
931 fDimReference("FEEDBACK/REFERENCE", "F:416",
932 "Amplitude reference value(s)"
933 "Vref[mV]:Amplitude reference"),
934 fDimDeviation("FEEDBACK/DEVIATION", "F:416;F:416",
935 "Control loop information"
936 "|DeltaAmpl[mV]:Amplitude offset measures"
937 "|DeltaBias[mV]:Correction value calculated"),
938 fDimCalibration("FEEDBACK/CALIBRATION", "F:416;F:416",
939 "Current offsets"
940 "|Avg[nA]:Average offset"
941 "|Rms[nA]:Rms of offset"),
942 fSP(416),
943 fKp(0), fKi(0), fKd(0), fT(-1), fOutputEnabled(false)
944 {
945 // ba::io_service::work is a kind of keep_alive for the loop.
946 // It prevents the io_service to go to stopped state, which
947 // would prevent any consecutive calls to run()
948 // or poll() to do nothing. reset() could also revoke to the
949 // previous state but this might introduce some overhead of
950 // deletion and creation of threads and more.
951
952// fSP.resize(416);
953
954 // State names
955 AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable",
956 "The Dim DNS is not reachable.");
957
958 AddStateName(kStateDisconnected, "Disconnected",
959 "The Dim DNS is reachable, but the required subsystems are not available.");
960
961 AddStateName(kStateConnecting, "Connecting",
962 "Only biasctrl is available and connected with its hardware.");
963
964 AddStateName(kStateConnectedFSC, "ConnectedFSC",
965 "biasctrl and fscctrl are available and connected with their hardware.");
966 AddStateName(kStateConnectedFAD, "ConnectedFAD",
967 "biasctrl and fadctrl are available and connected with their hardware.");
968 AddStateName(kStateConnected, "Connected",
969 "biasctrl, fadctrl and fscctrl are available and connected with their hardware.");
970
971 AddStateName(kStateFeedbackCtrlIdle, "FeedbackIdle",
972 "Feedback control activated, but voltage output disabled.");
973
974 AddStateName(kStateTempCtrlIdle, "FeedbackIdle",
975 "Temperature control activated, but voltage output disabled.");
976
977 AddStateName(kStateFeedbackCtrlRunning, "FeedbackControl",
978 "Feedback control activated and voltage output enabled.");
979
980 AddStateName(kStateTempCtrlRunning, "TempControl",
981 "Temperature control activated and voltage output enabled.");
982
983 AddStateName(kStateCalibrating, "Calibrating",
984 "Calibrating current offsets.");
985
986 AddEvent("START_FEEDBACK_CONTROL", "S:1", kStateConnectedFAD, kStateConnected)
987 (bind(&StateMachineFeedback::StartFeedback, this, placeholders::_1))
988 ("Start the feedback control loop"
989 "|Num[short]:Number of events 'medianed' to calculate the correction value");
990
991 AddEvent("START_GLOBAL_FEEDBACK", "S:1", kStateConnectedFAD, kStateConnected)
992 (bind(&StateMachineFeedback::StartFeedbackGlobal, this, placeholders::_1))
993 ("Start the global feedback control loop"
994 "Num[short]:Number of events averaged to calculate the correction value");
995
996 AddEvent("START_TEMP_CONTROL", "F:1", kStateConnectedFSC, kStateConnected)
997 (bind(&StateMachineFeedback::StartTempCtrl, this, placeholders::_1))
998 ("Start the temperature control loop"
999 "|offset[V]:Offset from the nominal temperature corrected value in Volts");
1000
1001 // kStateTempCtrlIdle, kStateFeedbackCtrlIdle, kStateTempCtrlRunning, kStateFeedbackCtrlRunning
1002 AddEvent("STOP")
1003 (bind(&StateMachineFeedback::StopFeedback, this))
1004 ("Stop any control loop");
1005
1006 AddEvent("ENABLE_OUTPUT", "B:1")//, kStateIdle)
1007 (bind(&StateMachineFeedback::EnableOutput, this, placeholders::_1))
1008 ("Enable sending of correction values caluclated by the control loop to the biasctrl");
1009
1010 AddEvent("STORE_REFERENCE")//, kStateIdle)
1011 (bind(&StateMachineFeedback::StoreReference, this))
1012 ("Store the last (averaged) value as new reference (for debug purpose only)");
1013
1014 AddEvent("SET_REFERENCE", "F:1")//, kStateIdle)
1015 (bind(&StateMachineFeedback::SetReference, this, placeholders::_1))
1016 ("Set a new global reference value (for debug purpose only)");
1017
1018 AddEvent("SET_Ki", "D:1")//, kStateIdle)
1019 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 0))
1020 ("Set integral constant Ki");
1021
1022 AddEvent("SET_Kp", "D:1")//, kStateIdle)
1023 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 1))
1024 ("Set proportional constant Kp");
1025
1026 AddEvent("SET_Kd", "D:1")//, kStateIdle)
1027 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 2))
1028 ("Set derivative constant Kd");
1029
1030 AddEvent("SET_T", "D:1")//, kStateIdle)
1031 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 3))
1032 ("Set time-constant. (-1 to use the cycle time, i.e. the time for the last average cycle, instead)");
1033
1034 AddEvent("CALIBRATE_CURRENTS", kStateConnectedFSC, kStateConnected)//, kStateIdle)
1035 (bind(&StateMachineFeedback::CalibrateCurrents, this))
1036 ("");
1037
1038 // Verbosity commands
1039// AddEvent("SET_VERBOSE", "B:1")
1040// (bind(&StateMachineMCP::SetVerbosity, this, placeholders::_1))
1041// ("set verbosity state"
1042// "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
1043
1044 AddEvent("PRINT")
1045 (bind(&StateMachineFeedback::Print, this))
1046 ("");
1047
1048 AddEvent("PRINT_CALIBRATION")
1049 (bind(&StateMachineFeedback::PrintCalibration, this))
1050 ("");
1051 }
1052
1053 int EvalOptions(Configuration &conf)
1054 {
1055 if (!fMap.Read(conf.Get<string>("pixel-map-file")))
1056 {
1057 Error("Reading mapping table from "+conf.Get<string>("pixel-map-file")+" failed.");
1058 return 1;
1059 }
1060
1061 fGain = 0.1; // V(Amplitude) / V(Bias)
1062
1063 // 148 -> 248
1064
1065 // 33 : 10s < 2%
1066 // 50 : 5s < 2%
1067 // 66 : 3s < 2%
1068 // 85 : 2s < 2%
1069
1070 fKp = 0;
1071 fKd = 0;
1072 fKi = 0.75;
1073 fT = 1;
1074
1075 // Is that independent of the aboslute real amplitude of
1076 // the light pulser?
1077
1078 ostringstream msg;
1079 msg << "Control loop parameters: ";
1080 msg << "Kp=" << fKp << ", Kd=" << fKd << ", Ki=" << fKi << ", ";
1081 if (fT>0)
1082 msg << fT;
1083 else
1084 msg << "<auto>";
1085 msg << ", Gain(DRS/BIAS)=" << fGain << "V/V";
1086
1087 Message(msg);
1088
1089 return -1;
1090 }
1091};
1092
1093// ------------------------------------------------------------------------
1094
1095#include "Main.h"
1096
1097template<class T>
1098int RunShell(Configuration &conf)
1099{
1100 return Main::execute<T, StateMachineFeedback>(conf);
1101}
1102
1103void SetupConfiguration(Configuration &conf)
1104{
1105 po::options_description control("Feedback options");
1106 control.add_options()
1107 ("pixel-map-file", var<string>("FACTmapV5a.txt"), "Pixel mapping file. Used here to get the default reference voltage.")
1108 ;
1109
1110 conf.AddOptions(control);
1111}
1112
1113/*
1114 Extract usage clause(s) [if any] for SYNOPSIS.
1115 Translators: "Usage" and "or" here are patterns (regular expressions) which
1116 are used to match the usage synopsis in program output. An example from cp
1117 (GNU coreutils) which contains both strings:
1118 Usage: cp [OPTION]... [-T] SOURCE DEST
1119 or: cp [OPTION]... SOURCE... DIRECTORY
1120 or: cp [OPTION]... -t DIRECTORY SOURCE...
1121 */
1122void PrintUsage()
1123{
1124 cout <<
1125 "The feedback control the BIAS voltages based on the calibration signal.\n"
1126 "\n"
1127 "The default is that the program is started without user intercation. "
1128 "All actions are supposed to arrive as DimCommands. Using the -c "
1129 "option, a local shell can be initialized. With h or help a short "
1130 "help message about the usuage can be brought to the screen.\n"
1131 "\n"
1132 "Usage: feedback [-c type] [OPTIONS]\n"
1133 " or: feedback [OPTIONS]\n";
1134 cout << endl;
1135}
1136
1137void PrintHelp()
1138{
1139 Main::PrintHelp<StateMachineFeedback>();
1140
1141 /* Additional help text which is printed after the configuration
1142 options goes here */
1143
1144 /*
1145 cout << "bla bla bla" << endl << endl;
1146 cout << endl;
1147 cout << "Environment:" << endl;
1148 cout << "environment" << endl;
1149 cout << endl;
1150 cout << "Examples:" << endl;
1151 cout << "test exam" << endl;
1152 cout << endl;
1153 cout << "Files:" << endl;
1154 cout << "files" << endl;
1155 cout << endl;
1156 */
1157}
1158
1159int main(int argc, const char* argv[])
1160{
1161 Configuration conf(argv[0]);
1162 conf.SetPrintUsage(PrintUsage);
1163 Main::SetupConfiguration(conf);
1164 SetupConfiguration(conf);
1165
1166 if (!conf.DoParse(argc, argv, PrintHelp))
1167 return -1;
1168
1169 //try
1170 {
1171 // No console access at all
1172 if (!conf.Has("console"))
1173 {
1174// if (conf.Get<bool>("no-dim"))
1175// return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
1176// else
1177 return RunShell<LocalStream>(conf);
1178 }
1179 // Cosole access w/ and w/o Dim
1180/* if (conf.Get<bool>("no-dim"))
1181 {
1182 if (conf.Get<int>("console")==0)
1183 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
1184 else
1185 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
1186 }
1187 else
1188*/ {
1189 if (conf.Get<int>("console")==0)
1190 return RunShell<LocalShell>(conf);
1191 else
1192 return RunShell<LocalConsole>(conf);
1193 }
1194 }
1195 /*catch (std::exception& e)
1196 {
1197 cerr << "Exception: " << e.what() << endl;
1198 return -1;
1199 }*/
1200
1201 return 0;
1202}
Note: See TracBrowser for help on using the repository browser.