Skip to main content

Meta-package: installs rf-bench-drivers-siglent, -icom, -yaesu, -utils

Project description

rf-bench

v0.3.0 — package has been split into focused sub-packages.

rf-bench is now a meta-package. Installing it pulls in the four independent driver packages listed below. Each can also be installed individually. All import paths (rf_bench.siglent, rf_bench.icom, rf_bench.yaesu, rf_bench.utils) are unchanged.

Package PyPI Provides
rf-bench-drivers-siglent PyPI SSA3000X, SDG1000X, SDS2000X, SDM3000X, SPD3303X
rf-bench-drivers-icom PyPI IC7300
rf-bench-drivers-yaesu PyPI FT891
rf-bench-drivers-utils PyPI RF math utilities

Python drivers and RF utilities for bench instrument automation. Connects to Siglent test equipment via raw TCP/SCPI (no pyvisa required) and to HF transceivers via Hamlib rigctld.

Instruments supported

Siglent (rf_bench.siglent)

Class Instrument family Tested with Protocol
SSA3000X SSA3000X Plus series spectrum analyzers SSA3032X Plus (9 kHz–3.2 GHz) SCPI / TCP port 5025
SDG1000X SDG1000X series function generators SDG1062X (2-ch, 60 MHz) EasyWave / TCP port 5025
SDS2000X SDS2000X Plus series oscilloscopes SDS2504X Plus (500 MHz) SCPI / TCP port 5025
SDM3000X SDM3000 series bench multimeters SDM3045X (4.5-digit) SCPI / TCP port 5025
SPD3303X SPD3303X series triple-output PSUs SPD3303X-E (2×32 V/3.2 A + fixed) SCPI / TCP port 5025

Icom (rf_bench.icom)

Class Instrument Protocol
IC7300 IC-7300 HF/6m transceiver Hamlib rigctld / TCP port 4532

Yaesu (rf_bench.yaesu)

Class Instrument Protocol
FT891 FT-891 HF/6m transceiver Hamlib rigctld / TCP port 4532

Utilities (rf_bench.utils)

rf_utils — pure-Python RF math library. Power conversions, impedance and reflection math, noise figure, IP3, frequency formatting. No instruments, no side effects; safe to import anywhere.

Installation

Install everything at once (recommended):

pip install rf-bench

Or install only the sub-packages you need:

pip install rf-bench-drivers-siglent   # Siglent instruments
pip install rf-bench-drivers-icom      # Icom IC-7300
pip install rf-bench-drivers-yaesu     # Yaesu FT-891
pip install rf-bench-drivers-utils     # RF math utilities (no instruments)

Dependency: NumPy (for rf_bench.utils and the SDS2000X waveform decoder).

For radio control: Hamlib must be installed and rigctld must be running before using IC7300 or FT891.

# IC-7300  (CI-V baud set to 115200 in radio menu)
rigctld -m 3073 -r /dev/ttyUSB0 -s 115200 &

# FT-891  (CAT baud set to 38400 in Menu 031)
rigctld -m 1036 -r /dev/ttyUSB0 -s 38400 &

Quick start

from rf_bench import SDG1000X, IC7300, dbm_to_vpp, format_freq

# Function generator — two-tone test signal
with SDG1000X("10.1.1.61") as sdg:
    sdg.set_sine(1, freq_hz=14_001_000, level_dbm=-30)
    sdg.set_sine(2, freq_hz=14_001_500, level_dbm=-30)
    sdg.output_on(1)
    sdg.output_on(2)
    # ... run test ...

# IC-7300 S-meter reading
with IC7300() as rig:
    rig.set_frequency(14_200_000)
    rig.set_mode("usb")
    rig.set_agc("off")
    strength = rig.get_strength_settled(settle_s=0.5)
    print(f"Signal: {strength:.1f} STRENGTH units")

# RF math
dbm_to_vpp(-20)          # → 0.0632 Vpp  (P = Vpp²/8R, 50 Ω)
format_freq(14_200_000)  # → '14.2000 MHz'

Or import from subpackages:

from rf_bench.siglent import SSA3000X, SDG1000X
from rf_bench.icom   import IC7300
from rf_bench.yaesu  import FT891, PREAMP_OFF, PREAMP_AMP1
from rf_bench.utils  import thermal_noise_floor, ip3_from_imd, rl_to_vswr

Siglent drivers

SSA3000X

Tested with: Siglent SSA3032X Plus

from rf_bench.siglent import SSA3000X

with SSA3000X("10.1.1.60") as ssa:
    ssa.enable_tracking_generator(dbm=0)
    rbw = ssa.setup_band(14_000_000, 14_350_000, points=1001)
    ssa.single_sweep()           # blocks until sweep completes
    trace = ssa.get_trace()      # → np.ndarray of dBm values (length = points)

SDG1000X

Tested with: Siglent SDG1062X

from rf_bench.siglent import SDG1000X

with SDG1000X("10.1.1.61") as sdg:
    sdg.set_sine(1, freq_hz=14_001_000, level_dbm=-30)
    sdg.output_on(1)
    sdg.set_level(1, level_dbm=-40)   # change level only, preserve frequency
    info = sdg.query_channel(1)       # → {freq_hz, amp_vpp, amp_dbm, ...}

Amplitude range: ≈ −50 dBm (2 mVpp) to +24 dBm (10 Vpp) into 50 Ω.

SDS2000X

Tested with: Siglent SDS2504X Plus (firmware 5.4.x)

from rf_bench.siglent import SDS2000X

with SDS2000X("10.1.1.58") as scope:
    # Waveform capture (deep memory, auto V/div)
    voltages, sample_rate = scope.capture_audio(channel=1, duration_s=2.0)
    rms  = scope.measure_rms(channel=1)
    vpp  = scope.measure_vpp(channel=1)
    freq = scope.measure_freq(channel=1)
    vdiv = scope.autoscale_vdiv(channel=1)

Built-in AWG [Option — requires AWG license]

The SDS2000X Plus has a licensed 25 MHz single-channel AWG output on the dedicated "Gen Out" BNC.

Tested with: SDS2504X Plus (firmware 5.4.x) — AWG license confirmed present.

# Waveforms — each call configures and enables the output
scope.set_awg_sine(freq_hz=1000, amplitude_vpp=1.0)
scope.set_awg_sine(freq_hz=1000, amplitude_vpp=1.0, offset_v=0.5, phase_deg=90)

scope.set_awg_square(freq_hz=1000, amplitude_vpp=2.0)
scope.set_awg_square(freq_hz=1000, amplitude_vpp=2.0, duty_pct=30.0)

scope.set_awg_ramp(freq_hz=500, amplitude_vpp=1.5)             # sawtooth (100% symmetry)
scope.set_awg_ramp(freq_hz=500, amplitude_vpp=1.5, symmetry_pct=50.0)  # triangle

scope.set_awg_dc(offset_v=2.5)    # constant DC voltage

# Output control
scope.awg_output_on()
scope.awg_output_off()

# Query current state
state = scope.get_awg_state()
# → {'function': 'SINE', 'freq_hz': 1000.0, 'amplitude_vpp': 1.0,
#    'offset_v': 0.0, 'output_on': True}

Frequency range: 1 mHz – 25 MHz. Amplitude: 2 mVpp – 6 Vpp into high impedance (1 mVpp – 3 Vpp into 50 Ω). Output impedance: 50 Ω.

MSO / Digital channels [Option — hardware not tested]

The SDS2000X Plus supports 16 digital channels (D0–D15) via an optional MSO hardware probe pod. Channels D0–D7 form pod 1; D8–D15 form pod 2; thresholds are set per pod.

Note: This code is implemented from the official Siglent EN11F programming guide but has not been tested with physical MSO hardware — the author does not currently have the digital probe pod. Feedback on correctness, especially the bit-packing format and custom threshold voltage syntax, is welcome.

# Enable digital display and configure channels
scope.digital_enable()
scope.digital_channel_enable(0)              # turn on D0
scope.digital_channel_enable(5)              # turn on D5
scope.set_digital_threshold(1, "TTL")        # D0–D7: TTL (1.4 V)
scope.set_digital_threshold(2, "LVCMOS33")   # D8–D15: 3.3 V LVCMOS
scope.set_digital_threshold(1, 1.8)          # D0–D7: 1.8 V custom

scope.set_digital_label(0, "CLK")            # label D0 as "CLK"

# Capture digital waveform from current (frozen) acquisition
scope.stop()
samples, sr = scope.capture_digital(0)       # D0 → (bool array, sample_rate Hz)

sr_hz  = scope.get_digital_sample_rate()     # → e.g. 1.25e9
n_pts  = scope.get_digital_point_count()     # → e.g. 2500
print(scope.get_digital_threshold(1))        # → 'TTL'
print(scope.is_digital_channel_enabled(0))   # → True

scope.digital_disable()

Digital data returned by capture_digital() is a numpy bool array with one element per sample (True = logic HIGH). Internally the scope returns packed bits (1 bit per sample, LSB of each byte = earliest sample); the driver unpacks them automatically.

SDM3000X

Tested with: Siglent SDM3045X (4.5-digit) Compatible with: SDM3045X, SDM3055 (5.5-digit), SDM3065X (6.5-digit)

All measurement functions return SI units (V, A, Ω, Hz, F, °C). MEAS commands are one-shot; use configure_*() + read_multiple() for repeated measurements.

from rf_bench.siglent import SDM3000X

with SDM3000X("10.1.1.63") as dmm:
    v = dmm.measure_vdc()                    # DC voltage, auto-range → V
    v = dmm.measure_vdc(range_v=20)          # DC voltage, 20 V range → V
    i = dmm.measure_idc()                    # DC current → A
    r = dmm.measure_resistance()             # 2-wire resistance → Ω
    r = dmm.measure_resistance(four_wire=True)   # 4-wire (Kelvin) → Ω
    f = dmm.measure_frequency()              # frequency → Hz
    dmm.measure_continuity()                 # resistance; beeps if < ~30 Ω
    dmm.measure_diode()                      # forward voltage → V

    # SDM3055 / SDM3065X only:
    c = dmm.measure_capacitance()            # → F
    t = dmm.measure_temperature()            # → °C (FRTD probe default)

    # Multi-sample: configure once, read many
    dmm.configure_vdc(range_v=5)
    samples = dmm.read_multiple(20)          # → [float, ...] 20 samples

SPD3303X

Tested with: Siglent SPD3303X-E (2× 0–32 V / 0–3.2 A + fixed CH3) Compatible with: SPD3303C, SPD3303X, SPD3303X-E

CH1 and CH2 are fully programmable CC/CV channels. CH3 is a fixed-voltage output (2.5 V, 3.3 V, or 5 V selected by front-panel switch); its voltage cannot be set via SCPI but its output can be enabled/disabled and measured.

from rf_bench.siglent import SPD3303X, TRACKING_INDEPENDENT, TRACKING_SERIES

with SPD3303X("10.1.1.64") as psu:
    # Basic CH1 setup
    psu.set_voltage(1, 5.0)          # 5 V setpoint
    psu.set_current(1, 0.5)          # 500 mA current limit
    psu.enable(1)

    v    = psu.measure_voltage(1)    # actual output voltage → V
    i    = psu.measure_current(1)    # actual output current → A
    p    = psu.measure_power(1)      # actual output power → W
    mode = psu.get_mode(1)           # 'CV' or 'CC'

    state = psu.measure_all(1)       # {'voltage_v', 'current_a', 'power_w'}

    # CH3 (fixed voltage — 2.5/3.3/5 V set by front-panel switch)
    psu.enable(3)
    psu.measure_voltage(3)           # reads actual CH3 output voltage

    psu.disable_all()

    # Series tracking: CH1+CH2 in series for up to 64 V
    psu.set_tracking(TRACKING_SERIES)
    psu.set_voltage(1, 24.0)         # CH2 mirrors CH1 automatically
    psu.enable(1)
    psu.enable(2)
    psu.get_status()   # → {'ch1_mode': 'CV', 'ch2_mode': 'CV', 'track_mode': 'SER'}

Radio drivers

IC7300 and FT891 share an identical core interface and are drop-in substitutable.

from rf_bench.icom  import IC7300
from rf_bench.yaesu import FT891, PREAMP_OFF, PREAMP_AMP1

# Shared interface
for RigClass in (IC7300, FT891):
    with RigClass() as rig:
        rig.set_frequency(14_200_000)
        rig.set_mode("usb", passband_hz=2400)
        rig.set_agc("slow")
        rig.set_rf_gain(1.0)
        strength = rig.get_strength_settled()

# FT-891 additions: preamp / attenuator
with FT891() as rig:
    rig.set_preamp(PREAMP_OFF)   # IPO — bypass preamp for large-signal tests
    rig.set_preamp(PREAMP_AMP1)  # AMP1 — ~10 dB gain for sensitivity tests
    rig.set_att(6)               # 0, 6, or 12 dB front-end attenuation

AGC note: set_agc("off") is a true hardware bypass on the IC-7300. On the FT-891 it maps to the slowest AGC constant — not a true bypass.

If both radios are in use simultaneously, run each rigctld on a separate port:

rigctld -m 3073 -r /dev/ttyUSB0 -s 115200 -T localhost -t 4532 &
rigctld -m 1036 -r /dev/ttyUSB1 -s 38400  -T localhost -t 4533 &
ic  = IC7300("localhost", 4532)
ft  = FT891("localhost", 4533)

RF utilities

rf_bench.utils is a pure-Python RF math library. No instruments, no side effects; safe to import anywhere.

from rf_bench.utils import (
    # Constants
    SPEED_OF_LIGHT,                  # 299 792 458 m/s (exact)
    S9_HF_DBM, S9_VHF_DBM,          # −73 / −93 dBm (ITU S-meter references)

    # Power / voltage (50 Ω default; pass impedance= to override)
    dbm_to_vpp, vpp_to_dbm,         # dBm ↔ Vpp  (sine: P = Vpp²/8R; 0 dBm → 0.6325 Vpp)
    dbm_to_vrms, vrms_to_dbm,       # dBm ↔ Vrms
    dbm_to_watts, watts_to_dbm,     # dBm ↔ Watts
    dbm_to_uv, uv_to_dbm,           # dBm ↔ µVrms

    # Power ratio / extended dB units
    db_to_linear, linear_to_db,     # power ratio ↔ dB
    db_to_voltage_ratio,            # voltage ratio from dB (10^(dB/20))
    voltage_ratio_to_db,            # dB from voltage ratio (20·log10)
    dbm_to_dbw, dbw_to_dbm,         # dBm ↔ dBW
    dbm_to_dbuv, dbuv_to_dbm,       # dBm ↔ dBµV (0 dBm at 50 Ω = 106.99 dBµV)

    # Impedance / reflection
    rl_to_vswr, vswr_to_rl,         # return loss ↔ VSWR
    gamma_to_vswr, vswr_to_gamma,   # reflection coeff ↔ VSWR
    rl_to_gamma, gamma_to_rl,
    rl_to_vswr_v, vswr_to_rl_v,     # vectorized (numpy array) versions
    gamma_to_vswr_v,

    # Noise and dynamic range
    thermal_noise_floor,             # kTB in dBm (exact Boltzmann constant)
    noise_figure_from_mds,           # NF from measured MDS and bandwidth
    mds_from_noise_figure,           # MDS from NF and bandwidth
    ip3_from_imd,                    # OIP3 or IIP3 from two-tone IMD levels
    ip3_to_dynamic_range,            # SFDR = (2/3)(IP3 − noise floor)
    cascaded_noise_figure,           # Friis formula for cascade of (gain_db, nf_db) stages
    noise_temp_to_nf,                # noise temperature (K) → NF (dB)
    nf_to_noise_temp,                # NF (dB) → noise temperature (K)

    # Propagation / antenna
    wavelength, quarter_wave,        # λ, λ/4 in metres; optional velocity_factor
    half_wave,                       # λ/2 in metres
    freespace_path_loss,             # FSPL = 20·log10(4πdf/c) in dB

    # Passive components
    capacitive_reactance,            # Xc = 1/(2πfC) Ω
    inductive_reactance,             # Xl = 2πfL Ω
    lc_resonant_freq,                # f = 1/(2π√(LC)) Hz
    l_from_resonant, c_from_resonant,# compute L or C from resonant frequency
    q_factor, bw_from_q,             # Q = f0/BW ↔ BW = f0/Q
    parallel_resistance,             # 1/Σ(1/Rᵢ) — 2 or more values
    voltage_divider,                 # Vout = Vin · R2 / (R1+R2)
    skin_depth,                      # δ in metres (copper default: 5.8×10⁷ S/m)

    # Attenuator design
    pi_attenuator,                   # π-pad: {'r_shunt': Ω, 'r_series': Ω}
    t_attenuator,                    # T-pad:  {'r_series': Ω, 'r_shunt': Ω}

    # IM products
    intermod_products,               # two-tone near-carrier IM products, odd orders

    # S-meter
    s_unit_to_dbm, dbm_to_s_unit,   # ITU S-unit ↔ dBm (HF default; vhf=True for VHF)

    # Formatting
    format_freq,                     # 14200000 → '14.2000 MHz'; also GHz and Hz
    format_freq_short,               # 14200000 → '14.2 MHz' (trailing zeros trimmed)
    nearest_rbw,                     # nearest Siglent RBW step
    nearest_value,                   # nearest value in any list (E-series, RBW, etc.)

    # Standard value series
    SIGLENT_RBW_SERIES,
    E12_SERIES, E24_SERIES, E48_SERIES, E96_SERIES,
)

Default instrument addresses

Driver class Tested instrument Default IP / address Port Notes
SSA3000X SSA3032X Plus 10.1.1.60 5025 LAN/SCPI
SDG1000X SDG1062X 10.1.1.55 5025 LAN/SCPI
SDS2000X SDS2504X Plus 10.1.1.58 5025 LAN/SCPI
SDM3000X SDM3045X 10.1.1.63 5025 LAN/SCPI
SPD3303X SPD3303X-E 10.1.1.56 5025 LAN/SCPI
IC7300 IC-7300 localhost 4532 rigctld -m 3073 -r /dev/ttyUSB0 -s 115200
FT891 FT-891 localhost 4532 rigctld -m 1036 -r /dev/ttyUSB0 -s 38400

All drivers accept host and port constructor arguments to override defaults.

Firmware bugs and quirks

Known instrument firmware issues discovered during live testing. Workarounds are implemented in the drivers; this section explains the underlying behaviour for reference when working on driver code or upgrading firmware.

SDS2000X Plus — firmware 5.4.0.1.6.2R5

Bug 1: :WAVeform:DATA? intermittently returns display-buffer data instead of deep memory

:WAVeform:POINt MAX followed by :WAVeform:DATA? occasionally returns 1 000 samples at 2 GSps (the 1 000-point display buffer) instead of full acquisition memory (10 M samples at 20 MHz for a 1 s capture). The bug is state-dependent — it is not tied to any specific VDIV or TDIV value. It appears more frequently when the scope's ADC configuration has been recently changed (e.g., by an autorange probe at a different VDIV).

Workaround: capture_audio() detects the display-buffer condition (len ≤ 1000 and sample_rate > 500 MHz) and retries once; the second attempt almost always succeeds because re-arming the trigger clears the state.

Bug 2: ADC not reconfigured after VDIV change — INVESTIGATED, NOT REPRODUCED

Initially suspected: after C1:VDIV changes the vertical scale, WAVEDESC would report the new (correct) vgain while the ADC data remained scaled for the old VDIV — a "phantom vgain." Live debug output disproved this: on attempt 1 of a deep-memory capture, WAVEDESC-reported vgain matched the ADC data correctly, and the 99th-percentile amplitude matched the expected signal level. The inflated amplitude seen in earlier testing was caused by numpy.max() picking up isolated noise spikes, not a firmware miscalibration.

Status: No driver workaround is needed or present. Retained here to document the investigation in case the symptom reappears on a different firmware version.

Bug 3: Stale bytes in socket after binary block read cause PAVA pipeline shift

After _read_binary_block() reads a waveform or preamble block, the scope appends a trailing \n that arrives slightly after the binary payload. Without an explicit drain, this byte sits in the TCP receive buffer. The next _query() call reads the stale byte and returns empty; all subsequent PAVA queries are shifted by one response (each call returns the previous call's data). Symptom: measure_rms() returns NaN and subsequent calls return each other's values.

Workaround: _read_binary_block() drains all trailing bytes with a 50 ms timeout after each binary read. measure_rms/vpp/freq() additionally call _drain() before each PAVA query.

Bug 4: PAVA returns a silent incorrect value when V/div is poorly matched to the signal

At large V/div (e.g., 2 V/div for a signal only a few mV peak), the signal occupies only ±1–3 ADC counts. PAVA returns a near-zero Vpp rather than an error, with no indication that the measurement is unreliable. If autorange naively trusts this small reading it selects a far-too-fine V/div, the ADC clips the actual signal, and the error compounds.

Workaround: _autorange_vdiv() captures a short waveform at 0.1 V/div and computes the 99th-percentile peak directly from the ADC counts, bypassing PAVA entirely for autorange decisions.

Behavior note: :WAVeform:POINt MAX must be sent before every data read

Without it, the scope returns the 1 000-point display-decimated trace regardless of acquisition memory depth. This is consistent with general SCPI oscilloscope behavior (default is display resolution, not deep memory) but can surprise users who expect a long-timebase capture to return deep-memory data automatically.

Practice: always issued in capture_audio().

Bug 6: TDIV/VDIV changes must be made while scope is stopped

Changing TDIV or VDIV while the scope is running produces a corrupt acquisition (may return 5 bytes, all +127).

Workaround: capture_audio() always issues :STOP before reconfiguring.

Bug 7: PAVA requires a fresh, complete sweep before it returns valid data

After :STOP, calling :RUN and querying PAVA immediately returns NaN or wrong values. PAVA needs at least one full sweep to complete.

Workaround: measure_rms/vpp/freq() set a short TDIV (2 ms/div = 20 ms window) before running, so the sweep finishes well within the 0.5 s sleep.

Bug 8: Changing TDIV while stopped silently corrupts the VDIV register

When measure_rms/vpp/freq() are called after capture_audio(), the _pava_setup() helper issues :STOP, changes TDIV to 2 ms, then runs. Even though the scope is stopped at the time of the TDIV change (workaround for Bug 6), the firmware silently resets the VDIV register to an undefined value. The acquisition runs at the new TDIV but with a wrong VDIV, causing PAVA FREQ to return wildly incorrect values (e.g. 45200 Hz, 99500 Hz) while PAVA RMS and PKPK appear plausible but are off by the VDIV ratio.

Workaround: _pava_setup() now explicitly re-applies C{n}:VDIV after every TDIV change, using the VDIV that was last set by capture_audio() for that channel (tracked in self._last_capture_vdiv), or 0.5 V/div if no prior capture has been done.

SSA3000X Plus — firmware 3.2.2.6.3R2

Quirk: :SENS:SWE:POIN command silently ignored — trace length fixed at 751

The setup_band() method sends :SENS:SWE:POIN {n} to request a specific number of sweep points, but firmware 3.2.x on the SSA3032X Plus ignores the command. The instrument always returns traces with exactly 751 points, regardless of what was requested. This makes the RBW calculation in setup_band() incorrect if it uses the requested point count instead of the actual count.

Workaround: setup_band() calls get_sweep_points() to read the actual point count before computing the RBW, ensuring the RBW matches what the firmware will use. The :SENS:SWE:POIN command is still sent (it may work on other firmware versions) but the actual count is read back and used.

SDG1062X — firmware 1.01.01.33R3

Bug: BSWV response appends unit suffixes to all numeric values

Querying C1:BSWV? returns fields like FRQ,1000HZ, AMP,0.2V, AMPDBM,-10.0dBm, OFST,0.0V. The float() built-in raises ValueError on these strings, silently returning 0 for all channel parameters.

Workaround: _strip_unit() in SDG1000X strips the unit suffix via regex before parsing.

SPD3303X-E — firmware (all versions tested)

Quirk: OUTP? does not respond reliably

:OUTP? CH1 sometimes returns an empty response.

Workaround: is_enabled() uses the SYST:STAT? status register bitmask (bits 4–6 = CH1/CH2/CH3 output enable) instead.

License

GPL-3.0-or-later — see LICENSE.

Project details


Download files

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

Source Distribution

rf_bench-0.3.0.tar.gz (24.0 kB view details)

Uploaded Source

Built Distribution

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

rf_bench-0.3.0-py3-none-any.whl (22.9 kB view details)

Uploaded Python 3

File details

Details for the file rf_bench-0.3.0.tar.gz.

File metadata

  • Download URL: rf_bench-0.3.0.tar.gz
  • Upload date:
  • Size: 24.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for rf_bench-0.3.0.tar.gz
Algorithm Hash digest
SHA256 f67271d8c1ea7c070b21e18e8c365dc8d8ea813e94c5ef89f4924ebf618a53b6
MD5 ea8909d4342023084da620649cf8517f
BLAKE2b-256 a235e58ab4bed8f7bff2c3d9f825c5dfcfe9984c7a9f82f8e274adafb7d15333

See more details on using hashes here.

File details

Details for the file rf_bench-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: rf_bench-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 22.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for rf_bench-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4b887aa7080ee5fd5b452502a6b2f02b02dee34de233d6f94f513358ca25c970
MD5 a89d1f3e52c733f3218793ece5226bc5
BLAKE2b-256 45d51a8877cc61a6e43e2dd2e98a7fe159879096ec31cbcd32a2c8162aa0d5ba

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page