Fluke Calibration                                           MET/CAL Procedure
=============================================================================
INSTRUMENT:            Sub Initialize /DMM
INSTRUMENT:            Sub Reset /DMM
INSTRUMENT:            Sub Measure /DMM
INSTRUMENT:            Sub Setup /DMM
INSTRUMENT:            Sub Read /DMM
INSTRUMENT:            Sub Get Accuracy /DMM
DATE:                  2019-03-14 08:35:48
AUTHOR:                Fluke
REVISION:              $Revision: 26841 $
ADJUSTMENT THRESHOLD:  70%
NUMBER OF TESTS:       4
NUMBER OF LINES:       481
CONFIGURATION:         Digital Multimeter
=============================================================================
#
#  Procedure Author:
#        DAC, DFM
#
#  Compatibility:
#        MET/CAL 7.2 or later
#
#  Subprocedures:
#        None
#
#  Required Files:
#        user_config_instr.ini
#
#  This procedure is intended for use with MET/CAL calibration software;
#  the terms and conditions set forth in your MET/CAL license apply to this
#  procedure.
#
#  Due to Fluke's policy of continuously updating our products, this procedure
#  may contain minor differences in methods used and/or specifications to
#  those found in the manual or other documentation. While every effort has
#  been made to ensure that this procedure is accurate, Fluke cannot be held
#  responsible for the consequences of error or omissions found within this
#  procedure.
#
#  The copyright in this procedure is owned by Fluke Corporation.
#
#    Initialize DMM parameters
#
#  Parameters       Values
#  ---------------  ---------------------------------------------------------
#  @DMM_Func        DC_Voltage     | AC_Voltage | Resistance2W | Resistance4W
#                   DC_Current     | AC_Current |
#                   HighDC_Current | HighAC_Current
#                   Frequency      | Period
#
#  @DMM_Meas        This parameter is used for accuracy lookup and must
#                   contain the DMM reading prior to calling
#                     "Sub Get Accuracy /DMM"
#
#  @DMM_Ampl        <NR3>[][<prefix>][V | Vp | Vpp]
#
#                   For frequency and period measurements, this parameter
#                   must contain the voltage prior to calling
#                     "Sub Get Accuracy /DMM"
#
#  @DMM_Freq        <NR3>[][<prefix>][Hz]
#
#                   For AC amplitude measurements, this parameter must contain
#                   the frequency prior to calling
#                     "Sub Get Accuracy /DMM"
#
#  @DMM_Guard       Int | Ext
#
#  @DMM_TrigSrc     Int | Ext
#                     Do not specify trigger source for IEEE-488 trigger.
#
#  @DMM_TrigDly     <NR3>[][<prefix>]s
#                     Delay from trigger to first sample/reading.
#
#  @DMM_dB_Ref      <NR3>[][<prefix>]Ohm
#
#  Terminals        @DMM_InputHi,       @DMM_InputLo,
#                   @DMM_SenseHi,       @DMM_SenseLo,
#                   @DMM_CurrentHi,     @DMM_CurrentLo,
#                   @DMM_HighCurrentHi, @DMM_HighCurrentLo,
#                   @DMM_GrdTerm,       @DMM_OhmGrdTerm,
#                   @DMM_ExtTrig
#
#  Example Usage:
#
#      CALL         Sub Initialize /DMM
#
#      IF           EMPTY(@DMM_OhmGrdTerm)
#      MATH         @DMM_OhmGrdTerm = @DMM_GrdTerm
#      ENDIF
#
#      MATH         @DMM_Func = "Resistance4W"; @DMM_Guard = "Int"
#      TARGET       -p
#      CALL         Sub Reset /DMM
#      CALL         Sub Setup /DMM
#      DISP         Connect the following:
#      DISP         [32]       DUT         [V @DMM_DevName]
#      DISP         [32] OUTPUT HI ------> [V @DMM_InputHi]
#      DISP         [32] OUTPUT LO ------> [V @DMM_InputLo]
#      DISP         [32] SENSE HI -------> [V @DMM_SenseHi]
#      DISP         [32] SENSE LO -------> [V @DMM_SenseLo]
#      DISP         [32] GUARD ----------> [V @DMM_OhmGrdTerm]
#      DISP
#      DISP         Connect the sense leads to the DUT first!
#      TARGET       -m
#      CALL         Sub Read /DMM
#      CALL         Sub Get Accuracy /DMM
#      MATH         L[1] = @DMM_Acc
#      ACC    100   Ohms           L1U
#      MEMC         100.0Ohms      1%
#
#      MATH         @DMM_Func = "AC_Voltage"; @DMM_Freq = "1kHz"
#      MATH         @DMM_Guard = "Ext"
#      TARGET       -p
#      CALL         Sub Setup /DMM
#      TARGET       -m
#      CALL         Sub Read /DMM
#      CALL         Sub Get Accuracy /DMM
#      MATH         L[1] = @DMM_Acc
#      ACC    100   V              L1U
#      MEMC         100.0V         1%            1kHz
#
#      MATH         @DMM_Func = "Frequency"; @DMM_Ampl = "1V"
#      TARGET       -p
#      CALL         Sub Setup /DMM
#      TARGET       -m
#      CALL         Sub Read /DMM
#      CALL         Sub Get Accuracy /DMM
#      MATH         MEM = MEM / 1E+3; L[1] = @DMM_Acc / 1E+3
#      ACC    1     kHz             L1U
#      MEMC         1.000kHz        1%            1V
#
 STEP    FSC    RANGE NOMINAL        TOLERANCE     MOD1        MOD2  3  4 CON

  1.001  JMPL         INITIALIZE                   PSUBI("Initialize")
  1.002  JMPL         RESET                        PSUBI("Reset")
  1.003  JMPL         SETUP                        PSUBI("Measure")
  1.004  JMPL         SETUP                        PSUBI("Setup")
  1.005  JMPL         READ                         PSUBI("Read")
  1.006  JMPL         GET_ACCURACY                 PSUBI("Get Accuracy")
  1.007  DISP         Subprocedure not found!
  1.008  END

  1.009  EVAL   Increment test number

# =====  Sub Initialize /DMM  ===============================================

  2.001  LABEL        INITIALIZE
# Get and store device name.
  2.002  MATH         @DMM_DevName = INSTR("DMM")
# Get and store confidence.
  2.003  MATH         @DMM_Conf = CONF(@DMM_DevName)

# Get and store programming section name.
  2.004  MATH         @DMM_ProgSecName = RINFE(@DMM_DevName, "ProgSecName")

# Get and store FSC.
  2.005  MATH         @DMM_FSC = RINFE(@DMM_ProgSecName, "FSC")

# Get and store terminal names.
  2.006  MATH         @DMM_InputHi = RINFE(@DMM_ProgSecName, "InputHi")
  2.007  MATH         @DMM_InputLo = RINFE(@DMM_ProgSecName, "InputLo")

# 4-wire sense terminals are optional.
  2.008  MATH         @DMM_SenseHi = RINF(@DMM_ProgSecName, "SenseHi")

  2.009  IF           NOT(EMPTY(@DMM_SenseHi))
  2.010  MATH         @DMM_SenseLo = RINFE(@DMM_ProgSecName, "SenseLo")
  2.011  ENDIF

# If no current Hi terminal is specified, use Input Hi.
  2.012  MATH         @DMM_CurrentHi = RINF(@DMM_ProgSecName, "CurrentHi")

  2.013  IF           EMPTY(@DMM_CurrentHi)
  2.014  MATH         @DMM_CurrentHi = @DMM_InputHi
  2.015  ENDIF

# If no low current Lo terminal is specified, use Input Lo.
  2.016  MATH         @DMM_CurrentLo = RINF(@DMM_ProgSecName, "CurrentLo")

  2.017  IF           EMPTY(@DMM_CurrentLo)
  2.018  MATH         @DMM_CurrentLo =@DMM_InputLo
  2.019  ENDIF

# If no high current Hi terminal is specified, use Current Hi.
  2.020  MATH         HighCurrentHi = RINF(@DMM_ProgSecName, "HighCurrentHi")

  2.021  IF           EMPTY(HighCurrentHi)
  2.022  MATH         @DMM_HighCurrentHi = @DMM_CurrentHi
  2.023  ELSE
  2.024  MATH         @DMM_HighCurrentHi = HighCurrentHi
  2.025  ENDIF

# If no high current Lo terminal is specified, use Current Lo.
  2.026  MATH         HighCurrentLo = RINF(@DMM_ProgSecName, "HighCurrentLo")

  2.027  IF           EMPTY(HighCurrentLo)
  2.028  MATH         @DMM_HighCurrentLo = @DMM_CurrentLo
  2.029  ELSE
  2.030  MATH         @DMM_HighCurrentLo = HighCurrentLo
  2.031  ENDIF

  2.032  MATH         @DMM_GrdTerm    = RINF(@DMM_ProgSecName, "Guard")
  2.033  MATH         @DMM_OhmGrdTerm = RINF(@DMM_ProgSecName, "OhmGuard")
  2.034  MATH         @DMM_ExtTrig    = RINF(@DMM_ProgSecName, "ExtTrig")

# Initialize parameters to the empty string (unset).
  2.035  MATH         @DMM_Ampl    = ""
  2.036  MATH         @DMM_dB_Ref  = ""
  2.037  MATH         @DMM_Freq    = ""
  2.038  MATH         @DMM_Func    = ""
  2.039  MATH         @DMM_Guard   = ""
  2.040  MATH         @DMM_TrigSrc = ""
  2.041  MATH         @DMM_TrigDly = ""

# Unset flag to trigger operator message to manually select guard, on DMMs
# with manual guard switch.
  2.042  MATH         @DMM_GuardSet = 0

# Get programming string for RESET FSC.
  2.043  MATH         ResetCmd = RINF(@DMM_ProgSecName, "RESET_FSC")

# If RESET_FSC is defined, establish the RESET FSC.
  2.044  IF           NOT(EMPTY(ResetCmd))

  2.045  IF           ZCMPI(ResetCmd, "[SDC]")
  2.046  RESET        [@DMM][SDC]
  2.047  ELSE
  2.048  RESET        [@DMM][V ResetCmd]
  2.049  ENDIF

  2.050  ENDIF

# See if input termination other than EOI is specified.
  2.051  MATH         InputTerm = RINF(@DMM_ProgSecName, "TERM")

# See CR or LF termination was specified...
  2.052  IF           ZCMPI(InputTerm, "CR")
  2.053  IEEE         [@DMM][TERM CR]
  2.054  ELSEIF       ZCMPI(InputTerm, "LF")
  2.055  IEEE         [@DMM][TERM LF]
  2.056  ENDIF

  2.057  END

# =====  Sub Reset /DMM  ====================================================

  2.058  LABEL        RESET
  2.059  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "Reset")
  2.060  CALL         Sub Send Command /DMM
  2.061  END

  2.062  EVAL   Increment test number

# =====  Sub Setup /DMM  ====================================================
# or
# =====  Sub Measure /DMM  ==================================================

  3.001  LABEL        SETUP

# -----  Function

  3.002  MATH         @DMM_Cmd = RINFE(@DMM_ProgSecName, @DMM_Func)
  3.003  CALL         Sub Send Command /DMM

# -----  dB Reference
  3.004  IF           NOT(EMPTY(@DMM_dB_Ref))
  3.005  MATH         dB_Ref = BASE(@DMM_dB_Ref)

# If there is a discrete command for 50, 75, 300, or 600 Ohms, use it.
# Otherwise the dB reference must be dynamically set.
  3.006  IF           dB_Ref == 50
  3.007  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "dB_Ref50_Ohm")
  3.008  ELSEIF       dB_Ref == 75
  3.009  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "dB_Ref75_Ohm")
  3.010  ELSEIF       dB_Ref == 300
  3.011  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "dB_Ref300_Ohm")
  3.012  ELSEIF       dB_Ref == 600
  3.013  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "dB_Ref600_Ohm")
  3.014  ENDIF

  3.015  IF           ZCMP(@DMM_Cmd, "")
  3.016  MATH         @DMM_Cmd = RINFE(@DMM_ProgSecName, "dB_Ref")
  3.017  MATH         @DMM_Cmd = REPL("<val>", dB_Ref, @DMM_Cmd)
  3.018  ENDIF

  3.019  CALL         Sub Send Command /DMM
  3.020  ENDIF        ; IF dB Reference

# -----  Guard

  3.021  IF           NOT(EMPTY(@DMM_Guard))

  3.022  IF           ZCMPI(@DMM_Guard, "Int")
  3.023  MATH         Guard = "GuardInt"
  3.024  ELSE
  3.025  MATH         Guard = "GuardExt"
  3.026  ENDIF

  3.027  MATH         GuardSelect = RINF(@DMM_ProgSecName, "GuardSelect")

  3.028  IF           ZCMPI(GuardSelect, "Manual")

  3.029  IF           NOT(@DMM_GuardSet)
  3.030  MATH         Switch = RINFE(@DMM_ProgSecName, "GuardSwitch")
  3.031  MATH         Setting = RINFE(@DMM_ProgSecName, Guard)
  3.032  DISP         Set [V @DMM_DevName] [V Switch] switch to [V Setting].
  3.033  MATH         @DMM_GuardSet = 1
  3.034  ENDIF        ; Prompt to set Reference Oscillator switch

  3.035  ELSEIF       ZCMPI(GuardSelect, "Auto")
#                     Automatic selection; nothing to do.
  3.036  ELSE
  3.037  MATH         @DMM_Cmd = RINFE(@DMM_ProgSecName, Guard)
  3.038  CALL         Sub Send Command /DMM
  3.039  ENDIF

  3.040  ENDIF        ; IF Guard

# -----  Trigger Source

  3.041  IF           NOT(EMPTY(@DMM_TrigSrc))

  3.042  IF           ZCMPI(@DMM_TrigSrc, "Bus")
  3.043  MATH         TrigSrc = "TriggerSourceBus"
  3.044  ELSEIF       ZCMPI(@DMM_TrigSrc, "Int")
  3.045  MATH         TrigSrc = "TriggerSourceInt"
  3.046  ELSE
  3.047  MATH         TrigSrc = "TriggerSourceExt"
  3.048  ENDIF

  3.049  MATH         @DMM_Cmd = RINFE(@DMM_ProgSecName, TrigSrc)
  3.050  CALL         Sub Send Command /DMM
  3.051  ENDIF

# -----  Trigger Delay

  3.052  IF           NOT(EMPTY(@DMM_TrigDly))
# Get the command string.
  3.053  MATH         TrigDlyCmd = RINFE(@DMM_ProgSecName, "TriggerDelay")
# Convert to base units and insert in programming string.
  3.054  MATH         @DMM_Cmd = REPL("<val>", BASE(@DMM_TrigDly), TrigDlyCmd)
  3.055  CALL         Sub Send Command /DMM
  3.056  ENDIF

# Exit here if Sub Setup was called, else drop through for Sub Measure.
  3.057  IF           PSUBI("Setup")
  3.058  END
  3.059  ENDIF

# =====  Sub Read /DMM  =====================================================
# or
# =====  Sub Measure /DMM  ==================================================

  3.060  LABEL        READ

# When user_config_instr.ini was first created, the Initiate parameter was
# overloaded for SCPI compliant DMMs.  The trigger command was combined with
# the initiate command (e.g. "INIT;*TRG").  This will not work with external
# trigger.  Therefore, InitiateOnly was added to support external trigger.
  3.061  IF           ZCMPI(@DMM_TrigSrc, "Ext")
  3.062  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "InitiateOnly")
  3.063  ELSE
  3.064  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "Initiate")
  3.065  ENDIF

# If there is an initiate command, send it.
  3.066  IF           NOT(EMPTY(@DMM_Cmd))
  3.067  CALL         Sub Send Command /DMM
  3.068  ENDIF

# If there is a frequency specific fetch command, use it.
  3.069  IF           ZCMPI(@DMM_Func, "Frequency")
  3.070  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "FetchFreq")
# If there is a frequency specific fetch command, use it.
  3.071  ELSEIF       ZCMPI(@DMM_Func, "Period")
  3.072  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "FetchPer")
  3.073  ELSE
  3.074  MATH         @DMM_Cmd = ""
  3.075  ENDIF

# Otherwise see if there is a general fetch command.
  3.076  IF           EMPTY(@DMM_Cmd)
  3.077  MATH         @DMM_Cmd = RINF(@DMM_ProgSecName, "Fetch")
  3.078  ENDIF

# If there is no fetch command simply get the reading.
  3.079  IF           EMPTY(@DMM_Cmd)
  3.080  IEEE         [@DMM][I]
# 8508A generates SRQ error if IEEE2 FSC is used for fetch.
  3.081  ELSEIF       ZCMP(@DMM_DevName, "Fluke 8508A")
  3.082  IEEE         [@DMM][V @DMM_Cmd][I]
# Otherwise send the fetch command and get the reading.
  3.083  ELSEIF       ZCMPI(@DMM_FSC, "SCPI")
  3.084  SCPI         [@DMM][V @DMM_Cmd][I]
  3.085  ELSEIF       ZCMPI(@DMM_FSC, "IEEE2")
  3.086  IEEE2        [@DMM][V @DMM_Cmd][I]
  3.087  ELSE
  3.088  IEEE         [@DMM][V @DMM_Cmd][I]
  3.089  ENDIF

# Inform MET/CAL that the measurement is the System Actual.
  3.090  TSET         MEAS = SA
  3.091  MATH         @DMM_Meas = MEM
  3.092  END

  3.093  EVAL   Increment test number

# =====  Sub Get Accuracy /DMM  =============================================

  4.001  LABEL        GET_ACCURACY
  4.002  MATH         FreqAccToPerAcc = 0

# Get DMM mode string from ini file for accuracy lookup.
  4.003  IF           ZCMPI(@DMM_Func, "DC_Voltage")
  4.004  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringVDC")
# Set accuracy modifier (frequency) lookup switch to off.
  4.005  MATH         AC = 0
  4.006  ELSEIF       ZCMPI(@DMM_Func, "AC_Voltage")
  4.007  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringVAC")
  4.008  MATH         AC = 1; Modifier = @DMM_Freq
  4.009  ELSEIF       ZCMPI(@DMM_Func, "Resistance2W")
  4.010  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringRes2W")
  4.011  MATH         AC = 0
  4.012  ELSEIF       ZCMPI(@DMM_Func, "Resistance4W")
  4.013  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringRes4W")
  4.014  MATH         AC = 0
  4.015  ELSEIF       ZCMPI(@DMM_Func, "DC_Current")
  4.016  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringADC")
  4.017  MATH         AC = 0
  4.018  ELSEIF       ZCMPI(@DMM_Func, "AC_Current")
  4.019  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringAAC")
  4.020  MATH         AC = 1; Modifier = @DMM_Freq
  4.021  ELSEIF       ZCMPI(@DMM_Func, "HighDC_Current")
  4.022  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringADC")
  4.023  MATH         AC = 0
  4.024  ELSEIF       ZCMPI(@DMM_Func, "HighAC_Current")
  4.025  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringAAC")
  4.026  MATH         AC = 1; Modifier = @DMM_Freq
  4.027  ELSEIF       ZCMPI(@DMM_Func, "Frequency")
  4.028  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringFreq")
  4.029  MATH         AC = 1; Modifier = @DMM_Ampl
  4.030  ELSEIF       ZCMPI(@DMM_Func, "Period")
  4.031  MATH         ModeStr = RINF(@DMM_DevName, "ModeStringPer")

# If there is no "ModeStringPer" entry, lookup "ModeStringFreq".
  4.032  IF           EMPTY(ModeStr)
  4.033  MATH         ModeStr = RINFE(@DMM_DevName, "ModeStringFreq")
  4.034  MATH         FreqAccToPerAcc = 1
  4.035  ENDIF

  4.036  MATH         AC = 1; Modifier = @DMM_Ampl
  4.037  ENDIF

  4.038  IF           ISVAR("Modifier")
  4.039  MATH         Modifier = BASE(Modifier)
  4.040  ENDIF

  4.041  MATH         Device = @DMM_DevName

  4.042  IF           FreqAccToPerAcc
  4.043  MATH         Freq = 1 / @DMM_Meas
  4.044  MATH         Freq_Acc = ACCV2(Device, ModeStr, Freq, Modifier)
  4.045  MATH         @DMM_RefAcc = (Freq_Acc / Freq) * @DMM_Meas
  4.046  ELSEIF       AC
  4.047  MATH         @DMM_RefAcc = ACCV2(Device, ModeStr, @DMM_Meas,Modifier)
  4.048  ELSE
  4.049  MATH         @DMM_RefAcc = ACCV(Device, ModeStr, @DMM_Meas)
  4.050  ENDIF

  4.051  MATH         Confidence = VGET("CONF")

# VGET query returns empty string if CONF is set to default (2).
  4.052  IF           EMPTY(Confidence)
  4.053  MATH         Confidence = 2
  4.054  ENDIF

  4.055  MATH         @DMM_Acc = (@DMM_RefAcc / CONF(Device)) * Confidence

  4.056  END
