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

Last change on this file since 13142 was 13138, checked in by tbretz, 13 years ago
Implemented request of current sttaus during current feedback once in a second to ensure a good average.
File size: 39.8 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+416] = 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+416] += 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(416, 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((char*)"BIAS_CONTROL/SET_GAPD_REFERENCE_OFFSET",
218 (void*)&diff, sizeof(float));
219
220 fCursorTemp++;
221 }
222
223 int AverageCurrents()
224 {
225 if (fBiasA.getSize()!=416*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<416; 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(416*2);
263 for (int i=0; i<416; i++)
264 {
265 fCalibration[i] = double(fCurrentsAvg[i])/fCursorCur;
266 fCalibration[i+416] = 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+416] = 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((char*)"BIAS_CONTROL/ADD_REFERENCE_VOLTAGES",
489 (void*)(vec.data()+416), 416*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(416);
572 for (int i=0; i<416; i++)
573 A[i] = double(fCurrentsAvg[i]) / fCursorCur;
574
575 fCurrentsAvg.assign(416, 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((char*)"BIAS_CONTROL/ADD_REFERENCE_VOLTAGES",
621 (void*)(vec.data()+nch), nch*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+416]);
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., 416);
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(416, 0);
779 fCurrentsRms.assign(416, 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(416, 0);
927 fCurrentsRms.assign(416, 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::kRamping)
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(416),
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.