Files
MAG160C/docs/superpowers/specs/2026-07-12-mag160c-linux-sdk-design.md

266 lines
7.9 KiB
Markdown

# MAG160C Linux SDK Design
Date: 2026-07-12
## Status
Draft for user review. Approved approach: C++ core library + stable C ABI + CLI + Python-callable layer.
## Goal
Build an original Linux-usable SDK for the MAG160C USB thermal camera kit, based on interoperability evidence from the bundled Windows and Android SDKs.
The deliverable should support:
- a reusable C/C++ library;
- a command-line sample/tool;
- Python-callable access to the same library;
- clear boundaries between confirmed behavior and unknown IR camera private protocol details.
## Key evidence driving the design
- IR camera USB identity: VID 0x833C, PID 0x0001.
- Android MagDevice passes an Android USB file descriptor into native LinkCamera(fd), then calls MAG-style native APIs.
- Android libcoresdk.so exports MAG_* APIs and uses libusb_bulk_transfer.
- Windows ThermalSDK.dll wraps CoreSDKLib.dll MAG_* APIs.
- Windows CoreSDKLib.dll imports libusb0 bulk read/write APIs.
- TCM control framing is recoverable from Java bytecode:
- packet starts with 0x7e;
- big-endian body length;
- additive byte checksums for header and body;
- main/sub/frame fields;
- payload starts after frame id.
- TCM commands for motor, proximity, light, audio, power, schedule task, and firmware update are known enough to implement.
## Architecture
The project should be split into four layers.
### 1. Core C++ library
Internal name: libmag160c_core.
Responsibilities:
- Own libusb context/device handles.
- Enumerate USB devices.
- Match known MAG IR camera VID/PID.
- Discover bulk endpoints for IR/TCM-like devices.
- Encode and decode TCM frames.
- Provide IR camera lifecycle abstractions matching the SDK shape:
- open/close;
- start/stop;
- trigger FFC;
- camera info;
- raw capture/probe hooks;
- callback plumbing.
- Return explicit unsupported/protocol-unknown errors where low-level IR packet details are not yet known.
The C++ layer may use RAII and internal classes, but those classes are not the public ABI.
### 2. Stable C ABI
Public header: include/mag160c/mag160c.h.
Responsibilities:
- Expose opaque handles.
- Provide simple structs and error codes.
- Avoid C++ types in public signatures.
- Support CLI and Python bindings through the same ABI.
Proposed API families:
- Context:
- mag160c_init
- mag160c_shutdown
- mag160c_last_error
- Device discovery:
- mag160c_list_devices
- mag160c_free_device_list
- IR camera:
- mag160c_ir_open
- mag160c_ir_close
- mag160c_ir_get_info
- mag160c_ir_start
- mag160c_ir_stop
- mag160c_ir_trigger_ffc
- mag160c_ir_read_raw_once
- TCM:
- mag160c_tcm_open
- mag160c_tcm_close
- mag160c_tcm_rotate
- mag160c_tcm_read_distance
- mag160c_tcm_set_light
- mag160c_tcm_set_audio
- mag160c_tcm_get_version
- Utilities:
- mag160c_tcm_encode_frame
- mag160c_tcm_decode_frame
### 3. CLI tool
Binary name: mag160c-cli.
Initial commands:
- probe
- list matching USB devices and endpoints;
- print VID/PID, bus/address, interfaces, endpoints.
- tcm-rotate <angle>
- send known motor rotate command.
- tcm-light <off|red|green|blue|yellow> [steady|blink|breath]
- send known light command.
- tcm-distance
- read/parse proximity distance if the TCM device is available.
- ir-info
- open IR device if possible and show descriptors/endpoints.
- ir-capture-raw --out <file>
- attempt one raw read using discovered endpoints.
- if protocol command is unknown, fail with a clear MAG160C_ERR_PROTOCOL_UNKNOWN.
The CLI is both a sample and a hardware diagnostic tool.
### 4. Python-callable layer
Package name: mag160c.
Recommended first implementation: ctypes wrapper over the C ABI.
Why ctypes first:
- no compiled Python extension needed;
- easier to use in experiments;
- shares the exact tested C ABI used by the CLI.
Python modules:
- mag160c.core
- shared-library loading, error handling, ctypes structs.
- mag160c.tcm
- rotate, light, distance helpers.
- mag160c.ir
- probe, open, info, raw capture helper.
- mag160c.cli
- optional Python CLI wrapper for quick scripts.
## Data flow
### TCM command flow
Caller -> C ABI -> C++ TCM codec -> libusb bulk OUT -> device -> libusb bulk IN -> C++ TCM parser -> caller.
TCM frames are framed, checksummed, and correlated by frame id. The implementation should keep a 15-bit-ish monotonically increasing frame id compatible with the Android behavior: start at 1, increment, wrap before 0x8000.
### IR camera flow
Caller -> C ABI -> C++ IR device -> libusb endpoint discovery -> protocol-specific start/read/stop path.
Known today:
- device is vendor-specific bulk USB;
- proprietary SDK converts raw/response words into temperature data;
- exact start/read packet protocol remains unknown.
Therefore initial IR behavior should focus on descriptor probing, safe endpoint discovery, raw read experiments, and clearly marked unsupported operations until protocol evidence is recovered.
## Error handling
Use explicit error codes:
- MAG160C_OK
- MAG160C_ERR_INVALID_ARGUMENT
- MAG160C_ERR_NO_DEVICE
- MAG160C_ERR_PERMISSION
- MAG160C_ERR_USB
- MAG160C_ERR_TIMEOUT
- MAG160C_ERR_CHECKSUM
- MAG160C_ERR_PROTOCOL_UNKNOWN
- MAG160C_ERR_UNSUPPORTED
- MAG160C_ERR_INTERNAL
Every public function returns an error code or a documented sentinel. Detailed messages are available through mag160c_last_error.
The CLI must print actionable messages, especially for Linux USB permission problems and unknown IR protocol paths.
## Build system
Use CMake.
Targets:
- mag160c_core shared library
- mag160c_cli executable
- mag160c_tests test executable
- optional install target
Dependencies:
- libusb-1.0
- standard C/C++ runtime
- Python only for wrapper/tests, not for core build
No vendored proprietary SDK binaries should be linked into the implementation.
## Testing strategy without hardware
Unit tests:
- TCM frame encode/decode round trips.
- checksum validation.
- command payload construction.
- frame id wrapping.
- error-code behavior for null/invalid arguments.
Mock tests:
- fake USB transport interface for TCM read/write.
- fake device descriptor/endpoints for probe logic.
CLI dry-run tests:
- commands that support --dry-run print the bytes they would send.
- probe gracefully reports no device when hardware is absent.
Hardware tests to document:
- verify udev permissions;
- run mag160c-cli probe;
- run tcm-light/tcm-rotate with small safe values;
- run ir-info;
- run ir-capture-raw and preserve captured bytes for protocol analysis.
## Scope boundaries
In scope for first implementation:
- project scaffolding;
- C ABI and Python wrapper;
- TCM frame codec and known commands;
- USB enumeration and endpoint discovery;
- CLI diagnostics;
- IR API skeleton with honest protocol-unknown errors.
Not in scope until more evidence exists:
- exact proprietary IR start/stream command sequence;
- full raw-to-temperature parity;
- face/RGB camera logic beyond noting that RGB is ordinary UVC/V4L2 territory;
- firmware update execution by default. Firmware update commands may be encoded but should be guarded and not exposed as an easy accidental CLI action.
## Open decisions
- Whether to expose firmware update commands at all in the first public CLI. Recommendation: keep them internal or behind an explicit experimental flag.
- Whether to use C++17 or C++20. Recommendation: C++17 for broader Linux compatibility.
- Whether Python packaging should initially be source-tree-only or installable via pyproject.toml. Recommendation: include pyproject.toml once the shared library layout is settled.
## Acceptance criteria for first implementation
- CMake configures without proprietary SDK dependencies.
- Unit tests pass without hardware.
- CLI probe works and reports no-device cleanly when absent.
- TCM codec tests include byte-exact known examples generated from the recovered frame rules.
- Python can import mag160c and call at least probe/dry-run TCM helpers.
- IR unsupported/protocol-unknown operations are explicit, not silent stubs.