#include #include "Dim.h" #include "Event.h" #include "Shell.h" #include "StateMachineDim.h" #include "Connection.h" #include "Configuration.h" #include "Console.h" #include "Converter.h" #include "DimServiceInfoList.h" #include "PixelMap.h" #include "tools.h" #include "LocalControl.h" #include "HeadersFAD.h" #include "HeadersBIAS.h" namespace ba = boost::asio; namespace bs = boost::system; namespace dummy = ba::placeholders; using namespace std; // ------------------------------------------------------------------------ #include "DimDescriptionService.h" // ------------------------------------------------------------------------ class StateMachineFeedback : public StateMachineDim, public DimInfoHandler { /* int Wrap(boost::function f) { f(); return T::GetCurrentState(); } boost::function Wrapper(boost::function func) { return bind(&StateMachineMCP::Wrap, this, func); }*/ private: enum states_t { kStateDimNetworkNA = 1, kStateDisconnected, kStateConnecting, kStateConnectedFSC, kStateConnectedFAD, kStateConnected, kStateTempCtrlIdle, // 7 kStateFeedbackCtrlIdle, // 8 kStateCurrentCtrlIdle, // 9 kStateTempCtrlRunning, // 9->10 kStateFeedbackCtrlRunning, // 10->11 kStateCurrentCtrlRunning, // 12 kStateCalibrating, // 11->13 }; enum control_t { kIdle, kTemp, kFeedback, kFeedbackGlobal, kCurrents, }; control_t fControlType; PixelMap fMap; DimServiceInfoList fNetwork; pair fStatusDim; pair fStatusFAD; pair fStatusFSC; pair fStatusBias; DimStampedInfo fDim; DimStampedInfo fFAD; DimStampedInfo fFSC; DimStampedInfo fBias; DimStampedInfo fBiasA; DimStampedInfo fBiasData; DimStampedInfo fBiasNom; DimStampedInfo fCameraTemp; DimDescribedService fDimReference; DimDescribedService fDimDeviation; DimDescribedService fDimCalibration; vector fCurrentsAvg; vector fCurrentsRms; vector fCalibration; vector fVoltGapd; vector> fData; int64_t fCursorCur; uint64_t fCursorAmpl; uint64_t fCursorTemp; Time fBiasLast; Time fStartTime; valarray fPV[3]; // Process variable (intgerated/averaged amplitudes) valarray fSP; // Set point (target amplitudes) double fKp; // Proportional constant double fKi; // Integral constant double fKd; // Derivative constant double fT; // Time constant (cycle time) double fGain; // Gain (conversion from a DRS voltage deviation into a BIAS voltage change at G-APD reference voltage) double fT21; double fBiasOffset; double fCalibrationOffset; double fAppliedOffset; uint16_t fCurrentRequestInterval; uint16_t fNumCalibIgnore; uint16_t fNumCalibRequests; bool fOutputEnabled; pair GetNewState(DimStampedInfo &info) const { const bool disconnected = info.getSize()==0; // Make sure getTimestamp is called _before_ getTimestampMillisecs const int tsec = info.getTimestamp(); const int tms = info.getTimestampMillisecs(); return make_pair(Time(tsec, tms*1000), disconnected ? -2 : info.getQuality()); } void HandleCameraTemp() { if (fCameraTemp.getSize()!=60*sizeof(float)) return; const float *ptr = static_cast(fCameraTemp.getData()); double avgt = 0; int numt = 0; for (int i=1; i<32; i++) if (ptr[i]!=0) { avgt += ptr[i]; numt++; } if (numt==0) return; avgt /= numt; // [deg C] const double dUt = (avgt-25)*4./70; // [V] if (GetCurrentState()==kStateCalibrating && fBiasOffset>dUt-1.2) { ostringstream msg; msg << " (applied calibration offset " << fBiasOffset << "V exceeds temperature correction " << avgt << "V - 1.2V."; Warn("Trying to calibrate above G-APD breakdown volatge!"); Warn(msg); return; } // FIXME: If calibrating do not wait for the temperature! fAppliedOffset = fBiasOffset; if (GetCurrentState()!=kStateCalibrating) fAppliedOffset += dUt; vector vec(2*BIAS::kNumChannels); for (int i=0; i med[2]; med[0].resize(416); med[1].resize(416); if (fControlType==kCurrents) { if (fCursorCur==0) { //DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); return; } // Convert from DAC counts to uA const double conv = 5000./4096; // 3900 Ohm/n + 1000 Ohm + 1100 Ohm (with n=4 or n=5) const double R[2] = { 3075, 2870 }; const float *Iavg = fCalibration.data(); // Offset at U=fCalibrationOffset const float *Ravg = fCalibration.data()+BIAS::kNumChannels*2; // Measured resistance // U0 = fCalibrationOffset // dT = fAppliedVoltage // Ifeedback = Im[i] - (U[i]-U0)/Ravg[i] - Iavg[i]; // dUapplied[i] + dUneu[i] = R[g] * (Im[i] - (dUapplied[i]+dUneu[i]-U0+dT)/Ravg[i] - Iavg[i]) // The assumption here is that the offset calculated from the temperature // does not significanly change within a single step // dU[i] := dUtotal[i] = dUapplied[i] + dUneu[i] // dU[i] / R[g] = Im[i] - (dU[i]+dT-U0)/Ravg[i] - Iavg[i] // dU[i]/R[g] + dU[i]/Ravg[i] = Im[i] + U0/Ravg[i] - dT/Ravg[i] - Iavg[i] // dU[i]*(1/R[g]+1/Ravg[i]) = Im[i] - Iavg[i] + U0/Ravg[i] - dT/Ravg[i] // dU[i] = (Im[i] - Iavg[i] + U0/Ravg[i] - dT/Ravg[i]) / (1/R[g]+1/Ravg[i]) // dU[i] = { Im[i] - Iavg[i] + (U0-dT)/Ravg[i] } * r with r := 1 / (1/R[g]+1/Ravg[i]) const double U0 = fAppliedOffset-fCalibrationOffset; for (int i=0; iIavg[i]) and we operate above the calibration point) const double I = Im>Iavg[i] ? Im - Iavg[i] : 0; // [uA] // Make sure that the averaged resistor is valid const double dU = Ravg[i]>10000 ? r*(I*1e-6 - dI) : 0; vec[i+BIAS::kNumChannels] += dU; // Calculate statistics only for channels with a valid calibration if (Iavg[i]>0) { med[g][num[g]] = dU; avg[g] += dU; num[g]++; if (dUmax[g]) max[g] = dU; } } sort(med[0].begin(), med[0].begin()+num[0]); sort(med[1].begin(), med[1].begin()+num[1]); fCurrentsAvg.assign(BIAS::kNumChannels, 0); fCursorCur = 0; } fDimDeviation.setQuality(fControlType); fDimDeviation.Update(vec); if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn) return; // Trigger calibration if (GetCurrentState()==kStateCalibrating && fCursorTemp==1) { DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); return; } ostringstream msg; msg << setprecision(4) << "Sending new absolute offset (" << fAppliedOffset << "V+" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << "V) to biasctrl."; Info(msg); if (fControlType==kCurrents && num[0]>0 && num[1]>0) { msg.str(""); msg << " Avg0=" << setw(7) << avg[0]/num[0] << " | Avg1=" << setw(7) << avg[1]/num[1]; Debug(msg); msg.str(""); msg << " Med0=" << setw(7) << med[0][num[0]/2] << " | Med1=" << setw(7) << med[1][num[1]/2]; Debug(msg); msg.str(""); msg << " Min0=" << setw(7) << min[0] << " | Min1=" << setw(7) << min[1]; Debug(msg); msg.str(""); msg << " Max0=" << setw(7) << max[0] << " | Max1=" << setw(7) << max[1]; Debug(msg); } DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_OFFSET", vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float)); fCursorTemp++; } int AverageCurrents() { if (fBiasA.getSize()!=BIAS::kNumChannels*sizeof(int16_t)) return -1; if (fStatusBias.second!=BIAS::kVoltageOn) return false; if (fCursorCur++<0) return true; const int16_t *ptr = static_cast(fBiasA.getData()); for (int i=0; iboost::posix_time::seconds(30)) { Warn("Last received event data older than 30s... resetting average calculation."); ResetData(); } fBiasLast = tm; // -------- Store new event -------- fData[fCursorAmpl%fData.size()].assign(reinterpret_cast(fBiasData.getData()), reinterpret_cast(fBiasData.getData())+1440); if (++fCursorAmpl med(1440); for (int ch=0; ch<1440; ch++) { vector arr(fData.size()); for (size_t i=0; i med(1440); vector rms(1440); for (size_t i=0; i avg(BIAS::kNumChannels); vector num(BIAS::kNumChannels); for (int i=0; i<1440; i++) { const PixelMapEntry &ch = fMap.hw(i); // FIXME: Add a consistency check if the median makes sense... // FIXME: Add a consistency check to remove pixels with bright stars (median?) avg[ch.hv()] += med[i]; num[ch.hv()]++; } for (int i=0; i0) return; // FIXME: Take out broken / dead boards. const Time tm0 = Time(); /*const*/ double T21 = fT>0 ? fT : (tm0-fStartTime).total_microseconds()/1000000.; const double T10 = fT21; fT21 = T21; fStartTime = tm0; ostringstream out; out << "New " << fData.size() << " event received: " << fCursorAmpl << " / " << setprecision(3) << T21 << "s"; Info(out); if (fPV[0].size()==0) { fPV[0].resize(avg.size()); fPV[0] = valarray(avg.data(), avg.size()); return; } if (fPV[1].size()==0) { fPV[1].resize(avg.size()); fPV[1] = valarray(avg.data(), avg.size()); return; } if (fPV[2].size()==0) { fPV[2].resize(avg.size()); fPV[2] = valarray(avg.data(), avg.size()); return; } fPV[0] = fPV[1]; fPV[1] = fPV[2]; fPV[2].resize(avg.size()); fPV[2] = valarray(avg.data(), avg.size()); if (T10<=0 || T21<=0) return; //cout << "Calculating (" << fCursor << ":" << T21 << ")... " << endl; // fKi[j] = response[j]*gain; // Kp = 0; // Kd = 0; // => Kp = 0.01 * gain = 0.00005 // => Ki = 0.8 * gain/20s = 0.00025 // => Kd = 0.1 * gain/20s = 0.00003 /* fKp = 0; fKd = 0; fKi = 0.00003*20; T21 = 1; */ //valarray correction = - Kp*(PV[2] - PV[1]) + Ki * dT * (SP-PV[2]) - Kd/dT * (PV[2] - 2*PV[1] + PV[0]); //valarray correction = // - Kp * (PV[2] - PV[1]) // + dT * Ki * (SP - PV[2]) // - Kd / dT * (PV[2] - 2*PV[1] + PV[0]); // // - (Kp+Kd/dT1) * (PV[2] - PV[1]) // + dT2 * Ki * (SP - PV[2]) // + Kd / dT1 * (PV[1] - PV[0]); // // - Kp * (PV[2] - PV[1]) // + Ki * (SP - PV[2])*dT // - Kd * (PV[2] - PV[1])/dT // + Kd * (PV[1] - PV[0])/dT; // //valarray correction = // - Kp*(PV[2] - PV[1]) + Ki * T21 * (SP-PV[2]) - Kd*(PV[2]-PV[1])/T21 - Kd*(PV[0]-PV[1])/T01; const valarray correction = 1./fGain/1000* ( - (fKp+fKd/T21)*(fPV[2] - fPV[1]) + fKi*T21*(fSP-fPV[2]) + fKd/T10*(fPV[1]-fPV[0]) ); /* integral = 0 start: integral += (fSP - fPV[2])*dt output = Kp*(fSP - fPV[2]) + Ki*integral - Kd*(fPV[2] - fPV[1])/dt wait(dt) goto start */ vector vec(2*BIAS::kNumChannels); for (int i=0; i arr(reinterpret_cast(fBiasData.getData()), reinterpret_cast(fBiasData.getData())+1440); sort(arr.begin(), arr.end()); const float med = arr[arr.size()/2]; fData[fCursorAmpl%fData.size()].resize(1); //assign(&med, &med); fData[fCursorAmpl%fData.size()][0] = med; //assign(&med, &med); if (++fCursorAmpl(&avg, 1); return; } if (fPV[1].size()==0) { fPV[1].resize(1); fPV[1] = valarray(&avg, 1); return; } if (fPV[2].size()==0) { fPV[2].resize(1); fPV[2] = valarray(&avg, 1); return; } fPV[0] = fPV[1]; fPV[1] = fPV[2]; fPV[2].resize(1); fPV[2] = valarray(&avg, 1); // ----- Calculate average currents ----- vector A(BIAS::kNumChannels); for (int i=0; i correction = 1./fGain/1000* ( - (fKp+fKd/T21)*(fPV[2] - fPV[1]) + fKi*T21*(fSP[0]-fPV[2]) + fKd/T10*(fPV[1]-fPV[0]) ); */ // pow of 1.6 comes from the non-linearity of the // amplitude vs bias voltage const valarray correction = 1./fGain/1000* ( //fKi*(pow(fSP[0], 1./1.6)-pow(fPV[2], 1./1.6)) fKi*(fSP[0]-fPV[2]) ); Out() << "Correction: " << correction[0] << "V (" << fSP[0] << ")" << endl; const int nch = BIAS::kNumChannels; // FIXME: Sanity check! vector vec; vec.reserve(2*nch); vec.insert(vec.begin(), nch, fPV[2][0]-fSP[0]); vec.insert(vec.begin()+nch, nch, correction[0]); fDimDeviation.setQuality(fControlType); fDimDeviation.Update(vec); if (!fOutputEnabled || fStatusBias.second!=BIAS::kVoltageOn) return; Info("Sending new global relative offset to biasctrl."); DimClient::sendCommandNB("BIAS_CONTROL/INCREASE_ALL_CHANNELS_VOLTAGE", vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float)); } void infoHandler() { DimInfo *curr = getInfo(); // get current DimInfo address if (!curr) return; if (curr==&fBias) { fStatusBias = GetNewState(fBias); return; } if (curr==&fFAD) { fStatusFAD = GetNewState(fFAD); return; } if (curr==&fFSC) { fStatusFSC = GetNewState(fFSC); return; } if (curr==&fDim) { fStatusDim = GetNewState(fDim); fStatusDim.second = curr->getSize()==4 ? curr->getInt() : 0; return; } if (curr==&fBiasNom) { const float *ptr = reinterpret_cast(fBiasNom.getData()); fVoltGapd.assign(ptr, ptr+416); Info("Nominal bias voltages received."); return; } if (curr==&fCameraTemp && (fControlType==kTemp || fControlType==kCurrents)) HandleCameraTemp(); if (curr==&fBiasA && fControlType==kTemp && GetCurrentState()==kStateCalibrating) HandleCalibration(); if (curr==&fBiasA && (fControlType==kFeedbackGlobal || fControlType==kCurrents)) AverageCurrents(); if (curr==&fBiasData && fControlType==kFeedback) HandleFeedback(); if (curr==&fBiasData && fControlType==kFeedbackGlobal) HandleGlobalFeedback(); } bool CheckEventSize(size_t has, const char *name, size_t size) { if (has==size) return true; ostringstream msg; msg << name << " - Received event has " << has << " bytes, but expected " << size << "."; Fatal(msg); return false; } void PrintState(const pair &state, const char *server) { const State rc = fNetwork.GetState(server, state.second); Out() << state.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - "; Out() << kBold << server << ": "; Out() << rc.name << "[" << rc.index << "]"; Out() << kReset << " - " << kBlue << rc.comment << endl; } int Print() { Out() << fStatusDim.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - "; Out() << kBold << "DIM_DNS: "; if (fStatusDim.second==0) Out() << "Offline" << endl; else Out() << "V" << fStatusDim.second/100 << 'r' << fStatusDim.second%100 << endl; PrintState(fStatusFAD, "FAD_CONTROL"); PrintState(fStatusFSC, "FSC_CONTROL"); PrintState(fStatusBias, "BIAS_CONTROL"); return GetCurrentState(); } int PrintCalibration() { if (fCalibration.size()==0) { Out() << "No calibration performed so far." << endl; return GetCurrentState(); } const float *avg = fCalibration.data(); const float *rms = fCalibration.data()+BIAS::kNumChannels; const float *res = fCalibration.data()+BIAS::kNumChannels*2; Out() << "Average current at " << fCalibrationOffset << "V below G-APD operation voltage:\n"; for (int k=0; k<13; k++) for (int j=0; j<8; j++) { Out() << setw(2) << k << "|" << setw(2) << j*4 << "|"; for (int i=0; i<4; i++) Out() << Tools::Form(" %6.1f+-%4.1f", avg[k*32+j*4+i], rms[k*32+j*4+i]); Out() << '\n'; } Out() << '\n'; Out() << "Measured calibration resistor:\n"; for (int k=0; k<13; k++) for (int j=0; j<4; j++) { Out() << setw(2) << k << "|" << setw(2) << j*8 << "|"; for (int i=0; i<8; i++) Out() << Tools::Form(" %5.0f", res[k*32+j*8+i]); Out() << '\n'; } Out() << flush; return GetCurrentState(); } void WarnState(bool needfsc, bool needfad) { const bool bias = fStatusBias.second >= BIAS::kConnecting; const bool fsc = fStatusFSC.second >= 2; const bool fad = fStatusFAD.second >= FAD::kConnected; if (!bias) Warn("Bias control not yet ready."); if (needfsc && !fsc) Warn("FSC control not yet ready."); if (needfad && !fad) Warn("FAD control not yet ready."); } int SetConstant(const EventImp &evt, int constant) { if (!CheckEventSize(evt.GetSize(), "SetConstant", 8)) return kSM_FatalError; switch (constant) { case 0: fKi = evt.GetDouble(); break; case 1: fKp = evt.GetDouble(); break; case 2: fKd = evt.GetDouble(); break; case 3: fT = evt.GetDouble(); break; case 4: fGain = evt.GetDouble(); break; default: Fatal("SetConstant got an unexpected constant id -- this is a program bug!"); return kSM_FatalError; } return GetCurrentState(); } int EnableOutput(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "EnableOutput", 1)) return kSM_FatalError; fOutputEnabled = evt.GetBool(); return GetCurrentState(); } void ResetData(int16_t n=-1) { fData.assign(n>0 ? n : fData.size(), vector(0)); fCursorAmpl = 0; fCursorCur = 0; fCursorTemp = 0; fStartTime = Time(); fSP = valarray(0., BIAS::kNumChannels); vector vec(2*BIAS::kNumChannels); fDimDeviation.setQuality(kIdle); fDimDeviation.Update(vec); fPV[0].resize(0); fPV[1].resize(0); fPV[2].resize(0); fCurrentsAvg.assign(BIAS::kNumChannels, 0); fCurrentsRms.assign(BIAS::kNumChannels, 0); if (fKp==0 && fKi==0 && fKd==0) Warn("Control loop parameters are all set to zero."); } int StartFeedback(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "StartFeedback", 2)) return kSM_FatalError; WarnState(false, true); ResetData(evt.GetShort()); fControlType = kFeedback; return GetCurrentState(); } int StartFeedbackGlobal(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "StartFeedbackGlobal", 2)) return kSM_FatalError; WarnState(false, true); ResetData(evt.GetShort()); fControlType = kFeedbackGlobal; return GetCurrentState(); } int StartTempCtrl(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4)) return kSM_FatalError; WarnState(true, false); fBiasOffset = evt.GetFloat(); fControlType = kTemp; ostringstream out; out << "Starting temperature feedback with an offset of " << fBiasOffset << "V"; Message(out); if (fStatusBias.second==BIAS::kVoltageOn) DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); return GetCurrentState(); } int StartCurrentCtrl(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "StartCurrentCtrl", 4)) return kSM_FatalError; if (fCalibration.size()==0) { Warn("Current control needs a bias crate calibration first... command ignored."); return GetCurrentState(); } WarnState(true, false); ResetData(0); fBiasOffset = evt.GetFloat(); fControlType = kCurrents; ostringstream out; out << "Starting current/temp feedback with an offset of " << fBiasOffset << "V"; Message(out); return GetCurrentState(); } int StopFeedback() { fControlType = kIdle; return GetCurrentState(); } int StoreReference() { if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size()) { Warn("No values in memory. Take enough events first!"); return GetCurrentState(); } // FIXME: Check age if (!fPV[1].size() && !fPV[2].size()) fSP = fPV[0]; if (!fPV[2].size()) fSP = fPV[1]; else fSP = fPV[2]; vector vec(BIAS::kNumChannels); for (int i=0; i vec(BIAS::kNumChannels); for (int i=0; i= BIAS::kConnecting; const bool fad = fStatusFAD.second >= FAD::kConnected; const bool fsc = fStatusFSC.second >= 2; // All subsystems are not connected if (!bias && !fad && !fsc) return kStateDisconnected; // At least one subsystem apart from bias is connected if (bias && !fad && !fsc) return kStateConnecting; /* // All subsystems are connected if (GetCurrentStatus()==kStateConfiguringStep1) { if (fCursor<1) return kStateConfiguringStep1; if (fCursor==1) { fStartTime = Time(); return kStateConfiguringStep2; } } if (GetCurrentStatus()==kStateConfiguringStep2) { if (fCursor==1) { if ((Time()-fStartTime).total_microseconds()/1000000.<1.5) return kStateConfiguringStep2; Dim::SendCommand("BIAS_CONTROL/REQUEST_STATUS"); } if (fCursor==2) { int n=0; double avg = 0; for (size_t i=0; i=0) { avg += fCurrents[i]; n++; } cout << avg/n << endl; } return kStateConnected; } */ // Needs connection of FAD and BIAS if (bias && fad) { if (fControlType==kFeedback || fControlType==kFeedbackGlobal) return fOutputEnabled ? kStateFeedbackCtrlRunning : kStateFeedbackCtrlIdle; } // Needs connection of FSC and BIAS if (bias && fsc) { if (fControlType==kTemp) { if (GetCurrentState()==kStateCalibrating && fCursorCur0 && Time()-past>boost::posix_time::milliseconds(fCurrentRequestInterval)) { if (fStatusBias.second==BIAS::kVoltageOn) DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); past = Time(); } return fOutputEnabled ? kStateCurrentCtrlRunning : kStateCurrentCtrlIdle; } } if (bias && fad && !fsc) return kStateConnectedFAD; if (bias && fsc && !fad) return kStateConnectedFSC; return kStateConnected; } public: StateMachineFeedback(ostream &out=cout) : StateMachineDim(out, "FEEDBACK"), fStatusDim(make_pair(Time(), -2)), fStatusFAD(make_pair(Time(), -2)), fStatusBias(make_pair(Time(), -2)), fDim("DIS_DNS/VERSION_NUMBER", (void*)NULL, 0, this), fFAD("FAD_CONTROL/STATE", (void*)NULL, 0, this), fFSC("FSC_CONTROL/STATE", (void*)NULL, 0, this), fBias("BIAS_CONTROL/STATE", (void*)NULL, 0, this), fBiasA("BIAS_CONTROL/CURRENT", (void*)NULL, 0, this), fBiasData("FAD_CONTROL/FEEDBACK_DATA", (void*)NULL, 0, this), fBiasNom("BIAS_CONTROL/NOMINAL", (void*)NULL, 0, this), fCameraTemp("FSC_CONTROL/TEMPERATURE", (void*)NULL, 0, this), fDimReference("FEEDBACK/REFERENCE", "F:416", "Amplitude reference value(s)" "Vref[mV]:Amplitude reference"), fDimDeviation("FEEDBACK/DEVIATION", "F:416;F:416", "Control loop information" "|DeltaAmpl[mV]:Amplitude offset measures" "|DeltaBias[mV]:Correction value calculated"), fDimCalibration("FEEDBACK/CALIBRATION", "F:416;F:416;F:416", "Current offsets" "|Avg[uA]:Average offset" "|Rms[uA]:Rms of offset" "|R[Ohm]:Measured calibration resistor"), fSP(BIAS::kNumChannels), fKp(0), fKi(0), fKd(0), fT(-1), fCalibrationOffset(-3), fCurrentRequestInterval(0), fNumCalibIgnore(30), fNumCalibRequests(300), fOutputEnabled(false) { // ba::io_service::work is a kind of keep_alive for the loop. // It prevents the io_service to go to stopped state, which // would prevent any consecutive calls to run() // or poll() to do nothing. reset() could also revoke to the // previous state but this might introduce some overhead of // deletion and creation of threads and more. // fSP.resize(416); // State names AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable", "The Dim DNS is not reachable."); AddStateName(kStateDisconnected, "Disconnected", "The Dim DNS is reachable, but the required subsystems are not available."); AddStateName(kStateConnecting, "Connecting", "Only biasctrl is available and connected with its hardware."); AddStateName(kStateConnectedFSC, "ConnectedFSC", "biasctrl and fscctrl are available and connected with their hardware."); AddStateName(kStateConnectedFAD, "ConnectedFAD", "biasctrl and fadctrl are available and connected with their hardware."); AddStateName(kStateConnected, "Connected", "biasctrl, fadctrl and fscctrl are available and connected with their hardware."); AddStateName(kStateFeedbackCtrlIdle, "FeedbackIdle", "Feedback control activated, but voltage output disabled."); AddStateName(kStateTempCtrlIdle, "TempCtrlIdle", "Temperature control activated, but voltage output disabled."); AddStateName(kStateCurrentCtrlIdle, "CurrentCtrlIdle", "Current control activated, but voltage output disabled."); AddStateName(kStateFeedbackCtrlRunning, "FeedbackControl", "Feedback control activated and voltage output enabled."); AddStateName(kStateTempCtrlRunning, "TempControl", "Temperature control activated and voltage output enabled."); AddStateName(kStateCurrentCtrlRunning, "CurrentControl", "Current/Temp control activated and voltage output enabled."); AddStateName(kStateCalibrating, "Calibrating", "Calibrating current offsets."); AddEvent("START_FEEDBACK_CONTROL", "S:1", kStateConnectedFAD, kStateConnected) (bind(&StateMachineFeedback::StartFeedback, this, placeholders::_1)) ("Start the feedback control loop" "|Num[short]:Number of events 'medianed' to calculate the correction value"); AddEvent("START_GLOBAL_FEEDBACK", "S:1", kStateConnectedFAD, kStateConnected) (bind(&StateMachineFeedback::StartFeedbackGlobal, this, placeholders::_1)) ("Start the global feedback control loop" "Num[short]:Number of events averaged to calculate the correction value"); AddEvent("START_TEMP_CONTROL", "F:1", kStateConnectedFSC, kStateConnected) (bind(&StateMachineFeedback::StartTempCtrl, this, placeholders::_1)) ("Start the temperature control loop" "|offset[V]:Offset from the nominal temperature corrected value in Volts"); AddEvent("START_CURRENT_CONTROL", "F:1", kStateConnectedFSC, kStateConnected) (bind(&StateMachineFeedback::StartCurrentCtrl, this, placeholders::_1)) ("Start the current/temperature control loop" "|offset[V]:Offset from the nominal current/temperature corrected value in Volts"); // kStateTempCtrlIdle, kStateFeedbackCtrlIdle, kStateTempCtrlRunning, kStateFeedbackCtrlRunning AddEvent("STOP") (bind(&StateMachineFeedback::StopFeedback, this)) ("Stop any control loop"); AddEvent("ENABLE_OUTPUT", "B:1")//, kStateIdle) (bind(&StateMachineFeedback::EnableOutput, this, placeholders::_1)) ("Enable sending of correction values caluclated by the control loop to the biasctrl"); AddEvent("STORE_REFERENCE")//, kStateIdle) (bind(&StateMachineFeedback::StoreReference, this)) ("Store the last (averaged) value as new reference (for debug purpose only)"); AddEvent("SET_REFERENCE", "F:1")//, kStateIdle) (bind(&StateMachineFeedback::SetReference, this, placeholders::_1)) ("Set a new global reference value (for debug purpose only)"); AddEvent("SET_Ki", "D:1")//, kStateIdle) (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 0)) ("Set integral constant Ki"); AddEvent("SET_Kp", "D:1")//, kStateIdle) (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 1)) ("Set proportional constant Kp"); AddEvent("SET_Kd", "D:1")//, kStateIdle) (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 2)) ("Set derivative constant Kd"); AddEvent("SET_T", "D:1")//, kStateIdle) (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 3)) ("Set time-constant. (-1 to use the cycle time, i.e. the time for the last average cycle, instead)"); AddEvent("CALIBRATE_CURRENTS", kStateConnectedFSC, kStateConnected)//, kStateIdle) (bind(&StateMachineFeedback::CalibrateCurrents, this)) (""); AddEvent("SET_CURRENT_REQUEST_INTERVAL", kStateConnectedFSC, kStateConnected)//, kStateIdle) (bind(&StateMachineFeedback::SetCurrentRequestInterval, this, placeholders::_1)) ("|interval[ms]:Interval between two current requests in modes which need that."); // Verbosity commands // AddEvent("SET_VERBOSE", "B:1") // (bind(&StateMachineMCP::SetVerbosity, this, placeholders::_1)) // ("set verbosity state" // "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); AddEvent("PRINT") (bind(&StateMachineFeedback::Print, this)) (""); AddEvent("PRINT_CALIBRATION") (bind(&StateMachineFeedback::PrintCalibration, this)) (""); } int EvalOptions(Configuration &conf) { if (!fMap.Read(conf.Get("pixel-map-file"))) { Error("Reading mapping table from "+conf.Get("pixel-map-file")+" failed."); return 1; } fGain = 0.1; // V(Amplitude) / V(Bias) // 148 -> 248 // 33 : 10s < 2% // 50 : 5s < 2% // 66 : 3s < 2% // 85 : 2s < 2% fKp = 0; fKd = 0; fKi = 0.75; fT = 1; // Is that independent of the aboslute real amplitude of // the light pulser? ostringstream msg; msg << "Control loop parameters: "; msg << "Kp=" << fKp << ", Kd=" << fKd << ", Ki=" << fKi << ", "; if (fT>0) msg << fT; else msg << ""; msg << ", Gain(DRS/BIAS)=" << fGain << "V/V"; Message(msg); fCurrentRequestInterval = conf.Get("current-request-interval"); fNumCalibIgnore = conf.Get("num-calib-ignore"); fNumCalibRequests = conf.Get("num-calib-average"); fCalibrationOffset = conf.Get("calibration-offset"); return -1; } }; // ------------------------------------------------------------------------ #include "Main.h" template int RunShell(Configuration &conf) { return Main::execute(conf); } void SetupConfiguration(Configuration &conf) { po::options_description control("Feedback options"); control.add_options() ("pixel-map-file", var("FACTmapV5a.txt"), "Pixel mapping file. Used here to get the default reference voltage.") ("current-request-interval", var(1000), "Interval between two current requests.") ("num-calib-ignore", var(30), "Number of current requests to be ignored before averaging") ("num-calib-average", var(300), "Number of current requests to be averaged") ("calibration-offset", var(-3), "Absolute offset relative to the G-APD operation voltage when calibrating") ; conf.AddOptions(control); } /* Extract usage clause(s) [if any] for SYNOPSIS. Translators: "Usage" and "or" here are patterns (regular expressions) which are used to match the usage synopsis in program output. An example from cp (GNU coreutils) which contains both strings: Usage: cp [OPTION]... [-T] SOURCE DEST or: cp [OPTION]... SOURCE... DIRECTORY or: cp [OPTION]... -t DIRECTORY SOURCE... */ void PrintUsage() { cout << "The feedback control the BIAS voltages based on the calibration signal.\n" "\n" "The default is that the program is started without user intercation. " "All actions are supposed to arrive as DimCommands. Using the -c " "option, a local shell can be initialized. With h or help a short " "help message about the usuage can be brought to the screen.\n" "\n" "Usage: feedback [-c type] [OPTIONS]\n" " or: feedback [OPTIONS]\n"; cout << endl; } void PrintHelp() { Main::PrintHelp(); /* Additional help text which is printed after the configuration options goes here */ /* cout << "bla bla bla" << endl << endl; cout << endl; cout << "Environment:" << endl; cout << "environment" << endl; cout << endl; cout << "Examples:" << endl; cout << "test exam" << endl; cout << endl; cout << "Files:" << endl; cout << "files" << endl; cout << endl; */ } int main(int argc, const char* argv[]) { Configuration conf(argv[0]); conf.SetPrintUsage(PrintUsage); Main::SetupConfiguration(conf); SetupConfiguration(conf); if (!conf.DoParse(argc, argv, PrintHelp)) return -1; //try { // No console access at all if (!conf.Has("console")) { // if (conf.Get("no-dim")) // return RunShell(conf); // else return RunShell(conf); } // Cosole access w/ and w/o Dim /* if (conf.Get("no-dim")) { if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); } else */ { if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); } } /*catch (std::exception& e) { cerr << "Exception: " << e.what() << endl; return -1; }*/ return 0; }