Add thermal chamber driver
This commit is contained in:
@@ -5,5 +5,6 @@ and handles responses from instruments.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from py_dvt_ate.instruments.drivers.base import BaseDriver
|
from py_dvt_ate.instruments.drivers.base import BaseDriver
|
||||||
|
from py_dvt_ate.instruments.drivers.chamber import ThermalChamberDriver
|
||||||
|
|
||||||
__all__ = ["BaseDriver"]
|
__all__ = ["BaseDriver", "ThermalChamberDriver"]
|
||||||
|
|||||||
141
src/py_dvt_ate/instruments/drivers/chamber.py
Normal file
141
src/py_dvt_ate/instruments/drivers/chamber.py
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
"""Thermal chamber SCPI driver.
|
||||||
|
|
||||||
|
This module implements a client-side driver for thermal chambers that
|
||||||
|
communicate via SCPI commands.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from py_dvt_ate.instruments.drivers.base import BaseDriver
|
||||||
|
|
||||||
|
|
||||||
|
class ThermalChamberDriver(BaseDriver):
|
||||||
|
"""SCPI driver for thermal chambers.
|
||||||
|
|
||||||
|
Provides high-level Python API for controlling thermal chambers via
|
||||||
|
SCPI commands. Implements the IThermalChamber interface.
|
||||||
|
|
||||||
|
SCPI Commands Used:
|
||||||
|
TEMP:SETPOINT <value> - Set target temperature (°C)
|
||||||
|
TEMP:SETPOINT? - Query current setpoint
|
||||||
|
TEMP:ACTUAL? - Query actual chamber temperature
|
||||||
|
TEMP:STAB? - Query stability (1=stable, 0=settling)
|
||||||
|
TEMP:RAMP <rate> - Set temperature ramp rate (°C/min)
|
||||||
|
TEMP:RAMP? - Query ramp rate
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> transport = TCPTransport("localhost", 5001)
|
||||||
|
>>> chamber = ThermalChamberDriver(transport)
|
||||||
|
>>> chamber.connect()
|
||||||
|
>>> chamber.set_temperature(85.0)
|
||||||
|
>>> chamber.wait_until_stable(timeout=600.0)
|
||||||
|
>>> temp = chamber.get_temperature()
|
||||||
|
"""
|
||||||
|
|
||||||
|
def set_temperature(self, setpoint: float) -> None:
|
||||||
|
"""Set the chamber temperature setpoint.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
setpoint: Target temperature in degrees Celsius.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConnectionError: If not connected.
|
||||||
|
IOError: If command fails.
|
||||||
|
"""
|
||||||
|
self.write(f"TEMP:SETPOINT {setpoint:.2f}")
|
||||||
|
|
||||||
|
def get_temperature(self) -> float:
|
||||||
|
"""Get the actual chamber temperature.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Current chamber temperature in degrees Celsius.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConnectionError: If not connected.
|
||||||
|
IOError: If query fails.
|
||||||
|
"""
|
||||||
|
return self.query_float("TEMP:ACTUAL?")
|
||||||
|
|
||||||
|
def get_setpoint(self) -> float:
|
||||||
|
"""Get the current temperature setpoint.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Current setpoint in degrees Celsius.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConnectionError: If not connected.
|
||||||
|
IOError: If query fails.
|
||||||
|
"""
|
||||||
|
return self.query_float("TEMP:SETPOINT?")
|
||||||
|
|
||||||
|
def is_stable(self) -> bool:
|
||||||
|
"""Check if chamber temperature is stable.
|
||||||
|
|
||||||
|
Temperature is considered stable when it has settled within
|
||||||
|
the instrument's configured stability threshold of the setpoint.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if temperature is stable, False if still settling.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConnectionError: If not connected.
|
||||||
|
IOError: If query fails.
|
||||||
|
"""
|
||||||
|
return self.query_bool("TEMP:STAB?")
|
||||||
|
|
||||||
|
def wait_until_stable(
|
||||||
|
self, timeout: float = 300.0, poll_interval: float = 1.0
|
||||||
|
) -> bool:
|
||||||
|
"""Wait until chamber temperature stabilises.
|
||||||
|
|
||||||
|
Polls the stability status at regular intervals until stable
|
||||||
|
or timeout is reached.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
timeout: Maximum time to wait in seconds. Default 300s (5 minutes).
|
||||||
|
poll_interval: Time between stability checks in seconds. Default 1s.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if temperature stabilised within timeout, False if timed out.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConnectionError: If not connected.
|
||||||
|
IOError: If communication fails.
|
||||||
|
ValueError: If timeout or poll_interval are negative.
|
||||||
|
"""
|
||||||
|
if timeout < 0:
|
||||||
|
raise ValueError("Timeout must be non-negative")
|
||||||
|
if poll_interval <= 0:
|
||||||
|
raise ValueError("Poll interval must be positive")
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
while time.time() - start_time < timeout:
|
||||||
|
if self.is_stable():
|
||||||
|
return True
|
||||||
|
time.sleep(poll_interval)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_ramp_rate(self, rate: float) -> None:
|
||||||
|
"""Set the temperature ramp rate.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rate: Ramp rate in degrees Celsius per minute.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConnectionError: If not connected.
|
||||||
|
IOError: If command fails.
|
||||||
|
"""
|
||||||
|
self.write(f"TEMP:RAMP {rate:.2f}")
|
||||||
|
|
||||||
|
def get_ramp_rate(self) -> float:
|
||||||
|
"""Get the current temperature ramp rate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Ramp rate in degrees Celsius per minute.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConnectionError: If not connected.
|
||||||
|
IOError: If query fails.
|
||||||
|
"""
|
||||||
|
return self.query_float("TEMP:RAMP?")
|
||||||
Reference in New Issue
Block a user