From 71dc832e4757717b90aed172e29546a8ac653cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Sandstr=C3=B6m?= Date: Tue, 7 Apr 2020 16:08:23 +0200 Subject: [PATCH] Add config option RATE, update option descriptions.. --- .../Db/ecmcPluginFFT.template | 4 +- .../ecmcPlugin_FFTApp/src/ecmcFFT.cpp | 49 +++++++++++++++++++ .../ecmcPlugin_FFTApp/src/ecmcFFT.h | 24 ++++++--- .../ecmcPlugin_FFTApp/src/ecmcFFTDefs.h | 1 + .../ecmcPlugin_FFTApp/src/ecmcFFTWrap.cpp | 15 ++++++ .../ecmcPlugin_FFTApp/src/ecmcFFTWrap.h | 6 +++ .../ecmcPlugin_FFTApp/src/ecmcPluginFFT.c | 20 ++++---- 7 files changed, 100 insertions(+), 19 deletions(-) diff --git a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/Db/ecmcPluginFFT.template b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/Db/ecmcPluginFFT.template index 6eb9f71..bfbad4a 100644 --- a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/Db/ecmcPluginFFT.template +++ b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/Db/ecmcPluginFFT.template @@ -39,7 +39,7 @@ record(waveform,"$(P)Plugin-FFT${INDEX}-Spectrum-Amp-Act"){ field(DESC, "FFT spectrum amplitude result") field(PINI, "1") field(DTYP, "asynFloat64ArrayIn") - field(INP, "@asyn($(PORT),$(ADDR=0=),$(TIMEOUT=1000=1))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynFloat64ArrayIn/plugin.fft${INDEX}.fftamplitude?") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynFloat64ArrayIn/plugin.fft${INDEX}.fftamplitude?") field(FTVL, "DOUBLE") field(NELM, "$(NELM)") field(SCAN, "I/O Intr") @@ -71,7 +71,7 @@ record(longout,"$(P)Plugin-FFT${INDEX}-Mode-RB"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") - field(OUT, "@asyn($(PORT),$(ADDR=0=0),$(TIMEOUT=1000=0))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.fft${INDEX}.mode=") + field(OUT, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.fft${INDEX}.mode=") field(SCAN, "Passive") field(TSE, "$(TSE=0)") } diff --git a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFT.cpp b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFT.cpp index bc28af1..a934c61 100644 --- a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFT.cpp +++ b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFT.cpp @@ -31,6 +31,8 @@ // New data callback from ecmc static int printMissingObjError = 1; +/** This callback will not be used (sample data inteface is used instead to get an stable sample freq) + since the callback is called when data is updated it might */ void f_dataUpdatedCallback(uint8_t* data, size_t size, ecmcEcDataType dt, void* obj) { if(!obj) { if(printMissingObjError){ @@ -58,6 +60,7 @@ ecmcFFT::ecmcFFT(int fftIndex, // index of this object (if several is cr cfgDataSourceStr_ = NULL; dataBuffer_ = NULL; dataItem_ = NULL; + dataItemInfo_ = NULL; fftDouble_ = NULL; asynPort_ = NULL; asynEnable_ = NULL; // Enable @@ -74,6 +77,12 @@ ecmcFFT::ecmcFFT(int fftIndex, // index of this object (if several is cr objectId_ = fftIndex; scale_ = 1.0; triggOnce_ = 0; + cycleCounter_ = 0; + ignoreCycles_ = 0; + + ecmcSampleRateHz_ = getEcmcSampleRate(); + cfgFFTSampleRateHz_ = ecmcSampleRateHz_; + // Config defaults cfgDbgMode_ = 0; cfgNfft_ = ECMC_PLUGIN_DEFAULT_NFFT; // samples in fft (must be n^2) @@ -92,6 +101,20 @@ ecmcFFT::ecmcFFT(int fftIndex, // index of this object (if several is cr if(cfgNfft_ <= 0) { throw std::out_of_range("NFFT must be > 0 and even N^2."); } + + // Check valid sample rate + if(cfgFFTSampleRateHz_ <= 0) { + throw std::out_of_range("FFT Invalid sample rate"); + } + if(cfgFFTSampleRateHz_ > ecmcSampleRateHz_) { + printf("Warning FFT sample rate faster than ecmc rate. FFT rate will be set to ecmc rate.\n"); + cfgFFTSampleRateHz_ = ecmcSampleRateHz_; + } + + // Se if any data update cycles should be ignored + // example ecmc 1000Hz, fft 100Hz then ignore 9 cycles (could be strange if not multiples) + ignoreCycles_ = ecmcSampleRateHz_ / cfgFFTSampleRateHz_ -1; + // set scale factor scale_ = 1.0 / cfgNfft_; // sqrt((double)cfgNfft_); @@ -185,6 +208,12 @@ void ecmcFFT::parseConfigStr(char *configStr) { } } + // ECMC_PLUGIN_RATE_OPTION_CMD rate in HZ + else if (!strncmp(pThisOption, ECMC_PLUGIN_RATE_OPTION_CMD, strlen(ECMC_PLUGIN_RATE_OPTION_CMD))) { + pThisOption += strlen(ECMC_PLUGIN_RATE_OPTION_CMD); + cfgFFTSampleRateHz_ = atof(pThisOption); + } + pThisOption = pNextOption; } free(pOptions); @@ -202,6 +231,8 @@ void ecmcFFT::connectToDataSource() { if(!dataItem_) { throw std::runtime_error( "Data item NULL." ); } + + dataItemInfo_ = dataItem_->getDataItemInfo(); // Register data callback callbackHandle_ = dataItem_->regDataUpdatedCallback(f_dataUpdatedCallback, this); @@ -220,10 +251,19 @@ void ecmcFFT::connectToDataSource() { void ecmcFFT::dataUpdatedCallback(uint8_t* data, size_t size, ecmcEcDataType dt) { + // No buffer or full or not enabled if(!dataBuffer_ || !cfgEnable_) { return; } + + // See if data should be ignored + if(cycleCounter_ < ignoreCycles_) { + cycleCounter_++; + return; // ignore this callback + } + cycleCounter_ = 0; + if (cfgMode_ == TRIGG && !triggOnce_ ) { updateStatus(IDLE); return; // Wait for trigger from plc or asyn @@ -718,3 +758,12 @@ void ecmcFFT::updateStatus(FFT_STATUS status) { status_ = status; asynFFTStat_->refreshParamRT(1); } + +// Do nut use this as same time as callback! +void ecmcFFT::sampleData() { + + dataUpdatedCallback(dataItemInfo_->data, + dataItemInfo_->dataSize, + dataItemInfo_->dataType); +} + diff --git a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFT.h b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFT.h index dd68a73..d509219 100644 --- a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFT.h +++ b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFT.h @@ -45,6 +45,10 @@ class ecmcFFT { FFT_STATUS getStatusFFT(); void clearBuffers(); void triggFFT(); + + /** Do not use this as same time as callback! + * if used it should be called from ecmc realtime callback*/ + void sampleData(); private: void parseConfigStr(char *configStr); @@ -57,29 +61,33 @@ class ecmcFFT { static int dataTypeSupported(ecmcEcDataType dt); ecmcDataItem *dataItem_; + ecmcDataItemInfo *dataItemInfo_; ecmcAsynPortDriver *asynPort_; kissfft* fftDouble_; double* dataBuffer_; // Input data (real) std::complex* fftBuffer_; // Result (complex) double* fftBufferAmp_; // Resulting amplitude (abs of fftBuffer_) size_t elementsInBuffer_; + double ecmcSampleRateHz_; // ecmc callback handle for use when deregister at unload int callbackHandle_; int fftCalcDone_; int objectId_; // Unique object id int triggOnce_; + int cycleCounter_; + int ignoreCycles_; double scale_; // Config: Data set size FFT_STATUS status_; // Status/state (NO_STAT, IDLE, ACQ, CALC) // Config options - char* cfgDataSourceStr_; // Config: data source string - int cfgDbgMode_; // Config: allow dbg printouts - int cfgApplyScale_; // Config: apply scale 1/nfft - int cfgDcRemove_; // Config: remove dc (average) - size_t cfgNfft_; // Config: Data set size - int cfgEnable_; // Config: Enable data acq./calc. - FFT_MODE cfgMode_; // Config: Mode continous or triggered. - + char* cfgDataSourceStr_; // Config: data source string + int cfgDbgMode_; // Config: allow dbg printouts + int cfgApplyScale_; // Config: apply scale 1/nfft + int cfgDcRemove_; // Config: remove dc (average) + size_t cfgNfft_; // Config: Data set size + int cfgEnable_; // Config: Enable data acq./calc. + FFT_MODE cfgMode_; // Config: Mode continous or triggered. + double cfgFFTSampleRateHz_; // Config: Sample rate (defaukts to ecmc rate) // Asyn ecmcAsynDataItem* asynEnable_; // Enable/disable acq./calcs diff --git a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTDefs.h b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTDefs.h index e4b9706..7b78194 100644 --- a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTDefs.h +++ b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTDefs.h @@ -21,6 +21,7 @@ #define ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD "APPLY_SCALE=" #define ECMC_PLUGIN_DC_REMOVE_OPTION_CMD "DC_REMOVE=" #define ECMC_PLUGIN_ENABLE_OPTION_CMD "ENABLE=" +#define ECMC_PLUGIN_RATE_OPTION_CMD "RATE=" // CONT, TRIGG #define ECMC_PLUGIN_MODE_OPTION_CMD "MODE=" #define ECMC_PLUGIN_MODE_CONT_OPTION "CONT" diff --git a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTWrap.cpp b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTWrap.cpp index 0986661..c40ba5e 100644 --- a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTWrap.cpp +++ b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTWrap.cpp @@ -122,3 +122,18 @@ FFT_STATUS statFFT(int fftIndex) { return NO_STAT; } +int sampleFFTs() { + for(std::vector::iterator pfft = ffts.begin(); pfft != ffts.end(); ++pfft) { + if(*pfft) { + try { + (*pfft)->sampleData(); + } + catch(std::exception& e) { + printf("Exception: %s. Plugin will unload.\n",e.what()); + return ECMC_PLUGIN_FFT_ERROR_CODE; + } + } + } + return 0; +} + diff --git a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTWrap.h b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTWrap.h index fe4650c..851798e 100644 --- a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTWrap.h +++ b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcFFTWrap.h @@ -88,6 +88,12 @@ int triggFFT(int fftIndex); */ FFT_STATUS statFFT(int fftIndex); +/** \brief Sample data in all FFT objects + * + * This function should be executed in the realtime callback from ecmc.\ + */ +int sampleFFTs(); + /** \brief Link data to _all_ fft objects * * This tells the FFT lib to connect to ecmc to find it's data source.\n diff --git a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcPluginFFT.c b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcPluginFFT.c index cf6288d..4b91859 100644 --- a/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcPluginFFT.c +++ b/ecmcPlugin_FFT-loc/ecmcPlugin_FFTApp/src/ecmcPluginFFT.c @@ -61,9 +61,10 @@ void fftDestruct(void) * Return value other than 0 will be considered to be an error code in ecmc. **/ int fftRealtime(int ecmcError) -{ +{ lastEcmcError = ecmcError; - return 0; + // Let all fft:s sample data + return 0; // sampleFFTs(); Use callback instead } /** Link to data source here since all sources should be availabe at this stage @@ -115,13 +116,14 @@ struct ecmcPluginData pluginDataDef = { // Description .desc = "FFT plugin for use with ecmc.", // Option description - .optionDesc = "\n "ECMC_PLUGIN_DBG_OPTION_CMD"<1/0> : Enables/disables printouts from plugin.\n" - " "ECMC_PLUGIN_SOURCE_OPTION_CMD" : Sets source variable for FFT (example: ec0.s1.AI_1).\n" - " "ECMC_PLUGIN_NFFT_OPTION_CMD" : Data points to collect.\n" - " "ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD"<1/0> : Apply scale.\n" - " "ECMC_PLUGIN_DC_REMOVE_OPTION_CMD"<1/0> : Remove DC offset of input data (SOURCE).\n" - " "ECMC_PLUGIN_ENABLE_OPTION_CMD"<1/0> : Enable data acq. and calcs (can be controlled over asyn)." - " "ECMC_PLUGIN_MODE_OPTION_CMD" : Continious or triggered mode." + .optionDesc = "\n "ECMC_PLUGIN_DBG_OPTION_CMD"<1/0> : Enables/disables printouts from plugin, default = disabled.\n" + " "ECMC_PLUGIN_SOURCE_OPTION_CMD" : Sets source variable for FFT (example: ec0.s1.AI_1).\n" + " "ECMC_PLUGIN_NFFT_OPTION_CMD" : Data points to collect, default = 4096.\n" + " "ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD"<1/0> : Apply scale, defaults to disabled.\n" + " "ECMC_PLUGIN_DC_REMOVE_OPTION_CMD"<1/0> : Remove DC offset of input data (SOURCE), default = disabled.\n" + " "ECMC_PLUGIN_ENABLE_OPTION_CMD"<1/0> : Enable data acq. and calcs (can be controlled over asyn), default = disabled.\n" + " "ECMC_PLUGIN_MODE_OPTION_CMD" : Continious or triggered mode, defaults to TRIGG\n" + " "ECMC_PLUGIN_RATE_OPTION_CMD" : fft data sample rate in hz (must be lower than ecmc rate and (ecmc_rate/fft_rate)=integer), default = ecmc rate." , // Plugin version .version = ECMC_EXAMPLE_PLUGIN_VERSION,