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.
- 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
The mock controller is included in the SerDes Validation Framework. No additional installation is required.
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_MODEfrom 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}")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")# 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
)# 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)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
)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}")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"-
Always validate input types:
assert isinstance(value, float), f"Value must be float, got {type(value)}"
-
Use type hints:
def query_float(self, resource: str, query: str) -> float:
-
Handle cleanup properly:
try: controller.connect_instrument('GPIB::1::INSTR') # ... perform operations finally: controller.disconnect_instrument('GPIB::1::INSTR')
-
Use logging for debugging:
logger.debug(f"Mock response for {command}: {response}")
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
"""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
"""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
"""Run the built-in tests:
python -m src.serdes_validation_framework.instrument_control.mock_controllerExample 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!