Add config option RATE, update option descriptions..

This commit is contained in:
Anders Sandström
2020-04-07 16:08:23 +02:00
parent 29b01c87e8
commit 71dc832e47
7 changed files with 100 additions and 19 deletions

View File

@@ -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)")
}

View File

@@ -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);
}

View File

@@ -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<double>* fftDouble_;
double* dataBuffer_; // Input data (real)
std::complex<double>* 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

View File

@@ -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"

View File

@@ -122,3 +122,18 @@ FFT_STATUS statFFT(int fftIndex) {
return NO_STAT;
}
int sampleFFTs() {
for(std::vector<ecmcFFT*>::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;
}

View File

@@ -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

View File

@@ -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"<source> : Sets source variable for FFT (example: ec0.s1.AI_1).\n"
" "ECMC_PLUGIN_NFFT_OPTION_CMD"<nfft> : 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"<CONT/TRIGG> : 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"<source> : Sets source variable for FFT (example: ec0.s1.AI_1).\n"
" "ECMC_PLUGIN_NFFT_OPTION_CMD"<nfft> : 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"<CONT/TRIGG> : Continious or triggered mode, defaults to TRIGG\n"
" "ECMC_PLUGIN_RATE_OPTION_CMD"<rate in hz> : 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,