675 lines
21 KiB
Plaintext
675 lines
21 KiB
Plaintext
#*************************************************************************
|
|
# Copyright (c) 2013 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found
|
|
# in file LICENSE that is included with this distribution.
|
|
#*************************************************************************
|
|
|
|
=title Analog Input Record (ai)
|
|
|
|
This record type is normally used to obtain an analog value from a hardware
|
|
input and convert it to engineering units.
|
|
The record supports linear and break-point conversion to engineering units,
|
|
smoothing, alarm limits, alarm filtering, and graphics and control limits.
|
|
|
|
=head2 Parameter Fields
|
|
|
|
The record-specific fields are described below, grouped by functionality.
|
|
|
|
=recordtype ai
|
|
|
|
=cut
|
|
|
|
recordtype(ai) {
|
|
|
|
=head3 Input Specification
|
|
|
|
These fields control where the record will read data from when it is processed:
|
|
|
|
=fields DTYP, INP
|
|
|
|
The DTYP field selects which device support layer should be responsible for
|
|
providing input data to the record.
|
|
The ai device support layers provided by EPICS Base are documented in the
|
|
L<Device Support|devSoft> section.
|
|
External support modules may provide additional device support for this record
|
|
type.
|
|
If not set explicitly, the DTYP value defaults to the first device support that
|
|
is loaded for the record type, which will usually be the C<Soft Channel> support
|
|
that comes with Base.
|
|
|
|
The INP link field contains a database or channel access link or provides
|
|
hardware address information that the device support uses to determine where the
|
|
input data should come from.
|
|
The format for the INP field value depends on the device support layer that is
|
|
selected by the DTYP field.
|
|
See L<Address
|
|
Specification|https://docs.epics-controls.org/en/latest/guides/EPICS_Process_Database_Concepts.html#address-specification>
|
|
for a description of the various hardware address formats supported.
|
|
|
|
=head3 Units Conversion
|
|
|
|
These fields control if and how the raw input value gets converted into
|
|
engineering units:
|
|
|
|
=fields RVAL, ROFF, ASLO, AOFF, LINR, ESLO, EOFF, EGUL, EGUF
|
|
|
|
These fields are not used if the device support layer reads its value in
|
|
engineering units and puts it directly into the VAL field.
|
|
This applies to Soft Channel and Async Soft Channel device support, and is also
|
|
fairly common for GPIB and similar high-level device interfaces.
|
|
|
|
If the device support sets the RVAL field, the LINR field controls how this gets
|
|
converted into engineering units and placed in the VAL field as follows:
|
|
|
|
=over
|
|
|
|
=item 1.
|
|
RVAL is converted to a double and ROFF is added to it.
|
|
|
|
=item 2.
|
|
If ASLO is non-zero the value is multiplied by ASLO.
|
|
|
|
=item 3.
|
|
AOFF is added.
|
|
|
|
=item 4.
|
|
If LINR is C<NO CONVERSION> the units conversion is finished after the above
|
|
steps.
|
|
|
|
=item 5.
|
|
If LINR is C<LINEAR> or C<SLOPE>, the value from step 3 above is multiplied by
|
|
ESLO and EOFF is added to complete the units conversion process.
|
|
|
|
=item 6.
|
|
Any other value for LINR selects a particular breakpoint table to be used on the
|
|
value from step 3 above.
|
|
|
|
=back
|
|
|
|
The distinction between the C<LINEAR> and C<SLOPE> settings for the LINR field
|
|
are in how the conversion parameters are calculated:
|
|
|
|
=over
|
|
|
|
=item *
|
|
With C<LINEAR> conversion the user must set EGUL and EGUF to the lowest and
|
|
highest possible engineering units values respectively that can be converted by
|
|
the hardware.
|
|
The device support knows the range of the raw data and calculates ESLO and EOFF
|
|
from them.
|
|
|
|
=item *
|
|
C<SLOPE> conversion requires the user to calculate the appropriate scaling and
|
|
offset factors and put them directly in ESLO and EOFF.
|
|
|
|
=back
|
|
|
|
=head3 Smoothing Filter
|
|
|
|
This filter is usually only used if the device support sets the RVAL field and
|
|
the Units Conversion process is used.
|
|
Device support that directly sets the VAL field may implement the filter if
|
|
desired.
|
|
|
|
The filter is controlled with a single parameter field:
|
|
|
|
=fields SMOO
|
|
|
|
The SMOO field should be set to a number between 0 and 1.
|
|
If set to zero the filter is not used (no smoothing), while if set to one the
|
|
result is infinite smoothing (the VAL field will never change).
|
|
The calculation performed is:
|
|
|
|
=over
|
|
|
|
VAL = VAL * SMOO + (1 - SMOO) * New Data
|
|
|
|
=back
|
|
|
|
where C<New Data> was the result from the Units Conversion above.
|
|
This implements a first-order infinite impulse response (IIR) digital filter
|
|
with z-plane pole at SMOO.
|
|
The equivalent continuous-time filter time constant E<tau> is given by
|
|
|
|
=over
|
|
|
|
E<tau> = E<minus>T / ln(SMOO)
|
|
|
|
=back
|
|
|
|
where T is the time between record processing.
|
|
|
|
=head3 Undefined Check
|
|
|
|
If after applying the smoothing filter the VAL field contains a NaN
|
|
(Not-a-Number) value, the UDF field is set to a non-zero value, indicating that
|
|
the record value is undefined, which will trigger a C<UDF_ALARM> with severity
|
|
C<INVALID_ALARM>.
|
|
|
|
=fields UDF
|
|
|
|
=head3 Operator Display Parameters
|
|
|
|
These parameters are used to present meaningful data to the operator.
|
|
They do not affect the functioning of the record at all.
|
|
|
|
=over
|
|
|
|
=item *
|
|
NAME is the record's name, and can be useful when the PV name that a client
|
|
knows is an alias for the record.
|
|
|
|
=item *
|
|
DESC is a string that is usually used to briefly describe the record.
|
|
|
|
=item *
|
|
EGU is a string of up to 16 characters naming the engineering units that the VAL
|
|
field represents.
|
|
|
|
=item *
|
|
The HOPR and LOPR fields set the upper and lower display limits for the VAL,
|
|
HIHI, HIGH, LOW, and LOLO fields.
|
|
|
|
=item *
|
|
The PREC field determines the floating point precision (i.e. the number of
|
|
digits to show after the decimal point) with which to display VAL and the other
|
|
DOUBLE fields.
|
|
|
|
=back
|
|
|
|
See L<Fields Common to All Record Types|dbCommonRecord/Operator Display
|
|
Parameters> for more about the record name (NAME) and description (DESC) fields.
|
|
|
|
=fields NAME, DESC, EGU, HOPR, LOPR, PREC
|
|
|
|
=head3 Alarm Limits
|
|
|
|
The user configures limit alarms by putting numerical values into the HIHI,
|
|
HIGH, LOW and LOLO fields, and by setting the associated alarm severity in the
|
|
corresponding HHSV, HSV, LSV and LLSV menu fields.
|
|
|
|
The HYST field controls hysteresis to prevent alarm chattering from an input
|
|
signal that is close to one of the limits and suffers from significant readout
|
|
noise.
|
|
|
|
The AFTC field sets the time constant on a low-pass filter that delays the
|
|
reporting of limit alarms until the signal has been within the alarm range for
|
|
that number of seconds (the default AFTC value of zero retains the previous
|
|
behavior). The record must be scanned often enough for the filtering action to
|
|
work effectively and the alarm severity can only change when the record is
|
|
processed, but that processing does not have to be regular; the filter uses the
|
|
time since the record last processed in its calculation. Setting AFTC to a
|
|
positive number of seconds will delay the record going into or out of a minor
|
|
alarm severity or from minor to major severity until the input signal has been
|
|
in the alarm range for that number of seconds.
|
|
|
|
See L<Alarm Specification|https://docs.epics-controls.org/en/latest/guides/EPICS_Process_Database_Concepts.html#alarm-specification>
|
|
for a complete explanation of record alarms and of the standard fields.
|
|
L<Alarm Fields|dbCommonRecord/Alarm Fields> lists other fields related
|
|
to alarms that are common to all record types.
|
|
|
|
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC, LALM
|
|
|
|
=head3 Monitor Parameters
|
|
|
|
These parameters are used to determine when to send monitors placed on the VAL
|
|
field.
|
|
The monitors are sent when the current value exceeds the last transmitted value
|
|
by the appropriate deadband.
|
|
If these fields are set to zero, a monitor will be triggered every time the
|
|
value changes; if set to -1, a monitor will be sent every time the record is
|
|
processed.
|
|
|
|
The ADEL field sets the deadband for archive monitors (C<DBE_LOG> events), while
|
|
the MDEL field controls value monitors (C<DBE_VALUE> events).
|
|
|
|
The remaining fields are used by the record at run-time to implement the record
|
|
monitoring functionality.
|
|
|
|
=fields ADEL, MDEL, ALST, MLST, ORAW
|
|
|
|
=cut
|
|
|
|
include "dbCommon.dbd"
|
|
%
|
|
%/* Declare Device Support Entry Table */
|
|
%struct aiRecord;
|
|
%typedef struct aidset {
|
|
% dset common;
|
|
% long (*read_ai)(struct aiRecord *prec);
|
|
% long (*special_linconv)(struct aiRecord *prec, int after);
|
|
%} aidset;
|
|
%#define HAS_aidset
|
|
%
|
|
field(VAL,DBF_DOUBLE) {
|
|
prompt("Current EGU Value")
|
|
promptgroup("40 - Input")
|
|
asl(ASL0)
|
|
pp(TRUE)
|
|
}
|
|
field(INP,DBF_INLINK) {
|
|
prompt("Input Specification")
|
|
promptgroup("40 - Input")
|
|
interest(1)
|
|
}
|
|
field(PREC,DBF_SHORT) {
|
|
prompt("Display Precision")
|
|
promptgroup("80 - Display")
|
|
interest(1)
|
|
prop(YES)
|
|
}
|
|
field(LINR,DBF_MENU) {
|
|
prompt("Linearization")
|
|
promptgroup("60 - Convert")
|
|
special(SPC_LINCONV)
|
|
pp(TRUE)
|
|
interest(1)
|
|
menu(menuConvert)
|
|
}
|
|
field(EGUF,DBF_DOUBLE) {
|
|
prompt("Engineer Units Full")
|
|
promptgroup("60 - Convert")
|
|
special(SPC_LINCONV)
|
|
pp(TRUE)
|
|
interest(1)
|
|
}
|
|
field(EGUL,DBF_DOUBLE) {
|
|
prompt("Engineer Units Low")
|
|
promptgroup("60 - Convert")
|
|
special(SPC_LINCONV)
|
|
pp(TRUE)
|
|
interest(1)
|
|
}
|
|
field(EGU,DBF_STRING) {
|
|
prompt("Engineering Units")
|
|
promptgroup("80 - Display")
|
|
interest(1)
|
|
size(16)
|
|
prop(YES)
|
|
}
|
|
field(HOPR,DBF_DOUBLE) {
|
|
prompt("High Operating Range")
|
|
promptgroup("80 - Display")
|
|
interest(1)
|
|
prop(YES)
|
|
}
|
|
field(LOPR,DBF_DOUBLE) {
|
|
prompt("Low Operating Range")
|
|
promptgroup("80 - Display")
|
|
interest(1)
|
|
prop(YES)
|
|
}
|
|
field(AOFF,DBF_DOUBLE) {
|
|
prompt("Adjustment Offset")
|
|
promptgroup("60 - Convert")
|
|
pp(TRUE)
|
|
interest(1)
|
|
}
|
|
field(ASLO,DBF_DOUBLE) {
|
|
prompt("Adjustment Slope")
|
|
promptgroup("60 - Convert")
|
|
pp(TRUE)
|
|
interest(1)
|
|
initial("1")
|
|
}
|
|
field(SMOO,DBF_DOUBLE) {
|
|
prompt("Smoothing")
|
|
promptgroup("60 - Convert")
|
|
interest(1)
|
|
}
|
|
field(HIHI,DBF_DOUBLE) {
|
|
prompt("Hihi Alarm Limit")
|
|
promptgroup("70 - Alarm")
|
|
pp(TRUE)
|
|
interest(1)
|
|
prop(YES)
|
|
}
|
|
field(LOLO,DBF_DOUBLE) {
|
|
prompt("Lolo Alarm Limit")
|
|
promptgroup("70 - Alarm")
|
|
pp(TRUE)
|
|
interest(1)
|
|
prop(YES)
|
|
}
|
|
field(HIGH,DBF_DOUBLE) {
|
|
prompt("High Alarm Limit")
|
|
promptgroup("70 - Alarm")
|
|
pp(TRUE)
|
|
interest(1)
|
|
prop(YES)
|
|
}
|
|
field(LOW,DBF_DOUBLE) {
|
|
prompt("Low Alarm Limit")
|
|
promptgroup("70 - Alarm")
|
|
pp(TRUE)
|
|
interest(1)
|
|
prop(YES)
|
|
}
|
|
field(HHSV,DBF_MENU) {
|
|
prompt("Hihi Severity")
|
|
promptgroup("70 - Alarm")
|
|
pp(TRUE)
|
|
interest(1)
|
|
prop(YES)
|
|
menu(menuAlarmSevr)
|
|
}
|
|
field(LLSV,DBF_MENU) {
|
|
prompt("Lolo Severity")
|
|
promptgroup("70 - Alarm")
|
|
pp(TRUE)
|
|
interest(1)
|
|
prop(YES)
|
|
menu(menuAlarmSevr)
|
|
}
|
|
field(HSV,DBF_MENU) {
|
|
prompt("High Severity")
|
|
promptgroup("70 - Alarm")
|
|
pp(TRUE)
|
|
interest(1)
|
|
prop(YES)
|
|
menu(menuAlarmSevr)
|
|
}
|
|
field(LSV,DBF_MENU) {
|
|
prompt("Low Severity")
|
|
promptgroup("70 - Alarm")
|
|
pp(TRUE)
|
|
interest(1)
|
|
prop(YES)
|
|
menu(menuAlarmSevr)
|
|
}
|
|
field(HYST,DBF_DOUBLE) {
|
|
prompt("Alarm Deadband")
|
|
promptgroup("70 - Alarm")
|
|
interest(1)
|
|
}
|
|
field(AFTC,DBF_DOUBLE) {
|
|
prompt("Alarm Filter Time Constant")
|
|
promptgroup("70 - Alarm")
|
|
interest(1)
|
|
}
|
|
field(ADEL,DBF_DOUBLE) {
|
|
prompt("Archive Deadband")
|
|
promptgroup("80 - Display")
|
|
interest(1)
|
|
}
|
|
field(MDEL,DBF_DOUBLE) {
|
|
prompt("Monitor Deadband")
|
|
promptgroup("80 - Display")
|
|
interest(1)
|
|
}
|
|
field(LALM,DBF_DOUBLE) {
|
|
prompt("Last Value Alarmed")
|
|
special(SPC_NOMOD)
|
|
interest(3)
|
|
}
|
|
field(AFVL,DBF_DOUBLE) {
|
|
prompt("Alarm Filter Value")
|
|
special(SPC_NOMOD)
|
|
interest(3)
|
|
}
|
|
field(ALST,DBF_DOUBLE) {
|
|
prompt("Last Value Archived")
|
|
special(SPC_NOMOD)
|
|
interest(3)
|
|
}
|
|
field(MLST,DBF_DOUBLE) {
|
|
prompt("Last Val Monitored")
|
|
special(SPC_NOMOD)
|
|
interest(3)
|
|
}
|
|
field(ESLO,DBF_DOUBLE) {
|
|
prompt("Raw to EGU Slope")
|
|
promptgroup("60 - Convert")
|
|
pp(TRUE)
|
|
interest(2)
|
|
initial("1")
|
|
}
|
|
field(EOFF,DBF_DOUBLE) {
|
|
prompt("Raw to EGU Offset")
|
|
promptgroup("60 - Convert")
|
|
pp(TRUE)
|
|
interest(2)
|
|
}
|
|
field(ROFF,DBF_ULONG) {
|
|
prompt("Raw Offset")
|
|
pp(TRUE)
|
|
interest(2)
|
|
}
|
|
field(PBRK,DBF_NOACCESS) {
|
|
prompt("Ptrto brkTable")
|
|
special(SPC_NOMOD)
|
|
interest(4)
|
|
extra("void * pbrk")
|
|
}
|
|
field(INIT,DBF_SHORT) {
|
|
prompt("Initialized?")
|
|
special(SPC_NOMOD)
|
|
interest(3)
|
|
}
|
|
field(LBRK,DBF_SHORT) {
|
|
prompt("LastBreak Point")
|
|
special(SPC_NOMOD)
|
|
interest(3)
|
|
}
|
|
field(RVAL,DBF_LONG) {
|
|
prompt("Current Raw Value")
|
|
pp(TRUE)
|
|
}
|
|
field(ORAW,DBF_LONG) {
|
|
prompt("Previous Raw Value")
|
|
special(SPC_NOMOD)
|
|
interest(3)
|
|
}
|
|
|
|
=head3 Simulation Mode Parameters
|
|
|
|
The following fields are used to operate the record in simulation mode.
|
|
|
|
If SIMM (fetched through SIML) is YES or RAW, the record is put in SIMS
|
|
severity and the value is fetched through SIOL (buffered in SVAL).
|
|
If SIMM is YES, SVAL is written to VAL without conversion,
|
|
if SIMM is RAW, SVAL is trancated to RVAL and converted.
|
|
SSCN sets a different SCAN mechanism to use in simulation mode.
|
|
SDLY sets a delay (in sec) that is used for asynchronous simulation
|
|
processing.
|
|
|
|
See L<Input Simulation Fields|dbCommonInput/Input Simulation Fields>
|
|
for more information on simulation mode and its fields.
|
|
|
|
=fields SIML, SIMM, SIOL, SVAL, SIMS, SDLY, SSCN
|
|
|
|
=cut
|
|
|
|
field(SIOL,DBF_INLINK) {
|
|
prompt("Simulation Input Link")
|
|
promptgroup("90 - Simulate")
|
|
interest(1)
|
|
}
|
|
field(SVAL,DBF_DOUBLE) {
|
|
prompt("Simulation Value")
|
|
}
|
|
field(SIML,DBF_INLINK) {
|
|
prompt("Simulation Mode Link")
|
|
promptgroup("90 - Simulate")
|
|
interest(1)
|
|
}
|
|
field(SIMM,DBF_MENU) {
|
|
prompt("Simulation Mode")
|
|
special(SPC_MOD)
|
|
interest(1)
|
|
menu(menuSimm)
|
|
}
|
|
field(SIMS,DBF_MENU) {
|
|
prompt("Simulation Mode Severity")
|
|
promptgroup("90 - Simulate")
|
|
interest(2)
|
|
menu(menuAlarmSevr)
|
|
}
|
|
field(OLDSIMM,DBF_MENU) {
|
|
prompt("Prev. Simulation Mode")
|
|
special(SPC_NOMOD)
|
|
interest(4)
|
|
menu(menuSimm)
|
|
}
|
|
field(SSCN,DBF_MENU) {
|
|
prompt("Sim. Mode Scan")
|
|
promptgroup("90 - Simulate")
|
|
interest(1)
|
|
menu(menuScan)
|
|
initial("65535")
|
|
}
|
|
field(SDLY,DBF_DOUBLE) {
|
|
prompt("Sim. Mode Async Delay")
|
|
promptgroup("90 - Simulate")
|
|
interest(2)
|
|
initial("-1.0")
|
|
}
|
|
%#include "callback.h"
|
|
field(SIMPVT,DBF_NOACCESS) {
|
|
prompt("Sim. Mode Private")
|
|
special(SPC_NOMOD)
|
|
interest(4)
|
|
extra("epicsCallback *simpvt")
|
|
}
|
|
}
|
|
|
|
=head2 Device Support Interface
|
|
|
|
The record requires device support to provide an entry table (dset) which
|
|
defines the following members:
|
|
|
|
typedef struct {
|
|
long number;
|
|
long (*report)(int level);
|
|
long (*init)(int after);
|
|
long (*init_record)(aiRecord *prec);
|
|
long (*get_ioint_info)(int cmd, aiRecord *prec, IOSCANPVT *piosl);
|
|
long (*read_ai)(aiRecord *prec);
|
|
long (*special_linconv)(aiRecord *prec, int after);
|
|
} aidset;
|
|
|
|
The module must set C<number> to at least 6, and provide a pointer to its
|
|
C<read_ai()> routine; the other function pointers may be C<NULL> if their
|
|
associated functionality is not required for this support layer.
|
|
Most device supports also provide an C<init_record()> routine to configure the
|
|
record instance and connect it to the hardware or driver support layer, and if
|
|
using the record's L</Units Conversion> features they set C<special_linconv()>
|
|
as well.
|
|
|
|
The individual routines are described below.
|
|
|
|
=head3 Device Support Routines
|
|
|
|
long report(int level)
|
|
|
|
This optional routine is called by the IOC command C<dbior> and is passed the
|
|
report level that was requested by the user.
|
|
It should print a report on the state of the device support to stdout.
|
|
The C<level> parameter may be used to output increasingly more detailed
|
|
information at higher levels, or to select different types of information with
|
|
different levels.
|
|
Level zero should print no more than a small summary.
|
|
|
|
long init(int after)
|
|
|
|
This optional routine is called twice at IOC initialization time.
|
|
The first call happens before any of the C<init_record()> calls are made, with
|
|
the integer parameter C<after> set to 0.
|
|
The second call happens after all of the C<init_record()> calls have been made,
|
|
with C<after> set to 1.
|
|
|
|
long init_record(aiRecord *prec)
|
|
|
|
This optional routine is called by the record initialization code for each ai
|
|
record instance that has its DTYP field set to use this device support.
|
|
It is normally used to check that the INP address is the expected type and that
|
|
it points to a valid device; to allocate any record-specific buffer space and
|
|
other memory; and to connect any communication channels needed for the
|
|
C<read_ai()> routine to work properly.
|
|
|
|
If the record type's unit conversion features are used, the C<init_record()>
|
|
routine should calculate appropriate values for the ESLO and EOFF fields from
|
|
the EGUL and EGUF field values.
|
|
This calculation only has to be performed if the record's LINR field is set to
|
|
C<LINEAR>, but it is not necessary to check that condition first.
|
|
This same calculation takes place in the C<special_linconv()> routine, so the
|
|
implementation can usually just call that routine to perform the task.
|
|
|
|
long get_ioint_info(int cmd, aiRecord *prec, IOSCANPVT *piosl)
|
|
|
|
This optional routine is called whenever the record's SCAN field is being
|
|
changed to or from the value C<I/O Intr> to find out which I/O Interrupt Scan
|
|
list the record should be added to or deleted from.
|
|
If this routine is not provided, it will not be possible to set the SCAN field
|
|
to the value C<I/O Intr> at all.
|
|
|
|
The C<cmd> parameter is zero when the record is being added to the scan list,
|
|
and one when it is being removed from the list.
|
|
The routine must determine which interrupt source the record should be connected
|
|
to, which it indicates by the scan list that it points the location at C<*piosl>
|
|
to before returning.
|
|
It can prevent the SCAN field from being changed at all by returning a non-zero
|
|
value to its caller.
|
|
|
|
In most cases the device support will create the I/O Interrupt Scan lists that
|
|
it returns for itself, by calling C<void scanIoInit(IOSCANPVT *piosl)> once for
|
|
each separate interrupt source.
|
|
That routine allocates memory and inializes the list, then passes back a pointer
|
|
to the new list in the location at C<*piosl>.
|
|
|
|
When the device support receives notification that the interrupt has occurred,
|
|
it announces that to the IOC by calling C<void scanIoRequest(IOSCANPVT iosl)>
|
|
which will arrange for the appropriate records to be processed in a suitable
|
|
thread.
|
|
The C<scanIoRequest()> routine is safe to call from an interrupt service routine
|
|
on embedded architectures (vxWorks and RTEMS).
|
|
|
|
long read_ai(aiRecord *prec)
|
|
|
|
This essential routine is called when the record wants a new value from the
|
|
addressed device.
|
|
It is responsible for performing (or at least initiating) a read operation, and
|
|
(eventually) returning its value to the record.
|
|
|
|
... PACT and asynchronous processing ...
|
|
|
|
... return value ...
|
|
|
|
long special_linconv(aiRecord *prec, int after)
|
|
|
|
This optional routine should be provided if the record type's unit conversion
|
|
features are used by the device support's C<read_ai()> routine returning a
|
|
status value of zero.
|
|
It is called by the record code whenever any of the the fields LINR, EGUL or
|
|
EGUF are modified and LINR has the value C<LINEAR>.
|
|
The routine must calculate and set the fields EOFF and ESLO appropriately based
|
|
on the new values of EGUL and EGUF.
|
|
|
|
These calculations can be expressed in terms of the minimum and maximum raw
|
|
values that the C<read_ai()> routine can put in the RVAL field.
|
|
When RVAL is set to I<RVAL_max> the VAL field will be set to EGUF, and when RVAL
|
|
is set to I<RVAL_min> the VAL field will become EGUL.
|
|
|
|
The formulae to use are:
|
|
|
|
=over
|
|
|
|
EOFF = (I<RVAL_max> * EGUL E<minus> I<RVAL_min> * EGUF) /
|
|
(I<RVAL_max> E<minus> I<RVAL_min>)
|
|
|
|
ESLO = (EGUF E<minus> EGUL) / (I<RVAL_max> E<minus> I<RVAL_min>)
|
|
|
|
=back
|
|
|
|
Note that the record support sets EOFF to EGUL before calling this routine,
|
|
which is a very common case (when I<RVAL_min> is zero).
|
|
|
|
=head3 Extended Device Support
|
|
|
|
...
|
|
|
|
=cut
|