Skip to main content

Oliver's Instrument Library (pyoil)

Oliver's Instrument Library is an open-source collection of test-equipment driver code that saves me from having to write the same instrument control layer over and over. It is built around PyVISA, but presents supported instrument features as Pythonic getters and setters so normal use does not require hand-crafted SCPI commands.

The PyPI distribution is named pyoil; import it as oil.

python -m pip install pyoil

Supported instruments

  • Rohde & Schwarz SMR20 signal generator
  • Keysight E5071C vector network analyser
  • Keysight N9030 signal analyser
  • Dual-SP8T RF switch matrix

The supported surface is deliberately small and explicit. Instrument- and firmware-specific behaviour still needs a physical-instrument smoke test before it is relied on in production.

Instrument control

Create a driver with its VISA resource string, then use properties rather than composing SCPI messages yourself:

from oil.core import ip_address_string
from oil.sig_gens import SMR20

signal_generator = SMR20(ip_address_string("192.168.1.20"))
signal_generator.frequency = 1_000_000
signal_generator.power = -10
signal_generator.rf_enable = True

print(signal_generator.frequency)  # 1000000.0

Product-based test runs

TestManager turns sweeps into the Cartesian product of their values. For example, two frequencies and three power levels create six sequential test iterations. Each action receives the current TestIteration, which provides a data dictionary for passing settings and measurements to later actions.

This example sets an SMR20 and N9030 to each frequency, sweeps generator power, peak-searches the analyser, and records the result:

from oil.analyzers import N9030
from oil.core import ip_address_string
from oil.sig_gens import SMR20
from oil.test_manager import ListSweep, ParameterSweep, TestAction, TestManager, TestRecord

generator = SMR20(ip_address_string("192.168.1.20"))
analyser = N9030(ip_address_string("192.168.1.21"))
record = TestRecord(
    "measured_power.csv",
    ["frequency_hz", "generator_power_dbm", "measured_power_dbm"],
)


def set_frequency(iteration, frequency_hz):
    generator.frequency = frequency_hz
    analyser.frequency_center = frequency_hz
    iteration.data["frequency_hz"] = frequency_hz


def set_power(iteration, power_dbm):
    generator.power = power_dbm
    iteration.data["generator_power_dbm"] = power_dbm


def measure_and_record(iteration, _unused):
    marker = analyser.marker[1]
    marker.enabled = True
    marker.peak_search()
    iteration.data["measured_power_dbm"] = marker.power
    record.write(iteration.data)


test = TestManager([
    ParameterSweep("frequency", set_frequency, 1_000_000, 2_000_000, 1_000_000),
    ListSweep("power", set_power, [-20, -10, 0]),
    TestAction("measure", 0, measure_and_record),
])

generator.rf_enable = True
test.run()
generator.rf_enable = False

ParameterSweep includes both endpoints when the interval is divisible by the step. ListSweep preserves the supplied order. Use TestAction for a single operation that should be included in every product combination.

Test records

TestRecord creates a CSV with the columns you specify plus timestamp. It opens the file only while writing a row, which keeps each completed row on disk if the process stops unexpectedly. Extra keys in iteration.data are ignored; declared columns without a value are written as empty cells.

The example above produces rows shaped like:

frequency_hz,generator_power_dbm,measured_power_dbm,timestamp
1000000,-20,-20.14,2026-09-07-22:32:08

Test logging

Each TestManager configures an oil logger. Informational progress is shown on the console; a timestamped log file is written under logs/ in the current working directory and includes debug-level messages. Use the manager's helper methods from actions when a run needs contextual messages:

def verify_power(iteration, _unused):
    measured = iteration.data["measured_power_dbm"]
    if measured < -25:
        iteration.test_manager.error(f"Power too low: {measured:.2f} dBm")
    else:
        iteration.test_manager.info(f"Power OK: {measured:.2f} dBm")

Virtual instruments and unit tests

Virtual versions of the supported instruments run the same driver-level API without a VISA connection. They make unit tests deterministic and let driver tests assert the SCPI contract at the virtual boundary; they are not a substitute for hardware verification.

from oil.sig_gens import VirtualSMR20

generator = VirtualSMR20()
generator.frequency = 2_400_000_000
generator.power = -5

assert generator.frequency == 2_400_000_000
assert generator.power == -5

Run the project unit suite from the repository root:

python -m pytest

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyoil-0.0.15.tar.gz (21.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyoil-0.0.15-py3-none-any.whl (26.1 kB view details)

Uploaded Python 3

File details

Details for the file pyoil-0.0.15.tar.gz.

File metadata

  • Download URL: pyoil-0.0.15.tar.gz
  • Upload date:
  • Size: 21.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pyoil-0.0.15.tar.gz
Algorithm Hash digest
SHA256 b7d9cd4922554d7c0e37461e9723b050d733a0a09b189fbba6c5d4261075faa1
MD5 cbe2f300e6ba464d4ffa9ce60768a592
BLAKE2b-256 19bc4a4685eba82eafe4adce57431b42cfa7fe7f7190dc13fc419a850bf1e40d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyoil-0.0.15.tar.gz:

Publisher: publish-pypi.yml on ok65/oil

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyoil-0.0.15-py3-none-any.whl.

File metadata

  • Download URL: pyoil-0.0.15-py3-none-any.whl
  • Upload date:
  • Size: 26.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pyoil-0.0.15-py3-none-any.whl
Algorithm Hash digest
SHA256 396c999bf504c8b358eed83b2b047f90b50ca264ec321a714c4e80f679df3d5b
MD5 24094b01a559c2158362a832c90a9840
BLAKE2b-256 a1d52faccd940f704ed58601f98970707e5cb9e4820091b57dc1377cba5b30f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyoil-0.0.15-py3-none-any.whl:

Publisher: publish-pypi.yml on ok65/oil

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.0.15 This release

2 files

0.0.13

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page