Skip to content

Latest commit

 

History

History
353 lines (271 loc) · 8.99 KB

File metadata and controls

353 lines (271 loc) · 8.99 KB

Mock Controller

Overview

The mock controller provides comprehensive simulation capabilities for instrument control during development and testing, eliminating the need for physical hardware. It ensures type safety through strict validation and offers configurable behavior via environment variables. The framework includes 91+ core tests with advanced mock implementations and intelligent protocol detection.

Features

  • Environment variable-based mode control
  • Type-safe mock data generation with 91+ core tests
  • Intelligent protocol detection from signal characteristics
  • Multi-protocol integration testing (USB4, PCIe, Ethernet)
  • Configurable error simulation
  • Custom response configuration
  • Runtime hardware detection
  • Advanced signal generation for realistic testing

Installation

The mock controller is included in the SerDes Validation Framework. No additional installation is required.

Environment Configuration

Control mock mode via environment variables:

# Force mock mode (no hardware required)
export SVF_MOCK_MODE=1

# Force real hardware mode
export SVF_MOCK_MODE=0

# Auto-detect mode (default)
unset SVF_MOCK_MODE

Basic Usage

from serdes_validation_framework.instrument_control.mock_controller import (
    get_instrument_controller,
    get_instrument_mode,
    InstrumentMode
)

# Initialize controller (automatically detects mode)
controller = get_instrument_controller()

# Check current mode
mode = controller.get_mode()
print(f"Operating in {mode} mode")

# Basic operations
controller.connect_instrument('GPIB::1::INSTR')
response = controller.query_instrument('GPIB::1::INSTR', '*IDN?')
print(f"Response: {response}")

Type Safety

The mock controller enforces strict type checking:

def send_command(self, resource_name: str, command: str) -> None:
    """
    Send command to mock instrument with type validation
    
    Args:
        resource_name: VISA resource identifier
        command: SCPI command string
        
    Raises:
        TypeError: If arguments have invalid types
        ValueError: If instrument not connected
    """
    assert isinstance(resource_name, str), "Resource name must be a string"
    assert isinstance(command, str), "Command must be a string"
    
    if resource_name not in self.connected_instruments:
        raise ValueError(f"Instrument {resource_name} not connected")

Mock Response Configuration

Basic Responses

# Add simple response
controller.add_mock_response(
    '*IDN?',
    'Mock Instrument v1.0',
    delay=0.1
)

# Add dynamic response
controller.add_mock_response(
    'MEASure:VOLTage:DC?',
    lambda: f"{np.random.normal(0, 0.1):.6f}",
    delay=0.2
)

PAM4 Signal Generation

# Configure PAM4 waveform generation
controller.add_mock_response(
    ':WAVeform:DATA?',
    lambda: generate_pam4_waveform(
        num_points=1000000,
        noise_amplitude=0.05
    ),
    delay=0.5
)

def generate_pam4_waveform(
    num_points: int = 1000000,
    noise_amplitude: float = 0.05
) -> str:
    """
    Generate synthetic PAM4 waveform
    
    Args:
        num_points: Number of data points
        noise_amplitude: Noise amplitude (0-1)
        
    Returns:
        Comma-separated waveform data string
    """
    assert isinstance(num_points, int), "num_points must be an integer"
    assert isinstance(noise_amplitude, float), "noise_amplitude must be a float"
    assert 0 <= noise_amplitude <= 1, "noise_amplitude must be between 0 and 1"
    
    # Generate PAM4 levels
    levels = np.array([-3.0, -1.0, 1.0, 3.0], dtype=np.float64)
    symbols = np.random.choice(levels, size=num_points)
    
    # Add noise
    noise = np.random.normal(0, noise_amplitude, num_points)
    waveform = symbols + noise
    
    return ','.join(f"{x:.8f}" for x in waveform)

Error Simulation

Configure error rates for testing error handling:

# Set error rates
controller.set_error_rates(
    connection_error_rate=0.1,  # 10% connection failures
    command_error_rate=0.05,    # 5% command failures
    data_error_rate=0.01       # 1% data corruption
)

Advanced Usage

Custom Error Handling

try:
    controller.connect_instrument('GPIB::1::INSTR')
    controller.send_command('GPIB::1::INSTR', '*RST')
except MockConnectionError as e:
    print(f"Connection failed: {e}")
except MockCommandError as e:
    print(f"Command failed: {e}")

Response Validation

def validate_response(
    command: str,
    response: Union[str, Callable[[], str]],
    delay: float,
    error_rate: float
) -> None:
    """
    Validate mock response configuration
    
    Args:
        command: SCPI command string
        response: Response string or generator function
        delay: Response delay in seconds
        error_rate: Error probability (0-1)
        
    Raises:
        AssertionError: If parameters are invalid
    """
    assert isinstance(command, str), "Command must be a string"
    assert callable(response) or isinstance(response, str), \
        "Response must be string or callable"
    assert isinstance(delay, float), "Delay must be a float"
    assert isinstance(error_rate, float), "Error rate must be a float"
    assert delay >= 0, "Delay must be non-negative"
    assert 0 <= error_rate <= 1, "Error rate must be between 0 and 1"

Best Practices

  1. Always validate input types:

    assert isinstance(value, float), f"Value must be float, got {type(value)}"
  2. Use type hints:

    def query_float(self, resource: str, query: str) -> float:
  3. Handle cleanup properly:

    try:
        controller.connect_instrument('GPIB::1::INSTR')
        # ... perform operations
    finally:
        controller.disconnect_instrument('GPIB::1::INSTR')
  4. Use logging for debugging:

    logger.debug(f"Mock response for {command}: {response}")

API Reference

Core Functions

get_instrument_controller()

def get_instrument_controller():
    """
    Get appropriate instrument controller based on environment and availability
    
    Returns:
        Real or mock instrument controller
        
    Raises:
        RuntimeError: If real mode forced but hardware not available
    """

get_instrument_mode()

def get_instrument_mode() -> InstrumentMode:
    """
    Determine instrument mode based on environment variables
    
    Environment Variables:
        SVF_MOCK_MODE: Control mock mode (1=mock, 0=real, unset=auto)
    
    Returns:
        InstrumentMode enum value
        
    Raises:
        ValueError: If SVF_MOCK_MODE contains invalid value
    """

MockInstrumentController Class

class MockInstrumentController:
    """
    Mock controller for GPIB instruments in test environments
    
    This class provides a mock implementation of instrument control functions
    for testing without physical hardware. It generates realistic synthetic
    data and simulates common instrument behaviors.
    """
    
    def connect_instrument(self, resource_name: str) -> None:
        """
        Simulate instrument connection
        
        Args:
            resource_name: VISA resource identifier
            
        Raises:
            MockConnectionError: If connection simulation fails
            ValueError: If resource name is invalid
        """
    
    def disconnect_instrument(self, resource_name: str) -> None:
        """
        Simulate instrument disconnection
        
        Args:
            resource_name: VISA resource identifier
            
        Raises:
            ValueError: If instrument not connected
        """
    
    def send_command(self, resource_name: str, command: str) -> None:
        """
        Simulate sending command to instrument
        
        Args:
            resource_name: VISA resource identifier
            command: SCPI command string
            
        Raises:
            ValueError: If instrument not connected
            MockCommandError: If command simulation fails
        """
    
    def query_instrument(self, resource_name: str, query: str) -> str:
        """
        Simulate querying instrument with realistic delays
        
        Args:
            resource_name: VISA resource identifier
            query: SCPI query string
            
        Returns:
            Simulated response string
            
        Raises:
            ValueError: If instrument not connected or query invalid
            MockCommandError: If query simulation fails
        """

Testing

Run the built-in tests:

python -m src.serdes_validation_framework.instrument_control.mock_controller

Example test output:

Running mock controller tests...
Test 1: Basic connectivity... PASS
Test 2: Waveform generation... PASS
Test 3: Custom responses... PASS
Test 4: Error simulation... PASS
Test 5: Cleanup operations... PASS
All tests completed successfully!

See Also