75 Commits

Author SHA1 Message Date
a206ff1fca Use ecmc 11.0 2025-10-13 11:44:55 +02:00
23e8ea2a7f Compile for epics 7.0.9 2025-04-22 11:45:19 +02:00
30ceea65d6 Update makefile 2025-03-28 09:30:35 +01:00
01214019a6 Also compile for deb12 2025-03-21 10:35:30 +01:00
90339dae2f Update makefile 2025-03-03 15:04:30 +01:00
d17d5d7f5d Prep for ecmc v10 2024-12-16 10:40:08 +01:00
fcee0a8ff4 New plg concept and cleanup 2024-12-13 16:27:11 +01:00
Anders Sandstrom
a84383815d Cleanup 2021-02-03 18:47:28 +01:00
Anders Sandstrom
223fd94a86 Fix fft x-axis calc. 2021-02-03 18:45:46 +01:00
Anders Sandstrom
96d66df11b Allow use any breaktable (no need to be in menuConvert) 2021-01-26 16:08:36 +01:00
Anders Sandstrom
87c8084ae0 Fix printout for BREAKTABLE option. 2021-01-26 15:07:48 +01:00
Anders Sandstrom
a581ddc559 Update README with breaktable option. 2021-01-26 14:50:59 +01:00
Anders Sandstrom
5418c98488 Add breaktable support 2021-01-26 14:19:48 +01:00
Anders Sandstrom
e6f5286836 Add record to hold FFT title. 2020-12-04 10:30:23 +01:00
Anders Sandstrom
028d56d83b Update testscript 2020-12-04 09:29:27 +01:00
Anders Sandstrom
ae67695c6a ecmcPluginFFT.template: Macrofy DESC and EGU for raw ans spect data. 2020-12-04 09:29:07 +01:00
Anders Sandstrom
08542988a1 Update to ecmc 6.3.0 2020-12-04 09:28:36 +01:00
anderssandstrom
5938255f9e Update test_plugin_FFT.script
Use new name for config option: DC_REMOVE->RM_DC
2020-12-02 16:06:06 +01:00
Anders Sandström
f49fdb2d76 Use correct sample rate for x-axis 2020-12-01 09:04:17 +01:00
Anders Sandström
b43aa23f4b Fix samplerate asyn param 2020-12-01 08:40:38 +01:00
Anders Sandström
f1e460e6dc Add act buffer index to readInt32() 2020-11-24 17:43:45 +01:00
Anders Sandström
d89c34d041 Update .gitignore 2020-11-24 11:08:03 +01:00
Anders Sandström
c8f24ca340 Add dbl >pvs.log in test script 2020-11-24 11:07:21 +01:00
Anders Sandström
fd31b8957e Add current buffer index as asynparam. 2020-11-24 11:06:38 +01:00
Anders Sandström
e4e379a392 Update plugin name to ecmc_plugin_fft 2020-11-23 10:24:18 +01:00
anderssandstrom
a72c764098 Update README.md 2020-11-23 10:17:00 +01:00
Anders Sandström
bdd937ad47 Rename to comply with E3 naming (only lower case and no -loc) 2020-11-23 10:14:47 +01:00
Anders Sandström
47f5ce247c Asynparam rate is actual eleemnt sample rate and not ecmc samplerate (can differ if oversampling) 2020-11-10 14:22:20 +01:00
Anders Sandström
b163fcab98 Remove special case for ec-emtry. Handle in ecmc instead. 2020-11-10 14:00:23 +01:00
Anders Sandström
fd3115bbcf Update to require 3.3.0. Name module ecmc_plugin_fft. 2020-11-10 13:24:24 +01:00
Anders Sandstrom
6dbdc97957 Move tools to ecmccomgui. Update readmes 2020-10-13 09:36:01 +02:00
Anders Sandstrom
ecbf760a51 Correct labels in both offline and online mode 2020-10-09 22:37:02 +02:00
Anders Sandstrom
be359cbfa8 Only allow save when data present 2020-10-09 22:18:40 +02:00
Anders Sandstrom
e2f15ad698 Add testdata.npz 2020-10-09 22:15:24 +02:00
Anders Sandstrom
ca35cd929c Save prefix and plugin id instead of pvnames. Save source strings. Online and offline mode seems to work. 2020-10-09 22:14:33 +02:00
Anders Sandstrom
c37694e796 WIP 2020-10-09 18:25:14 +02:00
anderssandstrom
23dfc5284a Update README.md 2020-10-08 19:28:46 +02:00
Anders Sandstrom
90539a098e Add ecmcArrayGui pic 2020-10-08 19:23:41 +02:00
anderssandstrom
ceeb3da16c Update README.md 2020-10-08 19:20:17 +02:00
Anders Sandstrom
4cd8f922e4 Add GUI pic 2020-10-08 19:18:39 +02:00
anderssandstrom
ffc1d7820e Update README.md 2020-10-08 19:15:28 +02:00
Anders Sandstrom
41a07f984a Add gui screendump 2020-10-08 19:05:47 +02:00
Anders Sandstrom
7ae0efaef3 Update enable record with readback. 2020-10-08 19:05:32 +02:00
Anders Sandstrom
a7faed674d ecmcFFTMainGui.py: Add callbacks for enable and mode. Add combo for mode. 2020-10-08 19:04:04 +02:00
Anders Sandstrom
460f8e935e ecmcFFTMainGui.py: Add time on x-axis and some more info 2020-10-08 14:55:47 +02:00
Anders Sandstrom
6c64cb3607 ecmcFFTMainGui.py: Add tool to show/control FFT plugin. 2020-10-08 13:52:22 +02:00
Anders Sandstrom
a43f893336 ecmcArrayGui.py:Add tool to plot arrays 2020-10-08 13:51:57 +02:00
Anders Sandstrom
de04f2b686 Fix asyn indexes in initAsyn() 2020-10-08 09:11:43 +02:00
Anders Sandstrom
c5d255c82d Correct asyntype of samplerate param 2020-10-08 09:00:20 +02:00
Anders Sandstrom
e389f0b39d ecmcFFTGui.py: Check both X and Y data is valid. 2020-10-06 20:01:06 +02:00
Anders Sandstrom
f27f27df39 ecmcFFTGui.py: Remove unused libs 2020-10-06 19:56:37 +02:00
anderssandstrom
6029642b78 Update ecmcFFTGui.py 2020-10-06 19:53:38 +02:00
Anders Sandstrom
999bf637d2 ecmcFFTGui.py: Remove printouts 2020-10-06 19:51:05 +02:00
Anders Sandstrom
9ce4fbe14b ecmcFFTGui.py: Update plot when release pause 2020-10-06 19:49:52 +02:00
Anders Sandstrom
210a71e4ad ecmcFFTGui.py: Remove printout. 2020-10-06 18:58:10 +02:00
Anders Sandstrom
a863a7e400 Update README 2020-10-06 18:53:25 +02:00
Anders Sandstrom
9bcbbfd1b0 Cleanup of pyqt gui 2020-10-06 17:05:54 +02:00
Anders Sandstrom
deca91d7aa Add pvs as args 2020-10-06 17:01:32 +02:00
Anders Sandstrom
2767b2676e Disable autozoom after first update. 2020-10-06 16:49:18 +02:00
Anders Sandstrom
c4f1ee6574 Add draft fft pyqtgui, update template with egu, edit gitignore. 2020-10-06 15:35:54 +02:00
Anders Sandstrom
6775c92a60 Remove APPLY_SCALE cfg from example 2020-10-02 16:17:09 +02:00
Anders Sandstrom
88bbddd58a Remove APPLY_SCALE command (always enabled). Add cfg SCALE for scaling of source data 2020-10-02 16:15:47 +02:00
Anders Sandstrom
2cce34dbd1 Update make stuff 2020-10-02 15:53:40 +02:00
Anders Sandstrom
424e00e4e0 Add check if source is ec entry (always 8byte but not array).. 2020-10-02 15:53:20 +02:00
Anders Sandström
f212815a0e Update to ecmc v6.2.1 2020-05-27 14:15:53 +02:00
anderssandstrom
db67919b85 Update README.md 2020-04-24 15:43:02 +02:00
Anders Sandström
07385b261a Add description of RM_LIN conf. 2020-04-24 15:40:20 +02:00
Anders Sandström
375e5e7b21 Add wavefor record for preprocessed data array. 2020-04-24 15:08:20 +02:00
Anders Sandström
85b231537b Logic added but not tested. 2020-04-24 14:53:39 +02:00
Anders Sandström
24fd9bbb3d Add RM_LIN config. No real code yet. 2020-04-24 13:37:26 +02:00
Anders Sandström
4df1650541 Update from remote 2020-04-24 13:28:33 +02:00
Anders Sandström
0989b969ba Rename DC_REMOVE to RM_DC 2020-04-24 13:26:32 +02:00
anderssandstrom
c40fdcfabc Update README.md 2020-04-10 19:12:29 +02:00
anderssandstrom
4c8edccfb8 Update README.md 2020-04-10 19:11:38 +02:00
anderssandstrom
a136c5f4b2 Update README.md 2020-04-09 10:33:23 +02:00
58 changed files with 551 additions and 216 deletions

4
.gitignore vendored
View File

@@ -13,4 +13,6 @@ core.*
*_old/
*PVs.list
*-loc/*.Makefile
ecmcPlugin_Simple-loc/*.Makefile
ecmc_plugin_fft/*.Makefile
*__*
O.*

34
GNUmakefile Normal file
View File

@@ -0,0 +1,34 @@
include /ioc/tools/driver.makefile
MODULE = ecmc_plugin_fft
# "Transfer" module name to plugin
USR_CFLAGS +=-DECMC_PLUGIN_MODULE_NAME=${MODULE}
BUILDCLASSES = Linux
ARCH_FILTER = deb10% deb12%
EXCLUDE_VERSIONS+=3 7.0.5 7.0.6 7.0.7
IGNORE_MODULES += asynMotor
IGNORE_MODULES += motorBase
USR_CXXFLAGS += -std=c++17
OPT_CXXFLAGS_YES = -O3
# dependencies
ecmc_VERSION = 11.0
APP:=src
APPDB:=$(APP)/Db
APPSRC:=$(APP)/src
USR_CFLAGS += -shared -fPIC -Wall -Wextra
USR_LDFLAGS += -lstdc++
USR_INCLUDES += -I$(where_am_I)$(APPSRC)
TEMPLATES += $(wildcard $(APPDB)/*.db)
TEMPLATES += $(wildcard $(APPDB)/*.template)
SOURCES += $(APPSRC)/ecmcPluginFFT.c
SOURCES += $(APPSRC)/ecmcFFTWrap.cpp
SOURCES += $(APPSRC)/ecmcFFT.cpp

View File

@@ -1,28 +0,0 @@
#
# Copyright (c) 2018 - 2019 European Spallation Source ERIC
#
# The program is free software: you can redistribute
# it and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 2 of the
# License, or any newer version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
#
#
# Author : Jeong Han Lee
# email : han.lee@esss.se
# Date : 2020Mar22-1607-33CET
# version : 1.0.0
TOP:=$(CURDIR)
include $(TOP)/configure/CONFIG
include $(TOP)/configure/RULES

191
README.md
View File

@@ -1,4 +1,4 @@
e3-ecmcPlugin_FFT
e3-ecmc_plugin_fft
======
ESS Site-specific EPICS module : ecmcPlugin_FFT
@@ -10,6 +10,10 @@ Configuration is made through ecmccfg:
https://github.com/paulscherrerinstitute/ecmccfg (ot local ess fork https://github.com/icshwi/ecmccfg)
FFT:s are calculated with the kissfft lib:
https://github.com/mborgerding/kissfft
# Introduction
The main functionality of this plugin is to make FFT analysis of ecmc data. Most data in ecmc is accessible:
@@ -29,7 +33,7 @@ https://github.com/icshwi/ecmccfg/blob/master/scripts/loadPlugin.cmd
Example:
```
${SCRIPTEXEC} ${ecmccfg_DIR}loadPlugin.cmd, "PLUGIN_ID=0,FILE=libecmcPlugin_FFT.so,CONFIG='DBG_PRINT=1;SOURCE=ax1.actpos;', REPORT=1
${SCRIPTEXEC} ${ecmccfg_DIR}loadPlugin.cmd, "PLUGIN_ID=0,FILE=libecmcPlugin_FFT.so,CONFIG='DBG_PRINT=1;SOURCE=ax1.actpos;RM_LIN=1;', REPORT=1
dbLoadRecords(ecmcPluginFFT.template,"P=$(IOC):,INDEX=0, NELM=${FFT_NELM}")
```
This plugin supports multiple loading. For each load of the plugin a new FFT object will be created. In order to access these plugins, from plc:s or EPICS records, they can be accessed by an index. The first FFT plugin will have index 0. The next loaded FFT plugin will have index 1...
@@ -41,25 +45,27 @@ Note: If another plugin is loaded in between the loading of FFT plugins, it will
The different available configuration settings:
* SOURCE= source variable : Sets source variable for FFT (example: ec0.s1.AI_1). This config is mandatory.
* DBG_PRINT=1/0 : Enables/disables printouts from plugin, default = disabled.
* NFFT= nfft : Data points to collect, default = 4096.
* APPLY_SCALE=1/0 : Apply scale, default = enabled.
* DC_REMOVE=1/0 : Remove DC offset of input data (SOURCE), default = disabled.
* NFFT= nfft : Data points to collect, default = 4096.
* SCALE=scale : Apply scale to input data, default = 1.0.
* RM_DC=1/0 : Remove DC offset of input data (SOURCE), default = disabled.
* RM_LIN=1/0 : Remove linear component input data (SOURCE), default = disabled.
* ENABLE=1/0 : Enable data acq. and calcs (can be controlled over asyn), default = disabled.
* MODE=CONT/TRIGG : Continious or triggered mode, defaults to TRIGG
* RATE=rate in hz : fft data sample rate in hz (must be lower than ecmc rate and (ecmc_rate/fft_rate)=integer), default = ecmc rate.
* BREAKTABLE= EPICS breaktable : Apply breaktable to raw value.
Example configuration string:
```
"SOURCE=ax1.actpos;MODE=TRIGG;DBG_PRINT=1;ENABLE=1;"
"SOURCE=ax1.poserr;MODE=TRIGG;DBG_PRINT=1;ENABLE=1;"
```
#### SOURCE (mandatory)
The data source is defined by setting the SOURCE option in the plugin configuration string.
This configuration is mandatory.
Example: Axis 1 actpos
Example: Axis 1 actpos (See RM_LIN config below)
```
"DBG_PRINT=1;SOURCE=ax1.actpos;"
"DBG_PRINT=1;SOURCE=ax1.actpos;RM_LIN=1;"
```
Example: Ethercat slave 1 analog input ch1
@@ -69,9 +75,9 @@ Example: Ethercat slave 1 analog input ch1
#### DBG_PRINT (default: disabled)
Enable/disable printouts from plugin can be made bu setting the "DBG_PRINT" option.
Exmaple: Disable
Example: Disable
```
"DBG_PRINT=0;SOURCE=ax1.actpos;"
"DBG_PRINT=0;SOURCE=ax1.poserr;"
```
#### NFFT (default: 4096)
@@ -79,37 +85,46 @@ Defines number of samples for each measurement.
Note: Must be a n² number..
Exmaple: 1024
Example: 1024
```
"NFFT=1024;DBG_PRINT=0;SOURCE=ax1.actpos;"
"NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
```
#### APPLY_SCALE (default disabled)
Apply scaling in order to get correct amplitude of fft. Disabled as default (lower cpu usage).
#### SCALE (default 1.0)
Apply custom scale to input data.
Exmaple: Enable
Example: 5.0
```
"APPLY_SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.actpos;"
"SCALE=5.0;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
```
### RM_DC
Remove DC of input signal. Default is disabled.
#### DC_REMOVE (default: disabled)
Remove DC of input signal by substracting average of input signal. This can be usefull if low frequencies are of intresst since the DC component normally distorts the spectrum near 0Hz.
Exmaple: Enable
Example: Remove DC offset
```
"DC_REMOVE=1;APPLY_SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.actpos;"
"RM_DC=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
```
### RM_LIN
Remove linear component of input signal. Default is disabled.
The linear component is calculated by least square method.
Could be usefull for values that increase, like actual position.
Example: Remove linear component
```
"RM_LIN=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
```
#### ENABLE (default: disabled)
Enable data acq. and FFT calcs. The default settings is disabled so needs to be enabled from plc or over asyn in order to start calculations.
Exmaple: Enable at startup by config
Example: Enable at startup by config
```
"ENABLE=1;DC_REMOVE=1;APPLY_SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.actpos;"
"ENABLE=1;RM_DC=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
```
Exmaple: Enable FFT index 0 from EPICS:
Example: Enable FFT index 0 from EPICS:
```
caput IOC_TEST:Plugin-FFT0-Enable 1
```
Exmaple: Enable FFT index 0 from ecmc PLC code:
Example: Enable FFT index 0 from ecmc PLC code:
```
fft_enable(0,1)
```
@@ -138,11 +153,11 @@ Triggered mode:
5. FTT results are sent by callback over asyn to epics records
6. goto 1.
Exmaple: Mode triggered
Example: Mode triggered
```
"MODE=TRIGG;ENABLE=1;DC_REMOVE=1;APPLY_SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.actpos;"
"MODE=TRIGG;ENABLE=1;RM_DC=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
```
Exmaple: Mode from EPICS record
Example: Mode from EPICS record
```
# CONT
caput IOC_TEST:Plugin-FFT0-Mode-RB 1
@@ -155,10 +170,44 @@ Note: The record is a output record with readback so can both be read and writte
Sets the sample rate of the raw input data (from data source). The default value is the ecmc rate for that data source.
Note: only a lower and "integer" division of sample rate can be defined.
Exmaple: Rate = 100Hz
Example: Rate = 100Hz
```
RATE=100;MODE=TRIGG;ENABLE=1;DC_REMOVE=1;APPLY_SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.actpos;"
"RATE=100;MODE=TRIGG;ENABLE=1;RM_DC=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
```
#### BREAKTABLE (default: no break table)
Note: The break table must be added in EPICS
Example: Apply BREAKTABLE=typeTelemess33020_21877 to raw value
```
"...;BREAKTABLE=typeTelemess33020_21877;"
```
Example: Load custom breaktable in EPICS
```
dbLoadRecords ./bptBreakTable.dbd
```
Example: breaktable (in a bptBreakTable.dbd)
```
# <raw> <eng>
breaktable(typeTelemess33020_21877) {
2502.48 0
4058.89 1.5
6164.64 3
8880.74 4.5
11993.57 6
15259.00 7.5
18371.84 9
21271.05 10.5
24658.54 12
27344.13 13.5
29297.28 15
}
```
## EPICS records
Each FFT plugin object will create a new asynportdriver-port named "PLUGIN.FFT<index>" (index is explaine above).
The reason for a dedicated asynport is to disturb ecmc as little as possible.
@@ -202,6 +251,7 @@ Note: The FFT asynparameters will not be visible by the ecmcReport iocsh command
## PLC interface
### PLC Functions
1. "fft_clear(arg0);" double fft_clear(index) : Clear/resets all buffers fft[index].
2. "fft_enable(arg0, arg1);" double fft_enable(index, enable) : Set enable for fft[index].
3. "fft_trigg(arg0);" double fft_trigg(index) : Trigg new measurement for fft[index]. Will clear buffers.
@@ -248,6 +298,83 @@ Epics records:
| :---: |
|**Figure 2** Resulting FFT amplitude. |
#### ecmc PLC code for example:
```
static.time:=ec_get_time()/1E9;
static.sineval:=sin(2*pi*${FREQ=5}*static.time);
```
## FFT GUI
### FFT GUI (FFT and rawdata plots + controls)
A simple tool, [ecmcFFTMainGui.py](tools/ecmcFFTMainGui.py), to visualize the calculated spectrum, rawdata and also plugin controls can be found in the tools directory. The GUI connects to the plugin records over pypics framwork.
The gui are included in the ecmccomgui repo:
https://github.com/anderssandstrom/ecmccomgui
Example: ecmcFFTMainGui.py help printout
```
python ecmcFFTMainGui.py
ecmcFFTMainGui: Plots waveforms of FFT data (updates on Y data callback).
python ecmcFFTMainGui.py <prefix> <fftId>
<prefix>: Ioc prefix ('IOC_TEST:')
<fftId> : Id of fft plugin ('0')
example : python ecmcFFTMainGui.py 'IOC_TEST:' '0'
Will connect to Pvs: <prefix>Plugin-FFT<fftId>-*
```
Example: Start ecmcFFMainTGui.py for:
* predix="IOC_TEST:"
* fftPluginId=0 (the first loaded FFT plugin in the ioc)
```
python ecmcFFTMainGui.py IOC_TEST: 0
```
![ecmcFFTMainGui.py](docs/gui/ecmcFFTMainGui.png)
### FFT GUI (only FFT plot)
A simple tool, [ecmcFFTGui.py](tools/ecmcFFTGui.py), to visualize the calculated spectrum can be found in the tools directory.
The GUI connects to the plugin records over pypics framwork.
Example: ecmcFFTGui.py help printout
```
python ecmcFFTGui.py
ecmcFFTGui: Plots waveforms of FFT data (updates on Y data callback).
python ecmcFFTGui.py <x.pv> <y.pv>
example: python ecmcFFTGui.py IOC_TEST:Plugin-FFT1-Spectrum-X-Axis-Act IOC_TEST:Plugin-FFT1-Spectrum-Amp-Act
```
Example: Start ecmcFFTGui.py for spectrum amplitude and freq. waveform pvs
```
python ecmcFFTGui.py IOC_TEST:Plugin-FFT1-Spectrum-X-Axis-Act IOC_TEST:Plugin-FFT1-Spectrum-Amp-Act
```
![ecmcFFTMainGui.py](docs/gui/ecmcFFTGui.png)
### Array GUI (generic waveform plt)
A simple generic tool, [ecmcFFTGui.py](tools/ecmcFFTGui.py), to visualize wavforms.
The GUI connects to the plugin records over pypics framwork.
Example: ecmcArrayGui.py help printout
```
python ecmcArrayGui.py
ecmcArrayGui: Plots waveforms data (updates on data callback).
python ecmcArrayGui.py <y.pv>
example: python ecmcArrayGui.py IOC_TEST:Plugin-FFT0-Raw-Data-Act
```
Example: Start ecmcArrayGui.py for raw data wavform
```
python ecmcArrayGui.py IOC_TEST:Plugin-FFT0-Raw-Data-Act
```
![ecmcFFTMainGui.py](docs/gui/ecmcArrayGui.png)
### Needed packages:
* python 3.5
* epics
* PyQt5
* numpy
* matplotlib
## Plugin info
```
@@ -259,8 +386,8 @@ Plugin info:
DBG_PRINT=<1/0> : Enables/disables printouts from plugin, default = disabled.
SOURCE=<source> : Sets source variable for FFT (example: ec0.s1.AI_1).
NFFT=<nfft> : Data points to collect, default = 4096.
APPLY_SCALE=<1/0> : Apply scale, default = disabled.
DC_REMOVE=<1/0> : Remove DC offset of input data (SOURCE), default = disabled.
SCALE=<1/0> : Apply scale, default = disabled.
RM_DC=<1/0> : Remove DC offset of input data (SOURCE), default = disabled.
ENABLE=<1/0> : Enable data acq. and calcs (can be controlled over asyn), default = disabled.
MODE=<CONT/TRIGG> : Continious or triggered mode, defaults to TRIGG
RATE=<rate in hz> : fft data sample rate in hz (must be lower than ecmc rate and (ecmc_rate/fft_rate)=integer), default = ecmc rate.

View File

@@ -1,5 +1,5 @@
#
EPICS_MODULE_NAME:=ecmcPlugin_FFT
EPICS_MODULE_NAME:=ecmc_plugin_fft
EPICS_MODULE_TAG:=master
#
@@ -8,8 +8,8 @@ E3_MODULE_VERSION:=master
# DEPENDENT MODULE VERSION
# For Example,
ECMC_DEP_VERSION:=master
ASYN_DEP_VERSION:=4.36.0
ECMC_DEP_VERSION:=develop
ASYN_DEP_VERSION:=4.37.0
#DEVLIB2_DEP_VERSION:=2.9.0
#PCRE_DEP_VERSION:=8.41.0
@@ -27,7 +27,7 @@ ASYN_DEP_VERSION:=4.36.0
#
E3_MODULE_NAME:=$(EPICS_MODULE_NAME)
E3_MODULE_SRC_PATH:=ecmcPlugin_FFT-loc
E3_MODULE_SRC_PATH:=ecmc_plugin_fft
E3_MODULE_MAKEFILE:=$(EPICS_MODULE_NAME).Makefile

View File

@@ -1,8 +1,8 @@
#
EPICS_BASE:=/epics/base-7.0.3.1
EPICS_BASE:=${HOME}/epics/base-7.0.4
E3_REQUIRE_NAME:=require
E3_REQUIRE_VERSION:=3.1.2
E3_REQUIRE_VERSION:=3.4.0
# The definitions shown below can also be placed in an untracked RELEASE.local
-include $(TOP)/../../RELEASE.local

BIN
docs/gui/ecmcArrayGui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
docs/gui/ecmcFFTGui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/gui/ecmcFFTMainGui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -1,62 +0,0 @@
#
# Copyright (c) 2019 European Spallation Source ERIC
#
# The program is free software: you can redistribute
# it and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 2 of the
# License, or any newer version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
#
#
# Author : anderssandstrom
# email : anderssandstrom@esss.se
# Date : 2020Mar22-1607-33CET
# version : 0.0.0
#
# template file is generated by ./e3TemplateGenerator.bash with bf03d40
# Please look at many other _module_.Makefile in e3-* repository
#
## The following lines are mandatory, please don't change them.
where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include $(E3_REQUIRE_TOOLS)/driver.makefile
include $(E3_REQUIRE_CONFIG)/DECOUPLE_FLAGS
ifneq ($(strip $(ASYN_DEP_VERSION)),)
asyn_VERSION=$(ASYN_DEP_VERSION)
endif
ifneq ($(strip $(ECMC_DEP_VERSION)),)
ecmc_VERSION=$(ECMC_DEP_VERSION)
endif
APP:=ecmcPlugin_FFTApp
APPDB:=$(APP)/Db
APPSRC:=$(APP)/src
USR_CFLAGS += -shared -fPIC -Wall -Wextra
USR_LDFLAGS += -lstdc++
USR_INCLUDES += -I$(where_am_I)$(APPSRC)
TEMPLATES += $(wildcard $(APPDB)/*.db)
TEMPLATES += $(wildcard $(APPDB)/*.template)
SOURCES += $(APPSRC)/ecmcPluginFFT.c
SOURCES += $(APPSRC)/ecmcFFTWrap.cpp
SOURCES += $(APPSRC)/ecmcFFT.cpp
db:
.PHONY: db
vlibs:
.PHONY: vlibs
###

View File

@@ -7,12 +7,11 @@ epicsEnvSet("IOC" ,"$(IOC="IOC_TEST")")
epicsEnvSet("ECMCCFG_INIT" ,"") #Only run startup once (auto at PSI, need call at ESS), variable set to "#" in startup.cmd
epicsEnvSet("SCRIPTEXEC" ,"$(SCRIPTEXEC="iocshLoad")")
require ecmccfg master
require ecmccfg "6.3.0"
##############################################################################
###### Startup
require ecmc "master"
require stream "${stream_VER=2.8.10}"
require ecmc "6.3.0"
#-------------------------------------------------------------------------------
#- define default PATH for scripts and database/templates
@@ -41,29 +40,30 @@ ecmcConfigOrDie "Cfg.SetSampleRate(${ECMC_EC_SAMPLE_RATE})"
# No EtherCAT hardware..
##############################################################################
require ecmcPlugin_FFT master # te get access to db file..
require ecmc_plugin_fft master # te get access to db file..
epicsEnvSet("FFT_NELM", 1024)
########################################################################s######
## Load plugin: FFT
epicsEnvSet(ECMC_PLUGIN_FILNAME,"/epics/base-7.0.3.1/require/3.1.2/siteMods/ecmcPlugin_FFT/master/lib/${EPICS_HOST_ARCH=linux-x86_64}/libecmcPlugin_FFT.so")
epicsEnvSet(ECMC_PLUGIN_CONFIG,"SOURCE=plcs.plc0.static.sineval;DBG_PRINT=0;NFFT=1024;RATE=100;DC_REMOVE=1;APPLY_SCALE=1;MODE=CONT;")
epicsEnvSet(ECMC_PLUGIN_FILNAME,"/home/pi/epics/base-7.0.4/require/3.3.0/siteMods/ecmc_plugin_fft/master/lib/${EPICS_HOST_ARCH=linux-x86_64}/libecmc_plugin_fft.so")
epicsEnvSet(ECMC_PLUGIN_CONFIG,"SOURCE=plcs.plc0.static.sineval;DBG_PRINT=0;NFFT=1024;RATE=100;RM_DC=1;SCALE=1;MODE=CONT;ENABLE=1;")
${SCRIPTEXEC} ${ecmccfg_DIR}loadPlugin.cmd, "PLUGIN_ID=0,FILE=${ECMC_PLUGIN_FILNAME},CONFIG='${ECMC_PLUGIN_CONFIG}', REPORT=1"
# Note: INDEX is the index of FFT object in FFT plugin and not PLUGIN_ID. In this case the same
dbLoadRecords(ecmcPluginFFT.template,"P=$(IOC):,INDEX=0, NELM=${FFT_NELM}")
dbLoadRecords(ecmcPluginFFT.template,"P=$(IOC):,INDEX=0, NELM=${FFT_NELM}, AMP_DESC='Sine amplitude',AMP_EGU='',RAW_DESC='Sine',AMP_EGU='', TITLE='FFT of sinus in 5Hz'")
########################################################################s######
## Load plugin: FFT again (will create a new FFT object in the same namespace)
epicsEnvSet(ECMC_PLUGIN_FILNAME,"/epics/base-7.0.3.1/require/3.1.2/siteMods/ecmcPlugin_FFT/master/lib/${EPICS_HOST_ARCH=linux-x86_64}/libecmcPlugin_FFT.so")
epicsEnvSet(ECMC_PLUGIN_CONFIG,"SOURCE=ecmc.thread.latency.max;DBG_PRINT=0;NFFT=1024;")
epicsEnvSet(ECMC_PLUGIN_FILNAME,"/home/pi/epics/base-7.0.4/require/3.3.0/siteMods/ecmc_plugin_fft/master/lib/${EPICS_HOST_ARCH=linux-x86_64}/libecmc_plugin_fft.so")
epicsEnvSet(ECMC_PLUGIN_CONFIG,"SOURCE=ecmc.thread.latency.max;DBG_PRINT=0;NFFT=1024;ENABLE=1;")
${SCRIPTEXEC} ${ecmccfg_DIR}loadPlugin.cmd, "PLUGIN_ID=1,FILE=${ECMC_PLUGIN_FILNAME},CONFIG='${ECMC_PLUGIN_CONFIG}', REPORT=1"
# Note: INDEX is the index of FFT object in FFT plugin and not PLUGIN_ID. In this case the same
dbLoadRecords(ecmcPluginFFT.template,"P=$(IOC):,INDEX=1, NELM=${FFT_NELM}")
dbLoadRecords(ecmcPluginFFT.template,"P=$(IOC):,INDEX=1, NELM=${FFT_NELM}, AMP_DESC='Latency amplitude',AMP_EGU='ns',RAW_DESC='Latency',AMP_EGU='ns',TITLE='FFT of ecmc latencny'")
epicsEnvUnset(ECMC_PLUGIN_FILNAME)
epicsEnvUnset(ECMC_PLUGIN_CONFIG)
##############################################################################
## PLC 0: Generate a sine wave at 10Hz
## PLC 0: Generate a sine wave at 5Hz
$(SCRIPTEXEC) $(ecmccfg_DIR)loadPLCFile.cmd, "PLC_ID=0, SAMPLE_RATE_MS=10,FILE=./plc/plc_no_ec_fft_sin.plc, PLC_MACROS='FREQ=5'")
##############################################################################
@@ -80,4 +80,4 @@ dbLoadRecords("ecmcGeneral.db","P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,T
ecmcConfigOrDie "Cfg.SetAppMode(1)"
iocInit
dbl > pvs.log

View File

@@ -23,7 +23,7 @@ record(waveform,"$(P)Plugin-FFT${INDEX}-Source"){
# Rawdata
record(waveform,"$(P)Plugin-FFT${INDEX}-Raw-Data-Act"){
info(asyn:FIFO, "1000")
field(DESC, "Raw data")
field(DESC, "${RAW_DESC="Raw data"}")
field(PINI, "1")
field(DTYP, "asynFloat64ArrayIn")
field(INP, "@asyn(PLUGIN.FFT${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.fft${INDEX}.rawdata")
@@ -31,12 +31,26 @@ record(waveform,"$(P)Plugin-FFT${INDEX}-Raw-Data-Act"){
field(NELM, "$(NELM)")
field(SCAN, "I/O Intr")
field(TSE, "0")
field(EGU, "${RAW_EGU= }")
}
# Pre-processed data
record(waveform,"$(P)Plugin-FFT${INDEX}-PreProc-Data-Act"){
info(asyn:FIFO, "1000")
field(DESC, "Pre-processed data")
field(PINI, "1")
field(DTYP, "asynFloat64ArrayIn")
field(INP, "@asyn(PLUGIN.FFT${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.fft${INDEX}.preprocdata")
field(FTVL, "DOUBLE")
field(NELM, "$(NELM)")
field(SCAN, "I/O Intr")
field(TSE, "0")
}
# FFT amplitude result
record(waveform,"$(P)Plugin-FFT${INDEX}-Spectrum-Amp-Act"){
info(asyn:FIFO, "1000")
field(DESC, "FFT spectrum amplitude result")
field(DESC, "${AMP_DESC="Spectrum amplitude"}")
field(PINI, "1")
field(DTYP, "asynFloat64ArrayIn")
field(INP, "@asyn(PLUGIN.FFT${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.fft${INDEX}.fftamplitude")
@@ -44,12 +58,14 @@ record(waveform,"$(P)Plugin-FFT${INDEX}-Spectrum-Amp-Act"){
field(NELM, "$(NELM)")
field(SCAN, "I/O Intr")
field(TSE, "0")
field(EGU, "${AMP_EGU= }")
}
# FFT xaxis
record(waveform,"$(P)Plugin-FFT${INDEX}-Spectrum-X-Axis-Act"){
info(asyn:FIFO, "1000")
field(DESC, "Raw data")
field(DESC, "X-Axis data")
field(EGU, "Hz")
field(PINI, "1")
field(DTYP, "asynFloat64ArrayIn")
field(INP, "@asyn(PLUGIN.FFT${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.fft${INDEX}.fftxaxis")
@@ -60,6 +76,7 @@ record(waveform,"$(P)Plugin-FFT${INDEX}-Spectrum-X-Axis-Act"){
}
record(bo,"$(P)Plugin-FFT${INDEX}-Enable"){
info(asyn:READBACK,"1")
field(DESC, "FFT Enable")
field(DTYP,"asynInt32")
field(OUT, "@asyn(PLUGIN.FFT${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.fft${INDEX}.enable")
@@ -108,4 +125,18 @@ record(ai,"$(P)Plugin-FFT${INDEX}-SampleRate-Act"){
field(TSE, "0")
}
# Actual buffer index
record(longin,"$(P)Plugin-FFT${INDEX}-BuffIdAct"){
field(DESC, "Current buffer index")
field(PINI, "1")
field(DTYP, "asynInt32")
field(INP, "@asyn(PLUGIN.FFT${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.fft${INDEX}.buffid")
field(SCAN, "I/O Intr")
field(TSE, "0")
}
# Plot title (for epicscomgui)
record(stringin,"$(P)Plugin-FFT${INDEX}-Title"){
field(DESC, "Title of FFT plot")
field(VAL, "${TITLE="FFT"}")
}

View File

@@ -17,6 +17,7 @@
#define ECMC_PLUGIN_ASYN_PREFIX "plugin.fft"
#define ECMC_PLUGIN_ASYN_ENABLE "enable"
#define ECMC_PLUGIN_ASYN_RAWDATA "rawdata"
#define ECMC_PLUGIN_ASYN_PPDATA "preprocdata"
#define ECMC_PLUGIN_ASYN_FFT_AMP "fftamplitude"
#define ECMC_PLUGIN_ASYN_FFT_MODE "mode"
#define ECMC_PLUGIN_ASYN_FFT_STAT "status"
@@ -25,14 +26,27 @@
#define ECMC_PLUGIN_ASYN_FFT_X_FREQS "fftxaxis"
#define ECMC_PLUGIN_ASYN_NFFT "nfft"
#define ECMC_PLUGIN_ASYN_RATE "samplerate"
#define ECMC_PLUGIN_ASYN_BUFF_ID "buffid"
#include <sstream>
#include "ecmcFFT.h"
#include "ecmcPluginClient.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcAsynPortDriverUtils.h"
#include "epicsThread.h"
// Breaktable
#include "ellLib.h"
#include "dbStaticLib.h"
#include "dbAccess.h"
#include "epicsVersion.h"
#include "cvtTable.h"
#ifdef BASE_VERSION
#define EPICS_3_13
extern DBBASE *pdbbase;
#endif
// New data callback from ecmc
static int printMissingObjError = 1;
@@ -89,6 +103,7 @@ ecmcFFT::ecmcFFT(int fftIndex, // index of this object (if several is cr
0) /* Default stack size */
{
cfgDataSourceStr_ = NULL;
cfgBreakTableStr_ = NULL;
rawDataBuffer_ = NULL;
dataItem_ = NULL;
dataItemInfo_ = NULL;
@@ -104,10 +119,13 @@ ecmcFFT::ecmcFFT(int fftIndex, // index of this object (if several is cr
cycleCounter_ = 0;
ignoreCycles_ = 0;
dataSourceLinked_ = 0;
breakTable_ = NULL;
lastBreakPoint_ = 0;
// Asyn
asynEnableId_ = -1; // Enable/disable acq./calcs
asynRawDataId_ = -1; // Raw data (input) array (double)
asynPPDataId_ = -1; // Pre-processed data array (double)
asynFFTAmpId_ = -1; // FFT amplitude array (double)
asynFFTModeId_ = -1; // FFT mode (cont/trigg)
asynFFTStatId_ = -1; // FFT status (no_stat/idle/acq/calc)
@@ -116,17 +134,21 @@ ecmcFFT::ecmcFFT(int fftIndex, // index of this object (if several is cr
asynFFTXAxisId_ = -1; // FFT X-axis frequencies
asynNfftId_ = -1; // Nfft
asynSRateId_ = -1; // Sample rate Hz
asynElementsInBuffer_= -1;
ecmcSampleRateHz_ = getEcmcSampleRate();
cfgFFTSampleRateHz_ = ecmcSampleRateHz_;
ecmcSampleRateHz_ = getEcmcSampleRate();
cfgFFTSampleRateHz_ = ecmcSampleRateHz_;
cfgDataSampleRateHz_ = ecmcSampleRateHz_;
// Config defaults
cfgDbgMode_ = 0;
cfgNfft_ = ECMC_PLUGIN_DEFAULT_NFFT; // samples in fft (must be n^2)
cfgDcRemove_ = 0;
cfgApplyScale_ = 1; // Scale as default to get correct amplitude in fft
cfgLinRemove_ = 0;
//cfgApplyScale_ = 1; // Scale as default to get correct amplitude in fft
cfgEnable_ = 0; // start disabled (enable over asyn)
cfgMode_ = TRIGG;
cfgScale_ = 1.0;
parseConfigStr(configStr); // Assigns all configs
// Check valid nfft
@@ -143,6 +165,11 @@ ecmcFFT::ecmcFFT(int fftIndex, // index of this object (if several is cr
cfgFFTSampleRateHz_ = ecmcSampleRateHz_;
}
// Check if breaktable
if(cfgBreakTableStr_) {
verifyBreakTable();
}
// 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;
@@ -152,6 +179,7 @@ ecmcFFT::ecmcFFT(int fftIndex, // index of this object (if several is cr
// Allocate buffers
rawDataBuffer_ = new double[cfgNfft_]; // Raw input data (real)
prepProcDataBuffer_ = new double[cfgNfft_]; // Data for preprocessing
fftBufferInput_ = new std::complex<double>[cfgNfft_]; // FFT input (complex)
fftBufferResult_ = new std::complex<double>[cfgNfft_]; // FFT result (complex)
fftBufferResultAmp_ = new double[cfgNfft_ / 2 + 1]; // FFT result amplitude (real)
@@ -178,6 +206,11 @@ ecmcFFT::~ecmcFFT() {
if(rawDataBuffer_) {
delete[] rawDataBuffer_;
}
if(prepProcDataBuffer_) {
delete[] prepProcDataBuffer_;
}
// De register callback when unload
if(callbackHandle_ >= 0) {
dataItem_->deregDataUpdatedCallback(callbackHandle_);
@@ -185,6 +218,9 @@ ecmcFFT::~ecmcFFT() {
if(cfgDataSourceStr_) {
free(cfgDataSourceStr_);
}
if(cfgBreakTableStr_) {
free(cfgBreakTableStr_);
}
if(fftDouble_) {
delete fftDouble_;
}
@@ -220,22 +256,34 @@ void ecmcFFT::parseConfigStr(char *configStr) {
cfgDataSourceStr_=strdup(pThisOption);
}
// ECMC_PLUGIN_BREAKTABLE_OPTION_CMD (EPICS breaktable name)
else if (!strncmp(pThisOption, ECMC_PLUGIN_BREAKTABLE_OPTION_CMD, strlen(ECMC_PLUGIN_BREAKTABLE_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_BREAKTABLE_OPTION_CMD);
cfgBreakTableStr_=strdup(pThisOption);
}
// ECMC_PLUGIN_NFFT_OPTION_CMD (1/0)
else if (!strncmp(pThisOption, ECMC_PLUGIN_NFFT_OPTION_CMD, strlen(ECMC_PLUGIN_NFFT_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_NFFT_OPTION_CMD);
cfgNfft_ = atoi(pThisOption);
}
// ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD (1/0)
else if (!strncmp(pThisOption, ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD, strlen(ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD);
cfgApplyScale_ = atoi(pThisOption);
// // ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD (1/0)
// else if (!strncmp(pThisOption, ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD, strlen(ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD))) {
// pThisOption += strlen(ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD);
// cfgApplyScale_ = atoi(pThisOption);
// }
// ECMC_PLUGIN_RM_DC_OPTION_CMD (1/0)
else if (!strncmp(pThisOption, ECMC_PLUGIN_RM_DC_OPTION_CMD, strlen(ECMC_PLUGIN_RM_DC_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_RM_DC_OPTION_CMD);
cfgDcRemove_ = atoi(pThisOption);
}
// ECMC_PLUGIN_DC_REMOVE_OPTION_CMD (1/0)
else if (!strncmp(pThisOption, ECMC_PLUGIN_DC_REMOVE_OPTION_CMD, strlen(ECMC_PLUGIN_DC_REMOVE_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_DC_REMOVE_OPTION_CMD);
cfgDcRemove_ = atoi(pThisOption);
// ECMC_PLUGIN_RM_LIN_OPTION_CMD (1/0)
else if (!strncmp(pThisOption, ECMC_PLUGIN_RM_LIN_OPTION_CMD, strlen(ECMC_PLUGIN_RM_LIN_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_RM_LIN_OPTION_CMD);
cfgLinRemove_ = atoi(pThisOption);
}
// ECMC_PLUGIN_ENABLE_OPTION_CMD (1/0)
@@ -261,6 +309,12 @@ void ecmcFFT::parseConfigStr(char *configStr) {
cfgFFTSampleRateHz_ = atof(pThisOption);
}
// ECMC_PLUGIN_SCALE_OPTION_CMD rate in HZ
else if (!strncmp(pThisOption, ECMC_PLUGIN_SCALE_OPTION_CMD, strlen(ECMC_PLUGIN_SCALE_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_SCALE_OPTION_CMD);
cfgScale_ = atof(pThisOption);
}
pThisOption = pNextOption;
}
free(pOptions);
@@ -278,7 +332,7 @@ void ecmcFFT::connectToDataSource() {
if( dataSourceLinked_ ) {
return;
}
// Get dataItem
dataItem_ = (ecmcDataItem*) getEcmcDataItem(cfgDataSourceStr_);
if(!dataItem_) {
@@ -297,6 +351,12 @@ void ecmcFFT::connectToDataSource() {
if( !dataTypeSupported(dataItem_->getEcmcDataType()) ) {
throw std::invalid_argument( "Data type not supported." );
}
// Add oversampling
cfgDataSampleRateHz_ = cfgFFTSampleRateHz_ * dataItem_->getEcmcDataSize()/dataItem_->getEcmcDataElementSize();
setDoubleParam(asynSRateId_, cfgDataSampleRateHz_);
callParamCallbacks();
dataSourceLinked_ = 1;
updateStatus(IDLE);
}
@@ -350,7 +410,8 @@ void ecmcFFT::dataUpdatedCallback(uint8_t* data,
size_t dataElementSize = getEcDataTypeByteSize(dt);
uint8_t *pData = data;
for(unsigned int i = 0; i < size / dataElementSize; ++i) {
for(unsigned int i = 0; i < size / dataElementSize; ++i) {
//printf("dataElementSize=%d, size=%d\n",dataElementSize,size);
switch(dt) {
case ECMC_EC_U8:
addDataToBuffer((double)getUint8(pData));
@@ -391,16 +452,31 @@ void ecmcFFT::dataUpdatedCallback(uint8_t* data,
}
void ecmcFFT::addDataToBuffer(double data) {
if(rawDataBuffer_ && (elementsInBuffer_ < cfgNfft_) ) {
if( cfgBreakTableStr_ && interruptAccept ) {
double breakData = data;
// Supply a breaktable (init=0, LINR must be > 1 but only used if init > 0)
if (cvtRawToEngBpt(&breakData, 2, 0, &breakTable_, &lastBreakPoint_)!=0) {
//TODO: What does status here mean..
//throw std::runtime_error("Breaktable conversion failed.\n");
}
//printf("Index %d: Before %lf, after %lf\n",objectId_,data,breakData);
data = breakData * cfgScale_;
} else {
data = data * cfgScale_;
}
rawDataBuffer_[elementsInBuffer_] = data;
fftBufferInput_[elementsInBuffer_].real(data);
fftBufferInput_[elementsInBuffer_].imag(0);
prepProcDataBuffer_[elementsInBuffer_] = data;
}
elementsInBuffer_ ++;
}
void ecmcFFT::clearBuffers() {
memset(rawDataBuffer_, 0, cfgNfft_ * sizeof(double));
memset(prepProcDataBuffer_, 0, cfgNfft_ * sizeof(double));
memset(fftBufferResultAmp_, 0, (cfgNfft_ / 2 + 1) * sizeof(double));
memset(fftBufferXAxis_, 0, (cfgNfft_ / 2 + 1) * sizeof(double));
for(unsigned int i = 0; i < cfgNfft_; ++i) {
@@ -413,13 +489,21 @@ void ecmcFFT::clearBuffers() {
}
void ecmcFFT::calcFFT() {
// move pre-processed data to fft input buffer
for(unsigned int i = 0; i < cfgNfft_; ++i) {
fftBufferInput_[i].real(prepProcDataBuffer_[i]);
fftBufferInput_[i].imag(0);
}
// Do fft
fftDouble_->transform(fftBufferInput_, fftBufferResult_);
}
void ecmcFFT::scaleFFT() {
if(!cfgApplyScale_) {
return;
}
// Always scale
//if(!cfgApplyScale_) {
// return;
//}
for(unsigned int i = 0 ; i < cfgNfft_ ; ++i ) {
fftBufferResult_[i] = fftBufferResult_[i] * scale_;
@@ -434,10 +518,9 @@ void ecmcFFT::calcFFTAmp() {
// Should be enough todo once
void ecmcFFT::calcFFTXAxis() {
//fill x axis buffer with freqs
//fill x axis buffer with freqs
double freq = 0;
double deltaFreq = ecmcSampleRateHz_* ((double)dataItemInfo_->dataSize /
(double)dataItemInfo_->dataElementSize) / ((double)(cfgNfft_));
double deltaFreq = cfgDataSampleRateHz_ / ((double)(cfgNfft_));
for(unsigned int i = 0; i < (cfgNfft_ / 2 + 1); ++i) {
fftBufferXAxis_[i] = freq;
freq = freq + deltaFreq;
@@ -449,17 +532,36 @@ void ecmcFFT::removeDCOffset() {
return;
}
// calc average of raw data
// calc average of preprocess buffer data
double sum = 0;
for(unsigned int i = 0; i < cfgNfft_; ++i ) {
sum += fftBufferInput_[i].real();
sum += prepProcDataBuffer_[i];
}
double avg = sum / ((double)cfgNfft_);
for(unsigned int i = 0; i < cfgNfft_; ++i ) {
fftBufferInput_[i].real(fftBufferInput_[i].real()-avg);
prepProcDataBuffer_[i] = (prepProcDataBuffer_[i]-avg);
}
}
void ecmcFFT::removeLin() {
if(!cfgLinRemove_) {
return;
}
double k=0;
double m=0;
// calc least square (best fit of line)
if(leastSquare(cfgNfft_,prepProcDataBuffer_,&k,&m)) {
printf("%s/%s:%d: Error: " ECMC_PLUGIN_RM_LIN_OPTION_CMD " failed, divison by 0. Data will not be processed with the option/configuration.\n",
__FILE__, __FUNCTION__, __LINE__);
return;
}
// remove linear component (now we have k and m (y=k*x+m))
for(unsigned int x = 0; x < cfgNfft_; ++x ) {
prepProcDataBuffer_[x] = prepProcDataBuffer_[x] - (k*x + m);
}
}
void ecmcFFT::printEcDataArray(uint8_t* data,
size_t size,
@@ -683,6 +785,17 @@ void ecmcFFT::initAsyn() {
}
doCallbacksFloat64Array(rawDataBuffer_, cfgNfft_, asynRawDataId_,0);
// Add rawdata "plugin.fft%d.preprocdata"
paramName =ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_PPDATA;
if( createParam(0, paramName.c_str(), asynParamFloat64Array, &asynPPDataId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter preprocdata");
}
doCallbacksFloat64Array(prepProcDataBuffer_, cfgNfft_, asynPPDataId_,0);
// Add fft amplitude "plugin.fft%d.fftamplitude"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_AMP;
@@ -690,9 +803,9 @@ void ecmcFFT::initAsyn() {
if( createParam(0, paramName.c_str(), asynParamFloat64Array, &asynFFTAmpId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter fftamplitude");
}
doCallbacksFloat64Array(fftBufferResultAmp_, cfgNfft_/2+1, asynFFTXAxisId_,0);
doCallbacksFloat64Array(fftBufferResultAmp_, cfgNfft_/2+1, asynFFTAmpId_,0);
// Add fft mode "plugin.fft%d.mode"
// Add fft "plugin.fft%d.mode"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_MODE;
@@ -701,7 +814,7 @@ void ecmcFFT::initAsyn() {
}
setIntegerParam(asynFFTModeId_, (epicsInt32)cfgMode_);
// Add fft mode "plugin.fft%d.status"
// Add fft "plugin.fft%d.status"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_STAT;
@@ -710,16 +823,16 @@ void ecmcFFT::initAsyn() {
}
setIntegerParam(asynFFTStatId_, (epicsInt32)status_);
// Add fft mode "plugin.fft%d.source"
// Add fft "plugin.fft%d.source"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_SOURCE;
if( createParam(0, paramName.c_str(), asynParamInt8Array, &asynSourceId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter source");
}
doCallbacksInt8Array(cfgDataSourceStr_, strlen(cfgDataSourceStr_), asynSourceId_,0);
doCallbacksInt8Array((epicsInt8*)cfgDataSourceStr_, strlen(cfgDataSourceStr_), asynSourceId_,0);
// Add fft mode "plugin.fft%d.trigg"
// Add fft "plugin.fft%d.trigg"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_TRIGG;
@@ -728,7 +841,7 @@ void ecmcFFT::initAsyn() {
}
setIntegerParam(asynTriggId_, (epicsInt32)triggOnce_);
// Add fft mode "plugin.fft%d.fftxaxis"
// Add fft "plugin.fft%d.fftxaxis"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_X_FREQS;
@@ -737,23 +850,32 @@ void ecmcFFT::initAsyn() {
}
doCallbacksFloat64Array(fftBufferXAxis_,cfgNfft_ / 2 + 1, asynFFTXAxisId_,0);
// Add fft mode "plugin.fft%d.nfft"
// Add fft "plugin.fft%d.nfft"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_NFFT;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynNfftId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter trigg");
throw std::runtime_error("Failed create asyn parameter nfft");
}
setIntegerParam(asynNfftId_, (epicsInt32)cfgNfft_);
// Add fft mode "plugin.fft%d.rate"
// Add fft "plugin.fft%d.rate"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_RATE;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynSRateId_ ) != asynSuccess ) {
if( createParam(0, paramName.c_str(), asynParamFloat64, &asynSRateId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rate");
}
setDoubleParam(asynSRateId_, cfgDataSampleRateHz_);
// Add fft "plugin.fft%d.buffid"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_BUFF_ID;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynElementsInBuffer_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter trigg");
}
setDoubleParam(asynSRateId_, cfgFFTSampleRateHz_);
setIntegerParam(asynElementsInBuffer_, (epicsInt32)elementsInBuffer_);
// Update integers
callParamCallbacks();
@@ -789,6 +911,9 @@ FFT_STATUS ecmcFFT::getStatusFFT() {
void ecmcFFT::updateStatus(FFT_STATUS status) {
status_ = status;
setIntegerParam(asynFFTStatId_,(epicsInt32) status);
setIntegerParam(asynElementsInBuffer_, (epicsInt32)elementsInBuffer_);
callParamCallbacks();
}
@@ -800,14 +925,18 @@ void ecmcFFT::doCalcWorker() {
if(destructs_) {
break;
}
// Pre-process
removeDCOffset(); // Remove dc on rawdata
removeLin(); // Remove fitted line
// Process
calcFFT(); // FFT cacluation
// Post-process
scaleFFT(); // Scale FFT
calcFFTAmp(); // Calculate amplitude from complex
calcFFTXAxis(); // Calculate x axis
doCallbacksFloat64Array(rawDataBuffer_, cfgNfft_, asynRawDataId_, 0);
doCallbacksFloat64Array(prepProcDataBuffer_, cfgNfft_, asynPPDataId_, 0);
doCallbacksFloat64Array(fftBufferResultAmp_,cfgNfft_/2+1, asynFFTAmpId_, 0);
doCallbacksFloat64Array(fftBufferXAxis_, cfgNfft_/2+1, asynFFTXAxisId_,0);
callParamCallbacks();
@@ -860,6 +989,9 @@ asynStatus ecmcFFT::readInt32(asynUser *pasynUser, epicsInt32 *value) {
}else if( function == asynNfftId_ ){
*value = (epicsInt32)cfgNfft_;
return asynSuccess;
}else if( function == asynElementsInBuffer_){
*value = (epicsInt32)elementsInBuffer_;
return asynSuccess;
}
return asynError;
@@ -876,6 +1008,14 @@ asynStatus ecmcFFT::readFloat64Array(asynUser *pasynUser, epicsFloat64 *value,
memcpy (value, rawDataBuffer_, ncopy);
*nIn = ncopy;
return asynSuccess;
} else if( function == asynPPDataId_) {
unsigned int ncopy = cfgNfft_;
if(nElements < ncopy) {
ncopy = nElements;
}
memcpy (value, prepProcDataBuffer_, ncopy);
*nIn = ncopy;
return asynSuccess;
} else if( function == asynFFTXAxisId_ ) {
unsigned int ncopy = cfgNfft_/ 2 + 1;
if(nElements < ncopy) {
@@ -918,9 +1058,63 @@ asynStatus ecmcFFT::readInt8Array(asynUser *pasynUser, epicsInt8 *value,
asynStatus ecmcFFT::readFloat64(asynUser *pasynUser, epicsFloat64 *value) {
int function = pasynUser->reason;
if( function == asynSRateId_ ) {
*value = cfgFFTSampleRateHz_;
*value = cfgDataSampleRateHz_;
return asynSuccess;
}
return asynError;
}
/* y = k*x+m */
int ecmcFFT::leastSquare(int n, const double y[], double* k, double* m){
double sumx = 0.0;
double sumx2 = 0.0;
double sumxy = 0.0;
double sumy = 0.0;
double sumy2 = 0.0;
for (int x = 0; x < n; ++x){
//simulate x by just index
sumx += x;
sumx2 += x * x;
sumxy += x * y[x];
sumy += y[x];
sumy2 += y[x] * y[x];
}
double denom = (n * sumx2 - sumx * sumx);
if (denom == 0) {
// Cannot dive by 0.. something wrong..
*k = 0;
*m = 0;
return 1; // Error
}
*k = (n * sumxy - sumx * sumy) / denom;
*m = (sumy * sumx2 - sumx * sumxy) / denom;
return 0;
}
bool ecmcFFT::verifyBreakTable() {
brkTable *pbrkTable;
bool found = false;
for(pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList);
pbrkTable;
pbrkTable = (brkTable *)ellNext(&pbrkTable->node)){
if (strcmp(cfgBreakTableStr_,pbrkTable->name)==0)
{
found = true;
breakTable_ = pbrkTable;
break;
}
}
if(!found) {
breakTable_ = NULL;
throw std::out_of_range("Breaktable not found...");
}
return found;
}

View File

@@ -19,6 +19,7 @@
#include "inttypes.h"
#include <string>
#include "kissfft/kissfft.hh"
#include "dbBase.h"
class ecmcFFT : public asynPortDriver {
public:
@@ -64,15 +65,18 @@ class ecmcFFT : public asynPortDriver {
void calcFFTAmp();
void calcFFTXAxis();
void removeDCOffset();
void removeLin();
void initAsyn();
void updateStatus(FFT_STATUS status); // Also updates asynparam
static int dataTypeSupported(ecmcEcDataType dt);
bool verifyBreakTable();
ecmcDataItem *dataItem_;
ecmcDataItemInfo *dataItemInfo_;
ecmcAsynPortDriver *asynPort_;
kissfft<double>* fftDouble_;
double* rawDataBuffer_; // Input data (real)
double* prepProcDataBuffer_; // Preprocessed data (real)
std::complex<double>* fftBufferInput_; // Result (complex)
std::complex<double>* fftBufferResult_; // Result (complex)
double* fftBufferResultAmp_; // Resulting amplitude (abs of fftBufferResult_)
@@ -88,22 +92,29 @@ class ecmcFFT : public asynPortDriver {
int triggOnce_;
int cycleCounter_;
int ignoreCycles_;
void *breakTable_;
short lastBreakPoint_;
double scale_; // Config: Data set size
FFT_STATUS status_; // Status/state (NO_STAT, IDLE, ACQ, CALC)
// Config options
char* cfgDataSourceStr_; // Config: data source string
char* cfgBreakTableStr_; // Config: EPICS breaktable name
int cfgDbgMode_; // Config: allow dbg printouts
int cfgApplyScale_; // Config: apply scale 1/nfft
int cfgDcRemove_; // Config: remove dc (average)
int cfgLinRemove_; // Config: remove linear componet (by least square)
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)
double cfgFFTSampleRateHz_; // Config: Sample rate (defaults to ecmc rate)
double cfgScale_;
double cfgDataSampleRateHz_; // Config: Sample for data
// Asyn
int asynEnableId_; // Enable/disable acq./calcs
int asynRawDataId_; // Raw data (input) array (double)
int asynPPDataId_; // Pre-processed data array (double)
int asynFFTAmpId_; // FFT amplitude array (double)
int asynFFTModeId_; // FFT mode (cont/trigg)
int asynFFTStatId_; // FFT status (no_stat/idle/acq/calc)
@@ -112,6 +123,7 @@ class ecmcFFT : public asynPortDriver {
int asynFFTXAxisId_; // FFT X-axis frequencies
int asynNfftId_; // NFFT
int asynSRateId_; // Sample rate
int asynElementsInBuffer_; // Current buffer index
// Thread related
epicsEvent doCalcEvent_;
@@ -137,6 +149,10 @@ class ecmcFFT : public asynPortDriver {
size_t elements,
int objId);
static std::string to_string(int value);
static int leastSquare(int n,
const double y[],
double* k,
double* m); // y=kx+m
};
#endif /* ECMC_FFT_H_ */

View File

@@ -18,10 +18,14 @@
#define ECMC_PLUGIN_DBG_PRINT_OPTION_CMD "DBG_PRINT="
#define ECMC_PLUGIN_SOURCE_OPTION_CMD "SOURCE="
#define ECMC_PLUGIN_NFFT_OPTION_CMD "NFFT="
#define ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD "APPLY_SCALE="
#define ECMC_PLUGIN_DC_REMOVE_OPTION_CMD "DC_REMOVE="
//#define ECMC_PLUGIN_APPLY_SCALE_OPTION_CMD "APPLY_SCALE="
#define ECMC_PLUGIN_RM_DC_OPTION_CMD "RM_DC="
#define ECMC_PLUGIN_ENABLE_OPTION_CMD "ENABLE="
#define ECMC_PLUGIN_RATE_OPTION_CMD "RATE="
#define ECMC_PLUGIN_RM_LIN_OPTION_CMD "RM_LIN="
#define ECMC_PLUGIN_SCALE_OPTION_CMD "SCALE="
#define ECMC_PLUGIN_BREAKTABLE_OPTION_CMD "BREAKTABLE="
// CONT, TRIGG
#define ECMC_PLUGIN_MODE_OPTION_CMD "MODE="
#define ECMC_PLUGIN_MODE_CONT_OPTION "CONT"

View File

@@ -3,16 +3,18 @@
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcPluginExample.cpp
* ecmcPluginFFT.cpp
*
* Created on: Mar 21, 2020
* Author: anderssandstrom
*
* Instructions:
* - IMPORTANT: Add "USR_CFLAGS +=-DECMC_PLUGIN_MODULE_NAME=${MODULE}" to GNUMakefile
* - All functions and ecmcPluginData struct must be declared static
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#define ECMC_EXAMPLE_PLUGIN_VERSION 1
#define ECMC_EXAMPLE_PLUGIN_VERSION 2
#ifdef __cplusplus
extern "C" {
@@ -30,11 +32,11 @@ static int lastEcmcError = 0;
static char* lastConfStr = NULL;
/** Optional.
* Will be called once after successfull load into ecmc.
* Will be called once after successful load into ecmc.
* Return value other than 0 will be considered error.
* configStr can be used for configuration parameters.
**/
int fftConstruct(char *configStr)
static int fftConstruct(char *configStr)
{
//This module is allowed to load several times so no need to check if loaded
@@ -46,7 +48,7 @@ int fftConstruct(char *configStr)
/** Optional function.
* Will be called once at unload.
**/
void fftDestruct(void)
static void fftDestruct(void)
{
deleteAllFFTs();
if(lastConfStr){
@@ -55,12 +57,12 @@ void fftDestruct(void)
}
/** Optional function.
* Will be called each realtime cycle if definded
* ecmcError: Error code of ecmc. Makes it posible for
* Will be called each realtime cycle if defined
* ecmcError: Error code of ecmc. Makes it possible for
* this plugin to react on ecmc errors
* Return value other than 0 will be considered to be an error code in ecmc.
**/
int fftRealtime(int ecmcError)
static int fftRealtime(int ecmcError)
{
lastEcmcError = ecmcError;
return 0;
@@ -69,7 +71,7 @@ int fftRealtime(int ecmcError)
/** Link to data source here since all sources should be availabe at this stage
* (for example ecmc PLC variables are defined only at enter of realtime)
**/
int fftEnterRT(){
static int fftEnterRT(){
return linkDataToFFTs();
}
@@ -77,52 +79,54 @@ int fftEnterRT(){
* Will be called once just before leaving realtime mode
* Return value other than 0 will be considered error.
**/
int fftExitRT(void){
static int fftExitRT(void){
return 0;
}
// Plc function for clear of buffers
double fft_clear(double index) {
static double fft_clear(double index) {
return (double)clearFFT((int)index);
}
// Plc function for enable
double fft_enable(double index, double enable) {
static double fft_enable(double index, double enable) {
return (double)enableFFT((int)index, (int)enable);
}
// Plc function for trigg new measurement (will clear buffers)
double fft_trigg(double index) {
static double fft_trigg(double index) {
return (double)triggFFT((int)index);
}
// Plc function for enable
double fft_mode(double index, double mode) {
static double fft_mode(double index, double mode) {
return (double)modeFFT((int)index, (FFT_MODE)((int)mode));
}
// Plc function for enable
double fft_stat(double index) {
static double fft_stat(double index) {
return (double)statFFT((int)index);
}
// Register data for plugin so ecmc know what to use
struct ecmcPluginData pluginDataDef = {
static struct ecmcPluginData pluginDataDef = {
// Allways use ECMC_PLUG_VERSION_MAGIC
.ifVersion = ECMC_PLUG_VERSION_MAGIC,
// Name
.name = "ecmcPlugin_FFT",
.name = "ecmc_plugin_fft",
// Description
.desc = "FFT plugin for use with ecmc.",
// Option description
.optionDesc = "\n "ECMC_PLUGIN_DBG_PRINT_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, default = 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."
.optionDesc = "\n "ECMC_PLUGIN_DBG_PRINT_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_SCALE_OPTION_CMD"scalefactor : Apply scale to source data, default = 1.0.\n"
" "ECMC_PLUGIN_RM_DC_OPTION_CMD"<1/0> : Remove DC offset of input data (SOURCE), default = disabled.\n"
" "ECMC_PLUGIN_RM_LIN_OPTION_CMD"<1/0> : Remove linear component in data (SOURCE) by least square, 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.\n"
" "ECMC_PLUGIN_BREAKTABLE_OPTION_CMD"<brktab> : Use epics breaktable to convert raw values (applied before any other signal cond. alg.), default not used."
,
// Plugin version
.version = ECMC_EXAMPLE_PLUGIN_VERSION,
@@ -157,7 +161,8 @@ struct ecmcPluginData pluginDataDef = {
.funcArg7 = NULL,
.funcArg8 = NULL,
.funcArg9 = NULL,
.funcArg10 = NULL,
.funcArg10 = NULL,
.funcGenericObj = NULL,
},
.funcs[1] =
{ /*----fft_enable----*/
@@ -179,7 +184,8 @@ struct ecmcPluginData pluginDataDef = {
.funcArg7 = NULL,
.funcArg8 = NULL,
.funcArg9 = NULL,
.funcArg10 = NULL,
.funcArg10 = NULL,
.funcGenericObj = NULL,
},
.funcs[2] =
{ /*----fft_trigg----*/
@@ -201,7 +207,8 @@ struct ecmcPluginData pluginDataDef = {
.funcArg7 = NULL,
.funcArg8 = NULL,
.funcArg9 = NULL,
.funcArg10 = NULL,
.funcArg10 = NULL,
.funcGenericObj = NULL,
},
.funcs[3] =
{ /*----fft_mode----*/
@@ -223,7 +230,8 @@ struct ecmcPluginData pluginDataDef = {
.funcArg7 = NULL,
.funcArg8 = NULL,
.funcArg9 = NULL,
.funcArg10 = NULL,
.funcArg10 = NULL,
.funcGenericObj = NULL,
},
.funcs[4] =
{ /*----fft_stat----*/
@@ -245,7 +253,8 @@ struct ecmcPluginData pluginDataDef = {
.funcArg7 = NULL,
.funcArg8 = NULL,
.funcArg9 = NULL,
.funcArg10 = NULL,
.funcArg10 = NULL,
.funcGenericObj = NULL,
},
.funcs[5] = {0}, // last element set all to zero..
// PLC consts

8
tools/README.md Normal file
View File

@@ -0,0 +1,8 @@
# FFT tools
## GUI
A python gui for vizualization and control of the FFT plugin can be found in the ecmccomgui repo:
https://github.com/anderssandstrom/ecmccomgui
![ecmcFFTMainGui.py](docs/gui/ecmcFFTMainGui.png)