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

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