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-benchis 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-siglentSSA3000X, SDG1000X, SDS2000X, SDM3000X, SPD3303X rf-bench-drivers-icomIC7300 rf-bench-drivers-yaesuFT891 rf-bench-drivers-utilsRF 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f67271d8c1ea7c070b21e18e8c365dc8d8ea813e94c5ef89f4924ebf618a53b6
|
|
| MD5 |
ea8909d4342023084da620649cf8517f
|
|
| BLAKE2b-256 |
a235e58ab4bed8f7bff2c3d9f825c5dfcfe9984c7a9f82f8e274adafb7d15333
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b887aa7080ee5fd5b452502a6b2f02b02dee34de233d6f94f513358ca25c970
|
|
| MD5 |
a89d1f3e52c733f3218793ece5226bc5
|
|
| BLAKE2b-256 |
45d51a8877cc61a6e43e2dd2e98a7fe159879096ec31cbcd32a2c8162aa0d5ba
|