main
Rithesh03 Update 4: improved README (V1 overview, images, LED order), Version 2 proposal files b874429 13d ago
"""Version 2 LED order and WLED maps -> v2/firmware/ (Version 1's firmware/ files are only read, never written).

Version 2's chain is Version 1's chain reversed: the data now enters at the right end, next to the electronics.
  V2 LED n  sits where  V1 LED (108 - n)  sits     (WLED index: v2 = 106 - v1)
The letter pattern and the visible positions are the same; only the physical numbering (data order) changed.

Writes:
  v2/firmware/led-order.csv   LED1..LED107 with letter, row, column, V2 position (mm) and the V1 LED at that spot
  v2/firmware/ledmap.json     2D map, 41 x 7 grid ("RITHESH V2 41x7")
  v2/firmware/ledmap1.json    1D map, left to right, column by column ("RITHESH V2 left-to-right")
The WLED file names must stay ledmap.json / ledmap1.json on the board; the v2/ folder keeps them apart from V1's.

Each map is built twice (from the V2 positions, and by converting V1's map) and both must agree.

Usage: python3 make_v2_ledmaps.py
"""
import csv
import json
import os

V2HW = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
V2 = os.path.dirname(V2HW)
ROOT = os.path.dirname(V2)
OUT = os.path.join(V2, "firmware")
V1_FW = os.path.join(ROOT, "firmware")
COLS, ROWS = 41, 7
X0, Y0, PITCH = 9.1, 5.0, 5.0            # must match make_v2_plan.py
V1_X0, V1_PITCH = 10.0, 7.0


def main():
    v1 = list(csv.DictReader(open(os.path.join(V1_FW, "led-order.csv"))))
    leds = []                              # (v2 index, letter, row, col, v1 index)
    for r in v1:
        i1 = int(r["WLED index (0-based)"])
        col = round((float(r["x mm"]) - V1_X0) / V1_PITCH)
        leds.append((106 - i1, r["letter"], int(r["row (0=top)"]), col, i1))
    leds.sort()
    assert [l[0] for l in leds] == list(range(107))

    # 2D map from the V2 positions
    grid = [-1] * (COLS * ROWS)
    for i2, _, row, col, _ in leds:
        grid[row * COLS + col] = i2
    # ... and by converting V1's map; both must match
    m1 = json.load(open(os.path.join(V1_FW, "ledmap.json")))
    conv = [-1 if v < 0 else 106 - v for v in m1["map"]]
    assert conv == grid, "2D map mismatch"
    # 1D map: columns left to right, top to bottom inside a column
    order = [i2 for i2, _, row, col, _ in sorted(leds, key=lambda l: (l[3], l[2]))]
    m1d = json.load(open(os.path.join(V1_FW, "ledmap1.json")))
    assert [106 - v for v in m1d["map"]] == order, "1D map mismatch"
    assert sorted(order) == list(range(107))

    os.makedirs(OUT, exist_ok=True)
    with open(os.path.join(OUT, "ledmap.json"), "w") as f:
        json.dump({"n": "RITHESH V2 41x7", "width": COLS, "height": ROWS, "map": grid}, f, separators=(",", ":"))
    with open(os.path.join(OUT, "ledmap1.json"), "w") as f:
        json.dump({"n": "RITHESH V2 left-to-right", "map": order}, f, separators=(",", ":"))
    with open(os.path.join(OUT, "led-order.csv"), "w", newline="") as f:
        w = csv.writer(f)
        w.writerow(["WLED index (0-based)", "LED", "letter", "row (0=top)", "column (0=left)", "x mm", "y mm",
                    "V1 LED at the same spot"])
        for i2, letter, row, col, i1 in leds:
            w.writerow([i2, f"LED{i2 + 1}", letter, row, col, f"{X0 + col * PITCH:.1f}", f"{Y0 + row * PITCH:.1f}",
                        f"LED{i1 + 1}"])
    print("wrote", OUT, "(ledmap.json, ledmap1.json, led-order.csv); both map methods agree")
    first, last = leds[0], leds[-1]
    print(f"V2 LED1 = {first[1]} row {first[2]} col {first[3]} (V1 LED{first[4] + 1}); "
          f"V2 LED107 = {last[1]} row {last[2]} col {last[3]} (V1 LED{last[4] + 1})")
    starts = {}
    for i2, letter, row, col, _ in leds:
        key = (letter, col // 6)
        starts.setdefault(key, [i2 + 1, i2 + 1])[1] = i2 + 1
    print("LEDs per letter (V2 numbers):", ", ".join(f"{k[0]}{'' if k[1] != 6 else '(2nd)'} {a}-{b}"
                                                    for k, (a, b) in sorted(starts.items(), key=lambda kv: kv[1][0])))


if __name__ == "__main__":
    main()