Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30ceea65d6 | |||
| 01214019a6 | |||
| 90339dae2f | |||
| d17d5d7f5d | |||
| fcee0a8ff4 | |||
|
|
a84383815d | ||
|
|
223fd94a86 | ||
|
|
96d66df11b | ||
|
|
87c8084ae0 | ||
|
|
a581ddc559 | ||
|
|
5418c98488 | ||
|
|
e6f5286836 | ||
|
|
028d56d83b | ||
|
|
ae67695c6a | ||
|
|
08542988a1 | ||
|
|
5938255f9e | ||
|
|
f49fdb2d76 | ||
|
|
b43aa23f4b | ||
|
|
f1e460e6dc | ||
|
|
d89c34d041 | ||
|
|
c8f24ca340 | ||
|
|
fd31b8957e | ||
|
|
e4e379a392 | ||
|
|
a72c764098 | ||
|
|
bdd937ad47 | ||
|
|
47f5ce247c | ||
|
|
b163fcab98 | ||
|
|
fd3115bbcf | ||
|
|
6dbdc97957 | ||
|
|
ecbf760a51 | ||
|
|
be359cbfa8 | ||
|
|
e2f15ad698 | ||
|
|
ca35cd929c | ||
|
|
c37694e796 | ||
|
|
23dfc5284a | ||
|
|
90539a098e | ||
|
|
ceeb3da16c | ||
|
|
4cd8f922e4 | ||
|
|
ffc1d7820e | ||
|
|
41a07f984a | ||
|
|
7ae0efaef3 | ||
|
|
a7faed674d | ||
|
|
460f8e935e | ||
|
|
6c64cb3607 | ||
|
|
a43f893336 | ||
|
|
de04f2b686 | ||
|
|
c5d255c82d | ||
|
|
e389f0b39d | ||
|
|
f27f27df39 | ||
|
|
6029642b78 | ||
|
|
999bf637d2 | ||
|
|
9ce4fbe14b | ||
|
|
210a71e4ad | ||
|
|
a863a7e400 | ||
|
|
9bcbbfd1b0 | ||
|
|
deca91d7aa | ||
|
|
2767b2676e | ||
|
|
c4f1ee6574 | ||
|
|
6775c92a60 | ||
|
|
88bbddd58a | ||
|
|
2cce34dbd1 | ||
|
|
424e00e4e0 | ||
|
|
f212815a0e | ||
|
|
db67919b85 | ||
|
|
07385b261a | ||
|
|
375e5e7b21 | ||
|
|
85b231537b | ||
|
|
24fd9bbb3d | ||
|
|
4df1650541 | ||
|
|
0989b969ba | ||
|
|
c40fdcfabc | ||
|
|
4c8edccfb8 | ||
|
|
a136c5f4b2 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -13,4 +13,6 @@ core.*
|
||||
*_old/
|
||||
*PVs.list
|
||||
*-loc/*.Makefile
|
||||
ecmcPlugin_Simple-loc/*.Makefile
|
||||
ecmc_plugin_fft/*.Makefile
|
||||
*__*
|
||||
O.*
|
||||
34
GNUmakefile
Normal file
34
GNUmakefile
Normal 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 7.0.9
|
||||
|
||||
IGNORE_MODULES += asynMotor
|
||||
IGNORE_MODULES += motorBase
|
||||
|
||||
USR_CXXFLAGS += -std=c++17
|
||||
OPT_CXXFLAGS_YES = -O3
|
||||
|
||||
# dependencies
|
||||
ecmc_VERSION = 10.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
|
||||
28
Makefile
28
Makefile
@@ -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
191
README.md
@@ -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
|
||||
```
|
||||

|
||||
|
||||
### 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
|
||||
```
|
||||

|
||||
|
||||
### 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
|
||||
```
|
||||

|
||||
|
||||
### 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
BIN
docs/gui/ecmcArrayGui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/gui/ecmcFFTGui.png
Normal file
BIN
docs/gui/ecmcFFTGui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/gui/ecmcFFTMainGui.png
Normal file
BIN
docs/gui/ecmcFFTMainGui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
@@ -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
|
||||
|
||||
###
|
||||
@@ -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
|
||||
|
||||
@@ -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"}")
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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"
|
||||
@@ -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
8
tools/README.md
Normal 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
|
||||
|
||||

|
||||
Reference in New Issue
Block a user