Patent Pending

katlog

Custom Arcbit Codec

Location-first asset tracking through a patent-pending machine-readable arcbit format — scan any tag with any camera, capture GPS or IP-geolocated events on-device, and build a passive audit trail without active radios, firmware, or any camera data leaving the device.

4-bit nibble cells · CRC-16-CCITT · Pyodide WASM · GPS + IP geo tracking
16
symbols / cell
4-bit
nibble encoding
2
layout modes
256→4
linear classifier
0
images uploaded

01 / Encoding

The Custom Arcbit Format

Arcbit is a purpose-built 2D barcode codec — not QR, not Data Matrix. Each cell encodes a 4-bit nibble via quadrant arc geometry. A custom framing protocol provides magic identification, versioning, CRC integrity, and an optional raw bit-payload field.

Frame Structure

MAGIC
Arcbit
4 B
VER
0x01
1 B
TXT_LEN
uint16
2 B
XBITS_LEN
uint16
2 B
TEXT
UTF-8
N B
EXTRA BITS
packed
⌈M/8⌉ B
CRC-16
CCITT
2 B
Magic: 47 4C 51 32 | Poly: 0x1021 | Init: 0xFFFF

Circular Mode

Concentric ring layout

Data cells arranged in concentric rings around a central bull's-eye anchor ring. The anchor encodes orientation via a notch in the outer ring band — a weighted circular mean gives sub-degree accuracy. Optimised for Waveshare e-Paper displays.

  • Radial layout, configurable ring count
  • Timing ticks every 3.75° on outer ring
  • Cardinal bar guides at 0°/90°/180°/270°
  • Capacity scales linearly with ring count

Square Grid Mode

N × N flat layout

Uniform N×N grid with corner bull's-eye anchors and alternating timing rails in the quiet-zone border. SVD-based homography correction handles camera perspective distortion. Default 8×8 grid fits ~21 bytes payload.

  • 8×8 default → scales to 128×128
  • 4 corner anchors (bull's-eye markers)
  • Alternating timing rails in quiet zone
  • ~21 bytes at 8×8, ~8KB at 128×128

Nibble Cell Encoding

Each cell encodes one 4-bit nibble (0–15) via quadrant arc geometry. Two nibbles pack into one byte. bit-1 = filled arc from center · bit-0 = corner circle cutout

code 0x0
code 0x5
code 0xA
code 0xF
TL=(code>>0)&1 · BL=(code>>1)&1 · BR=(code>>2)&1 · TR=(code>>3)&1

02 / Classification

ML-Powered Cell Classification

Each decoded cell passes through a trained linear classifier. No template matching, no thresholding heuristics per-cell. The model is pre-trained, embedded in the binary, and runs inside CPython-on-WebAssembly in a background worker thread.

Classifier Architecture

Input256 features

16×16 grayscale patch downsampled via OpenCV INTER_AREA, flattened and normalized: (px − 128) / 128 → [−1, 1]

Linear Layer4 × 256 weights

z = W · x + b  —  W: (4, 256)   b: (4,)
Weights pre-trained on synthetic arcbit renders, embedded directly in the WASM Python backend at load time.

Output4-bit code (0–15)

Binary threshold (no sigmoid): bits = (z > 0).astype(int)
Pack: (b[0]<<3)|(b[1]<<2)|(b[2]<<1)|b[3]

# Core inference — numpy inside Pyodide WASM def _match_cell(cell, refs, thr): out = cv2.resize(cell, (16, 16), interpolation=cv2.INTER_AREA) features = (out.flatten() - 128.0) / 128.0 z = np.dot(ml_weights, features) + ml_biases bits = (z > 0).astype(int) return (bits[0]<<3)|(bits[1]<<2)|(bits[2]<<1)|bits[3]

Otsu Threshold Estimation

Per-frame adaptive binarization using Otsu's between-class variance. Clamped to [70, 210]. Three offsets (0, −12, +12) tried per candidate to handle partial or uneven lighting.

Homography Correction

Perspective distortion corrected via SVD-based Direct Linear Transform. Corner detection uses adaptive thresholding + contour analysis with squareness (aspect 0.7–1.3) and convexity (>80%) filters.

Multi-Pass Candidate Search

Centered crop candidates at 8-px step intervals ensure grid alignment across all scales. Off-center candidates at ±6% handle imperfectly framed arcbits. Magic-header fast-reject eliminates false candidates before full cell decode.

Circular Anchor Detection

Ring contrast score from three concentric disk means. Angular notch detected via 720-sample polar ring scan. Weighted circular mean gives sub-degree orientation for rotation correction.

03 / Architecture

Privacy-First Decode Pipeline

Decoding is entirely on-device — no image is uploaded, no frame ever leaves the browser. CPython compiled to WebAssembly (Pyodide) runs the full CV and ML stack in a background Web Worker, keeping the camera feed private and at full frame rate. Only the decoded text result is sent to the platform.

01

Camera Frame Capture

getUserMedia() requests the environment-facing camera (up to 1920×1080 @ 30fps). A camera scoring algorithm selects the best lens — back wide/main preferred over telephoto or front. Auto-scan fires every 120ms desktop / 220ms mobile.

02

ROI Crop + Grayscale

Scan-frame overlay maps to video pixel coordinates accounting for object-fit letterboxing. ROI padded 8% and scaled to ≤960px mobile / 900px desktop. BT.601 luma: Y = (R·77 + G·150 + B·29) >> 8

03

Zero-Copy Transfer to Web Worker

The Uint8Array is transferred via postMessage() Transferable — no copy, no serialization. An RPC-style ID sequence tracks concurrent in-flight requests.

04

Pyodide WASM Runtime

On first decode the worker boots Pyodide v0.27 (CPython 3.11 → WASM), installs numpy and opencv-python, then fetches and executes arcbit_backend.py. Subsequent calls invoke decode_frame() directly.

05

CV + ML Decode

Otsu threshold → multi-pass crop candidates → magic-byte fast-reject → homography correction → ML cell classify (256→4 linear) → nibble assembly → _parse_framed_payload() → CRC-16-CCITT verify → UTF-8 decode. Normal and inverted frames both tried.

06

Result Delivery

Decoded text posted back to main thread. 800ms cooldown prevents duplicate events. Scan frame flashes emerald on success. Results stored to Django session history.

Decoder Telemetry
boot: 1  vis: visible
worker: ready
camera: ready
video: 1920×1080
decode: 900×900
attempts: 142
hits: 3  misses: 139
decode ms: 38.4 / 41.2
result: square:
  https://katlog.io/…
reason: decoded
thr: 118 | 106 | 130
gray: μ:127 σ:44
life: boot-complete
err: -
Live HUD visible in the scanner UI. Tracks timing, backend state, and per-attempt diagnostics.

04 / Deployment

Location-First Asset Tracking

Arcbits printed or displayed on e-Paper become physical asset tags with zero ongoing power cost. Every scan is a timestamped location event — GPS-precise where available, IP-geolocated as fallback — building a passive movement audit trail without any active radio, battery, or network dependency on the tag itself.

Bi-stable e-Paper Tags

e-Paper displays are bi-stable — they hold the last rendered arcbit indefinitely with zero power draw. Tags on assets, cases, or shelving consume power only during updates, giving months of operational life on a small cell.

  • Zero idle power — fully passive between scans
  • ~200 DPI — timing rings readable at distance
  • High contrast — works in direct sunlight
  • No RF, no network stack on the tag

GPS + IP Geolocation

Every scan event carries location context. The scanner requests the device's GPS coordinates first; if unavailable or denied, the platform falls back to IP geolocation to resolve a city-level fix. Both coordinates and source are recorded per scan.

  • GPS via Geolocation API (meter-level)
  • IP fallback — city/region resolution
  • Source flagged: gps | ip | none
  • Timestamp + location stored per scan

Passive Audit Trail

No active tracking infrastructure required. Each time an asset tag is scanned — by any camera, any device — the platform logs who scanned it, when, and where. The arcbit payload itself encodes the asset identity.

  • Asset ID embedded in Arcbit payload
  • Scan events: user + location + time
  • Movement history from scan sequence
  • No tag firmware — pure optics + server

Scan Event — Location Record

# Arcbit payload — asset identity "ASSET-4821·ZONE-B3·REV-2" # Decoded on-device, posted to platform POST /api/v1/track/ { "asset": "ASSET-4821", "zone": "ZONE-B3", "loc": { "lat": 37.7749, "lng": -122.4194, "src": "gps", "acc": 4.2 }, "ts": "2024-06-01T14:32:11Z", "scanner": "[email protected]" }
Platform response
assetASSET-4821
location37.77, -122.41
loc sourceGPS · ±4.2 m
prior scan6h ago · ZONE-A1
total scans47 events
statuslogged ✓

05 / Platform

Backend Platform

Django handles auth, session history, server-side arcbit generation, DuckDB scan caching, and the location event pipeline. The decoder is fully client-side — the backend never receives camera frames, raw images, or any visual data. Privacy by design.

Django
Auth + API
DuckDB
Scan event cache
Geo-IP
Location fallback
Vite
Frontend build

API Endpoints

POST/api/v1/encode/
POST/api/v1/decode/
POST/api/v1/track/
GET/history/
GET/assets/{id}/trail/

Encoder + Tag Stack

  • Client-side JS encoder — no server round-trip
  • CRC-16-CCITT integrity on every payload
  • PNG/SVG export for print or e-Paper push
  • GPS geolocation + IP fallback on scan
  • Per-scan audit log: user · location · time