"""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)