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

Last change on this file since 13071 was 13071, checked in by tbretz, 13 years ago
Set the bias create resistor to 1150 Ohm which (according to the measurements of last night) seem to be a good compromise to include also cables, etc; added some more informative output to the Info
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 += vec[i+416];
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.