597 lines
16 KiB
Markdown
597 lines
16 KiB
Markdown
# Helios Laser System Driver
|
|
|
|
## Overview
|
|
|
|
This directory contains a complete implementation of the Helios laser system driver. The driver implements the full RS-232 ASCII communications protocol as specified in the helios_comms_protocol.pdf document.
|
|
|
|
The Helios laser system is a pulsed solid-state laser with:
|
|
- Diode-pumped Nd:YAG/Nd:YLF laser head
|
|
- Q-switched operation
|
|
- Frequency control (16.7 kHz - 125 kHz)
|
|
- Current control (0-7000 mA)
|
|
- Power monitoring
|
|
- Temperature monitoring (4 sensors)
|
|
- External trigger capability
|
|
|
|
## Architecture
|
|
|
|
The Helios driver is split into three layers:
|
|
|
|
### 1. Protocol Layer (`helios_protocol.py`)
|
|
|
|
Low-level protocol implementation that handles:
|
|
- ASCII command construction
|
|
- Response parsing and validation
|
|
- Unit conversions (Hz ↔ ns, °C ↔ m°C)
|
|
- Parameter range validation
|
|
- Status register decoding
|
|
|
|
**Key Classes:**
|
|
- `HeliosCommand`: Command constants and builders
|
|
- `HeliosProtocol`: High-level protocol interface with validation
|
|
|
|
### 2. Driver Layer (`helios_driver.py`)
|
|
|
|
Complete driver implementation providing:
|
|
- RS-232 serial communication (9600 baud, 8N1)
|
|
- Thread-safe command/query operations
|
|
- Comprehensive status monitoring
|
|
- Temperature monitoring (pump, resonator, q-switch, power stage)
|
|
- Power monitoring
|
|
- Laser enable/disable control
|
|
- Pulse mode control (single, gating, continuous)
|
|
- Frequency and current control
|
|
|
|
**Key Classes:**
|
|
- `PulseMode`: Enumeration of pulse modes
|
|
- `HeliosStatus`: Status data structure
|
|
- `HeliosDriver`: Main driver class for laser communication
|
|
|
|
### 3. Integration Layer (`hardware/microscope.py`)
|
|
|
|
Application-specific integration that:
|
|
- Combines Helios with Genesis microscope systems
|
|
- Provides unified status monitoring
|
|
- Integrates with nueScan application
|
|
- Implements safety interlocks
|
|
- Provides emergency stop functionality
|
|
|
|
## Features
|
|
|
|
### Communication
|
|
- ASCII-based RS-232 protocol
|
|
- Baud rate: 9600, 8 data bits, no parity, 1 stop bit
|
|
- Commands terminated with carriage return (CR)
|
|
- Thread-safe operation with mutex locking
|
|
- Configurable timeout (default: 1 second)
|
|
|
|
### Laser Control
|
|
- Laser enable/disable
|
|
- Three pulse modes:
|
|
- Single pulse (one pulse per trigger)
|
|
- Continuous gating (pulse train while triggered)
|
|
- Continuous pulsing (free-running)
|
|
- Frequency control (16.7 kHz to 125 kHz)
|
|
- Diode current control (0-7000 mA)
|
|
|
|
### Monitoring
|
|
- Real-time power measurement (mW)
|
|
- Four temperature sensors:
|
|
- Pump diode temperature
|
|
- Resonator temperature
|
|
- Q-switch temperature
|
|
- Power stage temperature
|
|
- Operation hours counter
|
|
- Comprehensive status register
|
|
- Error detection
|
|
|
|
### Safety
|
|
- Temperature monitoring with warnings
|
|
- Error status detection
|
|
- Laser enable/disable control
|
|
- Emergency stop capability
|
|
- Integration with system interlocks
|
|
|
|
## Usage
|
|
|
|
### Connection Methods
|
|
|
|
The driver supports connection to a specific COM port:
|
|
|
|
```python
|
|
from hardware.helios_driver import HeliosDriver
|
|
|
|
# Create driver instance
|
|
driver = HeliosDriver(timeout=1.0)
|
|
|
|
# List available COM ports
|
|
ports = HeliosDriver.list_available_ports()
|
|
for port in ports:
|
|
print(f"Available port: {port}")
|
|
|
|
# Connect to specific port
|
|
driver.connect('COM5') # or '/dev/ttyUSB0' on Linux
|
|
|
|
# Get device information
|
|
print(f"Controller S/N: {driver.get_controller_serial()}")
|
|
print(f"Head S/N: {driver.get_head_serial()}")
|
|
```
|
|
|
|
### Basic Laser Control
|
|
|
|
```python
|
|
# Set frequency (in Hz)
|
|
driver.set_frequency_hz(10000) # 10 kHz
|
|
|
|
# Set diode current (in mA)
|
|
driver.set_current_ma(500) # 500 mA
|
|
|
|
# Set pulse mode
|
|
from hardware.helios_driver import PulseMode
|
|
driver.set_pulse_mode(PulseMode.CONTINUOUS_PULSING)
|
|
|
|
# Enable laser
|
|
driver.set_laser_enable(True)
|
|
|
|
# Check if laser is enabled
|
|
if driver.is_laser_enabled():
|
|
print("Laser is ON")
|
|
|
|
# Disable laser
|
|
driver.set_laser_enable(False)
|
|
|
|
# Disconnect
|
|
driver.disconnect()
|
|
```
|
|
|
|
### Monitoring
|
|
|
|
```python
|
|
# Get current power
|
|
power_mw = driver.get_power_mw()
|
|
print(f"Output power: {power_mw} mW")
|
|
|
|
# Get temperatures (in Celsius)
|
|
temps = driver.get_all_temperatures()
|
|
print(f"Pump: {temps['pump_temp_c']:.1f}°C")
|
|
print(f"Resonator: {temps['resonator_temp_c']:.1f}°C")
|
|
print(f"Q-switch: {temps['qswitch_temp_c']:.1f}°C")
|
|
print(f"Power stage: {temps['power_stage_temp_c']:.1f}°C")
|
|
|
|
# Get operation hours
|
|
hours = driver.get_operation_hours()
|
|
print(f"Operation time: {hours} hours")
|
|
|
|
# Get comprehensive status
|
|
status = driver.get_status()
|
|
print(f"Connected: {status['connected']}")
|
|
print(f"Laser enabled: {status['laser_enabled']}")
|
|
print(f"Frequency: {status['frequency_hz']} Hz")
|
|
print(f"Current: {status['current_ma']} mA")
|
|
print(f"Power: {status['power_mw']} mW")
|
|
print(f"Has errors: {status['has_errors']}")
|
|
```
|
|
|
|
### Status Updates
|
|
|
|
```python
|
|
# Manually update status from hardware
|
|
driver.update_status()
|
|
|
|
# Status is automatically updated on each get_status() call
|
|
status = driver.get_status()
|
|
|
|
# Access cached values without querying hardware
|
|
freq = driver.get_frequency_hz() # Returns last read value
|
|
current = driver.get_current_ma() # Returns last read value
|
|
```
|
|
|
|
### Using Through Microscope Controller
|
|
|
|
The Helios driver is integrated into the application through the `MicroscopeController`:
|
|
|
|
```python
|
|
from hardware.microscope import MicroscopeController
|
|
|
|
# Create controller
|
|
microscope = MicroscopeController()
|
|
|
|
# Connect Helios
|
|
microscope.connect_helios('COM5')
|
|
|
|
# Apply settings from dialog
|
|
settings = {
|
|
'com_port': 'COM5',
|
|
'frequency_hz': 10000,
|
|
'current_ma': 500
|
|
}
|
|
microscope.apply_helios_settings(settings)
|
|
|
|
# Enable laser
|
|
microscope.helios_enable_laser(True)
|
|
|
|
# Get status
|
|
status = microscope.get_helios_status()
|
|
print(f"Helios ready: {status['ready']}")
|
|
print(f"Power: {status['power_mw']} mW")
|
|
|
|
# Disable laser
|
|
microscope.helios_enable_laser(False)
|
|
|
|
# Disconnect
|
|
microscope.disconnect_helios()
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Frequency Control
|
|
|
|
The Helios laser operates by setting the pulse period in nanoseconds. The driver automatically converts between frequency (Hz) and period (ns):
|
|
|
|
```python
|
|
# Set frequency in Hz (driver converts to period in ns)
|
|
driver.set_frequency_hz(10000) # 10 kHz → 100,000 ns period
|
|
|
|
# Valid frequency range: 16.7 kHz to 125 kHz
|
|
# Valid period range: 8000 ns to 60000 ns
|
|
```
|
|
|
|
Conversion formulas:
|
|
```
|
|
Period (ns) = 1,000,000,000 / Frequency (Hz)
|
|
Frequency (Hz) = 1,000,000,000 / Period (ns)
|
|
```
|
|
|
|
### Current Control
|
|
|
|
The diode current controls the laser output power:
|
|
|
|
```python
|
|
# Set current in milliamps
|
|
driver.set_current_ma(500) # 500 mA
|
|
|
|
# Valid range: 0 to 7000 mA
|
|
```
|
|
|
|
**Important:** Higher currents produce more power but also more heat. Monitor temperatures when operating at high current.
|
|
|
|
### Pulse Modes
|
|
|
|
Three pulse modes are available:
|
|
|
|
```python
|
|
from hardware.helios_driver import PulseMode
|
|
|
|
# Single pulse mode (one pulse per trigger)
|
|
driver.set_pulse_mode(PulseMode.SINGLE_PULSE)
|
|
|
|
# Continuous gating mode (pulse train while triggered)
|
|
driver.set_pulse_mode(PulseMode.CONTINUOUS_GATING)
|
|
|
|
# Continuous pulsing mode (free-running)
|
|
driver.set_pulse_mode(PulseMode.CONTINUOUS_PULSING)
|
|
```
|
|
|
|
**Mode Descriptions:**
|
|
- **Single Pulse (LDG=0)**: One pulse generated per external trigger
|
|
- **Continuous Gating (LDG=1)**: Pulse train while external trigger is high
|
|
- **Continuous Pulsing (LDG=2)**: Free-running at set frequency (default)
|
|
|
|
### Temperature Monitoring
|
|
|
|
The driver monitors four temperature sensors:
|
|
|
|
```python
|
|
# Individual temperatures
|
|
pump_temp = driver.query_pump_temp_c()
|
|
resonator_temp = driver.query_resonator_temp_c()
|
|
qswitch_temp = driver.query_qswitch_temp_c()
|
|
power_stage_temp = driver.query_power_stage_temp_c()
|
|
|
|
# All temperatures at once
|
|
temps = driver.get_all_temperatures()
|
|
```
|
|
|
|
**Temperature Ranges:**
|
|
- Normal operation: < 50°C
|
|
- Warning threshold: > 60°C
|
|
- Critical threshold: > 70°C
|
|
|
|
## Integration with nueScan
|
|
|
|
The Helios driver is integrated into nueScan through the settings dialog and microscope controller.
|
|
|
|
### Configuration in UI
|
|
|
|
1. **Open Helios Settings**
|
|
- Click "Helios Device Settings" button in main window
|
|
|
|
2. **Configure Parameters**
|
|
- **COM Port**: Select from dropdown (automatically populated)
|
|
- **Frequency**: Enter in Hz (16,666 - 125,000 Hz)
|
|
- **Current**: Enter in mA (0 - 7000 mA)
|
|
|
|
3. **Apply Settings**
|
|
- Click OK to apply and connect
|
|
- Settings are validated before sending to hardware
|
|
|
|
### Settings Dialog Integration
|
|
|
|
The `HeliosDialog` class provides:
|
|
- Automatic COM port enumeration
|
|
- Input validation with range checking
|
|
- User-friendly error messages
|
|
- Settings persistence
|
|
|
|
```python
|
|
# Dialog usage (called from main window)
|
|
from dialogs.helios_dialog import HeliosDialog
|
|
|
|
dialog = HeliosDialog(parent=self)
|
|
if dialog.exec() == QDialog.DialogCode.Accepted:
|
|
settings = dialog.get_settings() # Returns None if validation fails
|
|
if settings:
|
|
self.microscope.apply_helios_settings(settings)
|
|
```
|
|
|
|
### Validation Rules
|
|
|
|
The dialog validates all inputs before accepting:
|
|
|
|
**Frequency Validation:**
|
|
- Range: 16,666 Hz to 125,000 Hz
|
|
- Reason: Hardware period limit of 8000-60000 ns
|
|
- Error message shows entered value and valid range
|
|
|
|
**Current Validation:**
|
|
- Range: 0 to 7000 mA
|
|
- Reason: Maximum diode current rating
|
|
- Error message shows entered value and valid range
|
|
|
|
**COM Port Validation:**
|
|
- Must select valid port from list
|
|
- Cannot accept "No ports found" placeholder
|
|
- Error message prompts to check connections
|
|
|
|
## Protocol Details
|
|
|
|
### Command Format
|
|
|
|
All commands follow the format:
|
|
```
|
|
COMMAND [value]<CR>
|
|
```
|
|
|
|
Where:
|
|
- `COMMAND` is a 3-letter mnemonic (e.g., LDO, LDF, LDS)
|
|
- `[value]` is optional numeric parameter
|
|
- `<CR>` is carriage return (0x0D)
|
|
|
|
### Command Set
|
|
|
|
| Command | Parameter | Description |
|
|
|---------|-----------|-------------|
|
|
| LDO | 0/1 | Laser enable (0=off, 1=on) |
|
|
| LDG | 0/1/2 | Pulse mode (0=single, 1=gating, 2=continuous) |
|
|
| LDF | 8000-60000 | Pulse period in nanoseconds |
|
|
| LDS | 0-7000 | Diode current in milliamps |
|
|
| LDP | - | Query output power (mW) |
|
|
| LDPT | - | Query pump temperature (m°C) |
|
|
| LDRT | - | Query resonator temperature (m°C) |
|
|
| LDQT | - | Query q-switch temperature (m°C) |
|
|
| LDPST | - | Query power stage temperature (m°C) |
|
|
| LDSR | - | Query status register |
|
|
| LDOH | - | Query operation hours |
|
|
| LDCSN | - | Query controller serial number |
|
|
| LDHSN | - | Query head serial number |
|
|
|
|
### Response Format
|
|
|
|
Responses are numeric values terminated with `<CR>`:
|
|
```
|
|
12345<CR>
|
|
```
|
|
|
|
**Exception:** Serial numbers are returned as strings:
|
|
```
|
|
SN12345678<CR>
|
|
```
|
|
|
|
### Status Register
|
|
|
|
The status register (LDSR) returns a 16-bit value with error flags:
|
|
|
|
| Bit | Mask | Meaning |
|
|
|-----|------|---------|
|
|
| 0 | 0x0001 | Pump temperature error |
|
|
| 1 | 0x0002 | Resonator temperature error |
|
|
| 2 | 0x0004 | Q-switch temperature error |
|
|
| 3 | 0x0008 | Power stage temperature error |
|
|
| 4 | 0x0010 | Diode current error |
|
|
| 5 | 0x0020 | Interlock open |
|
|
| 6 | 0x0040 | Over-power condition |
|
|
| 7 | 0x0080 | Under-voltage condition |
|
|
|
|
A status of 0 indicates no errors.
|
|
|
|
### Set and Verify Pattern
|
|
|
|
For critical parameters, the driver uses a set-and-verify pattern:
|
|
|
|
```python
|
|
def _set_and_verify(self, set_cmd: bytes, query_cmd: bytes, expected: str) -> bool:
|
|
# Send set command
|
|
self._serial.write(set_cmd)
|
|
time.sleep(0.05) # Allow hardware to process
|
|
|
|
# Query back the value
|
|
self._serial.write(query_cmd)
|
|
response = self._read_response()
|
|
|
|
# Verify it matches
|
|
return response.strip() == expected.strip()
|
|
```
|
|
|
|
This ensures commands are executed correctly and hardware state matches software state.
|
|
|
|
## Troubleshooting
|
|
|
|
### Connection Issues
|
|
|
|
**Problem:** Cannot connect to laser
|
|
|
|
**Solutions:**
|
|
- Verify COM port name is correct (`HeliosDriver.list_available_ports()`)
|
|
- Check RS-232 cable connection
|
|
- Verify laser controller is powered on
|
|
- Try different COM port
|
|
- Check device permissions on Linux (`sudo usermod -a -G dialout $USER`)
|
|
|
|
### Communication Errors
|
|
|
|
**Problem:** Commands fail or no response
|
|
|
|
**Solutions:**
|
|
- Verify baud rate is 9600 (default)
|
|
- Check cable for proper null-modem configuration if needed
|
|
- Increase timeout: `driver = HeliosDriver(timeout=2.0)`
|
|
- Check for CR line termination (0x0D)
|
|
- Verify no other software has port open
|
|
|
|
### Frequency/Current Not Updating
|
|
|
|
**Problem:** Settings don't change on hardware
|
|
|
|
**Solutions:**
|
|
- Check return value of `set_frequency_hz()` and `set_current_ma()`
|
|
- Verify parameters are in valid range
|
|
- Check status register for errors: `driver.query_status_register()`
|
|
- Ensure laser is not in error state
|
|
- Try power cycling the controller
|
|
|
|
### Temperature Warnings
|
|
|
|
**Problem:** High temperature readings
|
|
|
|
**Solutions:**
|
|
- Check ventilation around laser head and controller
|
|
- Reduce diode current if at maximum
|
|
- Allow longer cool-down between operations
|
|
- Clean air filters if present
|
|
- Check for blocked cooling fans
|
|
|
|
### Laser Won't Enable
|
|
|
|
**Problem:** `set_laser_enable(True)` fails or laser stays off
|
|
|
|
**Solutions:**
|
|
- Check interlock connections (bit 5 of status register)
|
|
- Verify all interlocks are closed
|
|
- Check for error flags in status register
|
|
- Ensure parameters (frequency, current) are set
|
|
- Check external enable switch if present
|
|
- Review safety interlock documentation
|
|
|
|
### Status Register Errors
|
|
|
|
**Problem:** Status register shows error bits set
|
|
|
|
**Solutions:**
|
|
- Decode status register: `HeliosProtocol.decode_status_register(value)`
|
|
- Address specific error conditions:
|
|
- Temperature errors: Improve cooling
|
|
- Current error: Reduce current setting
|
|
- Interlock open: Check safety connections
|
|
- Over-power: Reduce current
|
|
- Under-voltage: Check power supply
|
|
|
|
## Debug Output
|
|
|
|
The driver provides extensive debug output:
|
|
|
|
```
|
|
INFO: Informational messages about operations
|
|
DEBUG: Detailed command/response information
|
|
WARNING: Potential issues (high temp, errors)
|
|
ERROR: Operation failures
|
|
```
|
|
|
|
Enable Python logging to capture all output:
|
|
|
|
```python
|
|
import logging
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
```
|
|
|
|
Example debug output:
|
|
```
|
|
INFO: Connecting to Helios laser on COM5
|
|
DEBUG: Sending command: b'LDCSN\r'
|
|
DEBUG: Received response: SN12345678
|
|
INFO: Successfully connected to Helios on COM5
|
|
DEBUG: Sending command: b'LDF 100000\r'
|
|
DEBUG: Verifying frequency setting...
|
|
INFO: Frequency set to 10000.0 Hz (period: 100000 ns)
|
|
```
|
|
|
|
## Performance Notes
|
|
|
|
- Command response time: 50-100ms typical
|
|
- Temperature queries: ~100ms per sensor
|
|
- Status register query: ~50ms
|
|
- All queries are synchronous (blocking)
|
|
- Thread-safe for concurrent access (mutex protected)
|
|
- Set-and-verify adds ~50ms overhead for reliability
|
|
|
|
## Safety Considerations
|
|
|
|
### Laser Safety
|
|
- **Class 4 Laser**: Hazardous to eyes and skin
|
|
- Always verify laser is disabled before opening beam paths
|
|
- Use appropriate laser safety eyewear
|
|
- Follow all facility laser safety procedures
|
|
- Ensure proper interlock connections
|
|
|
|
### Thermal Management
|
|
- Monitor temperatures during operation
|
|
- Allow adequate cool-down between high-power operations
|
|
- Ensure proper ventilation
|
|
- Do not block cooling vents
|
|
|
|
### Electrical Safety
|
|
- Verify proper grounding
|
|
- Use shielded cables for trigger/status connections
|
|
- Follow proper ESD procedures when servicing
|
|
|
|
## Hardware Connections
|
|
|
|
### Utility Connector (9-pin D-Sub)
|
|
|
|
The utility connector provides external control:
|
|
|
|
| Pin | Signal | Description |
|
|
|-----|--------|-------------|
|
|
| 1 | GND | Ground |
|
|
| 2 | Laser Disable | Input: Pull low to disable laser |
|
|
| 3 | External Trigger | Input: Rising edge triggers pulse |
|
|
| 4 | Status Out | Output: High when ready |
|
|
| 5 | GND | Ground |
|
|
| 6-9 | NC | Not connected |
|
|
|
|
Trigger specifications:
|
|
- Input: TTL/CMOS compatible
|
|
- Minimum pulse width: 100ns
|
|
- Maximum frequency: Limited by pulse mode setting
|
|
|
|
## References
|
|
|
|
- **Protocol Documentation**: `helios_comms_protocol.pdf`
|
|
- **RS-232 Standard**: EIA/TIA-232
|
|
- **Integration Guide**: `SETUP.md`
|
|
- **Connection Guide**: See main window Helios settings dialog
|
|
|
|
## License
|
|
|
|
Copyright (C) 2025 Thomas Ales
|
|
Licensed under GNU General Public License v2.0
|