Unofficial Python driver for the Radex Obsidian radiation dosimeter Неофициальный Python-драйвер для дозиметра Radex Obsidian
English · Русский
Reverse-engineered USB/serial driver for the Radex Obsidian (QUARTA-RAD) dosimeter: a clean SDK, a live metrics streamer, and — because the sensor is a source of genuine quantum noise — a random number generator seeded from radioactive decay.
The protocol was reverse-engineered from a live device; the full write-up is in
docs/reverse-engineering/protocol-notes.md.
| Live metrics | Dose rate, CPS, beta, accumulated dose, temperature, battery, device clock |
| Streaming | Console streamer with CSV / JSONL logging |
| Device info | Manufacturer, model, serial number, firmware version |
| Hardware RNG | RNG seeded from decay noise, with SP 800-90B entropy accounting |
| Pure Python | Only dependency is pyserial; cross-platform |
| Auto-detect | Finds the COM port by USB VID/PID — no configuration |
| Device | USB VID:PID | Status |
|---|---|---|
| Radex Obsidian | ABBA:A006 |
✅ tested on hardware |
| Radex Obsidian G Pro | ABBA:A008 |
⚙️ autodetected, untested |
| Radex Obsidian 2 | ABBA:A00A |
⚙️ autodetected, untested |
The device shows up as a Silicon Labs CP210x virtual COM port (115200 8N1). The same frame protocol is also tunneled over BLE by the vendor apps, so a BLE transport can be added behind the same interface.
pip install -e .python -m radex # live stream, port auto-detected
python -m radex stream --once # one sample and exit
python -m radex stream --csv log.csv --jsonl log.jsonl --interval 2
python -m radex stream --port COM3Example output:
# QUARTA-RAD Obsidian sn=04250001002186 fw=v1.21
2026-08-20T16:08:46 DER 0.1260 +-0.0116 uSv/h CPS 11.912 dose 235.1463 uSv T 26.4C bat 100%
from radex import RadexObsidian
with RadexObsidian.open() as dev: # auto-detects the COM port
print(dev.device_info()) # manufacturer, model, serial, firmware
m = dev.read_metrics()
print(m.der, "uSv/h", m.cps, "cps", m.temperature_c, "C")
for m in dev.stream(interval=1.0): # forever
...read_spectrum() is declared but not implemented yet — the gamma-spectrum readout
command hasn't been found in the protocol.
The scintillator counts radioactive decays, and the number of decays per measurement
cycle is fundamentally unpredictable — real quantum entropy. radex.entropy turns that
into random bytes:
python -m radex rng --bytes 32 --true # blocks until entropy is collected
python -m radex rng --bytes 1024 # fast: HMAC-DRBG after a one-time seedfrom radex import RadexRNG
with RadexRNG.open() as rng:
seed = rng.true_random_bytes(32) # pure quantum entropy (slow)
bulk = rng.random_bytes(10**6) # fast CSPRNG, continuously reseededHow the accounting works. The firmware reports processed aggregates once per second;
per cycle the device sees a Poisson-distributed count of decays (~12 cps at background).
Entropy is estimated with the MCV min-entropy estimator (NIST SP 800-90B, 99%
confidence) over the round(cps_instant) stream — taking the smaller of the measured
and modelled value so it never over-credits (~1.8 bit/sample on hardware). Continuous
RCT + APT health tests gate the credit; it stalls if the register freezes or CPS
leaves a sane range. The whole raw register goes into a SHA-256 pool, but only the
physics is credited (× 0.5 margin, capped at 256 bits).
Not a certified TRNG. The firmware sits between the sensor and us and only exposes 1 Hz aggregates, not the raw noise. Good as a seed source; not a per-pulse, auditable entropy source.
python -m pytest # no hardware (uses captured frames)
RADEX_DEVICE=1 python -m pytest tests/test_hardware.py -v # with the device plugged inUnofficial project, not affiliated with or endorsed by QUARTA-RAD. Do not use it for radiation-safety decisions.
Реверс-инжинированный USB/serial-драйвер для дозиметра Radex Obsidian (QUARTA-RAD): аккуратный SDK, консольный стример измерений и — поскольку сенсор ловит настоящий квантовый шум — генератор случайных чисел на радиоактивном распаде.
Протокол разобран по живому прибору; полный разбор — в
docs/reverse-engineering/protocol-notes.md.
| Живые метрики | Мощность дозы, CPS, бета, накопленная доза, температура, батарея, часы прибора |
| Стриминг | Консольный стример с записью в CSV / JSONL |
| Паспорт прибора | Производитель, модель, серийный номер, версия прошивки |
| Аппаратный ГСЧ | Генератор на шуме распада с учётом энтропии по SP 800-90B |
| Чистый Python | Единственная зависимость — pyserial; кроссплатформенно |
| Автоопределение | Находит COM-порт по USB VID/PID — без настройки |
| Прибор | USB VID:PID | Статус |
|---|---|---|
| Radex Obsidian | ABBA:A006 |
✅ проверено на железе |
| Radex Obsidian G Pro | ABBA:A008 |
⚙️ автоопределяется, не тестировался |
| Radex Obsidian 2 | ABBA:A00A |
⚙️ автоопределяется, не тестировался |
Прибор виден как виртуальный COM-порт Silicon Labs CP210x (115200 8N1). Тот же кадровый протокол вендорские приложения туннелируют и по BLE, так что BLE-транспорт можно добавить за тем же интерфейсом.
pip install -e .python -m radex # живой стрим, порт ищется сам
python -m radex stream --once # одно измерение и выход
python -m radex stream --csv log.csv --jsonl log.jsonl --interval 2
python -m radex stream --port COM3Пример вывода:
# QUARTA-RAD Obsidian sn=04250001002186 fw=v1.21
2026-08-20T16:08:46 DER 0.1260 +-0.0116 uSv/h CPS 11.912 dose 235.1463 uSv T 26.4C bat 100%
from radex import RadexObsidian
with RadexObsidian.open() as dev: # порт определяется автоматически
print(dev.device_info()) # производитель, модель, серийник, прошивка
m = dev.read_metrics()
print(m.der, "uSv/h", m.cps, "cps", m.temperature_c, "C")
for m in dev.stream(interval=1.0): # бесконечно
...read_spectrum() объявлен, но пока не реализован — команда выгрузки гамма-спектра
в протоколе ещё не найдена.
Сцинтиллятор считает распады, и число распадов за цикл измерения принципиально
непредсказуемо — это настоящая квантовая энтропия. Модуль radex.entropy превращает
её в случайные байты:
python -m radex rng --bytes 32 --true # блокируется, пока не наберётся энтропия
python -m radex rng --bytes 1024 # fast: HMAC-DRBG после одноразового сидаfrom radex import RadexRNG
with RadexRNG.open() as rng:
seed = rng.true_random_bytes(32) # чистая квантовая энтропия (медленно)
bulk = rng.random_bytes(10**6) # быстрый CSPRNG, непрерывный подсевКак считается энтропия. Прошивка отдаёт обработанные агрегаты раз в секунду; за цикл
прибор видит Пуассоновское число распадов (~12 cps на фоне). Энтропию оцениваем
MCV-эстиматором min-энтропии (NIST SP 800-90B, 99% доверие) по потоку
round(cps_instant) — берём меньшее из измеренного и модельного значения, чтобы не
завысить (~1.8 бита на сэмпл на реальном приборе). Непрерывные health-тесты RCT и APT
гейтят зачёт; он стопается, если регистр завис или CPS вне разумного диапазона. Весь
сырой регистр идёт в SHA-256-пул, но зачитывается только физика (× 0.5 запаса,
кап 256 бит).
Это не сертифицированный TRNG. Между сенсором и нами стоит прошивка, наружу она отдаёт только 1 Гц агрегаты, а не сырой шум. Годится как источник для засева, но не как попульсовый аудируемый источник энтропии.
python -m pytest # без железа (на захваченных кадрах)
RADEX_DEVICE=1 python -m pytest tests/test_hardware.py -v # с подключённым приборомНеофициальный проект, не аффилирован с QUARTA-RAD и не одобрен им. Не использовать для принятия решений о радиационной безопасности.