installed spectrum record

This commit is contained in:
Jeff Hill
2004-02-10 21:46:53 +00:00
parent 13a779ca81
commit d6c0e9dcc5
4 changed files with 556 additions and 0 deletions

View File

@@ -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"

View File

@@ -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

437
src/rec/spectrumRecord.cpp Normal file
View File

@@ -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 <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <complex>
#include <iostream>
#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 );
}

116
src/rec/spectrumRecord.dbd Normal file
View File

@@ -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)
}
}