Metadata-Version: 2.4
Name: radeye-b20
Version: 0.1.0
Summary: Python driver for the Thermo Scientific RadEye B20 radiation survey meter (reverse-engineered serial protocol).
Author: Navya Sharma
License: MIT
Project-URL: Homepage, https://github.com/navyasharmabits/radeye-b20
Keywords: radeye,radiation,survey-meter,serial,geiger,dosimetry,instrument-driver
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyserial>=3.4
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Dynamic: license-file

# radeye-b20

A small Python driver for the **Thermo Scientific RadEye B20** handheld
radiation survey meter.

The RadEye B20 streams live readings over its infrared read-head, but the serial
protocol is undocumented and — as far as I can tell — no public library speaks
it. This one does. It was **reverse-engineered** by watching the wire and
checking decoded values against the meter's own display, so it's honest about
what it can and can't do (see [Limitations](#limitations)).

```python
from radeye_b20 import RadEye

with RadEye() as meter:            # auto-detects the IR-USB cable
    for reading in meter.stream():
        print(reading.value, reading.unit)   # e.g. 3.51 cps
```

## Install

```bash
pip install radeye-b20
```

Or from source:

```bash
git clone https://github.com/navyasharmabits/radeye-b20
cd radeye-b20
pip install -e .
```

Requires Python 3.8+ and `pyserial`. Works on Linux, macOS, and Windows (the
meter appears as a normal USB serial port).

## Command line

```bash
radeye ports                 # list serial ports, flag the likely RadEye
radeye stream                # live readings until Ctrl+C
radeye stream --csv          # timestamp,value,unit  (pipe to a file)
radeye stream --port /dev/ttyUSB0 --scale-cps 0.01
```

## Library

```python
from radeye_b20 import RadEye, find_port

print(find_port())                       # '/dev/ttyUSB0' or None

with RadEye(port="auto", scale_cps=0.01, scale_cpm=0.1) as meter:
    r = meter.read()                     # one reading, or None on a skipped frame
    if r:
        print(r.value, r.unit, r.timestamp, r.checksum)
```

Each `Reading` carries the scaled `value`, the `unit` (`"cps"`, `"cpm"`, or
`"code:<n>"` for anything unverified), a unix `timestamp` taken at decode time,
the raw integer `digits`, the unit `code`, the trailing `checksum` token, and
the cleaned `raw` frame text.

## How it works

The meter sends fixed ASCII frames at **9600 baud, 7 data bits, even parity, 1
stop bit (7E1)**, each framed by `STX … ETX CR LF`. A decoded frame looks like:

```
351 5  0 0 04 FH41B2  0 42
 |   |            |       └─ checksum (algorithm unknown — exposed, not verified)
 |   |            └───────── device / probe tag
 |   └────────────────────── unit code: 5 = cps, 3 = cpm
 └────────────────────────── value digits (decimal point implied by the unit)
```

There's no decimal point on the wire, so the integer is scaled per unit
(`cps × 0.01`, `cpm × 0.1`). The cable is a Prolific PL2303, which the driver
auto-detects by USB vendor ID / `by-id` symlink, and whose occasional
zero-length-read glitches it tolerates without dropping the connection.

## Limitations

I'd rather you know these up front than discover them:

- **Reverse-engineered, not from a spec.** Verified against one meter's display.
- **Only cps and cpm are trusted.** Other unit/range modes are surfaced as
  `code:<n>` and passed through **unscaled** — the meter has modes I couldn't
  test, and I won't pretend the scaling for those is right.
- **The checksum is not verified.** The algorithm is unknown; the field is
  exposed so you can experiment, but frames aren't validated against it.
- **Scaling may be meter-dependent.** If readings are off by a constant factor,
  check against the display and set `scale_cps` / `scale_cpm` accordingly.

## ⚠️ Not for safety-critical use

This is a hobbyist/research convenience for logging and visualisation. It is
**not** calibrated, validated, or certified, and must not be relied on for
radiation-safety decisions, dose assessment, or any situation where a wrong or
missing reading could harm someone. For that, use the instrument's own display
and your organisation's approved procedures.

## Contributing

If you have a RadEye B20 and can capture frames in a mode this doesn't handle
(especially dose-rate / µSv·h⁻¹ modes), a sample of raw frames alongside the
displayed value is exactly what's needed to extend it. Open an issue or PR.

## License

MIT © Navya Sharma
