260 lines
7.4 KiB
Markdown
260 lines
7.4 KiB
Markdown
# team1k
|
|
|
|
Python control library and server for the TEAM1k X-ray detector.
|
|
|
|
Communicates directly with the detector FPGA via UDP. Uses EPICS PV Access (PVA) for the image stream and basic controls (areaDetector-style), and a TCP protocol for the Python client.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Client machine Detector machine
|
|
+--------------+ +-------------------------------+
|
|
| | TCP (control+data) | team1k-server |
|
|
| Client |<--------------------->| Register I/O (UDP:42000) |
|
|
| (Python) | | TCP client server |
|
|
| | | Buffered frame capture |
|
|
+--------------+ | PVA server (areaDetector) |
|
|
| PVA streamer (IMAGE) |
|
|
EPICS clients | Power supply monitoring |
|
|
+--------------+ | Auto-reconnect |
|
|
| pvget/CSS/ | PVA (IMAGE stream) | |
|
|
| other tools |<--------------------->| Acquisition subprocess |
|
|
+--------------+ | UDP data recv (UDP:41000) |
|
|
| Frame assembly |
|
|
| -> locking_shmring |
|
|
+-------------------------------+
|
|
```
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
pip install .
|
|
```
|
|
|
|
For notebook progress bars (optional):
|
|
|
|
```bash
|
|
pip install ".[client]"
|
|
```
|
|
|
|
### Dependencies
|
|
|
|
- `locking_shmring` — shared memory ring buffer (must be installed separately)
|
|
- `p4p` — EPICS PV Access for Python
|
|
- `pyyaml` — YAML config parsing
|
|
- `pyvisa`, `pyvisa-py` — power supply control
|
|
- `numpy`, `pyserial`
|
|
|
|
## Configuration
|
|
|
|
Create a YAML config file:
|
|
|
|
```yaml
|
|
detector:
|
|
ip: "10.0.0.32"
|
|
register_port: 42000
|
|
data_port: 41000
|
|
auto_reconnect: true
|
|
reconnect_interval: 5.0
|
|
|
|
adc:
|
|
clock_frequency_mhz: 50
|
|
data_delay: 0x180
|
|
order_7to0: 0x89abcdef
|
|
order_15to8: 0x01234567
|
|
|
|
digital_signals:
|
|
order_7to0: 0x24FFFFFF
|
|
order_15to8: 0xF5F310FF
|
|
polarity: 0x0014
|
|
|
|
defaults:
|
|
exposure_mode: 3
|
|
trigger_mode: external
|
|
trigger_polarity: rising
|
|
integration_time_ms: 4.0
|
|
|
|
server:
|
|
pv_prefix: "TEAM1K:"
|
|
client_port: 42010
|
|
|
|
peripherals:
|
|
bellow_port: "/dev/CameraBellowStage"
|
|
|
|
detector_power:
|
|
port: "/dev/DetectorPowerSupply"
|
|
voltage_step: 0.2
|
|
channels:
|
|
1: { voltage: 5.0, current: 2.0, ovp: 6.0 }
|
|
2: { voltage: 3.3, current: 1.0, ovp: 4.0 }
|
|
|
|
tec:
|
|
port: "/dev/TECsPowerSupply"
|
|
voltage_step: 0.2
|
|
channels:
|
|
1: { voltage: 12.0, current: 3.0, ovp: 14.0 }
|
|
```
|
|
|
|
## Running the server
|
|
|
|
```bash
|
|
team1k-server --config config.yaml --log-level INFO
|
|
```
|
|
|
|
Options:
|
|
|
|
```
|
|
--config YAML config file path
|
|
--detector-ip Override detector IP address
|
|
--pv-prefix Override PVA prefix
|
|
--client-port Override TCP client port
|
|
--log-level DEBUG, INFO, WARNING, ERROR (default: INFO)
|
|
```
|
|
|
|
The server starts DAQ automatically after initialization (continuous mode). The PVA IMAGE stream is always live.
|
|
|
|
## Client usage
|
|
|
|
The Python client connects via TCP. No EPICS/p4p installation needed.
|
|
|
|
### Basic usage
|
|
|
|
```python
|
|
from team1k import Client
|
|
|
|
client = Client("detector-machine")
|
|
|
|
# Configure
|
|
client.exposure_mode = 3 # GlobalCDS
|
|
client.trigger_mode = 1 # External
|
|
client.integration_time = 4.0 # ms
|
|
|
|
# Status
|
|
print(client.state) # "Acquiring"
|
|
print(client.frame_rate) # 500.0
|
|
|
|
# Capture frames (shows progress bar)
|
|
frames = client.capture(100)
|
|
print(frames.shape) # (100, 1024, 1024)
|
|
print(frames.dtype) # uint16
|
|
|
|
client.close()
|
|
```
|
|
|
|
### Capture modes
|
|
|
|
```python
|
|
# Fixed number of frames
|
|
frames = client.capture(num_frames=100)
|
|
|
|
# Capture for a duration
|
|
frames = client.capture(duration=5.0)
|
|
|
|
# Open-ended capture (start/stop)
|
|
stream = client.start_capture()
|
|
# ... do something ...
|
|
frames = stream.stop()
|
|
|
|
# Disable progress bar
|
|
frames = client.capture(100, progress=False)
|
|
```
|
|
|
|
### Peripherals
|
|
|
|
```python
|
|
# Bellow stage
|
|
client.insert_detector()
|
|
client.retract_detector()
|
|
|
|
# Detector power
|
|
client.power_on()
|
|
client.power_off()
|
|
print(client.power_status) # {1: {"voltage": 5.01, "current": 1.2}, ...}
|
|
|
|
# TEC
|
|
client.tec_on()
|
|
client.tec_off()
|
|
print(client.tec_status)
|
|
```
|
|
|
|
### Advanced
|
|
|
|
```python
|
|
# Direct register access
|
|
value = client.read_register(22)
|
|
client.write_register(22, 3)
|
|
|
|
# Reconnect after power cycle
|
|
client.power_on()
|
|
client.reconnect()
|
|
|
|
# Full status
|
|
print(client.status())
|
|
|
|
# Context manager
|
|
with Client("detector-machine") as c:
|
|
frames = c.capture(100)
|
|
```
|
|
|
|
## PV Access interface
|
|
|
|
All PVs are prefixed with `TEAM1K:` by default. Follows areaDetector conventions with RW + `_RBV` readback pattern.
|
|
|
|
### Acquisition PVs
|
|
|
|
| PV | Type | RW | Description |
|
|
|----|------|----|-------------|
|
|
| `Acquire` / `_RBV` | bool | RW | Start/stop DAQ |
|
|
| `AcquireTime` / `_RBV` | float | RW | Integration time (seconds) |
|
|
| `ExposureMode` / `_RBV` | enum | RW | Rolling/Rolling with Pause/Global/Global with CDS |
|
|
| `TriggerMode` / `_RBV` | enum | RW | Internal/External |
|
|
| `ArrayCounter_RBV` | int | RO | Total frames acquired |
|
|
| `FrameRate_RBV` | float | RO | Current frame rate (Hz) |
|
|
| `DetectorState_RBV` | enum | RO | Disconnected/Initializing/Idle/Acquiring/Error |
|
|
| `MaxSizeX_RBV` / `MaxSizeY_RBV` | int | RO | 1024, 1024 |
|
|
| `IMAGE` | NTNDArray | RO | Live image stream |
|
|
|
|
### Peripheral PVs
|
|
|
|
| PV | Type | RW | Description |
|
|
|----|------|----|-------------|
|
|
| `Bellow:Insert` | bool | RW | Write true to insert |
|
|
| `Bellow:Retract` | bool | RW | Write true to retract |
|
|
| `Bellow:Position_RBV` | int | RO | Current position |
|
|
| `Power:Enable` / `_RBV` | bool | RW | Detector power on/off |
|
|
| `Power:ChN:Voltage_RBV` | float | RO | Channel N voltage |
|
|
| `Power:ChN:Current_RBV` | float | RO | Channel N current |
|
|
| `TEC:Enable` / `_RBV` | bool | RW | TEC power on/off |
|
|
| `TEC:ChN:Voltage_RBV` | float | RO | Channel N voltage |
|
|
| `TEC:ChN:Current_RBV` | float | RO | Channel N current |
|
|
|
|
## Package structure
|
|
|
|
```
|
|
src/team1k/
|
|
detector/ # Direct UDP communication with detector FPGA
|
|
udp_transport.py # Low-level UDP socket
|
|
registers.py # Register read/write protocol
|
|
data_port.py # Data port + loopback registration
|
|
chip_config.py # Chip constants (1024x1024, packet layout)
|
|
commands.py # Detector commands (exposure, trigger, etc.)
|
|
adc.py # Si570 I2C clock programming
|
|
acquisition/ # High-throughput data path
|
|
receiver.py # Acquisition subprocess (UDP -> shmring)
|
|
pva/ # EPICS PV Access (areaDetector-style)
|
|
interface.py # PVA server, RW PVs with _RBV readbacks
|
|
streamer.py # shmring -> NTNDArray live stream
|
|
peripherals/ # Hardware peripherals
|
|
power_base.py # VISA power supply base class
|
|
power_supply.py # Detector power supply
|
|
tec.py # TEC controller
|
|
bellow_stage.py # Camera bellow stage (serial)
|
|
config.py # YAML configuration loader
|
|
state.py # DetectorState enum
|
|
server.py # Main server entry point
|
|
tcp_server.py # TCP client handler
|
|
tcp_protocol.py # Shared TCP message framing
|
|
capture.py # Buffered frame capture (shmring -> TCP)
|
|
client.py # Pure-TCP client library
|
|
```
|