Pair calibration
Estimate one antenna-delay correction for a specific initiator/responder pair, then hold it fixed while measuring performance across distance and orientation.
Do not recalibrate at every test distance. That would force each test toward its reference value and hide scale error, multipath sensitivity, and distance-dependent bias.
Physical setup
- Use a surveyed or carefully measured 1 m separation between consistent antenna reference points.
- Mount both tags rigidly at the same height with clear line of sight.
- Use the same antenna orientation planned for the validation sweep.
- Keep people, metal objects, and large reflecting surfaces away from the direct path.
- Allow the stream to stabilize before collecting samples.
Procedure
For the browser, follow Pair Calibration in the quickstart. For Python, first download and install the host tools, disconnect the browser's serial connection, and use the actual responder port:
python3 calibrate.py /dev/tty.usbmodem1201 \
--true-mm 1000 \
--samples 200 --timeout 30
The script reads responder distance records, trims the lowest and highest 10%, computes a robust mean, converts the distance residual to DW3000 device-time units, sends CALIB <offset>, and stores the result by responder hardware ID.
Correction model
- Residual
measured_mm - reference_mm. Positive means the pair reads long.- Conversion
- The calibration tool converts the residual to a responder antenna-delay adjustment using the firmware's two-way-ranging timing model.
- Applied value
- A signed DTU offset sent to the responder with
CALIB. - Scope
- Specific hardware pair, firmware/PHY configuration, and antenna orientation.
Persistence
CALIB changes responder RAM and is lost on reset or power removal. The browser stores the value by responder ID in that browser and site; localhost and the live site have separate storage. Python stores its own values in ~/.config/opentags/calibration.json. These stores do not synchronize, and the ROS 2 driver does not restore either store.
Record both module IDs, the offset, PHY, orientation, and reference distance. A value is fitted to a pair even though the stores are keyed only by responder. Check it again when swapping an initiator.
# Inspect the value after a power cycle without automatic restoration:
python3 info.py /dev/tty.usbmodem1201 --no-restore
# Restore Python's saved value, wait for acknowledgement, and confirm INFO:
python3 info.py /dev/tty.usbmodem1201
After calibrating in the browser, flashing that responder again from the same identified device session can include the saved value in its image. A generic downloaded image contains the default. Neither method is an on-device persistent CALIB save command. Always check the boot value before moving the pair to a robot.
Calibration acceptance checks
| Check | Pass condition | Failure suggests |
|---|---|---|
| Sample count | Requested valid samples collected without motion | Link loss or unstable setup |
| Spread | No abrupt multimodal clusters or large drift | Multipath, movement, weak signal |
| Repeatability | Two independent runs produce similar offsets | Reference or geometry is not controlled |
| Post-check | Independent 1 m run has low bias | Wrong slot, wrong true distance, stale calibration |
After calibration
Without changing the offset, collect repeated static runs at 1 m, 3 m, 5 m, and 10 m. Report mean error, median error, standard deviation, 95th-percentile absolute error, valid sample rate, and miss rate. Repeat at multiple antenna orientations and in at least one representative deployment environment.