159 lines
5.1 KiB
Python
159 lines
5.1 KiB
Python
"""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)
|