EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  2025  <2026 Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  2025  <2026
<== Date ==> <== Thread ==>

Subject: Re: How to output a calculated waveform using aSub record in EPICS
From: Peter Milne via Tech-talk <tech-talk at aps.anl.gov>
To: Varuna Crishan Meddage <vmeddage at fnal.gov>
Cc: "tech-talk at aps.anl.gov" <tech-talk at aps.anl.gov>
Date: Thu, 2 Apr 2026 22:23:04 +0100
Hi Varuna

You can do that if you like,  or you can simply read acq2106_347:1:AI:WF:17:V.VALA, which is itself an ASUB record that has already taken
acq2106_347:1:AI:WF:17 and applied the calibration math that you're proposing to do.

On the Phoebus support, if you chose to plot "CODES", then the raw data acq2106_347:1:AI:WF:17 is plotting, while plotting "VOLTS", it's acq2106_347:1:AI:WF:V.VALA, as shown on the plot legend.

Apologies for the confusing V.VALA presentation, it would be more obvious if some discrete record eg acq2206_088:1:AI:WF:01:V  were itself the voltage output waveform, and the ASUB action were some "private" record.
The ASUB was originally chosen for efficiency (typically, there are 100K points per WF and up to 192 separate WF channels) and to handle various sample word sizes (16, 24, 32 bit) transparently. When initially developing the feature, I worried that we'd be allocating storage for both the VALA.. records and perhaps a second array for the public interface as well.

And, while we've got the values in cache, it's a near-zero cost to include some stats like min, max, rms.

Best regards


Peter




On Thu, 2 Apr 2026 at 20:53, Varuna Crishan Meddage via Tech-talk <tech-talk at aps.anl.gov> wrote:
Hi,

I want to perform some operation using elements in 3 EPICS records, where one record is a waveform and other two are subArrays. (As indicated below)

========================= Waveform record ================================

record(waveform, "$(acc):$(sys)_$(group):acq2106_current")
{
  field(INP, {pva:{pv:"acq2106_347:1:AI:WF:17", "proc": "CP"}})
  field(FTVL, "DOUBLE")
  field(NELM, "4096")
  field(SCAN, "1 second")
  }

======================== subArrary record 1 ==============================

record(subArray, "$(acc):$(sys)_$(group):acq2106_calibvalue_eslo") {
    field(DESC, "Extracted subset of WAVE")
    field(INP, {pva:{pv:"acq2106_347:1:AI:CAL:ESLO", "proc":"CP"}})
    field(FTVL, "DOUBLE")     
    field(MALM, "10")        
    field(NELM, "1")         
    field(INDX, "17")
    field(SCAN, "1 second")
}

==================== subArray record 2 ================================

record(subArray, "$(acc):$(sys)_$(group):acq2106_calibvalue_eoff") {
    field(DESC, "Extracted subset of WAVE")
    field(INP, {pva:{pv:"acq2106_347:1:AI:CAL:EOFF", "proc":"CP"}})
    field(FTVL, "DOUBLE")     
    field(MALM, "10") # If set this to 1, does not return the expected outcome       
    field(NELM, "1")         
    field(INDX, "17")
    field(SCAN, "1 second")
}

===================================================================

Here I am trying to loop over every element of the wave form and multiply each element by the single element of the subarray 1 and add the only element in the subarray 2 to the result (subarray 1 and subarray 2 have only one element)

So to achieve this goal, I created  aSub record (pasted below) and inside that I called the function named calibWave.c (pasted below).

========================== calibWave.c ==============================

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>
#include <math.h>
#include <ncurses.h>

#include "dbDefs.h"
#include "registryFunction.h"
#include "epicsExport.h"
#include "epicsExit.h"
#include "epicsThread.h"
#include "iocsh.h"
#include "aSubRecord.h"

static double calibWave(aSubRecord *prec)
{
 double *in  = (double*)prec->a;
 double *out = (double*)prec->vala;

 double eslo = *(double*)prec->b;
 double eoff = *(double*)prec->c;

 int n = prec->nea;

 for(int i = 0; i < n; i++) {
     out[i] = in[i] * eslo + eoff;
 }

 return 0;
}

EpicsRegisterFunction(calibWave);

====================================================================

==================== aSub record for the calculated waveform ===============

record(aSub, "$(acc):$(sys)_$(group):acq2106_calib_current") {
    field(INPA, "$(acc):$(sys)_$(group):acq2106_current CPP")
    field(INPB, "$(acc):$(sys)_$(group):acq2106_calibvalue_eslo CPP")
    field(INPC, "$(acc):$(sys)_$(group):acq2106_calibvalue_eoff CPP")
    field(FTA, "DOUBLE")
    field(FTB, "DOUBLE")
    field(FTC, "DOUBLE")
    field(NOA, "4096")
    field(NOB, "1")
    field(NOC, "1")
    field(FTVA, "DOUBLE")
    field(NOVA, "4096")
    field(SNAM, "calibWave")  
    field(SCAN, "1 second")
    field(PREC, 5)
}

===================================================================

But this aSub only returns a single number instead of a waveform (as indicated below)

=================== pvxget for asub record ==========================

PIP2:CTRL_MPS:acq2106_calib_current
    value int32_t = 4090
    alarm.severity int32_t = 0
    alarm.status int32_t = 0
    alarm.message string = ""
    timeStamp.secondsPastEpoch int64_t = 1775158362
    timeStamp.nanoseconds int32_t = 7852163
    timeStamp.userTag int32_t = 0
    display.limitLow int32_t = 0
    display.limitHigh int32_t = 0
    display.description string = ""
    display.units string = ""
    display.form.index int32_t = 0
    display.form.choices string[] = {7}["Default", "String", "Binary", "Decimal", "Hex", "Exponential", "Engineering"]
    control.limitLow int32_t = -2147483648
    control.limitHigh int32_t = 2147483647
    valueAlarm.lowAlarmLimit int32_t = 0
    valueAlarm.lowWarningLimit int32_t = 0
    valueAlarm.highWarningLimit int32_t = 0
    valueAlarm.highAlarmLimit int32_t = 0

====================================================================

I am not sure, where the problem happens (either calibWave.C function or aSub record ) or not sure whether aSub record is the best solution to achieve this goal.

Any help to debug this is highly appreciated.

============================================== Additional information ==============================================================

I am not 100% the way I created the custom calibWave. Function. Following are the steps, I followed.

  1.  First I created a .dbd file named waveCalib.dbd inside "src" directory and added the function name

************************************** content of waveCalib.dbd ********************************************
Function(calibWave)

***********************************************************************************************************

  1.  Modified Make file at "src" directory by adding the commented lines

******************************************** Makefile content at src directory *******************************

TOP=../..

include $(TOP)/configure/CONFIG
#----------------------------------------
#  ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================

#=============================
# Build the IOC application
PROD_IOC = XRM

# XRM.dbd will be created and installed
DBD += XRM.dbd

# XRM.dbd will be made up from these files:
XRM_DBD += base.dbd

# modified menuScan.dbd to include FNAL scan rates
XRM_DBD += menuScan.dbd

# waveCalib.dbd including the calibWave.c function
XRM_DBD += waveCalib.dbd  // ***********************************Addition

# Include dbd files from all support applications:
XRM_DBD += pvxsIoc.dbd
XRM_DBD += iocAdmin.dbd
XRM_DBD += aliveRecord.dbd
XRM_DBD += sseqRecord.dbd
XRM_DBD += sscanSupport.dbd
XRM_DBD += acnetPV.dbd
XRM_DBD += tcast.dbd
XRM_DBD += reccaster.dbd
XRM_DBD += asSupport.dbd
XRM_DBD += caPutJsonLog.dbd
XRM_DBD += sCalcoutRecord.dbd
XRM_DBD += calcSupport.dbd
XRM_DBD += aCalcoutRecord.dbd

# Add all the support libraries needed by this IOC
XRM_LIBS += devIocStats
XRM_LIBS += alive
XRM_LIBS += acnetPV
XRM_LIBS += calc
XRM_LIBS += pv seq
XRM_LIBS += sscan
XRM_LIBS += tcast
XRM_LIBS += reccaster
XRM_LIBS += autosave
XRM_LIBS += caPutLog
XRM_LIBS += pvxsIoc pvxs

# XRM_registerRecordDeviceDriver.cpp derives from XRM.dbd
XRM_SRCS += XRM_registerRecordDeviceDriver.cpp
XRM_SRCS += CopyArray.c
XRM_SRCS += calibWave.c // *************************** Addition

# Build the main IOC entry point on workstation OSs.
XRM_SRCS_DEFAULT += XRMMain.cpp
XRM_SRCS_vxWorks += -nil-

# Add header files:
#USR_INCLUDES += -I/usr/include

#
# Finally link to the EPICS Base libraries
XRM_LIBS += $(EPICS_BASE_IOC_LIBS)

#===========================

include $(TOP)/configure/RULES
#----------------------------------------
#  ADD RULES AFTER THIS LINE
USR_CXXFLAGS_DEFAULT += -fno-operator-names -pedantic
USR_CXXFLAGS += -W -Wall

**************************************************************************************************************

After compilng the project successfully, I checked the XRM.dbd file at .dbd directory and I can that it contains the line

Function(calibWave)

================================================== End of Additional Information ====================


Thanks
Varuna Meddage





--
Peter Milne, Director of Sales
www.d-tacq.com

References:
How to output a calculated waveform using aSub record in EPICS Varuna Crishan Meddage via Tech-talk

Navigate by Date:
Prev: Re: How to output a calculated waveform using aSub record in EPICS Varuna Crishan Meddage via Tech-talk
Next: Python workshop for the upcoming EPICS meeting TZVETKOV Stéphane via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  2025  <2026
Navigate by Thread:
Prev: Re: How to output a calculated waveform using aSub record in EPICS Varuna Crishan Meddage via Tech-talk
Next: Python workshop for the upcoming EPICS meeting TZVETKOV Stéphane via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  2025  <2026
ANJ, 03 Apr 2026 · Home · News · About · Talk · Base · Modules · Extensions ·
· Distributions · Download · Documents · Links · Licensing ·