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

Last change on this file since 13137 was 13137, checked in by tbretz, 13 years ago
Ensure that the calibration starts now that the biasctrl doesn't sent a voltage set request (which would return the currents) anymore when the voltage is already set.
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 if (fStatusBias.second==BIAS::kVoltageOn)
176 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
177 return;
178 }
179
180 // Convert from DAC counts to uA
181 const double conv = 5000e-6/4096;
182 for (int i=0; i<BIAS::kNumChannels; i++)
183 {
184 // 3900 Ohm/n + 1000 Ohm + 1150 Ohm
185 const double R = fMap.hv(i).group()==0 ? 3125 : 2930;
186 const double Im = double(fCurrentsAvg[i])/fCursorCur;
187 const double I = Im>fCalibration[i] ? Im-fCalibration[i] : 0;
188 vec[i+416] += R * I*conv;
189 if (fCalibration[i]>0)
190 {
191 avg += R * I*conv;
192 num++;
193 }
194 }
195 avg /= num;
196
197 fCurrentsAvg.assign(416, 0);
198 fCursorCur = 0;
199 }
200
201 fDimDeviation.setQuality(fControlType);
202 fDimDeviation.Update(vec);
203
204 if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn)
205 return;
206
207 // Trigger calibration
208 if (GetCurrentState()==kStateCalibrating && fCursorTemp==1)
209 {
210 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
211 return;
212 }
213
214 ostringstream msg;
215 msg << setprecision(4) << "Sending new absolute offset (" << diff << "V+" << avg << "V) to biasctrl.";
216 Info(msg);
217
218 DimClient::sendCommandNB((char*)"BIAS_CONTROL/SET_GAPD_REFERENCE_OFFSET",
219 (void*)&diff, sizeof(float));
220
221 fCursorTemp++;
222 }
223
224 int AverageCurrents()
225 {
226 if (fBiasA.getSize()!=416*sizeof(int16_t))
227 return -1;
228
229 if (fStatusBias.second!=BIAS::kVoltageOn)
230 return false;
231
232 if (fCursorCur++<0)
233 return true;
234
235 const int16_t *ptr = static_cast<int16_t*>(fBiasA.getData());
236
237 for (int i=0; i<416; i++)
238 {
239 fCurrentsAvg[i] += ptr[i];
240 fCurrentsRms[i] += ptr[i]*ptr[i];
241 }
242
243 return true;
244 }
245
246
247 void HandleCalibration()
248 {
249 const int rc = AverageCurrents();
250 if (rc<0)
251 return;
252
253 if (fCursorCur<100)
254 {
255 if (fStatusBias.second==BIAS::kVoltageOn)
256 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
257 return;
258 }
259
260 if (rc==0)
261 return;
262
263 fCalibration.resize(416*2);
264 for (int i=0; i<416; i++)
265 {
266 fCalibration[i] = double(fCurrentsAvg[i])/fCursorCur;
267 fCalibration[i+416] = sqrt(double(fCurrentsRms[i])/fCursorCur-fCalibration[i]*fCalibration[i]);
268 }
269
270 fDimCalibration.Update(fCalibration);
271
272 fOutputEnabled = false;
273 fControlType = kIdle;
274
275 if (fStatusBias.second==BIAS::kVoltageOn)
276 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
277 }
278
279 void HandleFeedback()
280 {
281 if (fBiasData.getSize()!=1440*sizeof(float))
282 return;
283
284 // -------- Check age of last stored event --------
285
286 // Must be called in this order
287 const int tsec = fBiasData.getTimestamp();
288 const int tms = fBiasData.getTimestampMillisecs();
289
290 const Time tm(tsec, tms*1000);
291
292 if (Time()-fBiasLast>boost::posix_time::seconds(30))
293 {
294 Warn("Last received event data older than 30s... resetting average calculation.");
295 ResetData();
296 }
297 fBiasLast = tm;
298
299 // -------- Store new event --------
300
301 fData[fCursorAmpl%fData.size()].assign(reinterpret_cast<float*>(fBiasData.getData()),
302 reinterpret_cast<float*>(fBiasData.getData())+1440);
303
304 if (++fCursorAmpl<fData.size())
305 return;
306
307 // -------- Calculate statistics --------
308
309 valarray<double> med(1440);
310
311 for (int ch=0; ch<1440; ch++)
312 {
313 vector<float> arr(fData.size());
314 for (size_t i=0; i<fData.size(); i++)
315 arr[i] = fData[i][ch];
316
317 sort(arr.begin(), arr.end());
318
319 med[ch] = arr[arr.size()/2];
320 }
321
322 /*
323 vector<float> med(1440);
324 vector<float> rms(1440);
325 for (size_t i=0; i<fData.size(); i++)
326 {
327 if (fData[i].size()==0)
328 return;
329
330 for (int j=0; j<1440; j++)
331 {
332 med[j] += fData[i][j];
333 rms[j] += fData[i][j]*fData[i][j];
334 }
335 }
336 */
337
338 vector<double> avg(BIAS::kNumChannels);
339 vector<int> num(BIAS::kNumChannels);
340 for (int i=0; i<1440; i++)
341 {
342 const PixelMapEntry &ch = fMap.hw(i);
343
344 // FIXME: Add a consistency check if the median makes sense...
345 // FIXME: Add a consistency check to remove pixels with bright stars (median?)
346
347 avg[ch.hv()] += med[i];
348 num[ch.hv()]++;
349 }
350
351 for (int i=0; i<BIAS::kNumChannels; i++)
352 {
353 if (num[i])
354 avg[i] /= num[i];
355
356 }
357
358 // -------- Calculate correction --------
359
360 // http://bestune.50megs.com/typeABC.htm
361
362 // CO: Controller output
363 // PV: Process variable
364 // SP: Set point
365 // T: Sampling period (loop update period)
366 // e = SP - PV
367 //
368 // Kp : No units
369 // Ki : per seconds
370 // Kd : seconds
371
372 // 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) ]
373
374 if (fCursorAmpl%fData.size()>0)
375 return;
376
377 // FIXME: Take out broken / dead boards.
378
379 const Time tm0 = Time();
380
381 /*const*/ double T21 = fT>0 ? fT : (tm0-fStartTime).total_microseconds()/1000000.;
382 const double T10 = fT21;
383 fT21 = T21;
384
385 fStartTime = tm0;
386
387 ostringstream out;
388 out << "New " << fData.size() << " event received: " << fCursorAmpl << " / " << setprecision(3) << T21 << "s";
389 Info(out);
390
391 if (fPV[0].size()==0)
392 {
393 fPV[0].resize(avg.size());
394 fPV[0] = valarray<double>(avg.data(), avg.size());
395 return;
396 }
397
398 if (fPV[1].size()==0)
399 {
400 fPV[1].resize(avg.size());
401 fPV[1] = valarray<double>(avg.data(), avg.size());
402 return;
403 }
404
405 if (fPV[2].size()==0)
406 {
407 fPV[2].resize(avg.size());
408 fPV[2] = valarray<double>(avg.data(), avg.size());
409 return;
410 }
411
412 fPV[0] = fPV[1];
413 fPV[1] = fPV[2];
414
415 fPV[2].resize(avg.size());
416 fPV[2] = valarray<double>(avg.data(), avg.size());
417
418 if (T10<=0 || T21<=0)
419 return;
420
421 //cout << "Calculating (" << fCursor << ":" << T21 << ")... " << endl;
422
423 // fKi[j] = response[j]*gain;
424 // Kp = 0;
425 // Kd = 0;
426
427 // => Kp = 0.01 * gain = 0.00005
428 // => Ki = 0.8 * gain/20s = 0.00025
429 // => Kd = 0.1 * gain/20s = 0.00003
430
431 /*
432 fKp = 0;
433 fKd = 0;
434 fKi = 0.00003*20;
435 T21 = 1;
436 */
437
438 //valarray<double> correction = - Kp*(PV[2] - PV[1]) + Ki * dT * (SP-PV[2]) - Kd/dT * (PV[2] - 2*PV[1] + PV[0]);
439 //valarray<double> correction =
440 // - Kp * (PV[2] - PV[1])
441 // + dT * Ki * (SP - PV[2])
442 // - Kd / dT * (PV[2] - 2*PV[1] + PV[0]);
443 //
444 // - (Kp+Kd/dT1) * (PV[2] - PV[1])
445 // + dT2 * Ki * (SP - PV[2])
446 // + Kd / dT1 * (PV[1] - PV[0]);
447 //
448 // - Kp * (PV[2] - PV[1])
449 // + Ki * (SP - PV[2])*dT
450 // - Kd * (PV[2] - PV[1])/dT
451 // + Kd * (PV[1] - PV[0])/dT;
452 //
453 //valarray<double> correction =
454 // - Kp*(PV[2] - PV[1]) + Ki * T21 * (SP-PV[2]) - Kd*(PV[2]-PV[1])/T21 - Kd*(PV[0]-PV[1])/T01;
455 const valarray<double> correction = 1./fGain/1000*
456 (
457 - (fKp+fKd/T21)*(fPV[2] - fPV[1])
458 + fKi*T21*(fSP-fPV[2])
459 + fKd/T10*(fPV[1]-fPV[0])
460 );
461
462 /*
463 integral = 0
464 start:
465 integral += (fSP - fPV[2])*dt
466
467 output = Kp*(fSP - fPV[2]) + Ki*integral - Kd*(fPV[2] - fPV[1])/dt
468
469 wait(dt)
470
471 goto start
472 */
473
474 vector<float> vec(2*BIAS::kNumChannels);
475 for (int i=0; i<BIAS::kNumChannels; i++)
476 vec[i] = fPV[2][i]-fSP[i];
477
478 for (int i=0; i<BIAS::kNumChannels; i++)
479 vec[i+416] = avg[i]<5*2.5 ? 0 : correction[i];
480
481 fDimDeviation.setQuality(fControlType);
482 fDimDeviation.Update(vec);
483
484 if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn)
485 return;
486
487 Info("Sending new relative offset to biasctrl.");
488
489 DimClient::sendCommandNB((char*)"BIAS_CONTROL/ADD_REFERENCE_VOLTAGES",
490 (void*)(vec.data()+416), 416*sizeof(float));
491 }
492
493 void HandleGlobalFeedback()
494 {
495 if (fBiasData.getSize()!=1440*sizeof(float))
496 return;
497
498 // -------- Store new event --------
499
500 vector<float> arr(reinterpret_cast<float*>(fBiasData.getData()),
501 reinterpret_cast<float*>(fBiasData.getData())+1440);
502
503 sort(arr.begin(), arr.end());
504
505 const float med = arr[arr.size()/2];
506
507 fData[fCursorAmpl%fData.size()].resize(1); //assign(&med, &med);
508 fData[fCursorAmpl%fData.size()][0] = med; //assign(&med, &med);
509
510 if (++fCursorAmpl<fData.size())
511 return;
512
513 // -------- Calculate statistics --------
514
515 double avg=0;
516 double rms=0;
517 for (size_t i=0; i<fData.size(); i++)
518 {
519 avg += fData[i][0];
520 rms += fData[i][0]*fData[i][0];
521 }
522
523 avg /= fData.size();
524 rms /= fData.size();
525
526 rms = sqrt(rms-avg*avg);
527
528 // -------- Calculate correction --------
529
530 if (fCursorAmpl%fData.size()!=0)
531 return;
532
533 Out() << "Amplitude: " << avg << " +- " << rms << endl;
534
535 // FIXME: Take out broken / dead boards.
536
537 /*
538 ostringstream out;
539 out << "New " << fData.size() << " event received: " << fCursor << " / " << setprecision(3) << T21 << "s";
540 Info(out);
541 */
542
543 if (fPV[0].size()==0)
544 {
545 fPV[0].resize(1);
546 fPV[0] = valarray<double>(&avg, 1);
547 return;
548 }
549
550 if (fPV[1].size()==0)
551 {
552 fPV[1].resize(1);
553 fPV[1] = valarray<double>(&avg, 1);
554 return;
555 }
556
557 if (fPV[2].size()==0)
558 {
559 fPV[2].resize(1);
560 fPV[2] = valarray<double>(&avg, 1);
561 return;
562 }
563
564 fPV[0] = fPV[1];
565 fPV[1] = fPV[2];
566
567 fPV[2].resize(1);
568 fPV[2] = valarray<double>(&avg, 1);
569
570 // ----- Calculate average currents -----
571
572 vector<float> A(416);
573 for (int i=0; i<416; i++)
574 A[i] = double(fCurrentsAvg[i]) / fCursorCur;
575
576 fCurrentsAvg.assign(416, 0);
577 fCursorCur = 0;
578
579 // -------- Calculate correction --------
580
581 // correction = (fSP[0]-fPV[2])*fKi
582 /*
583 const double T21 = 1; // feedback is 1s
584 const double T10 = 1; // feedback is 20s
585
586 const valarray<double> correction = 1./fGain/1000*
587 (
588 - (fKp+fKd/T21)*(fPV[2] - fPV[1])
589 + fKi*T21*(fSP[0]-fPV[2])
590 + fKd/T10*(fPV[1]-fPV[0])
591 );
592 */
593
594 // pow of 1.6 comes from the non-linearity of the
595 // amplitude vs bias voltage
596 const valarray<double> correction = 1./fGain/1000*
597 (
598 //fKi*(pow(fSP[0], 1./1.6)-pow(fPV[2], 1./1.6))
599 fKi*(fSP[0]-fPV[2])
600 );
601
602 Out() << "Correction: " << correction[0] << "V (" << fSP[0] << ")" << endl;
603
604 const int nch = BIAS::kNumChannels;
605
606 // FIXME: Sanity check!
607
608 vector<float> vec;
609 vec.reserve(2*nch);
610 vec.insert(vec.begin(), nch, fPV[2][0]-fSP[0]);
611 vec.insert(vec.begin()+nch, nch, correction[0]);
612
613 fDimDeviation.setQuality(fControlType);
614 fDimDeviation.Update(vec);
615
616 if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn)
617 return;
618
619 Info("Sending new global relative offset to biasctrl.");
620
621 DimClient::sendCommandNB((char*)"BIAS_CONTROL/ADD_REFERENCE_VOLTAGES",
622 (void*)(vec.data()+nch), nch*sizeof(float));
623 }
624
625 void infoHandler()
626 {
627 DimInfo *curr = getInfo(); // get current DimInfo address
628 if (!curr)
629 return;
630
631 if (curr==&fBias)
632 {
633 fStatusBias = GetNewState(fBias);
634 return;
635 }
636
637 if (curr==&fFAD)
638 {
639 fStatusFAD = GetNewState(fFAD);
640 return;
641 }
642
643 if (curr==&fFSC)
644 {
645 fStatusFSC = GetNewState(fFSC);
646 return;
647 }
648
649 if (curr==&fDim)
650 {
651 fStatusDim = GetNewState(fDim);
652 fStatusDim.second = curr->getSize()==4 ? curr->getInt() : 0;
653 return;
654 }
655
656 if (curr==&fCameraTemp && (fControlType==kTemp || fControlType==kCurrents))
657 HandleCameraTemp();
658
659 if (curr==&fBiasA && fControlType==kTemp && GetCurrentState()==kStateCalibrating)
660 HandleCalibration();
661
662 if (curr==&fBiasA && (fControlType==kFeedbackGlobal || fControlType==kCurrents))
663 AverageCurrents();
664
665 if (curr==&fBiasData && fControlType==kFeedback)
666 HandleFeedback();
667
668 if (curr==&fBiasData && fControlType==kFeedbackGlobal)
669 HandleGlobalFeedback();
670 }
671
672 bool CheckEventSize(size_t has, const char *name, size_t size)
673 {
674 if (has==size)
675 return true;
676
677 ostringstream msg;
678 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
679 Fatal(msg);
680 return false;
681 }
682
683 void PrintState(const pair<Time,int> &state, const char *server)
684 {
685 const State rc = fNetwork.GetState(server, state.second);
686
687 Out() << state.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
688 Out() << kBold << server << ": ";
689 Out() << rc.name << "[" << rc.index << "]";
690 Out() << kReset << " - " << kBlue << rc.comment << endl;
691 }
692
693 int Print()
694 {
695 Out() << fStatusDim.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
696 Out() << kBold << "DIM_DNS: ";
697 if (fStatusDim.second==0)
698 Out() << "Offline" << endl;
699 else
700 Out() << "V" << fStatusDim.second/100 << 'r' << fStatusDim.second%100 << endl;
701
702 PrintState(fStatusFAD, "FAD_CONTROL");
703 PrintState(fStatusFSC, "FSC_CONTROL");
704 PrintState(fStatusBias, "BIAS_CONTROL");
705
706 return GetCurrentState();
707 }
708
709 int PrintCalibration()
710 {
711 if (fCalibration.size()==0)
712 {
713 Out() << "No calibration performed so far." << endl;
714 return GetCurrentState();
715 }
716
717 for (int k=0; k<13; k++)
718 for (int j=0; j<8; j++)
719 {
720 Out() << setw(2) << k << "|" << setw(2) << j*4 << "|";
721 for (int i=0; i<4; i++)
722 Out() << Tools::Form(" %6.1f+-%4.1f", fCalibration[k*32+j*4+i], fCalibration[k*32+j*4+i+416]);
723 Out() << endl;
724 }
725
726 return GetCurrentState();
727 }
728
729 int SetConstant(const EventImp &evt, int constant)
730 {
731 if (!CheckEventSize(evt.GetSize(), "SetConstant", 8))
732 return kSM_FatalError;
733
734 switch (constant)
735 {
736 case 0: fKi = evt.GetDouble(); break;
737 case 1: fKp = evt.GetDouble(); break;
738 case 2: fKd = evt.GetDouble(); break;
739 case 3: fT = evt.GetDouble(); break;
740 case 4: fGain = evt.GetDouble(); break;
741 default:
742 Fatal("SetConstant got an unexpected constant id -- this is a program bug!");
743 return kSM_FatalError;
744 }
745
746 return GetCurrentState();
747 }
748
749 int EnableOutput(const EventImp &evt)
750 {
751 if (!CheckEventSize(evt.GetSize(), "EnableOutput", 1))
752 return kSM_FatalError;
753
754 fOutputEnabled = evt.GetBool();
755
756 return GetCurrentState();
757 }
758
759 void ResetData(int16_t n=-1)
760 {
761 fData.assign(n>0 ? n : fData.size(), vector<float>(0));
762
763 fCursorAmpl = 0;
764 fCursorCur = 0;
765 fCursorTemp = 0;
766
767 fStartTime = Time();
768
769 fSP = valarray<double>(0., 416);
770
771 vector<float> vec(2*BIAS::kNumChannels);
772 fDimDeviation.setQuality(kIdle);
773 fDimDeviation.Update(vec);
774
775 fPV[0].resize(0);
776 fPV[1].resize(0);
777 fPV[2].resize(0);
778
779 fCurrentsAvg.assign(416, 0);
780 fCurrentsRms.assign(416, 0);
781
782 if (fKp==0 && fKi==0 && fKd==0)
783 Warn("Control loop parameters are all set to zero.");
784 }
785
786 int StartFeedback(const EventImp &evt)
787 {
788 if (!CheckEventSize(evt.GetSize(), "StartFeedback", 2))
789 return kSM_FatalError;
790
791 ResetData(evt.GetShort());
792
793 fControlType = kFeedback;
794
795 return GetCurrentState();
796 }
797
798 int StartFeedbackGlobal(const EventImp &evt)
799 {
800 if (!CheckEventSize(evt.GetSize(), "StartFeedbackGlobal", 2))
801 return kSM_FatalError;
802
803 ResetData(evt.GetShort());
804
805 fControlType = kFeedbackGlobal;
806
807 return GetCurrentState();
808 }
809
810 int StartTempCtrl(const EventImp &evt)
811 {
812 if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))
813 return kSM_FatalError;
814
815 fBiasOffset = evt.GetFloat();
816 fControlType = kTemp;
817
818 ostringstream out;
819 out << "Starting temperature feedback with an offset of " << fBiasOffset << "V";
820 Message(out);
821
822 if (fStatusBias.second==BIAS::kVoltageOn)
823 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);
824
825 return GetCurrentState();
826 }
827
828 int StartCurrentCtrl(const EventImp &evt)
829 {
830 if (!CheckEventSize(evt.GetSize(), "StartCurrentCtrl", 4))
831 return kSM_FatalError;
832
833 if (fCalibration.size()==0)
834 {
835 Warn("Current control needs a bias crate calibration first... command ignored.");
836 return GetCurrentState();
837 }
838
839 ResetData(0);
840
841 fBiasOffset = evt.GetFloat();
842 fControlType = kCurrents;
843
844 ostringstream out;
845 out << "Starting current/temp feedback with an offset of " << fBiasOffset << "V";
846 Message(out);
847
848 return GetCurrentState();
849 }
850
851 int StopFeedback()
852 {
853 fControlType = kIdle;
854
855 return GetCurrentState();
856 }
857
858 int StoreReference()
859 {
860 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())
861 {
862 Warn("No values in memory. Take enough events first!");
863 return GetCurrentState();
864 }
865
866 // FIXME: Check age
867
868 if (!fPV[1].size() && !fPV[2].size())
869 fSP = fPV[0];
870
871 if (!fPV[2].size())
872 fSP = fPV[1];
873 else
874 fSP = fPV[2];
875
876 vector<float> vec(BIAS::kNumChannels);
877 for (int i=0; i<BIAS::kNumChannels; i++)
878 vec[i] = fSP[i];
879 fDimReference.Update(vec);
880
881 return GetCurrentState();
882 }
883
884 int SetReference(const EventImp &evt)
885 {
886 if (!CheckEventSize(evt.GetSize(), "SetReference", 4))
887 return kSM_FatalError;
888
889 const float val = evt.GetFloat();
890 /*
891 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())
892 {
893 Warn("No values in memory. Take enough events first!");
894 return GetCurrentState();
895 }*/
896
897 vector<float> vec(BIAS::kNumChannels);
898 for (int i=0; i<BIAS::kNumChannels; i++)
899 vec[i] = fSP[i] = val;
900 fDimReference.Update(vec);
901
902 Out() << "New global reference value: " << val << "mV" << endl;
903
904 return GetCurrentState();
905 }
906
907 int CalibrateCurrents()
908 {
909// if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))
910// return kSM_FatalError;
911
912 if (fStatusBias.second==BIAS::kRamping)
913 {
914 Warn("Calibration cannot be started when biasctrl is in state Ramping.");
915 return GetCurrentState();
916 }
917
918 ostringstream out;
919 out << "Starting temperature feedback for calibration with an offset of -2V";
920 Message(out);
921
922 fBiasOffset = -2;
923 fControlType = kTemp;
924 fCursorCur = -10;
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 /*
1031 static Time past;
1032 if (fCurrentRequestInterval>0 && Time()-past>boost::posix_time::milliseconds(fCurrentRequestInterval))
1033 {
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.