# 고밀도 멀티 루핑 컬러 QR 데이터 전송 시스템 — 설계 노트

## 한 줄 요약
화면(송신부)에 QR을 연속으로 띄우고, 카메라(수신부)로 촬영해서 파일을 전송한다.
일반 QR보다 훨씬 빠르게 보내기 위해 3가지 축으로 밀도를 올린다:
1. **멀티(그리드)** — 한 프레임에 QR 여러 개
2. **컬러(RGB 채널)** — 흑백 대신 R/G/B 3개 레이어에 각각 다른 비트스트림
3. **루핑(분수 부호, Fountain Code)** — 프레임 순서/드롭에 안전하게 무한 반복 재생

## 왜 이렇게 하나
- 카메라-화면 광학 링크는 대역폭이 낮고 프레임 드롭이 잦다 (모션 블러, 초점, 노출 싱크 등).
- 순서를 지켜야 하는 단순 "QR 애니메이션"은 한 프레임이라도 놓치면 처음부터 다시 찍어야 한다.
- Fountain Code(LT Code류)를 쓰면 수신부는 "어떤 심볼을 받았는가"가 아니라 "충분한 개수를 받았는가"만 신경 쓰면 되므로, 송신부는 그냥 계속 루프 재생만 하면 되고 수신부는 원하는 타이밍에 시작/재시작해도 된다.

## 데이터 파이프라인 (송신부)

```
파일(bytes)
  → 블록 분할 (고정 크기, 예: 180B/블록)
  → Fountain Code 인코딩 (LT Code: XOR 조합 심볼을 무한 생성)
  → 심볼 헤더 부착 (session id, degree, 구성 블록 인덱스, CRC32)
  → 심볼 payload를 QR 데이터로 직렬화 (Base45/Base64 등 QR 친화 인코딩)
  → N개 심볼을 그리드(1x1~4x4)로 배치
  → 그리드의 각 칸을 R/G/B 3계층으로 분리해 컬러 합성
  → <canvas>에 렌더 → requestAnimationFrame으로 지정 FPS 재생, 무한 루프
```

### 헤더 프레임 (부트스트랩)
전송 시작 전, **순수 흑백 QR 1장**을 몇 초간 고정 표시해서 메타데이터를 전달한다:
- 파일명, 전체 크기, 전체 CRC32
- 블록 크기, 총 블록 수 K
- 그리드 크기, 컬러 모드 on/off, FPS
- Fountain code 파라미터(도 분포 시드 등)

이 헤더는 컬러/루핑 없이 단순 QR로 보내야 수신부가 아무 상태 없이도 즉시 파싱 가능.

## 컬러 채널 멀티플렉싱
- 진짜 JAB Code 수준의 다색(8色) 모듈은 구현 난이도가 높으니, 1차는 **R/G/B 3장의 독립 QR을 겹쳐서 합성**하는 방식으로 시작.
  - 예: `canvas` 위에 QR_R을 빨강 채널에, QR_G를 초록 채널에, QR_B를 파랑 채널에 그림 (각 QR은 모듈값 0/255를 해당 채널에만 씀).
  - 즉 최종 픽셀 = `rgb(qrR_bit*255, qrG_bit*255, qrB_bit*255)`.
- 수신부는 카메라 프레임을 캡처 → 채널 3장으로 분리(R,G,B) → 각각을 이진화(threshold) → 독립된 QR 이미지처럼 jsQR로 디코딩.
- 리스크: 카메라 색 보정/화이트밸런스/JPEG 압축이 채널을 섞을 수 있음 → 색상 대비를 극단적으로 주고(순수 R/G/B), 캘리브레이션 단계(색상 체커 패턴 프레임)를 앞에 넣어서 임계값 자동 보정.

## 그리드 멀티플렉싱
- 화면을 2x2, 3x3, 4x4 셀로 나누고 각 셀에 독립 QR을 렌더.
- 셀 개수 × 컬러 3채널 = 프레임당 최대 `grid² × 3`개의 독립 QR 스트림.
- 카메라 해상도/거리에 따라 셀당 QR 모듈이 충분히 커야 인식되므로, UI에서 그리드 크기·FPS를 실시간 조절해 카메라 성능에 맞게 튜닝.

## 루핑 & Fountain Code
- LT Code(Luby Transform) 방식:
  - 블록 K개 중 무작위 degree `d`(Robust Soliton 분포)개를 골라 XOR한 것이 심볼.
  - 수신부는 심볼을 계속 모으다가, degree-1 심볼(원본 블록 그대로)을 찾으면 다른 심볼들에서 XOR로 소거(peeling decoder) → 연쇄적으로 전체 블록 복원.
  - 필요한 심볼 수는 K보다 살짝 많은 정도(overhead ~5~10%)면 충분 → 전체를 다 못 찍어도 복원 가능.
- 송신부는 심볼을 실시간 생성하며 무한 루프 재생 (같은 심볼 재전송 걱정 없음 — 어차피 랜덤 조합이라 매번 다른 심볼일 확률이 높음. 필요하면 시드 고정 후 순환도 가능).
- 수신부는 "복원 완료" 시점에 자동으로 멈추고 다운로드 트리거.

## 수신부 파이프라인
```
getUserMedia (카메라) → <video> → 매 tick마다 <canvas>에 캡처
  → 그리드 셀 분할
  → 각 셀에서 R/G/B 채널 분리 + 이진화
  → jsQR로 각 채널 디코딩 시도
  → 성공한 심볼들을 LT 디코더에 투입
  → peeling 진행 상황 UI 표시 (진행률 %, 처리량 bps, 복원된 블록 수)
  → 전체 복원되면 CRC 검증 → Blob 생성 → 다운로드 링크 표시
```

## 로컬 테스트 모드 (카메라 없이)
실제 카메라 없이도 개발 중 검증할 수 있도록:
- 수신부에 "가상 루프백" 모드 추가: `BroadcastChannel`이나 `postMessage`로 송신부 canvas의 ImageData를 직접 받아서, 카메라 캡처 단계만 건너뛰고 디코딩 파이프라인(채널 분리~복원)을 그대로 검증.
- 실전 테스트는 노트북 화면(송신) + 스마트폰 카메라(수신, 다른 브라우저)로 진행.

## 기술 스택
- 순수 HTML/JS/CSS, 빌드 없이 브라우저에서 바로 열림.
- 라이브러리 (CDN):
  - QR 생성: `qrcode` (또는 자체 경량 QR 인코더 — 컬러 채널별로 별도 QR을 만들어야 하므로 raw matrix를 뽑을 수 있는 라이브러리가 필요)
  - QR 디코딩: `jsQR`
  - Fountain code: 직접 구현 (LT code는 로직이 단순해서 자체 구현이 오히려 커스터마이징에 유리)
- 파일 구성 (예정):
  ```
  01-sender/index.html   (UI + 파일 선택 + 파라미터 조절)
  01-sender/encoder.js    (블록 분할, LT 인코더, QR 직렬화)
  01-sender/renderer.js   (그리드+컬러 합성, canvas 루프)
  02-reciever/index.html
  02-reciever/capture.js  (카메라 캡처, 채널 분리, 이진화)
  02-reciever/decoder.js  (jsQR 연동, LT 디코더/peeling)
  02-reciever/ui.js       (진행률, 다운로드)
  ```

## 단계별 진행 제안
1. **v0 (흑백, 1x1, 순서 보장)**: 가장 단순한 버전 — 순차 QR 애니메이션, fountain code 없이 인덱스 기반 재전송. 파이프라인 배관 확인용.
2. **v1 (그리드 추가)**: 1x1 → NxN 멀티 QR. 카메라 해상도 한계 테스트.
3. **v2 (컬러 채널 추가)**: R/G/B 3중화. 캘리브레이션 프레임 도입.
4. **v3 (Fountain Code 도입)**: 순서 무관 수신, 루프 재생, 진행률 UI.
5. **v4 (튜닝/자동화)**: 카메라 상태 보고 FPS/그리드 자동 조절, 에러 통계.

## 알려진 리스크
- 저가 카메라의 색 재현/자동노출이 채널 분리를 방해할 수 있음 → v2에서 캘리브레이션 프레임 필수.
- 모바일 브라우저의 `getUserMedia` 프레임레이트가 canvas 렌더 FPS보다 낮을 수 있음 → 실제 처리량은 카메라 캡처 FPS가 병목.
- jsQR은 프레임당 디코딩 비용이 있어 그리드 셀 수가 늘면 CPU 부담 증가 → Web Worker로 병렬 디코딩 고려.
