QR Communication Protocol
This document describes the technical implementation of the QR-based communication channel introduced in the qr_comm_test_incorporation branch.
Concept: Visual Network Coding
The goal is to transmit arbitrary binary data (files, game state, crypto keys) between devices using only the screen (as transmitter) and camera (as receiver).
Challenge: QR codes have limited capacity. A video stream of QR codes can transmit large data, but frames might be dropped (camera loses focus, tearing, lighting issues). Solution: Fountain Codes (Network Coding). Instead of chopping the file into pieces 1, 2, 3... and needing specifically piece #2 if it's lost, we transmit mathematical combinations of the pieces.
- If the file is split into
fragments. - The sender generates infinite random linear combinations (Equation frames).
- The receiver just needs to collect any
(plus a small overhead) linearly independent frames to solve the system and recover the file.
Core Structs & Modules
The qr_comm crate implements the math and data framing for this protocol.
Package@ crates/qr_comm/src/data_structures/application_package.rs:- Purpose: Represents the application-layer binary data holding files or messages.
- Usage: Wraps arbitrary data with its size. It provides methods to split the data into smaller, uniform
Fragments (into_fragments) and to re-assemble those fragments back into a full package (from_fragments).
Fragment@ crates/qr_comm/src/data_structures/fragment.rs:- Purpose: A fixed-size slice of a
Package. - Usage: The atomic unit over which math operations (like XOR and Galois field multiplication) are performed when generating combinations.
- Purpose: A fixed-size slice of a
FrameFactor&Factor@ crates/qr_comm/src/data_structures/factors.rs:- Purpose: Represents the set of mathematical multipliers (coding factors) used to create a linear combination of fragments.
- Usage: Encodes which fragments are included in a specific
Frameand with what weight.
Frame@ crates/qr_comm/src/data_structures/frame.rs:- Purpose: The wire format payload for a single QR Code.
- Structure: Consists of
FrameFactors (the coefficients), aFragment(the mathematically combined payload), and aFrameHeader(metadata about the sender). - Usage: This is the data structure that directly converts to and from a visual
qr_code::QrCode.
Equation@ crates/qr_comm/src/network_coding/equation.rs:- Purpose: Represents a row in the decoding matrix. Internally pairs a
Factor(abstract representation ofFrameFactor) with aFragment. - Usage: Supports core math operations (Add, Sub, Mul, Div) in the Galois field for Gaussian elimination.
- Purpose: Represents a row in the decoding matrix. Internally pairs a
GaloisField2p4@ crates/qr_comm/src/network_coding/galois.rs:- Purpose: Arithmetic constraint module (
). Ensures that all additions and multiplications of bytes wrap logically and stay within fixed bounds, keeping combinations fitting neatly into bytes.
- Purpose: Arithmetic constraint module (
Epoch@ crates/qr_comm/src/network_coding/epoch.rs:- Purpose: The core engine managing the transmission and reception state for a single session.
- Usage:
- Transmitter: Receives
Packages viaEpoch::write(). CallingEpoch::pop_recent_frame()randomly generates network-codedFrames based on the written fragments. - Receiver: Receives
Frames scanned by the camera viaEpoch::push_frame(). It manages aMatrixofEquations to perform Gaussian elimination. Once it achieves "full rank," it decodesFragments and stores them. Finished data is requested back viaEpoch::get_package().
- Transmitter: Receives
Sending Data (Transmitting)
The process of generating and displaying a QR code stream:
- Preparation: The application data (e.g., a file or a string message) is encapsulated into a
Package. - Writing to Epoch: The sender invokes
Epoch::write(package). Internally, thePackageis split into chunks creating an array ofFragments. - Frame Generation Cycle: At a fixed interval (e.g., ~20Hz), the transmitter calls
Epoch::pop_recent_frame().- The
Epochtakes a subset of the activeFragments. - It assigns random scalar coefficients (
GaloisField2p4) to them. - It combines these fragments mathematically into a single output
Fragmentpayload. - It constructs a
Framecontaining the combined payload and the coefficients used.
- The
- Displaying: The resulting
Frameis serialized and passed into theqrcodecrate to generate an image. This image is rendered to the UI texture, creating a rapid, continuous QR video stream.
Receiving Data (Assembling)
The process of capturing the QR code stream and decoding it:
- Scanning: The receiving device uses the
qr_scanner::QrScannerPopupto capture frames from the camera using the browser'smediaDevicesAPI. - QR Recognition: Pixel data is passed to the
rqrrlibrary to locate and decode QR code matrices periodically. - Frame Conversion: The decoded raw bytes are unpacked back into a
Framestruct, containing the coding coefficients and the mixed data fragment. - Matrix Push: The receiver calls
Epoch::push_frame(frame).- The
Frameis converted into anEquation. - The
Epochinserts thisEquationinto its internalMatrixand attempts Gaussian elimination. - If the equation provides linearly independent (novel) information, it is kept; otherwise, it resolves to zero and is discarded.
- The
- Reconstruction: The UI continuously monitors the rank of the matrix (# of useful equations) compared to the required number of fragments.
- Package Extraction: Once full rank is achieved (Gaussian elimination produces plain, single-variable equations), individual
Fragments are reconstructed. The frontend retrieves the full files by repeatedly callingEpoch::get_package(participant, index).
Usage Implementation
The frontend has been modified to support this "Test Mode". You can find the respective integrations in these components.
Scanning (frontend/src/qr_scanner.rs)
- Uses
web-systo accessnavigator.mediaDevices.getUserMedia. - Draws video frames to a
canvas. - Reads pixel data from canvas and passes it to the
rqrrlibrary (Rust QR reader). - Performance: Scanning happens periodically (e.g., every 5th frame) to avoid blocking the UI thread.
Receiving (frontend/src/game/screens/qr_test_receive.rs)
- Displays a "Scan QR" popup.
- When a QR is detected:
- Raw bytes are extracted and converted to a
Frame. - Pushed to the application's global
Epochinstance. - UI shows "Rank" (number of useful equations) vs "Needed".
- When full rank is achieved, the message is requested and displayed or saved.
- Raw bytes are extracted and converted to a
Transmitting (frontend/src/game/screens/qr_test_transmit.rs)
- Requests data (e.g., a file) from the backend via HTTP or an ad-hoc request mechanism, or accepts plain text input.
- Pushes the data to the sender's
Epochviawrite. - Continuously renders the
Frameproduced bypop_recent_frame()into anegui::Imageoverlayed on screen.