ESS Site-specific EPICS module : ecmcPlugin_FFT
A shared library with FFT functionalities loadable into ecmc:
https://github.com/epics-modules/ecmc (or local ess fork https://github.com/icshwi/ecmc).
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
The main functionality of this plugin is to make FFT analysis of ecmc data. Most data in ecmc is accessible:
- EtherCAT data ("ec0.s1.AI_1" or "ec0.s2.mm.CH1_ARRAY")
- Motion data ("ax1.actpos")
- PLC data ("plcs.plc0.static.testvalue")
Precautions have been taken in order to disturb ecmc real time thread as little as possible:
- All CPU intensive calculations are handled in a low prio work thread (not ecmc thread).
- Communication to epics is made via a dedicated asynPortDriver (not the same that ecmc uses).
See below for configuration options and the different modes supported by this ecmc plugin.
A plugin is loaded by the ecmccfg command loadPlugin: 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;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...
Note: If another plugin is loaded in between the loading of FFT plugins, it will have no affect on these FFT indexes (so the FFT index is not the same as plugin index).
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.
- 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.poserr;MODE=TRIGG;DBG_PRINT=1;ENABLE=1;"
The data source is defined by setting the SOURCE option in the plugin configuration string. This configuration is mandatory.
Example: Axis 1 actpos (See RM_LIN config below)
"DBG_PRINT=1;SOURCE=ax1.actpos;RM_LIN=1;"
Example: Ethercat slave 1 analog input ch1
"DBG_PRINT=1;SOURCE=ec0.s1.AI_1;"
Enable/disable printouts from plugin can be made bu setting the "DBG_PRINT" option.
Example: Disable
"DBG_PRINT=0;SOURCE=ax1.poserr;"
Defines number of samples for each measurement.
Note: Must be a n² number..
Example: 1024
"NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
Apply custom scale to input data.
Example: 5.0
"SCALE=5.0;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
Remove DC of input signal. Default is disabled.
Example: Remove DC offset
"RM_DC=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
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 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.
Example: Enable at startup by config
"ENABLE=1;RM_DC=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
Example: Enable FFT index 0 from EPICS:
caput IOC_TEST:Plugin-FFT0-Enable 1
Example: Enable FFT index 0 from ecmc PLC code:
fft_enable(0,1)
The FFT module can operate in two different modes:
- CONTINIOUS (CONT)
- TRIGGERED (TRIGG)
Continious mode:
- Clear data buffers
- Wait for enable (from plc or asyn/epics record or configuration(see above))
- Data acqusition starts
- When buffers are full a worker thread will be triggered to calculate the FFT.
- FTT results are sent by callback over asyn to epics records
- goto 1.
Note: data will be "lost" during calculation time.
Triggered mode:
- Clear data buffers
- Wait for enable (from plc or asyn/epics record or configuration(see above))
- Wait for trigger (from plc or asyn/epics record)
- Data acqusition starts
- When buffers are full a worker thread will be triggered to calculate the FFT.
- FTT results are sent by callback over asyn to epics records
- goto 1.
Example: Mode triggered
"MODE=TRIGG;ENABLE=1;RM_DC=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
Example: Mode from EPICS record
# CONT
caput IOC_TEST:Plugin-FFT0-Mode-RB 1
# TRIGG
caput IOC_TEST:Plugin-FFT0-Mode-RB 2
Note: The record is a output record with readback so can both be read and written to.
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.
Example: Rate = 100Hz
"RATE=100;MODE=TRIGG;ENABLE=1;RM_DC=1;SCALE=1;NFFT=1024;DBG_PRINT=0;SOURCE=ax1.poserr;"
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
}
Each FFT plugin object will create a new asynportdriver-port named "PLUGIN.FFT" (index is explaine above). The reason for a dedicated asynport is to disturb ecmc as little as possible. The plugin contains a template file, "ecmcPluginFFT.template", that will make most information availbe from records:
- Rawdata array (ro)
- FFT amplitude array (result) (ro)
- FFT x axis (frequencies) (ro)
- Status (ro)
- Mode (rw)
- Sample rate (ro)
- Enable cmd (rw)
- Trigger cmd (rw)
- NFFT (ro)
The available records from this template file can be listed by the cmd (here two FFT plugins loaded):
dbgrep *FFT*
IOC_TEST:Plugin-FFT0-SampleRate-Act
IOC_TEST:Plugin-FFT1-SampleRate-Act
IOC_TEST:Plugin-FFT0-Mode-RB
IOC_TEST:Plugin-FFT1-Mode-RB
IOC_TEST:Plugin-FFT0-Source
IOC_TEST:Plugin-FFT0-Raw-Data-Act
IOC_TEST:Plugin-FFT0-Spectrum-Amp-Act
IOC_TEST:Plugin-FFT0-Spectrum-X-Axis-Act
IOC_TEST:Plugin-FFT1-Source
IOC_TEST:Plugin-FFT1-Raw-Data-Act
IOC_TEST:Plugin-FFT1-Spectrum-Amp-Act
IOC_TEST:Plugin-FFT1-Spectrum-X-Axis-Act
IOC_TEST:Plugin-FFT0-Enable
IOC_TEST:Plugin-FFT0-Trigg
IOC_TEST:Plugin-FFT1-Enable
IOC_TEST:Plugin-FFT1-Trigg
IOC_TEST:Plugin-FFT0-stat
IOC_TEST:Plugin-FFT0-NFFT
IOC_TEST:Plugin-FFT1-stat
IOC_TEST:Plugin-FFT1-NFFT
Note: The FFT asynparameters will not be visible by the ecmcReport iocsh command since the FFT records belong to another port.
- "fft_clear(arg0);" double fft_clear(index) : Clear/resets all buffers fft[index].
- "fft_enable(arg0, arg1);" double fft_enable(index, enable) : Set enable for fft[index].
- "fft_trigg(arg0);" double fft_trigg(index) : Trigg new measurement for fft[index]. Will clear buffers.
- "fft_mode(arg0, arg1);" double fft_mode(index, mode) : Set mode Cont(1)/Trigg(2) for fft[index].
- "fft_stat(arg0);" double fft_stat(index) : Get status of fft (NO_STAT, IDLE, ACQ, CALC) for fft[index].
These constants are for use togheter with the above listed PLC functions:
- "fft_CONT" = 1: FFT Mode: Continious
- "fft_TRIGG" = 2: FFT Mode :Triggered
- "fft_NO_STAT" = 0: FFT Status: Invalid state
- "fft_IDLE" = 1: FFT Status: Idle state (waiting for trigger)
- "fft_ACQ" = 2: FFT Status: Acquiring data
- "fft_CALC" = 3: FFT Status: Calculating result
An example script can be found in the iocsh directory of this repo. This example load 2 FFT plugin objects:
- SOURCE="plcs.plc0.static.sineval" which is a plc variable updated as a sinus with default freq 5 Hz.
- SOURCE="ecmc.thread.latency.max". This is the execution latency of ecmc (in nanoseconds).
Start one FFT calc over EPICS records:
# Enable
caput IOC_TEST:Plugin-FFT0-Enable 1
# Trigg a new calc
caput IOC_TEST:Plugin-FFT1-Trigg 1
Epics record: IOC_TEST:Plugin-FFT0-Raw-Data-Act
Figure 1 Raw data. |
Epics records:
- X = IOC_TEST:Plugin-FFT0-Spectrum-X-Axis-Act
- Y = IOC_TEST:Plugin-FFT0-Spectrum-Amp-Act
Figure 2 Resulting FFT amplitude. |
static.time:=ec_get_time()/1E9;
static.sineval:=sin(2*pi*${FREQ=5}*static.time);
A simple tool, 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
A simple tool, 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
A simple generic tool, 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
- python 3.5
- epics
- PyQt5
- numpy
- matplotlib
Plugin info:
Index = 1
Name = ecmcPlugin_FFT
Description = FFT plugin for use with ecmc.
Option description =
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.
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.
Filename = /epics/base-7.0.3.1/require/3.1.2/siteMods/ecmcPlugin_FFT/master/lib/linux-arm/libecmcPlugin_FFT.so
Config string = SOURCE=ecmc.thread.latency.max;DBG_PRINT=0;NFFT=1024;
Version = 1
Interface version = 512 (ecmc = 512)
max plc funcs = 64
max plc func args = 10
max plc consts = 64
Construct func = @0xb5022044
Enter realtime func = @0xb5022090
Exit realtime func = @0xb502203c
Realtime func = @0xb5022034
Destruct func = @0xb502206c
dlhandle = @0x1d9de28
Plc functions:
funcs[00]:
Name = "fft_clear(arg0);"
Desc = double fft_clear(index) : Clear/reset fft[index].
Arg count = 1
func = @0xb5022094
funcs[01]:
Name = "fft_enable(arg0, arg1);"
Desc = double fft_enable(index, enable) : Set enable for fft[index].
Arg count = 2
func = @0xb50220b0
funcs[02]:
Name = "fft_trigg(arg0);"
Desc = double fft_trigg(index) : Trigg new measurement for fft[index]. Will clear buffers.
Arg count = 1
func = @0xb50220d4
funcs[03]:
Name = "fft_mode(arg0, arg1);"
Desc = double fft_mode(index, mode) : Set mode Cont(1)/Trigg(2) for fft[index].
Arg count = 2
func = @0xb50220f0
funcs[04]:
Name = "fft_stat(arg0);"
Desc = double fft_stat(index) : Get status of fft (NO_STAT, IDLE, ACQ, CALC) for fft[index].
Arg count = 1
func = @0xb5022114
Plc constants:
consts[00]:
Name = "fft_CONT" = 1.000
Desc = FFT Mode: Continious
consts[01]:
Name = "fft_TRIGG" = 2.000
Desc = FFT Mode :Triggered
consts[02]:
Name = "fft_NO_STAT" = 0.000
Desc = FFT Status: Invalid state
consts[03]:
Name = "fft_IDLE" = 1.000
Desc = FFT Status: Idle state (waiting for trigger)
consts[04]:
Name = "fft_ACQ" = 2.000
Desc = FFT Status: Acquiring data
consts[05]:
Name = "fft_CALC" = 3.000
Desc = FFT Status: Calculating result