Input-Scaling Verification¶
Verify the whole input chain — physical gain knobs included — by measuring a source of known level and comparing against the volts pydvma predicts. Unlike a relative level meter or coherence check, this validates absolute scaling.
Known-Source Verification¶
verify_input_scaling ¶
verify_input_scaling(settings, source='loopback', freq=997.0, level_vrms=0.1, duration=2.0, tol=0.05, channels=None)
Verify the WHOLE input chain -- physical gain knobs included -- by measuring a source of KNOWN level and comparing against the volts pydvma predicts.
This is the check a coherence/level meter cannot do: those confirm
a signal is present and roughly the right shape, but nothing
upstream knows the true input level, so a wrong preamp gain, a
wrong VmaxNI/VmaxSC, or a stale input_gain_db all pass
silently. Comparing against a KNOWN source closes that gap.
Two source options, with different validity:
'loopback'(default): pydvma generates a plain sine and plays it out the configured AO device. On an NI device (device_driver='nidaq'/output_device_driver='nidaq') the AO voltage is hardware-calibrated, so a physical AO->AI BNC cable exercises and verifies the WHOLE analogue input path with no extra equipment. On a sound card the analogue output level depends on an uncalibrated volume control, so this only proves chain CONSISTENCY (repeatable, internally coherent), not absolute scaling; a warning is printed (not a refusal -- consistency checks are still useful) whensettings.output_device_driver == 'soundcard'.- An object with
set_sine(freq_hz, vrms)andoutput(on)methods, e.g.RigolDG1022Z: the object is commanded to the requested tone, switched on, given ~0.5 s to settle, thensettingscaptures it with nooutputargument (nothing is played by pydvma). The source is switched off again afterwards regardless of outcome. This is the only path that verifies absolute scaling on a sound card whose loopback is DIGITAL (e.g. a Scarlett 2i2's inputs 3/4 copy the output stream pre-preamp, so a digital loopback cannot see the analogue input gain).
Per-channel attenuators, transducer sensitivities and
channel_sensitivities calibration are OUT OF SCOPE: this
function checks the digitiser chain in volts (the same domain
MySettings.channel_sensitivities is applied on top of), not any
downstream engineering-unit calibration.
Parameters:
-
settings(MySettings) –Acquisition configuration; captured via a shallow copy internally, never mutated in place. Its
fs/channels/device_drivergovern the capture. -
source(str or object, default:'loopback') –'loopback'(default) or an instrument object exposingset_sine(freq_hz, vrms)andoutput(on)(e.g.RigolDG1022Z). -
freq(float, default:997.0) –Tone frequency in Hz (default 997.0 -- avoids mains harmonics (50/60 Hz and multiples) and round-number divisor artefacts that 1000 Hz risks). Must satisfy
0 < freq < 0.8 * (settings.fs / 2). -
level_vrms(float, default:0.1) –Known tone RMS amplitude in volts (default 0.1) -- this is also the "expected" value each measured channel is compared against.
-
duration(float, default:2.0) –Capture duration in seconds (default 2.0).
-
tol(float, default:0.05) –Fractional tolerance on the measured/expected ratio (default 0.05, i.e. +-5%, about +-0.42 dB). A channel passes when
abs(ratio - 1) <= tol. -
channels(list of int or None, default:None) –Channel indices to check (default
None= every channel,range(settings.channels)).
Returns a list of one dict per checked channel:
{'channel': int, 'measured_vrms': float, 'expected_vrms': float,
'ratio': float, 'error_db': float, 'ok': bool}, where
expected_vrms is level_vrms for every channel (per-channel
sensitivity is out of scope, see above) and ok is
abs(ratio - 1) <= tol. As a side effect, prints a small aligned
per-channel table with a PASS/FAIL verdict, followed by either
"input scaling verified" or, per failing channel, a line like
"channel 1 reads +2.3 dB high -- check the gain setting".
Rigol DG1022Z SCPI Wrapper¶
RigolDG1022Z is a minimal SCPI wrapper (via pyvisa) used as the
known-level source for verify_input_scaling on hardware whose
loopback cannot verify absolute scaling (e.g. a sound card's digital
loopback, which copies the output stream pre-preamp).
RigolDG1022Z ¶
Minimal SCPI wrapper for a Rigol DG1022Z (or compatible DG1000Z)
function generator, used as a known-level source for
verify_input_scaling.
Talks VISA via pyvisa (pyvisa-py backend, no NI-VISA
dependency). Every method issues plain SCPI writes and is safe to
call with nothing connected except the instrument itself — there is
no polling loop or blocking wait beyond the VISA I/O timeout.
Amplitude is always commanded in Vrms, not Vpp: the DG1022Z
defaults to Vpp, but its :SOURn:VOLT:UNIT command accepts
VRMS directly (confirmed in the DG1022Z working notes, section
3), so set_sine switches the unit mode explicitly rather than
converting via the x2*sqrt(2) Vpp<->Vrms factor. That keeps the
amplitude math (and the number visible on the instrument's own
display) in the same units end to end, with no extra place for an
RMS/peak mixup to creep in.
Use as a context manager to guarantee the output relay is switched OFF when done, even if the code in between raises:
>>> with RigolDG1022Z() as gen: # doctest: +SKIP
... gen.set_sine(997, 0.1)
... gen.output(True)
... ...
# output is OFF here, on ANY exit path
Attributes¶
Methods:¶
__init__ ¶
Open a VISA session to a Rigol DG1022Z.
Parameters:
-
resource(str or None, default:None) –Explicit VISA resource string (e.g.
'USB0::0x1AB1::0x0642::DG1ZA000000::INSTR').None(default) auto-discovers viapyvisa'slist_resources()and picks the first resource string containing'DG1'. RaisesValueErrornaming every resource that WAS found (so a wrong-but-present instrument is easy to spot) if none match.
Raises ImportError with pip-install guidance if pyvisa
is not installed, and ValueError if auto-discovery finds no
DG1* resource.
set_sine ¶
Command a sine wave at freq_hz Hz, vrms V RMS, on
channel.
Sends, in order (order matters: the DG1022Z notes warn that
changing waveform shape while in Vrms mode recalculates the
amplitude, so the shape is set first, then the level) with
n = channel:
:SOURn:FUNC SIN:OUTPn:LOAD INF— assume a high-impedance load. A DAQ or scope input IS high-Z; leaving the DG1022Z's default 50 ohm load assumption would make it report (and the RMS-mode math compute) exactly double the voltage that actually appears across a high-Z load, per the notes' load-mismatch gotcha.:SOURn:VOLT:UNIT VRMS:SOURn:VOLT <vrms>:SOURn:VOLT:OFFS 0:SOURn:FREQ <freq_hz>
Parameters:
-
freq_hz(float) –Sine frequency in Hz.
-
vrms(float) –Sine RMS amplitude in volts.
-
channel(int, default:1) –DG1022Z output channel, 1 or 2 (default 1).
output ¶
Switch channel's output relay ON or OFF.
Sends :OUTPn:STAT ON or :OUTPn:STAT OFF (n =
channel).
Parameters:
-
on(bool) –Trueenables the output,Falsedisables it. -
channel(int, default:1) –DG1022Z output channel, 1 or 2 (default 1).
close ¶
Return the front panel to local control and close the VISA session.
Sends :SYST:LOCal — SCPI control locks the front panel out
until this is sent (or the instrument's own Local key is
pressed) — before closing the underlying VISA resource. Safe to
call more than once.
__exit__ ¶
Switch the output OFF and close, on every exit path including an exception — a bench source left running unattended is exactly the failure mode this class exists to avoid.