Source code for pyscpi.keysight.osc

from .. import scpi
import sys
import numpy as np


def _read_fixed_bytes(inst, size: int) -> bytes:
    data = inst.read_bytes()
    while len(data) < size:
        data += inst.read_bytes()

    return data


def _readWaveDate(inst, channel: int, points: int, debug: bool = False) -> np.ndarray:

    inst.query('*OPC?')
    inst.write(f':WAVeform:SOURce CHANnel{channel}')
    inst.query('*OPC?')
    _log('Reading channel ' + str(channel), debug)

    inst.write(':WAVeform:FORMat BYTE')
    inst.write(':WAVeform:POINts:MODE MAXimum')

    if points > 0:
        inst.write(f':WAVeform:POINts {points}')
    else:
        inst.write(':WAVeform:POINts MAXimum')

    inst.query('*OPC?')

    peram = inst.query(':WAVeform:PREamble?')
    peram = peram.split(',')
    _log(peram, debug)

    format = peram[0]
    type = peram[1]
    ppoints = int(peram[2])
    xinc = float(peram[4])
    xorg = float(peram[5])
    xref = float(peram[6])
    yinc = float(peram[7])
    yorg = float(peram[8])
    yref = float(peram[9])

    inst.write(':WAVeform:DATA?')
    data = _read_fixed_bytes(inst, int(ppoints))

    header = data[2:10].decode('utf-8')
    _log(header, debug)
    rpoints = int(header)

    if rpoints != (ppoints):
        print('ERROR: points mismatch, please investigate')

    data = data[10:-1]

    data = np.frombuffer(data, dtype=np.uint8)

    voltCH = (data-yref) * yinc + yorg

    return voltCH


[docs]def readChannels(inst, channels: list[int], points: int = 0, runAfter: bool = True, debug: bool = False) -> tuple[np.ndarray, np.ndarray]: """Reads multiple channels from the oscilloscope. :param inst: The instrument object from pyscpi or pyvisa :param channels: A list of channels to read eg. [1, 2] :param points: The number of points to read. If 0, read all points :param runAfter: Run the oscilloscope after reading :param debug: Print debug messages :return: A NumPy tuple of time and voltage arrays """ inst.write(':TIMebase:MODE MAIN') channelCommand = '' for channel in channels: channelCommand = channelCommand + f'CHANnel{channel}, ' _log(channelCommand, debug) inst.write(f':DIGitize {channelCommand}') peram = inst.query(':WAVeform:PREamble?') peram = peram.split(',') _log(peram, debug) format = peram[0] type = peram[1] ppoints = int(peram[2]) xinc = float(peram[4]) xorg = float(peram[5]) xref = float(peram[6]) allData = np.empty([ppoints, len(channels)]) for i in range(len(channels)): _log(f'Reading channel {channels[i]}', debug) data = _readWaveDate(inst, channels[i], points) allData[:, i] = data time = (np.arange(0, ppoints, 1)-xref) * xinc + xorg if runAfter: inst.write(':RUN') return time, allData
[docs]def readSingleChannel(inst, channel: int, points: int = 0, runAfter: bool = True, debug: bool = False) -> tuple[np.ndarray, np.ndarray]: """Reads a single channel from the oscilloscope. :param inst: The instrument object from pyscpi or pyvisa :param channel: The channel to read :param points: The number of points to read. If 0, read all points :param runAfter: Run the oscilloscope after reading :param debug: Print debug messages :return: A NumPy tuple of time and voltage arrays """ inst.write(':TIMebase:MODE MAIN') voltCH = _readWaveDate(inst, [channel], points, debug) peram = inst.query(':WAVeform:PREamble?') peram = peram.split(',') _log(peram, debug) format = peram[0] type = peram[1] ppoints = int(peram[2]) xinc = float(peram[4]) xorg = float(peram[5]) xref = float(peram[6]) yinc = float(peram[7]) yorg = float(peram[8]) yref = float(peram[9]) time = (np.arange(0, ppoints, 1)-xref) * xinc + xorg if runAfter: inst.write(':RUN') return time, voltCH
[docs]def autoScale(inst) -> None: """Autoscales the oscilloscope. :param inst: The instrument object from pyscpi or pyvisa """ inst.write(f':AUToscale') inst.query('*OPC?')
[docs]def setTimeAxis(inst, scale: float, position: float) -> None: """Sets the time axis of the oscilloscope. :param inst: The instrument object from pyscpi or pyvisa :param scale: The scale of the time axis in seconds :param position: The position of the time axis from the trigger in seconds """ inst.write(f':TIMebase:SCALe {scale}') inst.write(f':TIMebase:POSition {position}') inst.query('*OPC?')
[docs]def setChannelAxis(inst, channel: int, scale: float, offset: float) -> None: """Sets the channel axis (y-axis) of the oscilloscope. :param inst: The instrument object from pyscpi or pyvisa :param channel: The channel to set :param scale: The scale of the channel axis in volts :param offset: The offset of the channel axis in volts """ inst.write(f':CHANnel{channel}:SCALe {scale}') inst.write(f':CHANnel{channel}:OFFSet {offset}') inst.query('*OPC?')
[docs]def setWGenOutput(inst, state: int | str) -> None: """Sets the output state of the waveform generator. (Only available on specific models) :param inst: The instrument object from pyscpi or pyvisa :param state: The state to set the output to (0 or 1) or ('OFF' or 'ON') """ inst.write(f':WGEN:OUTPut {state}') inst.query('*OPC?')
[docs]def setWGenSin(inst, amp: float, offset: float, freq: float) -> None: """Sets the waveform generator to a sine wave. (Only available on specific models) :param inst: The instrument object from pyscpi or pyvisa :param amp: The amplitude of the sine wave in volts :param offset: The offset of the sine wave in volts :param freq: The frequency of the sine wave in Hz. The frequency can be adjusted from 100 mHz to 20 MHz. """ inst.write('WGEN:FUNCtion SINusoid') inst.write(f':WGEN:VOLTage {amp}') inst.write(f':WGEN:VOLTage:OFFSet {offset}') inst.write(f':WGEN:FREQuency {freq}') inst.query('*OPC?')
[docs]def setWGenSquare(inst, v0: float, v1: float, offset: float, freq: float, dutyCycle: int) -> None: """Sets the waveform generator to a square wave. (Only available on specific models) :param inst: The instrument object from pyscpi or pyvisa :param v0: The voltage of the low state in volts :param v1: The voltage of the high state in volts :param offset: The offset of the square wave in volts :param freq: The frequency of the square wave in Hz. The frequency can be adjusted from 100 mHz to 20 MHz. :param dutyCycle: The duty cycle can be adjusted from 1% to 99% up to 500 kHz. At higher frequencies, the adjustment range narrows so as not to allow pulse widths less than 20 ns. """ inst.write('WGEN:FUNCtion SQUare') inst.write(f':WGEN:VOLTage:LOW {v0}') inst.write(f':WGEN:VOLTage:HIGH {v1}') inst.write(f':WGEN:VOLTage:OFFSet {offset}') inst.write(f':WGEN:FREQuency {freq}') inst.write(f':WGEN:FUNCtion:SQUare:DCYCle {dutyCycle}') inst.query('*OPC?')
[docs]def setWGenRamp(inst, v0: float, v1: float, offset: float, freq: float, symmetry: float) -> None: """Sets the waveform generator to a ramp wave. (Only available on specific models) :param inst: The instrument object from pyscpi or pyvisa :param v0: The voltage of the low state in volts :param v1: The voltage of the high state in volts :param offset: The offset of the ramp wave in volts :param freq: The frequency of the ramp wave in Hz. The frequency can be adjusted from 100 mHz to 100 kHz. :param symmetry: Symmetry represents the amount of time per cycle that the ramp waveform is rising and can be adjusted from 0% to 100%. """ inst.write('WGEN:FUNCtion RAMP') inst.write(f':WGEN:VOLTage:LOW {v0}') inst.write(f':WGEN:VOLTage:HIGH {v1}') inst.write(f':WGEN:VOLTage:OFFSet {offset}') inst.write(f':WGEN:FREQuency {freq}') inst.write(f':WGEN:FUNCtion:RAMP:SYMMetry {symmetry}') inst.query('*OPC?')
[docs]def setWGenPulse(inst, v0: float, v1: float, offset: float, period: float, pulseWidth: float) -> None: """Sets the waveform generator to a pulse wave. (Only available on specific models) :param inst: The instrument object from pyscpi or pyvisa :param v0: The voltage of the low state in volts :param v1: The voltage of the high state in volts :param offset: The offset of the pulse wave in volts :param period: The period of the pulse wave in seconds. The period can be adjusted from 10 ns to 100 ns. :param pulseWidth: The pulse width can be adjusted from 20 ns to the period minus 20 ns. """ inst.write('WGEN:FUNCtion PULSe') inst.write(f':WGEN:VOLTage:LOW {v0}') inst.write(f':WGEN:VOLTage:HIGH {v1}') inst.write(f':WGEN:VOLTage:OFFSet {offset}') inst.write(f':WGEN:PERiod {period}') inst.write(f':WGEN:FUNCtion:PULSe:WIDTh {pulseWidth}') inst.query('*OPC?')
[docs]def setWGenDC(inst, offset: float) -> None: """Sets the waveform generator to a DC wave. (Only available on specific models) :param inst: The instrument object from pyscpi or pyvisa :param offset: The offset of the DC wave in volts """ inst.write('WGEN:FUNCtion DC') inst.write(f':WGEN:VOLTage:OFFSet {offset}') inst.query('*OPC?')
[docs]def setWGenNoise(inst, v0: float, v1: float, offset: float) -> None: """Sets the waveform generator to a noise wave. (Only available on specific models) :param inst: The instrument object from pyscpi or pyvisa :param v0: The voltage of the low state in volts :param v1: The voltage of the high state in volts :param offset: The offset of the noise wave in volts """ inst.write('WGEN:FUNCtion NOISe') inst.write(f':WGEN:VOLTage:LOW {v0}') inst.write(f':WGEN:VOLTage:HIGH {v1}') inst.write(f':WGEN:VOLTage:OFFSet {offset}') inst.query('*OPC?')
def _log(msg, EnableLog: bool) -> None: if EnableLog: print(msg)