ctrl API Reference
xdm1000_ctrl.py provides the user-facing high-level control API for the XDM1000 / XDM1241 instruments. The SCPI layer is a thin wrapper over SCPI commands; the ctrl layer organizes measurement mode, range, auto-ranging, internal measurement speed, and continuous-measurement settings into a higher-level API.
This reference describes the APIs documented for the current xdm1000_ctrl.py, xdm1000_worker.py, and server_fastapi/spec.py.
Design Policy
Do not mix measurement settings into the arguments of meas() or meas_continue(). Configure the instrument first using set_mode(), set_range(), set_auto_range(), and set_integration_speed().
Single and continuous measurements have separate entry points:
result = dmm.meas()
job = dmm.meas_continue()
Set the sampling interval and termination conditions required only for continuous measurement using set_continue_interval(), set_continue_duration(), and set_continue_count().
Public API
The main user-facing APIs of the current Xdm1000Ctrl are:
close()
disconnect()
get_state()
is_running()
idn()
set_mode(mode)
set_range(value=None, index=None, range_value=None)
set_auto_range(enabled)
set_integration_speed(speed)
set_continue_interval(interval)
set_continue_duration(duration)
set_continue_count(count)
meas(wait=None)
meas_continue(wait=None)
get_result(clear=False)
wait_until_done(poll_interval=0.2)
The following legacy APIs are absent from the current Xdm1000Ctrl:
set_meas_mode()
meas_one_shot()
meas_voltage_dc()
meas_current_dc()
start_continuous_meas()
start_statistics_mode()
get_continuous_result()
get_statistics_result()
stop_job()
remote(), local(), query(), and write() are not exposed as ctrl APIs; they are the responsibility of the SCPI layer.
Initializing Xdm1000Ctrl
Xdm1000Ctrl(
port=None,
timeout=2.0,
baudrate=115200,
logger=None,
log_level=None,
config=None,
use_process=True,
)
If config is supplied, PORT, TIMEOUT, and BAUDRATE from it take precedence. If port remains unspecified, ValueError is raised.
With use_process=True, the worker process owns the actual SCPI object. The parent sends commands to the worker and reads status and results from shared state and the result store.
With use_process=False, Xdm1000Ctrl owns the SCPI object directly in the same process.
State and Lifecycle
close()
Closes the connection or worker process.
With use_process=True, sends shutdown to the worker; if it does not exit, terminates the process, then shuts down the manager.
With use_process=False, attempts local() before closing the SCPI connection.
disconnect()
Alias for close().
get_state()
Returns the current state as a dictionary.
With use_process=True, adds process_alive to the shared state.
With use_process=False, adds process_alive=False to the direct-mode state.
The main keys are:
{
"worker_alive": True,
"process_alive": True,
"port": "/dev/cu.usbserial-10",
"baudrate": 115200,
"running": False,
"job_id": None,
"job_type": None,
"started_at": None,
"finished_at": None,
"error": None,
"traceback": None,
"message": "worker_ready",
"args": {},
"mode": "voltage_dc",
"range_value": 10,
"auto_range": False,
"integration_speed": "F",
"continue_interval": 0.1,
"continue_duration": 10.0,
"continue_count": None,
}
args stores the arguments received by the worker while a job is running.
is_running()
Returns True during a long-running measurement.
wait_until_done(poll_interval=0.2)
Waits until running becomes False. Returns a dictionary equivalent to get_state() at completion.
Args:
poll_interval: float = 0.2
Returns:
state: dict
Instrument Identification API
idn()
Returns the response to *IDN?.
Unavailable while running; calling it then raises DeviceBusyError.
Returns:
"OWON,..."
Configuration API
set_mode(mode)
Sets the measurement mode for both single and continuous measurements.
Args:
mode: str
Supported mode values are:
voltage_dc
voltage_ac
current_dc
current_ac
resistance
fresistance
frequency
period
capacitance
temperature_rtd
diode
continuity
The following mapping is used to set the SCPI FUNCtion:
voltage_dc -> VOLT
voltage_ac -> VOLT AC
current_dc -> CURR
current_ac -> CURR AC
resistance -> RES
fresistance -> FRES
frequency -> FREQ
period -> PER
capacitance -> CAP
temperature_rtd -> TEMP
diode -> DIOD
continuity -> CONT
Calling set_mode() invalidates the cached range_value and auto_range, since the range depends on the measurement mode.
Returns:
{"mode": mode}
set_range(value=None, index=None, range_value=None)
Sets a manual range for the current measurement mode, for both single and continuous measurements.
Args:
value: str | int | float | None
index: int | None
range_value: str | int | float | None
value specifies a physical quantity. Numeric values are interpreted in the base unit of the current measurement mode.
voltage_dc, voltage_ac -> V
current_dc, current_ac -> A
resistance, fresistance -> Ohm
capacitance -> F
temperature_rtd -> KITS90 or PT100
Unit-bearing strings are accepted with or without spaces.
dmm.set_mode("voltage_dc")
dmm.set_range(value=5.0) # 5 V range
dmm.set_range(value="500 mV") # 500 mV range
dmm.set_range(value="500mV") # 500 mV range
index directly specifies a discrete SCPI RANGE index from the XDM1000 Programming Manual.
dmm.set_mode("voltage_dc")
dmm.set_range(index=3) # DCV 5 V range
Do not specify value, index, and range_value together. range_value is a legacy-compatible alias for value.
The ctrl layer checks the requested value against the current mode and converts only supported discrete ranges to SCPI RANGE <index>. Afterward, the auto_range cache is set to False.
set_mode() is required before set_range().
Returns:
{
"range_value": value | None,
"range_index": index,
"range_label": "5 V",
}
set_auto_range(enabled)
Sets auto-ranging for both single and continuous measurements.
Args:
enabled: bool
When enabled=True, the SCPI layer issues AUTO.
When enabled=False, the XDM1000 SCPI interface is not sent a generic AUTO OFF. Instead, the most recent range_index determined by set_range() is re-applied using RANGE <index>. Therefore, call set_range() before setting enabled=False.
set_mode() is required before set_auto_range().
Returns:
{"auto_range": True | False}
set_integration_speed(speed)
Sets the internal measurement speed for both single and continuous measurements.
Args:
speed: str
The following values are accepted; lowercase input is converted to uppercase.
F: high speed
M: middle speed
S: low speed
The SCPI command is named RATE, but the ctrl API calls this integration_speed to avoid confusion with the interval between measurement samples. The continuous-measurement interval is set by set_continue_interval().
Returns:
{"integration_speed": speed}
Continuous-Measurement Settings API
set_continue_interval(interval)
Sets the interval between measurement starts during continuous measurement.
Args:
interval: float
The unit is seconds. The value must be nonnegative; 0.0 reads as quickly as possible.
The implementation treats it as the minimum interval from the start of one sample to the target start time of the next.
Returns:
{"continue_interval": interval}
set_continue_duration(duration)
Sets the maximum duration of continuous measurement.
Args:
duration: float | None
The unit is seconds. None disables the time limit; a numeric duration must be positive.
If both duration and continue_count are configured, measurement ends when the first limit is reached.
Returns:
{"continue_duration": duration}
set_continue_count(count)
Sets the maximum number of continuous-measurement samples.
Args:
count: int | None
None disables the sample-count limit; a numeric count must be a positive integer.
If both duration and count are None, meas_continue() raises ValueError.
Returns:
{"continue_count": count}
Measurement API
meas(wait=None)
Takes one measurement using the current settings.
Args:
wait: float | None = None
wait is not an SCPI feature. It is a Python-side delay, in seconds, immediately before measurement. None means no delay.
mode, range_value, and integration_speed are not measurement-call arguments. Set them beforehand using the configuration API.
set_mode() is required before meas().
Returns:
{
"type": "single",
"mode": "voltage_dc",
"range_value": 10,
"auto_range": False,
"integration_speed": "F",
"value": 1.234,
"host_time_start": 1710000000.0,
"host_time_end": 1710000000.02,
"read_elapsed": 0.02,
}
The returned result is stored for subsequent retrieval using get_result().
meas_continue(wait=None)
Starts or executes continuous measurement using the current settings.
Args:
wait: float | None = None
wait is not an SCPI feature. It is a Python-side delay, in seconds, before continuous measurement starts. None means no delay.
At minimum, configure the following before calling meas_continue():
dmm.set_mode("voltage_dc")
dmm.set_continue_duration(10.0)
Alternatively:
dmm.set_mode("voltage_dc")
dmm.set_continue_count(100)
Returns:
{"job_id": "..."}
With use_process=True, the worker process continues measuring and only the job ID is returned to the caller. Retrieve the result with get_result().
With use_process=False, the measurement loop runs in the calling process, and the job ID is returned after completion. The call therefore blocks until measurement finishes.
Result Retrieval API
get_result(clear=False)
Returns the most recent single- or continuous-measurement result.
Args:
clear: bool = False
With clear=True, the stored result is removed after retrieval.
A single-measurement result has type="single"; a continuous-measurement result has type="continuous".
Example single-measurement result:
{
"type": "single",
"mode": "voltage_dc",
"range_value": 10,
"auto_range": False,
"integration_speed": "F",
"value": 1.234,
"host_time_start": 1710000000.0,
"host_time_end": 1710000000.02,
"read_elapsed": 0.02,
}
Example continuous-measurement result:
{
"type": "continuous",
"job_id": "...",
"ok": True,
"mode": "voltage_dc",
"range_value": 10,
"auto_range": False,
"integration_speed": "F",
"continue_interval": 0.1,
"continue_duration": 10.0,
"continue_count": None,
"stats": {
"count": 100,
"minimum": 1.23,
"maximum": 1.25,
"average": 1.24,
"stdev_population": 0.01
},
"started_at": 1710000000.0,
"finished_at": 1710000010.0,
"samples": [
{
"index": 0,
"value": 1.234,
"host_time_start": 1710000000.0,
"host_time_end": 1710000000.02,
"read_elapsed": 0.02,
"t_rel_start": 0.0,
"t_rel_end": 0.02
}
]
}
In the current direct implementation (use_process=False), continuous results omit stats, started_at, and finished_at and include count instead.
Restrictions While Running
The following APIs cannot be called during a running measurement:
idn()
set_mode()
set_range()
set_auto_range()
set_integration_speed()
set_continue_interval()
set_continue_duration()
set_continue_count()
meas()
meas_continue()
Calling one of them while running raises DeviceBusyError.
The following APIs can still be called while running:
get_state()
is_running()
get_result()
wait_until_done()
Transparent FastAPI
The APIs exposed by server_fastapi/spec.py are:
get_state
is_running
idn
set_mode
set_range
set_auto_range
set_integration_speed
set_continue_interval
set_continue_duration
set_continue_count
meas
meas_continue
get_result
wait_until_done
Each API is registered with object_name="xdm1000" and HTTP method post.
The request models are:
set_mode -> ModeRequest(mode: str)
set_range -> RangeRequest(value: str | int | float | None, index: int | None, range_value: str | int | float | None)
set_auto_range -> AutoRangeRequest(enabled: bool)
set_integration_speed -> IntegrationSpeedRequest(speed: str)
set_continue_interval -> ContinueIntervalRequest(interval: float)
set_continue_duration -> ContinueDurationRequest(duration: float | None)
set_continue_count -> ContinueCountRequest(count: int | None)
meas -> WaitRequest(wait: float | None)
meas_continue -> WaitRequest(wait: float | None)
get_result -> ClearRequest(clear: bool = False)
wait_until_done -> WaitUntilDoneRequest(poll_interval: float = 0.2)
Minimal Usage Examples
Single Measurement
from xdm1000.xdm1000_ctrl import Xdm1000Ctrl
dmm = Xdm1000Ctrl(port="/dev/cu.usbserial-10")
try:
dmm.set_mode("voltage_dc")
dmm.set_auto_range(True)
dmm.set_integration_speed("F")
result = dmm.meas()
print(result)
finally:
dmm.close()
Continuous Measurement
from xdm1000.xdm1000_ctrl import Xdm1000Ctrl
dmm = Xdm1000Ctrl(port="/dev/cu.usbserial-10")
try:
dmm.set_mode("voltage_dc")
dmm.set_auto_range(True)
dmm.set_integration_speed("F")
dmm.set_continue_interval(0.1)
dmm.set_continue_duration(10.0)
job = dmm.meas_continue()
print(job)
dmm.wait_until_done()
result = dmm.get_result()
print(result)
finally:
dmm.close()