Add DVT test base class
This commit is contained in:
158
src/py_dvt_ate/tests/base.py
Normal file
158
src/py_dvt_ate/tests/base.py
Normal file
@@ -0,0 +1,158 @@
|
||||
"""Base class and utilities for DVT test implementations.
|
||||
|
||||
This module provides common functionality shared across all DVT tests,
|
||||
including thermal settling helpers, measurement utilities, and statistical
|
||||
calculations.
|
||||
"""
|
||||
|
||||
import time
|
||||
from abc import ABC
|
||||
from collections.abc import Callable
|
||||
|
||||
from py_dvt_ate.framework.context import ITest, TestContext
|
||||
|
||||
|
||||
class BaseDVTTest(ITest, ABC):
|
||||
"""Abstract base class for DVT tests with common utilities.
|
||||
|
||||
Provides helper methods for thermal settling, measurement averaging,
|
||||
and other common test patterns. All DVT tests should inherit from
|
||||
this class rather than directly from ITest.
|
||||
"""
|
||||
|
||||
def wait_for_temperature(
|
||||
self,
|
||||
context: TestContext,
|
||||
setpoint: float,
|
||||
timeout: float = 300.0,
|
||||
poll_interval: float = 1.0,
|
||||
) -> bool:
|
||||
"""Wait for thermal chamber to stabilise at setpoint.
|
||||
|
||||
Sets the chamber temperature and waits until stable. Logs progress
|
||||
to the test logger.
|
||||
|
||||
Args:
|
||||
context: Test context with instruments and logger.
|
||||
setpoint: Target temperature in degrees Celsius.
|
||||
timeout: Maximum wait time in seconds. Default 300s (5 minutes).
|
||||
poll_interval: Time between stability checks. Default 1s.
|
||||
|
||||
Returns:
|
||||
True if temperature stabilised within timeout, False if timed out.
|
||||
|
||||
Raises:
|
||||
ConnectionError: If instrument communication fails.
|
||||
IOError: If instrument reports error.
|
||||
"""
|
||||
chamber = context.instruments.chamber
|
||||
|
||||
# Set the temperature
|
||||
chamber.set_temperature(setpoint)
|
||||
context.logger.log_event(
|
||||
f"Set thermal chamber to {setpoint:.1f}°C, waiting for stability...",
|
||||
level="INFO",
|
||||
)
|
||||
|
||||
# Wait for stability
|
||||
start_time = time.time()
|
||||
elapsed = 0.0
|
||||
|
||||
while elapsed < timeout:
|
||||
if chamber.is_stable():
|
||||
actual = chamber.get_temperature()
|
||||
context.logger.log_event(
|
||||
f"Chamber stable at {actual:.2f}°C "
|
||||
f"(target {setpoint:.1f}°C) after {elapsed:.1f}s",
|
||||
level="INFO",
|
||||
)
|
||||
return True
|
||||
|
||||
time.sleep(poll_interval)
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
# Timeout
|
||||
actual = chamber.get_temperature()
|
||||
context.logger.log_event(
|
||||
f"Timeout waiting for stability. Chamber at {actual:.2f}°C, "
|
||||
f"target {setpoint:.1f}°C after {timeout:.1f}s",
|
||||
level="WARNING",
|
||||
)
|
||||
return False
|
||||
|
||||
def measure_averaged(
|
||||
self,
|
||||
measurement_func: Callable[[], float],
|
||||
num_samples: int = 5,
|
||||
settle_time: float = 0.1,
|
||||
) -> tuple[float, float]:
|
||||
"""Take multiple measurements and return mean and standard deviation.
|
||||
|
||||
Useful for reducing noise in measurements by averaging multiple samples.
|
||||
|
||||
Args:
|
||||
measurement_func: Function that returns a single measurement.
|
||||
num_samples: Number of samples to average. Default 5.
|
||||
settle_time: Delay between samples in seconds. Default 0.1s.
|
||||
|
||||
Returns:
|
||||
Tuple of (mean, standard_deviation).
|
||||
|
||||
Raises:
|
||||
ValueError: If num_samples < 1.
|
||||
Exception: If measurement_func raises an exception.
|
||||
"""
|
||||
if num_samples < 1:
|
||||
raise ValueError("num_samples must be at least 1")
|
||||
|
||||
samples: list[float] = []
|
||||
for _ in range(num_samples):
|
||||
if settle_time > 0 and len(samples) > 0:
|
||||
time.sleep(settle_time)
|
||||
samples.append(measurement_func())
|
||||
|
||||
mean = sum(samples) / len(samples)
|
||||
|
||||
if len(samples) == 1:
|
||||
std_dev = 0.0
|
||||
else:
|
||||
variance = sum((x - mean) ** 2 for x in samples) / (len(samples) - 1)
|
||||
std_dev = variance ** 0.5
|
||||
|
||||
return mean, std_dev
|
||||
|
||||
def thermal_settle(
|
||||
self,
|
||||
context: TestContext,
|
||||
additional_settle_time: float = 5.0,
|
||||
) -> None:
|
||||
"""Wait for additional thermal settling after chamber reports stable.
|
||||
|
||||
After the chamber reports stable temperature, this adds additional
|
||||
settling time to ensure the DUT junction temperature has also stabilised.
|
||||
This is important for measurements sensitive to self-heating effects.
|
||||
|
||||
Args:
|
||||
context: Test context with logger.
|
||||
additional_settle_time: Extra settling time in seconds. Default 5s.
|
||||
"""
|
||||
if additional_settle_time > 0:
|
||||
context.logger.log_event(
|
||||
f"Additional thermal settling for {additional_settle_time:.1f}s...",
|
||||
level="INFO",
|
||||
)
|
||||
time.sleep(additional_settle_time)
|
||||
|
||||
def delay(self, seconds: float, message: str | None = None) -> None:
|
||||
"""Sleep for specified duration.
|
||||
|
||||
Simple utility for adding delays in test sequences.
|
||||
|
||||
Args:
|
||||
seconds: Delay duration in seconds.
|
||||
message: Optional message describing reason for delay.
|
||||
"""
|
||||
if message:
|
||||
# Could log this if needed
|
||||
pass
|
||||
time.sleep(seconds)
|
||||
Reference in New Issue
Block a user