diff --git a/README.md b/README.md index c75be36..6f0dd12 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,212 @@ -files description: - deploy -- creates venv and installs python libs in it - run -- resets Generator_PCB by toggling PRi`s GPIO pin, activates venv and runs main program +# RadioPhotonic PCB — Laser Controller +GUI application and embeddable Python module for controlling a dual-laser board +over UART (115 200 baud). Designed to run on a Raspberry Pi or any Linux machine. + +--- + +## Project structure + +``` +. +├── _device_main.py # GUI application entry point +├── gui.py # FreeSimpleGUI layout definition +├── device_interaction.py # High-level device commands (legacy) +├── device_commands.py # Low-level protocol helpers (legacy) +├── device_conversion.py # Physical-unit conversion formulas (legacy) +│ +├── laser_control/ # Standalone embeddable module +│ ├── __init__.py # Public API +│ ├── controller.py # LaserController class +│ ├── protocol.py # Command encoding / response decoding +│ ├── validators.py # Input validation +│ ├── conversions.py # Physical-unit conversions +│ ├── models.py # Dataclasses (Measurements, DeviceStatus, …) +│ ├── constants.py # Protocol constants and physical limits +│ ├── exceptions.py # Exception hierarchy +│ └── example_usage.py # Usage examples +│ +├── tests/ # pytest test suite (75 tests) +│ ├── conftest.py +│ ├── test_validation.py +│ ├── test_protocol.py +│ └── test_integration.py +│ +├── pyproject.toml # Package metadata (laser_control) +├── run # Launch script for the GUI app +└── deploy # First-time environment setup script +``` + +--- + +## Setting up the virtual environment + +### First-time setup + +```bash +# 1. Create virtual environment +python3 -m venv .venv + +# 2. Activate it +source .venv/bin/activate + +# 3. Install GUI and serial dependencies +pip install FreeSimpleGUI pyserial + +# 4. Install laser_control as an editable package +# (required for imports to work in any subdirectory) +pip install -e . + +# 5. Install pytest (for running tests) +pip install pytest +``` + +> **Note:** Steps 3–5 can be run via the existing `deploy` script (steps 1–3), +> then manually run `pip install -e . && pip install pytest` once inside the venv. + +### Every subsequent session + +```bash +source .venv/bin/activate +``` + +--- + +## Running the GUI application + +```bash +source .venv/bin/activate +./run +# or directly: +python3 _device_main.py +``` + +The application auto-detects the USB serial port. If more than one port is +present, the first one found is used. + +--- + +## Running the tests + +```bash +source .venv/bin/activate +python3 -m pytest tests/ -v +``` + +Expected result: **75 passed**. + +--- + +## Running the usage example + +```bash +source .venv/bin/activate + +# Auto-detect port: +python3 laser_control/example_usage.py + +# Specify port explicitly: +python3 laser_control/example_usage.py /dev/ttyUSB0 +``` + +--- + +## Embedding laser_control in another application + +After `pip install -e .` (or copying the `laser_control/` folder into your +project and running `pip install -e .` there), import as follows: + +```python +from laser_control import ( + LaserController, + VariationType, + ValidationError, + CommunicationError, +) + +# --- Manual mode --- +with LaserController(port='/dev/ttyUSB0') as ctrl: + try: + ctrl.set_manual_mode( + temp1=25.0, # °C [15 … 40] + temp2=30.0, # °C [15 … 40] + current1=40.0, # mA [15 … 60] + current2=35.0, # mA [15 … 60] + ) + data = ctrl.get_measurements() + if data: + print(f"3.3 V rail: {data.voltage_3v3:.3f} V") + print(f"Laser 1 temperature: {data.temp1:.2f} °C") + except ValidationError as e: + print(f"Bad parameter: {e}") + except CommunicationError as e: + print(f"Device not responding: {e}") + + +# --- Current variation mode --- +def on_data(m): + print(f"I1={m.current1:.3f} mA T1={m.temp1:.2f} °C") + +with LaserController(port=None, on_data=on_data) as ctrl: # port=None → auto-detect + ctrl.start_variation( + variation_type=VariationType.CHANGE_CURRENT_LD1, + params={ + 'min_value': 20.0, # mA + 'max_value': 50.0, # mA + 'step': 0.5, # mA [0.002 … 0.5] + 'time_step': 50, # µs [20 … 100] + 'delay_time': 5, # ms [3 … 10] + 'static_temp1': 25.0, + 'static_temp2': 30.0, + 'static_current1': 35.0, + 'static_current2': 35.0, + } + ) + import time; time.sleep(2) + ctrl.stop_task() +``` + +### Parameter limits + +| Parameter | Min | Max | Unit | +|---|---|---|---| +| Temperature (T1, T2) | 15.0 | 40.0 | °C | +| Current (I1, I2) | 15.0 | 60.0 | mA | +| Current variation step | 0.002 | 0.5 | mA | +| Temperature variation step | 0.05 | 1.0 | °C | +| Time step | 20 | 100 | µs | +| Delay time | 3 | 10 | ms | + +### Exception hierarchy + +``` +LaserControlError +├── ValidationError +│ ├── TemperatureOutOfRangeError +│ ├── CurrentOutOfRangeError +│ └── InvalidParameterError +├── CommunicationError +│ ├── PortNotFoundError +│ ├── DeviceNotRespondingError +│ ├── CRCError +│ └── ProtocolError +└── DeviceError + ├── DeviceOverheatingError + ├── PowerSupplyError + └── DeviceStateError +``` + +--- + +## Device output + +Each measurement response contains: + +| Field | Description | Unit | +|---|---|---| +| `temp1`, `temp2` | Laser temperatures | °C | +| `temp_ext1`, `temp_ext2` | External thermistor temperatures | °C | +| `current1`, `current2` | Photodiode currents | mA | +| `voltage_3v3` | 3.3 V power rail | V | +| `voltage_5v1`, `voltage_5v2` | 5 V power rails | V | +| `voltage_7v0` | 7 V power rail | V | diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..48be018 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.backends.legacy:build" + +[project] +name = "laser_control" +version = "1.0.0" +dependencies = ["pyserial"] + +[tool.setuptools.packages.find] +where = ["."] +include = ["laser_control*"]