chessmatrix 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zach Mills
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,339 @@
1
+ Metadata-Version: 2.4
2
+ Name: chessmatrix
3
+ Version: 1.0.0
4
+ Summary: ChessMatrix — 8×8 four-color 2D barcode codec with Reed-Solomon error correction
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/hacker6284/ChessMatrix
7
+ Project-URL: Repository, https://github.com/hacker6284/ChessMatrix
8
+ Project-URL: Issues, https://github.com/hacker6284/ChessMatrix/issues
9
+ Keywords: barcode,2d-barcode,color-barcode,reed-solomon,chessmatrix
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Multimedia :: Graphics
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Provides-Extra: render
23
+ Requires-Dist: Pillow>=9.0; extra == "render"
24
+ Dynamic: license-file
25
+
26
+ # ChessMatrix
27
+
28
+ **A 4-color 8×8 2D barcode format.**
29
+
30
+ ChessMatrix is a compact, machine-readable 2D barcode that fits in an 8×8 cell grid — the same footprint as a chessboard — by using four colors (black, red, green, blue) instead of the traditional two, yielding 2 bits of information per cell. It is designed as a minimal, scannable symbol for applications where even the smallest standard formats (DataMatrix 10×10, Micro QR 11×11) are too large.
31
+
32
+ ## Installation
33
+
34
+ **Python**
35
+ ```bash
36
+ pip install chessmatrix
37
+ # render_image() also requires Pillow:
38
+ pip install "chessmatrix[render]"
39
+ ```
40
+
41
+ **JavaScript / Node.js**
42
+ ```bash
43
+ npm install chessmatrix
44
+ ```
45
+
46
+ **Browser (no bundler)**
47
+ ```html
48
+ <script type="module">
49
+ import { buildGrid, renderGrid } from 'https://cdn.jsdelivr.net/npm/chessmatrix/chessmatrix.js';
50
+ </script>
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 1. Motivation
56
+
57
+ The smallest standard 2D barcodes — DataMatrix 10×10 and Micro QR Version 1 (11×11) — cannot physically fit in an 8×8 cell footprint. ChessMatrix fills this gap by encoding 2 bits per cell via color rather than 1 bit per cell via luminance, achieving a useful payload within the 8×8 constraint.
58
+
59
+ **Net payload: 4 bytes (32 bits) per symbol**, protected by Reed-Solomon error correction.
60
+
61
+ ---
62
+
63
+ ## 2. Symbol Structure
64
+
65
+ The symbol is an 8×8 grid of colored cells, each one of:
66
+
67
+ | Symbol | Value | RGB (recommended) |
68
+ |--------|-------|-------------------|
69
+ | BLACK | `0b00` | `#0A0A0A` |
70
+ | RED | `0b01` | `#DC2828` |
71
+ | GREEN | `0b10` | `#28B428` |
72
+ | BLUE | `0b11` | `#2828DC` |
73
+
74
+ ### 2.1 Grid Zones
75
+
76
+ ```
77
+ Col: 0 1 2 3 4 5 6 7
78
+ ┌────┬────┬────┬────┬────┬────┬────┬────┐
79
+ R0 │ T │ t │ T │ t │ T │ t │ T │ t │ ← Top timing (alternating K/W)
80
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
81
+ R1 │ F │[K] │ D │ D │ D │ D │[R] │ tm │ ← [K] = BLACK anchor
82
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
83
+ R2 │ F │ D │ D │ D │ D │ D │ D │ tm │
84
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
85
+ R3 │ F │ D │ D │ D │ D │ D │ D │ tm │
86
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
87
+ R4 │ F │ D │ D │ D │ D │ D │ D │ tm │
88
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
89
+ R5 │ F │ D │ D │ D │ D │ D │ D │ tm │
90
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
91
+ R6 │ F │[G] │ D │ D │ D │ D │[B] │ tm │ ← [G] = GREEN anchor, [B] = BLUE anchor
92
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
93
+ R7 │ F │ F │ F │ F │ F │ F │ F │ F │ ← Bottom finder (all BLACK)
94
+ └────┴────┴────┴────┴────┴────┴────┴────┘
95
+
96
+ F = Finder bar (BLACK)
97
+ T/t = Timing cell (T=BLACK, t=WHITE, alternating)
98
+ tm = Right timing column (alternating K/W, row0=WHITE, row1=BLACK, ...)
99
+ [K][R][G][B] = Color calibration anchors
100
+ D = Data cell (2-bit color-encoded)
101
+ ```
102
+
103
+ ### 2.2 Border Cells (28 cells)
104
+
105
+ **Left finder bar** — column 0, rows 0–7: all **BLACK**
106
+
107
+ **Bottom finder bar** — row 7, cols 0–7: all **BLACK**
108
+
109
+ **Top timing strip** — row 0, cols 0–7:
110
+ - Even columns (0, 2, 4, 6): **BLACK**
111
+ - Odd columns (1, 3, 5, 7): **WHITE**
112
+
113
+ **Right timing strip** — col 7, rows 0–7:
114
+ - Even rows (0, 2, 4, 6): **WHITE**
115
+ - Odd rows (1, 3, 5, 7): **BLACK**
116
+
117
+ The timing strips continue to alternate even at their shared corners: (0,7) is WHITE (even column dominates in top timing; right timing confirms even row → WHITE).
118
+
119
+ ### 2.3 Color Calibration Anchors (4 cells)
120
+
121
+ Located at the four corners of the 6×6 interior:
122
+
123
+ | Position | Color | Purpose |
124
+ |----------|--------|----------------------------------|
125
+ | (row=1, col=1) | **BLACK** | Dark reference |
126
+ | (row=1, col=6) | **RED** | Red channel reference |
127
+ | (row=6, col=1) | **GREEN** | Green channel reference |
128
+ | (row=6, col=6) | **BLUE** | Blue channel reference |
129
+
130
+ A decoder samples these four cells first to calibrate its color discrimination thresholds before decoding any data cells. This compensates for lighting color temperature, ink variation, and camera white-balance errors.
131
+
132
+ ### 2.4 Data Cells (32 cells)
133
+
134
+ The remaining interior cells, read in row-major order (top to bottom, left to right), skipping the four anchors:
135
+
136
+ ```
137
+ Row 1: (1,2) (1,3) (1,4) (1,5) — 4 cells
138
+ Row 2: (2,1) (2,2) (2,3) (2,4) (2,5) (2,6) — 6 cells
139
+ Row 3: (3,1) (3,2) (3,3) (3,4) (3,5) (3,6) — 6 cells
140
+ Row 4: (4,1) (4,2) (4,3) (4,4) (4,5) (4,6) — 6 cells
141
+ Row 5: (5,1) (5,2) (5,3) (5,4) (5,5) (5,6) — 6 cells
142
+ Row 6: (6,2) (6,3) (6,4) (6,5) — 4 cells
143
+ TOTAL = 32 cells
144
+ ```
145
+
146
+ ---
147
+
148
+ ## 3. Data Encoding
149
+
150
+ ### 3.1 Color-to-Bit Mapping
151
+
152
+ Each cell encodes exactly 2 bits:
153
+
154
+ ```
155
+ BLACK = 0b00 = 0
156
+ RED = 0b01 = 1
157
+ GREEN = 0b10 = 2
158
+ BLUE = 0b11 = 3
159
+ ```
160
+
161
+ ### 3.2 Bit Packing
162
+
163
+ 32 cells × 2 bits = **64 bits = 8 bytes** raw capacity.
164
+
165
+ Cells are packed MSB-first, 4 cells per byte:
166
+
167
+ ```
168
+ Byte 0: cells[0] bits[7:6] | cells[1] bits[5:4] | cells[2] bits[3:2] | cells[3] bits[1:0]
169
+ Byte 1: cells[4] bits[7:6] | cells[5] bits[5:4] | cells[6] bits[3:2] | cells[7] bits[1:0]
170
+ ...
171
+ Byte 7: cells[28]...cells[31]
172
+ ```
173
+
174
+ To decode byte `b` from cells `4b`, `4b+1`, `4b+2`, `4b+3`:
175
+ ```
176
+ byte_val = (color[4b] << 6) | (color[4b+1] << 4) | (color[4b+2] << 2) | color[4b+3]
177
+ ```
178
+
179
+ ---
180
+
181
+ ## 4. Reed-Solomon Error Correction
182
+
183
+ ChessMatrix uses a **RS(8, 4)** code over GF(2⁸):
184
+
185
+ | Parameter | Value |
186
+ |-----------|-------|
187
+ | Total codeword length | 8 symbols (bytes) |
188
+ | Data symbols | 4 bytes |
189
+ | Parity symbols | 4 bytes |
190
+ | Maximum correctable errors | **2 symbol errors** |
191
+ | Maximum detectable errors | 4 symbol errors |
192
+ | Primitive polynomial | x⁸ + x⁴ + x³ + x² + 1 (`0x11D`) |
193
+ | Generator roots | α⁰, α¹, α², α³ (where α = 2) |
194
+
195
+ The 4 data bytes occupy the **first 4 bytes** of the codeword; the 4 parity bytes occupy bytes 5–8. The codeword is stored across the 32 data cells in byte order (data bytes first, then parity bytes).
196
+
197
+ ### 4.1 Error Budget Rationale
198
+
199
+ Color discrimination under real-world conditions is less reliable than luminance discrimination. A dirty scan, off-angle lighting, or color-shifted camera can misread an entire cell. Allocating 50% of capacity to error correction (vs. ~25% in standard DataMatrix 10×10) reflects this elevated error rate. RS(8,4) can correct any 2 completely wrong cells, which covers most single-color-channel failures.
200
+
201
+ ---
202
+
203
+ ## 5. Encoding Algorithm
204
+
205
+ 1. **Validate input**: data must be exactly 4 bytes.
206
+ 2. **RS encode**: compute 4 parity bytes via polynomial long division over GF(2⁸), producing an 8-byte codeword `[d0, d1, d2, d3, p0, p1, p2, p3]`.
207
+ 3. **Pack into cells**: convert the 8 bytes to 32 dibits (2-bit values), one per data cell.
208
+ 4. **Build grid**: initialize the 8×8 grid with the border pattern (finder + timing) and calibration anchors, then write the 32 data cells.
209
+
210
+ ---
211
+
212
+ ## 6. Decoding Algorithm
213
+
214
+ 1. **Locate symbol**: find the L-shaped finder corner (bottom-left) and orientation markers.
215
+ 2. **Rectify geometry**: use the finder bars and timing strips to establish the module grid (perspective correction, skew correction).
216
+ 3. **Calibrate colors**: sample the four anchor cells at (1,1), (1,6), (6,1), (6,6) to build color classification thresholds for this scan.
217
+ 4. **Classify cells**: for each of the 32 data cells, classify the sampled color as BLACK/RED/GREEN/BLUE using the calibrated thresholds.
218
+ 5. **Unpack bytes**: convert the 32 dibits back to 8 bytes.
219
+ 6. **RS decode**: compute syndromes. If nonzero, apply Berlekamp-Massey + Chien search + Forney algorithm to locate and correct up to 2 symbol errors.
220
+ 7. **Return payload**: the first 4 bytes of the corrected codeword are the user data.
221
+
222
+ ### 6.1 Color Calibration Detail
223
+
224
+ The decoder builds a calibration matrix from the four anchors:
225
+
226
+ ```python
227
+ reference = {
228
+ BLACK: sample(1, 1), # known-black pixel
229
+ RED: sample(1, 6), # known-red pixel
230
+ GREEN: sample(6, 1), # known-green pixel
231
+ BLUE: sample(6, 6), # known-blue pixel
232
+ }
233
+ ```
234
+
235
+ Each unknown cell is classified to the nearest anchor in RGB color space (Euclidean distance, or a simplified channel-dominance heuristic). This makes the format robust to uniform color shifts (e.g., tungsten lighting making everything yellowish).
236
+
237
+ ---
238
+
239
+ ## 7. Constraints and Limitations
240
+
241
+ - **Payload**: 4 bytes (32 bits). Sufficient for a serial number, short ID, timestamp, or small integer.
242
+ - **Color printing required**: cannot be produced on a monochrome printer.
243
+ - **Scanner requirements**: color camera or color sensor; standard laser barcode scanners cannot read ChessMatrix.
244
+ - **Print resolution**: each cell must be large enough to be unambiguously colored (~0.5mm minimum per cell in practice, so minimum symbol size ~4mm × 4mm).
245
+ - **No rotation invariance built in**: the finder pattern establishes a single canonical orientation. A 180° rotated symbol will fail to decode. (A future extension could add a rotation indicator cell.)
246
+
247
+ ---
248
+
249
+ ## 8. Python Implementation
250
+
251
+ Install: `pip install chessmatrix` (add `[render]` extra for PNG output)
252
+
253
+ ```python
254
+ from chessmatrix import encode, decode, render_image, render_ascii
255
+
256
+ data = b'\xDE\xAD\xBE\xEF'
257
+ grid = encode(data) # List[List[int]], 8×8, values 0-3 or -1
258
+ render_ascii(grid) # colored output in terminal
259
+ render_image(grid, 'code.png', cell_size=60) # requires Pillow
260
+
261
+ recovered = decode(grid)
262
+ assert recovered == data
263
+ ```
264
+
265
+ **API**
266
+
267
+ | Function | Description |
268
+ |---|---|
269
+ | `encode(data: bytes) -> Grid` | Encode 4 bytes → 8×8 color grid |
270
+ | `decode(grid: Grid) -> bytes` | Decode grid → 4 bytes (RS error correction applied) |
271
+ | `render_image(grid, path, cell_size=40)` | Save PNG (requires Pillow) |
272
+ | `render_ascii(grid)` | Print colored ASCII art to terminal |
273
+ | `CorrectionError` | Raised when RS decoding cannot correct errors |
274
+
275
+ `Grid` is `List[List[int]]` — an 8×8 nested list of color values (0–3, or -1 for white structural cells).
276
+
277
+ ---
278
+
279
+ ## 9. JavaScript Implementation
280
+
281
+ Install: `npm install chessmatrix`
282
+
283
+ ```javascript
284
+ import { lettersToBytes, buildGrid, renderGrid } from 'chessmatrix';
285
+
286
+ // Encode 4 bytes onto a canvas
287
+ const bytes = new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]);
288
+ const grid = buildGrid(bytes);
289
+ renderGrid(grid, document.getElementById('canvas'), 40); // 40px per cell
290
+ ```
291
+
292
+ ```javascript
293
+ import { bytesToLetters, lettersToBytes, buildGrid } from 'chessmatrix';
294
+
295
+ // 6-letter convenience codec (5-bit packing, A=0…Z=25)
296
+ const bytes = lettersToBytes('HELLO!'.replace(/[^A-Z]/gi, 'A')); // Uint8Array(4)
297
+ const letters = bytesToLetters(bytes); // → 'HELLOA'
298
+ ```
299
+
300
+ ```javascript
301
+ import { ChessMatrixScanner } from 'chessmatrix';
302
+
303
+ // Camera scanner (browser only — requires getUserMedia)
304
+ const scanner = new ChessMatrixScanner({
305
+ videoEl: document.getElementById('video'),
306
+ canvasEl: document.getElementById('canvas'),
307
+ onDecode: (letters) => console.log('Scanned:', letters),
308
+ onStatus: (msg, state) => console.log(state, msg),
309
+ });
310
+ await scanner.start();
311
+ ```
312
+
313
+ **API**
314
+
315
+ | Export | Description |
316
+ |---|---|
317
+ | `buildGrid(bytes: Uint8Array) → Int8Array` | Encode 4 bytes → 64-cell flat grid (row-major) |
318
+ | `renderGrid(grid, canvas, cellSize?)` | Draw grid on an HTMLCanvasElement |
319
+ | `lettersToBytes(str) → Uint8Array` | 6-letter string → 4 bytes (5-bit packing) |
320
+ | `bytesToLetters(bytes) → string` | 4 bytes → 6-letter string |
321
+ | `ChessMatrixScanner` | Semi-guided camera scanner class (browser only) |
322
+ | `CorrectionError` | Thrown when RS decoding cannot correct errors |
323
+ | `DATA_CELLS` | `[row, col][]` — the 32 data cell positions in read order |
324
+
325
+ > `renderGrid` and `ChessMatrixScanner` require a browser environment (Canvas API / `getUserMedia`). The codec functions (`buildGrid`, `lettersToBytes`, etc.) are pure JavaScript and work in Node.js.
326
+
327
+ **Browser (CDN, no bundler)**
328
+ ```html
329
+ <script type="module">
330
+ import { buildGrid, renderGrid } from
331
+ 'https://cdn.jsdelivr.net/npm/chessmatrix/chessmatrix.js';
332
+ </script>
333
+ ```
334
+
335
+ ---
336
+
337
+ ## 10. License
338
+
339
+ ChessMatrix is an open specification. Implement freely.
@@ -0,0 +1,314 @@
1
+ # ChessMatrix
2
+
3
+ **A 4-color 8×8 2D barcode format.**
4
+
5
+ ChessMatrix is a compact, machine-readable 2D barcode that fits in an 8×8 cell grid — the same footprint as a chessboard — by using four colors (black, red, green, blue) instead of the traditional two, yielding 2 bits of information per cell. It is designed as a minimal, scannable symbol for applications where even the smallest standard formats (DataMatrix 10×10, Micro QR 11×11) are too large.
6
+
7
+ ## Installation
8
+
9
+ **Python**
10
+ ```bash
11
+ pip install chessmatrix
12
+ # render_image() also requires Pillow:
13
+ pip install "chessmatrix[render]"
14
+ ```
15
+
16
+ **JavaScript / Node.js**
17
+ ```bash
18
+ npm install chessmatrix
19
+ ```
20
+
21
+ **Browser (no bundler)**
22
+ ```html
23
+ <script type="module">
24
+ import { buildGrid, renderGrid } from 'https://cdn.jsdelivr.net/npm/chessmatrix/chessmatrix.js';
25
+ </script>
26
+ ```
27
+
28
+ ---
29
+
30
+ ## 1. Motivation
31
+
32
+ The smallest standard 2D barcodes — DataMatrix 10×10 and Micro QR Version 1 (11×11) — cannot physically fit in an 8×8 cell footprint. ChessMatrix fills this gap by encoding 2 bits per cell via color rather than 1 bit per cell via luminance, achieving a useful payload within the 8×8 constraint.
33
+
34
+ **Net payload: 4 bytes (32 bits) per symbol**, protected by Reed-Solomon error correction.
35
+
36
+ ---
37
+
38
+ ## 2. Symbol Structure
39
+
40
+ The symbol is an 8×8 grid of colored cells, each one of:
41
+
42
+ | Symbol | Value | RGB (recommended) |
43
+ |--------|-------|-------------------|
44
+ | BLACK | `0b00` | `#0A0A0A` |
45
+ | RED | `0b01` | `#DC2828` |
46
+ | GREEN | `0b10` | `#28B428` |
47
+ | BLUE | `0b11` | `#2828DC` |
48
+
49
+ ### 2.1 Grid Zones
50
+
51
+ ```
52
+ Col: 0 1 2 3 4 5 6 7
53
+ ┌────┬────┬────┬────┬────┬────┬────┬────┐
54
+ R0 │ T │ t │ T │ t │ T │ t │ T │ t │ ← Top timing (alternating K/W)
55
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
56
+ R1 │ F │[K] │ D │ D │ D │ D │[R] │ tm │ ← [K] = BLACK anchor
57
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
58
+ R2 │ F │ D │ D │ D │ D │ D │ D │ tm │
59
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
60
+ R3 │ F │ D │ D │ D │ D │ D │ D │ tm │
61
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
62
+ R4 │ F │ D │ D │ D │ D │ D │ D │ tm │
63
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
64
+ R5 │ F │ D │ D │ D │ D │ D │ D │ tm │
65
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
66
+ R6 │ F │[G] │ D │ D │ D │ D │[B] │ tm │ ← [G] = GREEN anchor, [B] = BLUE anchor
67
+ ├────┼────┼────┼────┼────┼────┼────┼────┤
68
+ R7 │ F │ F │ F │ F │ F │ F │ F │ F │ ← Bottom finder (all BLACK)
69
+ └────┴────┴────┴────┴────┴────┴────┴────┘
70
+
71
+ F = Finder bar (BLACK)
72
+ T/t = Timing cell (T=BLACK, t=WHITE, alternating)
73
+ tm = Right timing column (alternating K/W, row0=WHITE, row1=BLACK, ...)
74
+ [K][R][G][B] = Color calibration anchors
75
+ D = Data cell (2-bit color-encoded)
76
+ ```
77
+
78
+ ### 2.2 Border Cells (28 cells)
79
+
80
+ **Left finder bar** — column 0, rows 0–7: all **BLACK**
81
+
82
+ **Bottom finder bar** — row 7, cols 0–7: all **BLACK**
83
+
84
+ **Top timing strip** — row 0, cols 0–7:
85
+ - Even columns (0, 2, 4, 6): **BLACK**
86
+ - Odd columns (1, 3, 5, 7): **WHITE**
87
+
88
+ **Right timing strip** — col 7, rows 0–7:
89
+ - Even rows (0, 2, 4, 6): **WHITE**
90
+ - Odd rows (1, 3, 5, 7): **BLACK**
91
+
92
+ The timing strips continue to alternate even at their shared corners: (0,7) is WHITE (even column dominates in top timing; right timing confirms even row → WHITE).
93
+
94
+ ### 2.3 Color Calibration Anchors (4 cells)
95
+
96
+ Located at the four corners of the 6×6 interior:
97
+
98
+ | Position | Color | Purpose |
99
+ |----------|--------|----------------------------------|
100
+ | (row=1, col=1) | **BLACK** | Dark reference |
101
+ | (row=1, col=6) | **RED** | Red channel reference |
102
+ | (row=6, col=1) | **GREEN** | Green channel reference |
103
+ | (row=6, col=6) | **BLUE** | Blue channel reference |
104
+
105
+ A decoder samples these four cells first to calibrate its color discrimination thresholds before decoding any data cells. This compensates for lighting color temperature, ink variation, and camera white-balance errors.
106
+
107
+ ### 2.4 Data Cells (32 cells)
108
+
109
+ The remaining interior cells, read in row-major order (top to bottom, left to right), skipping the four anchors:
110
+
111
+ ```
112
+ Row 1: (1,2) (1,3) (1,4) (1,5) — 4 cells
113
+ Row 2: (2,1) (2,2) (2,3) (2,4) (2,5) (2,6) — 6 cells
114
+ Row 3: (3,1) (3,2) (3,3) (3,4) (3,5) (3,6) — 6 cells
115
+ Row 4: (4,1) (4,2) (4,3) (4,4) (4,5) (4,6) — 6 cells
116
+ Row 5: (5,1) (5,2) (5,3) (5,4) (5,5) (5,6) — 6 cells
117
+ Row 6: (6,2) (6,3) (6,4) (6,5) — 4 cells
118
+ TOTAL = 32 cells
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 3. Data Encoding
124
+
125
+ ### 3.1 Color-to-Bit Mapping
126
+
127
+ Each cell encodes exactly 2 bits:
128
+
129
+ ```
130
+ BLACK = 0b00 = 0
131
+ RED = 0b01 = 1
132
+ GREEN = 0b10 = 2
133
+ BLUE = 0b11 = 3
134
+ ```
135
+
136
+ ### 3.2 Bit Packing
137
+
138
+ 32 cells × 2 bits = **64 bits = 8 bytes** raw capacity.
139
+
140
+ Cells are packed MSB-first, 4 cells per byte:
141
+
142
+ ```
143
+ Byte 0: cells[0] bits[7:6] | cells[1] bits[5:4] | cells[2] bits[3:2] | cells[3] bits[1:0]
144
+ Byte 1: cells[4] bits[7:6] | cells[5] bits[5:4] | cells[6] bits[3:2] | cells[7] bits[1:0]
145
+ ...
146
+ Byte 7: cells[28]...cells[31]
147
+ ```
148
+
149
+ To decode byte `b` from cells `4b`, `4b+1`, `4b+2`, `4b+3`:
150
+ ```
151
+ byte_val = (color[4b] << 6) | (color[4b+1] << 4) | (color[4b+2] << 2) | color[4b+3]
152
+ ```
153
+
154
+ ---
155
+
156
+ ## 4. Reed-Solomon Error Correction
157
+
158
+ ChessMatrix uses a **RS(8, 4)** code over GF(2⁸):
159
+
160
+ | Parameter | Value |
161
+ |-----------|-------|
162
+ | Total codeword length | 8 symbols (bytes) |
163
+ | Data symbols | 4 bytes |
164
+ | Parity symbols | 4 bytes |
165
+ | Maximum correctable errors | **2 symbol errors** |
166
+ | Maximum detectable errors | 4 symbol errors |
167
+ | Primitive polynomial | x⁸ + x⁴ + x³ + x² + 1 (`0x11D`) |
168
+ | Generator roots | α⁰, α¹, α², α³ (where α = 2) |
169
+
170
+ The 4 data bytes occupy the **first 4 bytes** of the codeword; the 4 parity bytes occupy bytes 5–8. The codeword is stored across the 32 data cells in byte order (data bytes first, then parity bytes).
171
+
172
+ ### 4.1 Error Budget Rationale
173
+
174
+ Color discrimination under real-world conditions is less reliable than luminance discrimination. A dirty scan, off-angle lighting, or color-shifted camera can misread an entire cell. Allocating 50% of capacity to error correction (vs. ~25% in standard DataMatrix 10×10) reflects this elevated error rate. RS(8,4) can correct any 2 completely wrong cells, which covers most single-color-channel failures.
175
+
176
+ ---
177
+
178
+ ## 5. Encoding Algorithm
179
+
180
+ 1. **Validate input**: data must be exactly 4 bytes.
181
+ 2. **RS encode**: compute 4 parity bytes via polynomial long division over GF(2⁸), producing an 8-byte codeword `[d0, d1, d2, d3, p0, p1, p2, p3]`.
182
+ 3. **Pack into cells**: convert the 8 bytes to 32 dibits (2-bit values), one per data cell.
183
+ 4. **Build grid**: initialize the 8×8 grid with the border pattern (finder + timing) and calibration anchors, then write the 32 data cells.
184
+
185
+ ---
186
+
187
+ ## 6. Decoding Algorithm
188
+
189
+ 1. **Locate symbol**: find the L-shaped finder corner (bottom-left) and orientation markers.
190
+ 2. **Rectify geometry**: use the finder bars and timing strips to establish the module grid (perspective correction, skew correction).
191
+ 3. **Calibrate colors**: sample the four anchor cells at (1,1), (1,6), (6,1), (6,6) to build color classification thresholds for this scan.
192
+ 4. **Classify cells**: for each of the 32 data cells, classify the sampled color as BLACK/RED/GREEN/BLUE using the calibrated thresholds.
193
+ 5. **Unpack bytes**: convert the 32 dibits back to 8 bytes.
194
+ 6. **RS decode**: compute syndromes. If nonzero, apply Berlekamp-Massey + Chien search + Forney algorithm to locate and correct up to 2 symbol errors.
195
+ 7. **Return payload**: the first 4 bytes of the corrected codeword are the user data.
196
+
197
+ ### 6.1 Color Calibration Detail
198
+
199
+ The decoder builds a calibration matrix from the four anchors:
200
+
201
+ ```python
202
+ reference = {
203
+ BLACK: sample(1, 1), # known-black pixel
204
+ RED: sample(1, 6), # known-red pixel
205
+ GREEN: sample(6, 1), # known-green pixel
206
+ BLUE: sample(6, 6), # known-blue pixel
207
+ }
208
+ ```
209
+
210
+ Each unknown cell is classified to the nearest anchor in RGB color space (Euclidean distance, or a simplified channel-dominance heuristic). This makes the format robust to uniform color shifts (e.g., tungsten lighting making everything yellowish).
211
+
212
+ ---
213
+
214
+ ## 7. Constraints and Limitations
215
+
216
+ - **Payload**: 4 bytes (32 bits). Sufficient for a serial number, short ID, timestamp, or small integer.
217
+ - **Color printing required**: cannot be produced on a monochrome printer.
218
+ - **Scanner requirements**: color camera or color sensor; standard laser barcode scanners cannot read ChessMatrix.
219
+ - **Print resolution**: each cell must be large enough to be unambiguously colored (~0.5mm minimum per cell in practice, so minimum symbol size ~4mm × 4mm).
220
+ - **No rotation invariance built in**: the finder pattern establishes a single canonical orientation. A 180° rotated symbol will fail to decode. (A future extension could add a rotation indicator cell.)
221
+
222
+ ---
223
+
224
+ ## 8. Python Implementation
225
+
226
+ Install: `pip install chessmatrix` (add `[render]` extra for PNG output)
227
+
228
+ ```python
229
+ from chessmatrix import encode, decode, render_image, render_ascii
230
+
231
+ data = b'\xDE\xAD\xBE\xEF'
232
+ grid = encode(data) # List[List[int]], 8×8, values 0-3 or -1
233
+ render_ascii(grid) # colored output in terminal
234
+ render_image(grid, 'code.png', cell_size=60) # requires Pillow
235
+
236
+ recovered = decode(grid)
237
+ assert recovered == data
238
+ ```
239
+
240
+ **API**
241
+
242
+ | Function | Description |
243
+ |---|---|
244
+ | `encode(data: bytes) -> Grid` | Encode 4 bytes → 8×8 color grid |
245
+ | `decode(grid: Grid) -> bytes` | Decode grid → 4 bytes (RS error correction applied) |
246
+ | `render_image(grid, path, cell_size=40)` | Save PNG (requires Pillow) |
247
+ | `render_ascii(grid)` | Print colored ASCII art to terminal |
248
+ | `CorrectionError` | Raised when RS decoding cannot correct errors |
249
+
250
+ `Grid` is `List[List[int]]` — an 8×8 nested list of color values (0–3, or -1 for white structural cells).
251
+
252
+ ---
253
+
254
+ ## 9. JavaScript Implementation
255
+
256
+ Install: `npm install chessmatrix`
257
+
258
+ ```javascript
259
+ import { lettersToBytes, buildGrid, renderGrid } from 'chessmatrix';
260
+
261
+ // Encode 4 bytes onto a canvas
262
+ const bytes = new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]);
263
+ const grid = buildGrid(bytes);
264
+ renderGrid(grid, document.getElementById('canvas'), 40); // 40px per cell
265
+ ```
266
+
267
+ ```javascript
268
+ import { bytesToLetters, lettersToBytes, buildGrid } from 'chessmatrix';
269
+
270
+ // 6-letter convenience codec (5-bit packing, A=0…Z=25)
271
+ const bytes = lettersToBytes('HELLO!'.replace(/[^A-Z]/gi, 'A')); // Uint8Array(4)
272
+ const letters = bytesToLetters(bytes); // → 'HELLOA'
273
+ ```
274
+
275
+ ```javascript
276
+ import { ChessMatrixScanner } from 'chessmatrix';
277
+
278
+ // Camera scanner (browser only — requires getUserMedia)
279
+ const scanner = new ChessMatrixScanner({
280
+ videoEl: document.getElementById('video'),
281
+ canvasEl: document.getElementById('canvas'),
282
+ onDecode: (letters) => console.log('Scanned:', letters),
283
+ onStatus: (msg, state) => console.log(state, msg),
284
+ });
285
+ await scanner.start();
286
+ ```
287
+
288
+ **API**
289
+
290
+ | Export | Description |
291
+ |---|---|
292
+ | `buildGrid(bytes: Uint8Array) → Int8Array` | Encode 4 bytes → 64-cell flat grid (row-major) |
293
+ | `renderGrid(grid, canvas, cellSize?)` | Draw grid on an HTMLCanvasElement |
294
+ | `lettersToBytes(str) → Uint8Array` | 6-letter string → 4 bytes (5-bit packing) |
295
+ | `bytesToLetters(bytes) → string` | 4 bytes → 6-letter string |
296
+ | `ChessMatrixScanner` | Semi-guided camera scanner class (browser only) |
297
+ | `CorrectionError` | Thrown when RS decoding cannot correct errors |
298
+ | `DATA_CELLS` | `[row, col][]` — the 32 data cell positions in read order |
299
+
300
+ > `renderGrid` and `ChessMatrixScanner` require a browser environment (Canvas API / `getUserMedia`). The codec functions (`buildGrid`, `lettersToBytes`, etc.) are pure JavaScript and work in Node.js.
301
+
302
+ **Browser (CDN, no bundler)**
303
+ ```html
304
+ <script type="module">
305
+ import { buildGrid, renderGrid } from
306
+ 'https://cdn.jsdelivr.net/npm/chessmatrix/chessmatrix.js';
307
+ </script>
308
+ ```
309
+
310
+ ---
311
+
312
+ ## 10. License
313
+
314
+ ChessMatrix is an open specification. Implement freely.