This commit is contained in:
Anders Sandstrom
2021-03-01 13:31:28 +01:00
parent 01a1f21321
commit 027c5c5d16
6 changed files with 62 additions and 1509 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,153 +0,0 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcFFT.h
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
*
\*************************************************************************/
#ifndef ECMC_FFT_H_
#define ECMC_FFT_H_
#include <stdexcept>
#include "ecmcDataItem.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcFFTDefs.h"
#include "inttypes.h"
#include <string>
#include "kissfft/kissfft.hh"
class ecmcFFT : public asynPortDriver {
public:
/** ecmc FFT class
* This object can throw:
* - bad_alloc
* - invalid_argument
* - runtime_error
* - out_of_range
*/
ecmcFFT(int fftIndex, // index of this object
char* configStr,
char* portName);
~ecmcFFT();
// Add data to buffer (called from "external" callback)
void dataUpdatedCallback(uint8_t* data,
size_t size,
ecmcEcDataType dt);
// Call just before realtime because then all data sources should be available
void connectToDataSource();
void setEnable(int enable);
void setModeFFT(FFT_MODE mode);
FFT_STATUS getStatusFFT();
void clearBuffers();
void triggFFT();
void doCalcWorker(); // Called from worker thread calc the results
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
virtual asynStatus readFloat64Array(asynUser *pasynUser, epicsFloat64 *value,
size_t nElements, size_t *nIn);
virtual asynStatus readInt8Array(asynUser *pasynUser, epicsInt8 *value,
size_t nElements, size_t *nIn);
virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value);
private:
void parseConfigStr(char *configStr);
void addDataToBuffer(double data);
void calcFFT();
void scaleFFT();
void calcFFTAmp();
void calcFFTXAxis();
void removeDCOffset();
void removeLin();
void initAsyn();
void updateStatus(FFT_STATUS status); // Also updates asynparam
static int dataTypeSupported(ecmcEcDataType dt);
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_)
double* fftBufferXAxis_; // FFT x axis with freqs
size_t elementsInBuffer_;
double ecmcSampleRateHz_;
int dataSourceLinked_; // To avoid link several times
// ecmc callback handle for use when deregister at unload
int callbackHandle_;
int fftWaitingForCalc_;
int destructs_;
int objectId_; // Unique object id
int triggOnce_;
int cycleCounter_;
int ignoreCycles_;
double scale_; // Config: Data set size
FFT_STATUS status_; // Status/state (NO_STAT, IDLE, ACQ, CALC)
// Config options
char* cfgDataSourceStr_; // Config: data source string
int cfgDbgMode_; // Config: allow dbg printouts
int cfgApplyScale_; // Config: apply scale 1/nfft
int cfgDcRemove_; // Config: remove dc (average)
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 (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)
int asynSourceId_; // SOURCE
int asynTriggId_; // Trigg new measurement
int asynFFTXAxisId_; // FFT X-axis frequencies
int asynNfftId_; // NFFT
int asynSRateId_; // Sample rate
int asynElementsInBuffer_; // Current buffer index
// Thread related
epicsEvent doCalcEvent_;
// Some generic utility functions
static uint8_t getUint8(uint8_t* data);
static int8_t getInt8(uint8_t* data);
static uint16_t getUint16(uint8_t* data);
static int16_t getInt16(uint8_t* data);
static uint32_t getUint32(uint8_t* data);
static int32_t getInt32(uint8_t* data);
static uint64_t getUint64(uint8_t* data);
static int64_t getInt64(uint8_t* data);
static float getFloat32(uint8_t* data);
static double getFloat64(uint8_t* data);
static size_t getEcDataTypeByteSize(ecmcEcDataType dt);
static void printEcDataArray(uint8_t* data,
size_t size,
ecmcEcDataType dt,
int objId);
static void printComplexArray(std::complex<double>* fftBuff,
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

@@ -1,54 +0,0 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcFFTDefs.h
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
#ifndef ECMC_FFT_DEFS_H_
#define ECMC_FFT_DEFS_H_
// Options
#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_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="
// CONT, TRIGG
#define ECMC_PLUGIN_MODE_OPTION_CMD "MODE="
#define ECMC_PLUGIN_MODE_CONT_OPTION "CONT"
#define ECMC_PLUGIN_MODE_TRIGG_OPTION "TRIGG"
typedef enum FFT_MODE{
NO_MODE = 0,
CONT = 1,
TRIGG = 2,
} FFT_MODE;
typedef enum FFT_STATUS{
NO_STAT = 0,
IDLE = 1, // Doing nothing, waiting for trigg
ACQ = 2, // Acquireing data
CALC = 3, // Calc FFT
} FFT_STATUS;
/** Just one error code in "c" part of plugin
(error handled with exceptions i c++ part) */
#define ECMC_PLUGIN_FFT_ERROR_CODE 1
// Default size (must be n²)
#define ECMC_PLUGIN_DEFAULT_NFFT 4096
#endif /* ECMC_FFT_DEFS_H_ */

View File

@@ -1,133 +0,0 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcFFTWrap.cpp
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#include <vector>
#include <stdexcept>
#include <string>
#include "ecmcFFTWrap.h"
#include "ecmcFFT.h"
#include "ecmcFFTDefs.h"
#define ECMC_PLUGIN_MAX_PORTNAME_CHARS 64
#define ECMC_PLUGIN_PORTNAME_PREFIX "PLUGIN.FFT"
static std::vector<ecmcFFT*> ffts;
static int fftObjCounter = 0;
static char portNameBuffer[ECMC_PLUGIN_MAX_PORTNAME_CHARS];
int createFFT(char* configStr) {
// create new ecmcFFT object
ecmcFFT* fft = NULL;
// create asynport name for new object ()
memset(portNameBuffer, 0, ECMC_PLUGIN_MAX_PORTNAME_CHARS);
snprintf (portNameBuffer, ECMC_PLUGIN_MAX_PORTNAME_CHARS,
ECMC_PLUGIN_PORTNAME_PREFIX "%d", fftObjCounter);
try {
fft = new ecmcFFT(fftObjCounter, configStr, portNameBuffer);
}
catch(std::exception& e) {
if(fft) {
delete fft;
}
printf("Exception: %s. Plugin will unload.\n",e.what());
return ECMC_PLUGIN_FFT_ERROR_CODE;
}
ffts.push_back(fft);
fftObjCounter++;
return 0;
}
void deleteAllFFTs() {
for(std::vector<ecmcFFT*>::iterator pfft = ffts.begin(); pfft != ffts.end(); ++pfft) {
if(*pfft) {
delete (*pfft);
}
}
}
int linkDataToFFTs() {
for(std::vector<ecmcFFT*>::iterator pfft = ffts.begin(); pfft != ffts.end(); ++pfft) {
if(*pfft) {
try {
(*pfft)->connectToDataSource();
}
catch(std::exception& e) {
printf("Exception: %s. Plugin will unload.\n",e.what());
return ECMC_PLUGIN_FFT_ERROR_CODE;
}
}
}
return 0;
}
int enableFFT(int fftIndex, int enable) {
try {
ffts.at(fftIndex)->setEnable(enable);
}
catch(std::exception& e) {
printf("Exception: %s. FFT index out of range.\n",e.what());
return ECMC_PLUGIN_FFT_ERROR_CODE;
}
return 0;
}
int clearFFT(int fftIndex) {
try {
ffts.at(fftIndex)->clearBuffers();
}
catch(std::exception& e) {
printf("Exception: %s. FFT index out of range.\n",e.what());
return ECMC_PLUGIN_FFT_ERROR_CODE;
}
return 0;
}
int triggFFT(int fftIndex) {
try {
ffts.at(fftIndex)->triggFFT();
}
catch(std::exception& e) {
printf("Exception: %s. FFT index out of range.\n",e.what());
return ECMC_PLUGIN_FFT_ERROR_CODE;
}
return 0;
}
int modeFFT(int fftIndex, FFT_MODE mode) {
try {
ffts.at(fftIndex)->setModeFFT(mode);
}
catch(std::exception& e) {
printf("Exception: %s. FFT index out of range.\n",e.what());
return ECMC_PLUGIN_FFT_ERROR_CODE;
}
return 0;
}
FFT_STATUS statFFT(int fftIndex) {
try {
return ffts.at(fftIndex)->getStatusFFT();
}
catch(std::exception& e) {
printf("Exception: %s. FFT index out of range.\n",e.what());
return NO_STAT;
}
return NO_STAT;
}

View File

@@ -1,112 +0,0 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcFFTWrap.h
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
*
\*************************************************************************/
#ifndef ECMC_FFT_WRAP_H_
#define ECMC_FFT_WRAP_H_
#include "ecmcFFTDefs.h"
# ifdef __cplusplus
extern "C" {
# endif // ifdef __cplusplus
/** \brief Create new FFT object
*
* The plugin supports creation of multiple FFT objects\n
* (if loaded several times).\n
* The different fft are adressed by fftindex (in other functions below).\n
* The first loaded fft get index 0 and then increases for each load.\n
* This function call will create the custom asynparameters dedicated for this plugin.\
* The configuration string needs to define a data source by:\n
* "SOURCE=<data source>;"\n
* Example:\n
* "SOURCE=ec0.s1.AI_1";\n
* \param[in] configStr Configuration string.\n
*
* \return 0 if success or otherwise an error code.\n
*/
int createFFT(char *configStr);
/** \brief Enable/disable FFT object
*
* Enable/disable FFT object. If disabled no data will be acquired\n
* and no calculations will be made.\n
* \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n
* \param[in] enable enable/disable (1/0).\n
*
* \return 0 if success or otherwise an error code.\n
*/
int enableFFT(int fftIndex, int enable);
/** \brief Clear FFT object\n
*
* Clears buffers. After this command the acquistion can start from scratch.\n
* \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n
*
* \return 0 if success or otherwise an error code.\n
*/
int clearFFT(int fftIndex);
/** \brief Set mode of FFT object
*
* The FFT object can measure in two differnt modes:\n
* CONT(1) : Continious measurement (Acq data, calc, then Acq data ..)\n
* TRIGG(2): Measurements are triggered from plc or over asyn and is only done once (untill next trigger)\n
* \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n
* \param[in] mode Mode CONT(1) or TRIGG(2)\n
*
* \return 0 if success or otherwise an error code.\n
*/
int modeFFT(int fftIndex, FFT_MODE mode);
/** \brief Trigger FFT object\n
*
* If in triggered mode a new measurment cycle is initiated (fft will be cleared first).\n
* \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n
*
* \return 0 if success or otherwise an error code.\n
*/
int triggFFT(int fftIndex);
/** \brief Get status of FFT object
*
* The FFT object can be in different states:\n
* NO_STAT(0): Invalid state (something is most likely wrong)\n
* IDLE(1) : Waiting for trigger in triggered mode\n
* ACQ(2) : Acquiring data (filling data buffer)\n
* CALC(3) : Calculating FFT results\n
* \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n
*
* \return Status of fft (if index is out of range NO_STAT will be returned).\n
*/
FFT_STATUS statFFT(int fftIndex);
/** \brief Link data to _all_ fft objects
*
* This tells the FFT lib to connect to ecmc to find it's data source.\n
* This function should be called just before entering realtime since then all\n
* data sources in ecmc will be definded (plc sources are compiled just before runtime\n
* so are only fist accesible now).\n
* \return 0 if success or otherwise an error code.\n
*/
int linkDataToFFTs();
/** \brief Deletes all created fft objects\n
*
* Should be called when destructs.\n
*/
void deleteAllFFTs();
# ifdef __cplusplus
}
# endif // ifdef __cplusplus
#endif /* ECMC_FFT_WRAP_H_ */

View File

@@ -299,3 +299,65 @@ ecmc_plugin_register(pluginDataDef);
# ifdef __cplusplus
}
# endif // ifdef __cplusplus
/**/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int
main(void)
{
int s;
int nbytes;
struct sockaddr_can addr;
struct can_frame frame;
struct ifreq ifr;
const char *ifname = "vcan0";
if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1) {
perror("Error while opening socket");
return -1;
}
strcpy(ifr.ifr_name, ifname);
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
printf("%s at index %d\n", ifname, ifr.ifr_ifindex);
if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("Error in socket bind");
return -2;
}
frame.can_id = 0x123;
frame.can_dlc = 2;
frame.data[0] = 0x11;
frame.data[1] = 0x22;
nbytes = write(s, &frame, sizeof(struct can_frame));
printf("Wrote %d bytes\n", nbytes);
return 0;
}