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

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