#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, kStateConnected, kStateRunning, kStateControlling, }; PixelMap fMap; DimServiceInfoList fNetwork; pair fStatusDim; pair fStatusFAD; pair fStatusBias; DimStampedInfo fDim; DimStampedInfo fFAD; DimStampedInfo fBias; DimStampedInfo *fBiasData; DimDescribedService fDimReference; DimDescribedService fDimDeviation; vector> fData; uint64_t fCursor; Time fBiasLast; Time fStartTime; valarray fPV[3]; // Process variable (intgerated/averaged amplitudes) valarray fSP; // Set point (target amplitudes) double fT; double fKp; // Proportional constant double fKi; // Integral constant double fKd; // Derivative constant 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 ResetData() { fData.clear(); fData.resize(500); fCursor = 0; fStartTime = Time(); fSP.resize(416); fSP = valarray(0., 416); vector vec(2*BIAS::kNumChannels); fDimDeviation.Update(vec); fPV[0].resize(0); fPV[1].resize(0); fPV[2].resize(0); if (fKp==0 && fKi==0 && fKd==0) Warn("Control loop parameters are all set to zero."); } 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==&fDim) { fStatusDim = GetNewState(fDim); fStatusDim.second = curr->getSize()==4 ? curr->getInt() : 0; return; } if (curr==fBiasData) { if (curr->getSize()==0) return; if (curr->getSize()!=1440*sizeof(float)) return; // -------- Check age of last stored event -------- // Must be called in this order const int tsec = curr->getTimestamp(); const int tms = curr->getTimestampMillisecs(); const Time tm(tsec, tms*1000); if (Time()-fBiasLast>boost::posix_time::seconds(30)) { Warn("Last received event data older than 30s... resetting average calculation."); ResetData(); } fBiasLast = tm; // -------- Store new event -------- fData[fCursor%fData.size()].assign(reinterpret_cast(curr->getData()), reinterpret_cast(curr->getData())+1440); fCursor++; if (fCursor 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; i(avg.data(), avg.size()); } else if (fPV[1].size()==0) { fPV[1].resize(avg.size()); fPV[1] = valarray(avg.data(), avg.size()); } else if (fPV[2].size()==0) { fPV[2].resize(avg.size()); fPV[2] = valarray(avg.data(), avg.size()); } else { 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; // -110 / -110 (-23 DAC / -0.51V) // Reference voltage: -238 / -203 // -360 / -343 ( 23 DAC / 0.51V) // 0.005 A/V // 220 Amplitude / 1V // Gain = 1V / 200 = 0.005 // => 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]) + Ki * T21 * (SP-PV[2]) - Kd*(PV[2]-PV[1])/T21 - Kd*(PV[0]-PV[1])/T01; const valarray correction = - (fKp+fKd/T21)*(fPV[2] - fPV[1]) + fKi*T21*(fSP-fPV[2]) + fKd/T10*(fPV[1]-fPV[0]); vector vec(2*BIAS::kNumChannels); for (int i=0; i correction(416); vector response(416); vector dev(416); double gain = 0; for (int j=0; j<416; j++) { //avg[j] /= fData.size(); //rms[j] /= sqrt((rms[j]*fData.size() - avg[j]*avg[j]))/fData.size(); dev[j] = avg[j] - target[j]; // Determine correction from response maxtrix and change voltages correction[j] = -dev[j]*response[j]*gain; if (correction[j]==0) continue; // Limit voltage steps // Limit changes to 100 mV // if (fabs(correction[j]) > 0.1) // correction[j] = 0.1*fabs(correction[j])/correction[j]; // Add voltage change command if not too noisy // if (fabs(Average[i]) > 2*Sigma[i]) // Cmd << fIDTable[i] << " " << std::showpos << Correction/Multiplicity[i] << " "; } */ return; } } 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(fStatusBias, "BIAS_CONTROL"); return GetCurrentState(); } 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; 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(); } int StartFeedback() { fBiasData = new DimStampedInfo("FAD_CONTROL/FEEDBACK_DATA", (void*)NULL, 0, this); ResetData(); return GetCurrentState(); } int StopFeedback() { delete fBiasData; fBiasData = 0; 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("pixel-map-file"))) { Error("Reading mapping table from "+conf.Get("pixel-map-file")+" failed."); return 1; } return -1; } }; // ------------------------------------------------------------------------ #include "Main.h" template int RunShell(Configuration &conf) { return Main::execute(conf); } void SetupConfiguration(Configuration &conf) { po::options_description control("BIAS control options"); control.add_options() ("pixel-map-file", var("FACTmapV5a.txt"), "Pixel mapping file. Used here to get the default reference voltage.") ; 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 ftmctrl controls the FSC (FACT Slow Control) board.\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: fscctrl [-c type] [OPTIONS]\n" " or: fscctrl [OPTIONS]\n"; cout << endl; } void 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; } const PixelMapEntry PixelMap::empty = { 0, 0, 0, 0, 0, 0 };