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

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