Skip to content
Merged

Docs #66

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,57 @@ jobs:

- name: Set up uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
cache-suffix: lint

- name: Ruff
run: |
uv run ruff check
uv run ruff format --check

docs:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6

- name: Set up uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
cache-suffix: docs

- name: Install documentation dependencies
run: uv sync --group docs --locked

- name: Configure GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: actions/configure-pages@v5

- name: Build documentation
run: uv run --no-sync mkdocs build --strict

- name: Upload GitHub Pages artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: actions/upload-pages-artifact@v4
with:
path: site

deploy-docs:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs: docs
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

test:
strategy:
fail-fast: false
Expand Down Expand Up @@ -65,7 +110,7 @@ jobs:

release:
if: startsWith(github.ref, 'refs/tags/')
needs: [lint, test]
needs: [docs, lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ __pycache__/
*.egg-info/
.mypy_cache/
dist/
site/
.aider*
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![PyPI version](https://img.shields.io/pypi/v/radiacode)](https://pypi.org/project/radiacode)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue.svg)](https://cdump.github.io/radiacode/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Python library for interfacing with the [RadiaCode-10x](https://www.radiacode.com/) radiation detectors and spectrometers. Control your device, collect measurements, and analyze radiation data with ease.
Expand Down Expand Up @@ -58,7 +59,7 @@ with RadiaCode() as device:

# Get spectrum data
spectrum = device.spectrum()
print(f"Live time: {spectrum.duration}s")
print(f"Live time: {spectrum.duration.total_seconds()}s")
print(f"Total counts: {sum(spectrum.counts)}")

# Configure device
Expand Down Expand Up @@ -88,6 +89,19 @@ device.set_vibro_on(True)
device.set_display_off_time(30) # Auto-off after 30 seconds
```

## 📚 Documentation

The [documentation site](https://cdump.github.io/radiacode/) contains setup
guides, measurement and unit notes, spectrum examples, device configuration,
and an API reference generated from the source docstrings and type annotations.

Build it locally with:

```bash
uv sync --group docs
uv run --no-sync mkdocs serve
```

## 🔧 Development Setup
1. Install prerequisites:
```bash
Expand All @@ -103,13 +117,13 @@ device.set_display_off_time(30) # Auto-off after 30 seconds

3. Run examples:
```bash
uv run python radiacode.examples/basic.py
uv run python -m radiacode.examples.basic
```

## ⚠️ Platform-Specific Notes

### macOS
- ✅ USB connectivity works out of the box
- ✅ USB connectivity supported via libusb
- ✅ Bluetooth connectivity supported via Bleak (use the device's CoreBluetooth UUID)
- 📝 Required: `brew install libusb`

Expand Down
90 changes: 90 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Getting started

## Install the library

=== "pip"

```shell
python -m pip install --upgrade radiacode
```

=== "uv"

```shell
uv add radiacode
```

Install the optional example dependencies if you want to run the web server,
Prometheus exporter, or plotting examples:

```shell
python -m pip install --upgrade "radiacode[examples]"
```

## Connect to a device

Use a context manager so the USB handle or Bluetooth connection is always
released:

```python
from radiacode import RadiaCode

with RadiaCode() as device:
print(device.serial_number())
print(device.fw_version())
```

With no arguments, `RadiaCode` connects to the first available USB device. Use
`serial_number` when more than one USB device is connected:

```python
with RadiaCode(serial_number="RC-10x-xxxxxx") as device:
print(device.serial_number())
```

For Bluetooth, pass the device identifier:

```python
with RadiaCode(bluetooth_mac="52:43:01:02:03:04") as device:
print(device.serial_number())
```

See [Connections](guides/connections.md) for platform requirements and error
handling.

## Read measurements

`data_buf()` returns all records currently buffered by the device. A call can
contain several record types, so narrow them with `isinstance`:

```python
from radiacode import RadiaCode, RareData, RealTimeData

with RadiaCode() as device:
for record in device.data_buf():
if isinstance(record, RealTimeData):
print(record.dt, record.count_rate, record.dose_rate)
elif isinstance(record, RareData):
print(record.dt, record.temperature, record.charge_level)
```

The records already contain timestamps calculated from the device data. See
[Measurements and units](guides/measurements.md) before converting dose values.

## Read a spectrum

```python
from radiacode import RadiaCode, spectrum_channel_to_energy

with RadiaCode() as device:
spectrum = device.spectrum()

for channel, counts in enumerate(spectrum.counts):
energy_kev = spectrum_channel_to_energy(
channel, spectrum.a0, spectrum.a1, spectrum.a2
)
print(channel, energy_kev, counts)
```

The returned `Spectrum.duration` is a `datetime.timedelta`; call
`total_seconds()` when a numeric duration is needed.
64 changes: 64 additions & 0 deletions docs/guides/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Device configuration

Configuration methods write their values to the connected device immediately.

```python
from radiacode import CTRL, DisplayDirection, RadiaCode

with RadiaCode() as device:
device.set_language("en")
device.set_display_brightness(5)
device.set_display_off_time(30)
device.set_display_direction(DisplayDirection.AUTO)
device.set_sound_on(True)
device.set_vibro_on(True)
device.set_sound_ctrl([CTRL.BUTTONS, CTRL.DOSE_RATE_ALARM_1])
```

## Accepted values

| Method | Accepted values |
| --- | --- |
| `set_language` | `"en"` or `"ru"` |
| `set_display_brightness` | Integer from 0 through 9 |
| `set_display_off_time` | 5, 10, 15, or 30 seconds |
| `set_display_direction` | A `DisplayDirection` member |
| `set_sound_on`, `set_vibro_on`, `set_device_on` | Boolean |

`set_sound_ctrl()` and `set_vibro_ctrl()` accept lists of `CTRL` flags.
`CTRL.CLICKS` is not accepted by `set_vibro_ctrl()`.

## Alarm limits

Read all current alarm settings with `get_alarm_limits()`:

```python
with RadiaCode() as device:
limits = device.get_alarm_limits()
print(limits.count_unit, limits.l1_count_rate, limits.l2_count_rate)
print(limits.dose_unit, limits.l1_dose_rate, limits.l2_dose_rate)
```

`set_alarm_limits()` updates only arguments that are supplied. Specify the
display units when you want the method to scale values and update the unit
registers:

```python
with RadiaCode() as device:
device.set_alarm_limits(
l1_count_rate=5,
l2_count_rate=10,
count_unit_cpm=False,
l1_dose_rate=0.3,
l2_dose_rate=1.0,
dose_unit_sv=True,
)
```

The method raises `ValueError` when no limit is supplied or when a numeric
limit is negative.

## Reset operations

`dose_reset()` and `spectrum_reset()` clear accumulated state on the device.
Treat them as irreversible device operations.
83 changes: 83 additions & 0 deletions docs/guides/connections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Connections

`RadiaCode` opens its connection during construction. Prefer a `with` block, or
call `close()` explicitly when the object is no longer needed.

## USB

```python
from radiacode import RadiaCode

with RadiaCode() as device:
print(device.serial_number())
```

When several supported devices are attached, select one by its device serial
number:

```python
with RadiaCode(serial_number="RC-10x-xxxxxx") as device:
...
```

Platform notes:

- **Linux:** install libusb. If access is denied, install the repository's
[`radiacode.rules`](https://github.com/cdump/radiacode/blob/master/radiacode.rules)
as an appropriate udev rule rather than running the application as root.
- **macOS:** install libusb, for example with `brew install libusb`.
- **Windows:** install a compatible USB driver for the device.

The USB-specific connection errors can be caught when an application needs to
distinguish them:

```python
from radiacode import RadiaCode
from radiacode.transports.usb import DeviceNotFound

try:
device = RadiaCode()
except DeviceNotFound:
print("No supported USB device was found")
else:
device.close()
```

## Bluetooth

Pass a MAC address on Linux and Windows. Pass the CoreBluetooth UUID reported
by the operating system on macOS:

```python
from radiacode import RadiaCode

with RadiaCode(bluetooth_mac="52:43:01:02:03:04") as device:
print(device.serial_number())
```

Bluetooth uses Bleak and starts a background event-loop thread for the lifetime
of the connection. `close()` disconnects the client and stops that thread.

```python
from radiacode import RadiaCode
from radiacode.transports.bluetooth import DeviceNotFound

try:
device = RadiaCode(bluetooth_mac="52:43:01:02:03:04")
except DeviceNotFound as error:
print(error)
else:
device.close()
```

!!! note

`serial_number` selects a USB device and is ignored when `bluetooth_mac` is
supplied.

## Firmware compatibility

Initialization rejects firmware older than 4.8 by default. Upgrade the device
firmware when possible. `ignore_firmware_compatibility_check=True` bypasses
that guard for diagnostic use, but it does not make an incompatible protocol
safe or supported.
Loading