From d6c0e9dcc504b98353cc79cb4e8fe8001cb1be19 Mon Sep 17 00:00:00 2001 From: Jeff Hill Date: Tue, 10 Feb 2004 21:46:53 +0000 Subject: [PATCH] installed spectrum record --- src/misc/base.dbd | 1 + src/rec/Makefile | 2 + src/rec/spectrumRecord.cpp | 437 +++++++++++++++++++++++++++++++++++++ src/rec/spectrumRecord.dbd | 116 ++++++++++ 4 files changed, 556 insertions(+) create mode 100644 src/rec/spectrumRecord.cpp create mode 100644 src/rec/spectrumRecord.dbd diff --git a/src/misc/base.dbd b/src/misc/base.dbd index dc63cf846..19092b644 100644 --- a/src/misc/base.dbd +++ b/src/misc/base.dbd @@ -33,6 +33,7 @@ include "stringoutRecord.dbd" include "subRecord.dbd" include "subArrayRecord.dbd" include "waveformRecord.dbd" +include "spectrumRecord.dbd" # "Soft Channel", "Raw Soft Channel", and "Async Soft Channel" device support include "devSoft.dbd" diff --git a/src/rec/Makefile b/src/rec/Makefile index 5a442c42d..e5f0fa24a 100644 --- a/src/rec/Makefile +++ b/src/rec/Makefile @@ -46,6 +46,7 @@ DBDINC += stringoutRecord DBDINC += subRecord DBDINC += subArrayRecord DBDINC += waveformRecord +DBDINC += spectrumRecord LIBSRCS += aaiRecord.c LIBSRCS += aaoRecord.c @@ -79,6 +80,7 @@ LIBSRCS += stringoutRecord.c LIBSRCS += subRecord.c LIBSRCS += subArrayRecord.c LIBSRCS += waveformRecord.c +LIBSRCS += spectrumRecord.c LIBRARY_IOC += recIoc diff --git a/src/rec/spectrumRecord.cpp b/src/rec/spectrumRecord.cpp new file mode 100644 index 000000000..16cc92727 --- /dev/null +++ b/src/rec/spectrumRecord.cpp @@ -0,0 +1,437 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* base/src/rec $Id$ */ + +/* + * spectrum analyzer record support routines + * + * Author: Jeff Hill + * Ancestry: Based on the waveform record by Bob Dalesio and Marty Kraimer + * Date: 9-29-03 + */ +#include +#include +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "dbScan.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#include "epicsDSP.h" + +typedef double fft_t; + +#define GEN_SIZE_OFFSET +#include "spectrumRecord.h" +#undef GEN_SIZE_OFFSET + +static const fft_t one = 1.0; +static const fft_t four = 4.0; +static const fft_t PI = four * atan ( one ); + +static void changeFrequencies ( struct spectrumRecord * pwf ) +{ + unsigned long L = 1ul << pwf->logl; + + // freq change is unsuccessful if improper input parameters + if ( pwf->hfrq <= pwf->lfrq ) { + pwf->frok = 0; + return; + } + if ( pwf->hfrq > pwf->ifrq / 2.0 ) { + pwf->frok = 0; + return; + } + if ( pwf->ifrq <= 0.0 ) { + pwf->frok = 0; + return; + } + + if ( ! ( pwf->g && pwf->h && pwf->ival && + pwf->mag && pwf->ang && pwf->hamw ) ) { + pwf->frok = 0; + return; + } + + const fft_t phi0d2 = ( PI / 2.0 / pwf->nelm ) * + ( pwf->hfrq - pwf->lfrq ) / pwf->ifrq ; + for ( unsigned n = 0; n < pwf->nelm; n++ ) + { + const fft_t nd = n; + const fft_t radians = nd * nd * phi0d2; + pwf->h[n] = std::polar ( one, radians ); + } + for ( unsigned n = pwf->nelm; n < L-pwf->nin; n++ ) + { + pwf->h[n] = std::complex < fft_t > ( 0.0, 0.0 ); + } + for ( unsigned n = L-pwf->nin; n < L; n++ ) + { + const fft_t index = L - n; + const fft_t radians = index * index * phi0d2; + pwf->h[n] = std::polar ( one, radians ); + } + epicsOneDimFFT ( pwf->h, pwf->logl, false ); + pwf->frok = true; +} + +static void changeResolution ( struct spectrumRecord * pwf ) +{ + { + long nElemInTmp; + long status = dbGetNelements ( + & pwf->inpw, & nElemInTmp ); + if ( status ) { + pwf->nin = 1; + } + else { + if ( nElemInTmp < 1 ) { + pwf->nin = 1; + } + else { + pwf->nin = static_cast < unsigned long > + ( nElemInTmp ); + } + } + } + + if ( pwf->nelm <= 0 ) + pwf->nelm = 1; + + { + double tot = pwf->nin + pwf->nelm; + double dblL = 1.0 + log ( tot ) / log ( 2.0 ); + pwf->logl = static_cast < unsigned long > ( dblL + 0.5 ); + } + + unsigned long L = 1ul << pwf->logl; + assert ( L >= pwf->nin + pwf->nelm ); + delete pwf->g; + delete pwf->h; + delete pwf->ival; + delete pwf->hamw; + delete pwf->mag; + delete pwf->ang; + try { + pwf->g = new std::complex < fft_t > [L]; + pwf->h = new std::complex < fft_t > [L]; + pwf->ival = new fft_t [pwf->nin]; + pwf->hamw = new fft_t [pwf->nin]; + pwf->mag = new fft_t [pwf->nelm]; + pwf->ang = new fft_t [pwf->nelm]; + // initialize hamming window + // see Oppenhiem and Schafer figure 5.33 + for ( unsigned long n = 0; n < pwf->nin; n++ ) + { + pwf->hamw[n] = 0.54 - 0.46 * + cos ( 2 * PI * n / ( pwf->nin - 1 ) ); + } + } + catch (... ) { + recGblSetSevr ( pwf, CALC_ALARM, INVALID_ALARM ); + pwf->frok = false; + return; + } + changeFrequencies ( pwf ); +} + +static long init_record ( struct spectrumRecord * pwf, int pass ) +{ + if ( pass == 1 ) { + changeResolution ( pwf ); + } + return 0; +} + +static long special ( struct dbAddr * paddr, int after ) +{ + struct spectrumRecord * pwf = + reinterpret_cast < struct spectrumRecord * > ( paddr->precord ); + long status = 0; + if ( after ) { + switch ( paddr->special ) { + case ( SPC_MOD ): + if ( paddr->pfield == (void *) & pwf->nelm || + paddr->pfield == (void *) & pwf->inpw ) { + changeResolution ( pwf ); + } + else { + changeFrequencies ( pwf ); + } + break; + default: + recGblDbaddrError ( S_db_badChoice, paddr, "spectrum: special" ); + status = S_db_badChoice; + break; + } + } + return status; +} + +static void monitor ( struct spectrumRecord *pwf ) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(pwf); + monitor_mask |= ( DBE_LOG | DBE_VALUE ); + if ( monitor_mask ) { + db_post_events ( pwf, pwf->mag, monitor_mask ); + db_post_events ( pwf, pwf->ang, monitor_mask ); + } + return; +} + +static long process ( struct spectrumRecord * pwf ) +{ + if ( ! pwf->frok ) { + recGblSetSevr ( pwf, CALC_ALARM, INVALID_ALARM ); + return 0; + } + long nRequest = static_cast < long > ( pwf->nin ); + long status = dbGetLink ( & pwf->inpw, DBR_DOUBLE, + pwf->ival, 0, & nRequest ); + if ( status ) { + recGblSetSevr ( pwf, CALC_ALARM, INVALID_ALARM ); + return 0; + } + + unsigned long nActual; + if ( nRequest < 0 ) { + recGblSetSevr ( pwf, CALC_ALARM, INVALID_ALARM ); + return 0; + } + else { + nActual = static_cast < unsigned long > ( nRequest ); + } + + //epicsTime begin = epicsTime::getCurrent(); + + const unsigned L = 1 << pwf->logl; + + const fft_t phi0d2 = ( PI / 2.0 / pwf->nelm ) * + ( pwf->hfrq - pwf->lfrq ) / pwf->ifrq ; + const fft_t theta0 = PI * pwf->lfrq / pwf->ifrq; + + for ( unsigned long n = 0; n < nActual; n++ ) + { + const fft_t nd = n; + const fft_t psi = nd * theta0 + + nd * nd * phi0d2; + pwf->g[n] = pwf->ival[n] * pwf->hamw[n] * + std::polar ( one, - psi ); + } + for( unsigned long n = nActual ; n < L; n++ ) + { + pwf->g[n] = 0.0; + } + + epicsOneDimFFT ( pwf->g, pwf->logl, false ); + + for( unsigned n = 0; n < L; n++ ) + { + pwf->g[n] = pwf->g[n] * pwf->h[n]; + // epicsOneDimFFT does not scale + pwf->g[n] /= L; + } + + epicsOneDimFFT ( pwf->g, pwf->logl, true ); + + for ( unsigned k = 0; k < pwf->nelm; k++ ) + { + const fft_t kd = k; + const fft_t psi = kd * kd * phi0d2; + pwf->g[k] = pwf->g[k] * std::polar ( one, - psi ); + pwf->mag[k] = abs ( pwf->g[k] ); + pwf->ang[k] = arg ( pwf->g[k] ); + } + + //epicsTime end = epicsTime::getCurrent(); + + //cout << "delay per spectrum point " << + // ((end - begin) / pwf->nelm ) * 1e6 << " uS " << endl; + + //for ( unsigned i = 0; i < pwf->nelm; i++ ) { + // cout << i << g[i] << endl; + //} + + pwf->udf = FALSE; + recGblGetTimeStamp ( pwf) ; + + monitor ( pwf ); + // process the forward scan link record + recGblFwdLink ( pwf ); + + pwf->pact=FALSE; + return 0; +} + +static long cvt_dbaddr( struct dbAddr * paddr ) +{ + struct spectrumRecord * pwf = + reinterpret_cast < struct spectrumRecord * > + ( paddr->precord ); + + if ( paddr->pfield == (void *) & pwf->val ) { + paddr->pfield = pwf->mag; + paddr->no_elements = pwf->nelm; + paddr->field_type = DBF_DOUBLE; + paddr->field_size = 8; + paddr->dbr_field_type = DBF_DOUBLE; + } + else if ( paddr->pfield == (void *) & pwf->mag ) { + paddr->pfield = pwf->mag; + paddr->no_elements = pwf->nelm; + paddr->field_type = DBF_DOUBLE; + paddr->field_size = 8; + paddr->dbr_field_type = DBF_DOUBLE; + } + else if ( paddr->pfield == (void *) & pwf->ang ) { + paddr->pfield = pwf->ang; + paddr->no_elements = pwf->nelm; + paddr->field_type = DBF_DOUBLE; + paddr->field_size = 8; + paddr->dbr_field_type = DBF_DOUBLE; + } + return(0); +} + +static long get_array_info ( struct dbAddr * paddr, long * no_elements, long * offset ) +{ + struct spectrumRecord * pwf = ( struct spectrumRecord * ) paddr->precord; + *no_elements = pwf->nelm; + *offset = 0; + return 0; +} + +static long put_array_info ( struct dbAddr * paddr, long nNew ) +{ + return 0; +} + +static long get_units ( struct dbAddr * paddr, char * units ) +{ + struct spectrumRecord * pwf = ( struct spectrumRecord * ) paddr->precord; + strncpy ( units,pwf->egu, DB_UNITS_SIZE ); + return 0; +} + +static long get_precision ( struct dbAddr * paddr, long * precision ) +{ + struct spectrumRecord * pwf = ( struct spectrumRecord * ) paddr->precord; + + if ( paddr->pfield == (void *) pwf->mag ) { + *precision = pwf->prec; + } + else if ( paddr->pfield == (void *) pwf->ang ) { + *precision = pwf->prec; + } + else { + recGblGetPrec ( paddr, precision ); + } + return 0; +} + +static long get_graphic_double ( struct dbAddr * paddr, struct dbr_grDouble *pgd ) +{ + struct spectrumRecord * pwf = (struct spectrumRecord *) paddr->precord; + if ( paddr->pfield == (void *) pwf->mag ) { + pgd->upper_disp_limit = pwf->hopr; + pgd->lower_disp_limit = pwf->lopr; + } + else if ( paddr->pfield == (void *) pwf->ang ) { + pgd->upper_disp_limit = +PI; + pgd->lower_disp_limit = -PI; + } + else if ( paddr->pfield == (void *) & pwf->hfrq ) { + pgd->upper_disp_limit = pwf->ifrq / 2.0; + pgd->lower_disp_limit = 0; + } + else if ( paddr->pfield == (void *) & pwf->lfrq ) { + pgd->upper_disp_limit = pwf->ifrq / 2.0; + pgd->lower_disp_limit = 0; + } + else { + recGblGetGraphicDouble ( paddr, pgd ) ; + } + return 0; +} + +static long get_control_double ( struct dbAddr * paddr, struct dbr_ctrlDouble * pcd ) +{ + struct spectrumRecord * pwf = ( struct spectrumRecord * ) paddr->precord; + + if ( paddr->pfield == (void *) pwf->mag ) { + pcd->upper_ctrl_limit = pwf->hopr; + pcd->lower_ctrl_limit = pwf->lopr; + } + else if ( paddr->pfield == (void *) pwf->ang ) { + pcd->upper_ctrl_limit = +PI; + pcd->lower_ctrl_limit = -PI; + } + else if ( paddr->pfield == (void *) & pwf->hfrq ) { + pcd->upper_ctrl_limit = pwf->ifrq / 2.0; + pcd->lower_ctrl_limit = 0; + } + else if ( paddr->pfield == (void *) & pwf->lfrq ) { + pcd->upper_ctrl_limit = pwf->ifrq / 2.0; + pcd->lower_ctrl_limit = 0; + } + else { + recGblGetControlDouble ( paddr, pcd ); + } + + return 0; +} + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +#define get_value NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_alarm_double NULL +rset spectrumRSET={ + RSETNUMBER, + (RECSUPFUN) report, + (RECSUPFUN) initialize, + (RECSUPFUN) init_record, + (RECSUPFUN) process, + (RECSUPFUN) special, + (RECSUPFUN) get_value, + (RECSUPFUN) cvt_dbaddr, + (RECSUPFUN) get_array_info, + (RECSUPFUN) put_array_info, + (RECSUPFUN) get_units, + (RECSUPFUN) get_precision, + (RECSUPFUN) get_enum_str, + (RECSUPFUN) get_enum_strs, + (RECSUPFUN) put_enum_str, + (RECSUPFUN) get_graphic_double, + (RECSUPFUN) get_control_double, + (RECSUPFUN) get_alarm_double +}; + +extern "C" { + epicsExportAddress ( rset, spectrumRSET ); +} + + diff --git a/src/rec/spectrumRecord.dbd b/src/rec/spectrumRecord.dbd new file mode 100644 index 000000000..9c8eed83f --- /dev/null +++ b/src/rec/spectrumRecord.dbd @@ -0,0 +1,116 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE Versions 3.13.7 +# and higher are distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +recordtype(spectrum) { + include "dbCommon.dbd" + field(VAL,DBF_DOUBLE) { + prompt("Power Spectral Density") + asl(ASL0) + special(SPC_DBADDR) + extra("fft_t * val") + } + field(MAG,DBF_NOACCESS) { + prompt("Power Spectral Density Magnitude Pointer") + special(SPC_DBADDR) + extra("fft_t * mag") + } + field(ANG,DBF_NOACCESS) { + prompt("Power Spectral Density Phase Pointer") + special(SPC_DBADDR) + extra("fft_t * ang") + } + field(IVAL,DBF_NOACCESS) { + prompt("Input Array Pointer") + special(SPC_NOMOD) + extra("fft_t * ival") + } + field(hamw,DBF_NOACCESS) { + prompt("Hamming window") + special(SPC_NOMOD) + extra("fft_t * hamw") + } + field(G,DBF_NOACCESS) { + prompt("Complex Array Pointer") + special(SPC_NOMOD) + extra("std::complex < fft_t > * g") + } + field(H,DBF_NOACCESS) { + prompt("Complex Array Pointer") + special(SPC_NOMOD) + extra("std::complex < fft_t > * h") + } + field(LFRQ,DBF_DOUBLE) { + prompt("Lowest frequency of interest (Hz)") + promptgroup(GUI_DISPLAY) + interest(1) + pp(TRUE) + special(SPC_MOD) + } + field(HFRQ,DBF_DOUBLE) { + prompt("Highest frequency of interest (Hz)") + promptgroup(GUI_DISPLAY) + interest(1) + pp(TRUE) + special(SPC_MOD) + } + field(IFRQ,DBF_DOUBLE) { + prompt("Input INPW samples per second (Hz)") + promptgroup(GUI_DISPLAY) + interest(1) + pp(TRUE) + special(SPC_MOD) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(INPW,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_INPUTS) + interest(1) + special(SPC_MOD) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units Name") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(NELM,DBF_ULONG) { + prompt("Number of Elements") + promptgroup(GUI_WAVE) + special(SPC_MOD) + } + field(NIN,DBF_ULONG) { + prompt("Max number of Input Elements") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + } + field(LOGL,DBF_ULONG) { + prompt("Log of Number of Computational Elements") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + } + field(FROK,DBF_USHORT) { + prompt("Freq parameters are valid") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + } +}