Skip to content

Latest commit

Β 

History

27 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

dab-rs

πŸ‡°πŸ‡· ν•œκ΅­ 졜초 Β· 🌐 세계 졜초 β€” 순수 Rust 둜 λ§Œλ“  λ©”λͺ¨λ¦¬ μ•ˆμ „ DAB Mode I OFDM 디코더 / T-DMB μˆ˜μ‹ κΈ°.

dab-rs κ°€ raw Airspy I/Q λ‘œλΆ€ν„° λ””μ½”λ“œν•œ YTN DMB μ˜μƒ

YTNγ€Œλ‰΄μŠ€νŠΉλ³΄γ€λ₯Ό dab-rs κ°€ λ‹¨λ…μœΌλ‘œ λ””μ½”λ“œν•œ ν•œ μž₯λ©΄ β€” Airspy Mini 의 3 MSPS int16 raw I/Q β†’ OFDM 동기/CFO/FFT β†’ FIC β†’ MSC β†’ 16-슬둯 Forney μ‹œκ°„-λ””μΈν„°λ¦¬λΈŒ β†’ EEP λ””νŽ‘ν¬μ²˜/λΉ„ν„°λΉ„ β†’ PRBS β†’ RS(204,188) β†’ MPEG-TS β†’ H.264, λ§ˆμ§€λ§‰ 그림만 ffmpeg 에 λ§‘κ²Όλ‹€. fudge factor 도 μ—†κ³  magic number 도 μ—†λ‹€. λͺ¨λ“  μƒμˆ˜λŠ” eti-stuff λ˜λŠ” DAB/DVB ν‘œμ€€μ—μ„œ μœ λž˜ν•œλ‹€. μ•½κ°„μ˜ λ§€ν¬λ‘œλΈ”λ‘ μ•„ν‹°νŒ©νŠΈλŠ” 이 캑처의 SNR μ—μ„œ t=8 μ •μ • ν•œκ³„λ₯Ό μ΄ˆκ³Όν•œ RS(204,188) 블둝 6.9% 와 μ •ν™•νžˆ μΌμΉ˜ν•œλ‹€.


μ™œ "ν•œκ΅­ 졜초 Β· 세계 졜초"인가

ν•­λͺ© ν˜„ν™©
ν•œκ΅­ 졜초 순수 Rust 둜 μž‘μ„±λœ ν•œκ΅­ν˜• T-DMB μˆ˜μ‹ κΈ°. ν•œκ΅­ 채널(K8B, YTN DMB)μ—μ„œ raw I/Q β†’ MPEG-TS β†’ H.264 κΉŒμ§€ end-to-end 검증 μ™„λ£Œ.
세계 졜초 Rust 둜 μž‘μ„±λœ λ©”λͺ¨λ¦¬ μ•ˆμ „ DAB Mode I OFDM 디코더. WebAssembly 배포λ₯Ό μ •μ‘°μ€€ β€” unsafe 0쀄, C/C++ μ˜μ‘΄μ„± μ—†μŒ.
검증 C++ eti-stuff 와 byte λ‹¨μœ„λ‘œ 동등. fudge factor 0개, magic number 0개. λͺ¨λ“  μƒμˆ˜λŠ” spec λ˜λŠ” eti-stuff 에 좔적 κ°€λŠ₯.
λ‘œλ“œλ§΅ 🌐 WASM 웹버전 β€” λΈŒλΌμš°μ €μ—μ„œ λ™μž‘ν•˜λŠ” 세계 졜초의 μ›Ή DMB μˆ˜μ‹ κΈ°. 별도 μ•±/ν”ŒλŸ¬κ·ΈμΈ 없이 URL 만으둜 라이브 DMB μ‹œμ²­.

λΉ λ₯Έ μ‹œμ—°

60초 라이브 캑처 β†’ MP4:

# 1) Airspy Mini 둜 K8B(YTN DMB) 60초 캑처
airspy_rx -f 183.008 -a 3000000 -t 2 -l 14 -m 15 -v 12 -b 0 \
          -n 180000000 -r /tmp/live.iq

# 2) FIC: 앙상블 + μ„œλΉ„μŠ€ λͺ©λ‘ 확인
dab fic-iq /tmp/live.iq
# β†’ fib_ok=7424/7524 (98.7%), EId=0xE040, label="YTN DMB", 5 services

# 3) MSC: SubCh 1 (mYTN λΉ„λ””μ˜€) β†’ MPEG-TS
dab msc-iq --scid 1 --ts /tmp/live.ts /tmp/live.iq
# β†’ ts_packets=12872, rs_corrected=7240 (98.7%)

# 4) (선택) SL 헀더 strip + SPS prepend β†’ MP4
python3 tools/extract_sl_h264_v2.py /tmp/live.ts --video-pid 0x113 \
        --out /tmp/live.h264
cat sps_pps.bin /tmp/live.h264 > /tmp/live_sps.h264
ffmpeg -r 30 -i /tmp/live_sps.h264 -c copy /tmp/live.mp4

κ²°κ³Ό: 320Γ—240 H.264 Baseline, 1784 frames @ 30 FPS, 60초 μ˜μƒ.


ν˜„μž¬ μƒνƒœ

Week 3e + Week 4 μ™„λ£Œ β€” raw I/Q λΆ€ν„° MP4 κΉŒμ§€ fudge-free end-to-end λ™μž‘.

크레이트 μ—­ν•  μƒνƒœ
dab-ofdm Mode I OFDM 볡쑰기 (핡심 κΈ°μ—¬) βœ… fudge-free 90%+ (sim5, sim4, k8b_v4)
dab-iq 파일 I/Q reader (Cs8/Cs16Le/Cf32Le, JSON sidecar) βœ… done; libairspy 직결 streaming 은 μΆ”ν›„
dab-fic FIB CRC-16, FIG 0/xΒ·1/x β†’ Ensemble βœ… done β€” byte-identical vs eti-stuff
dab-viterbi Rate-1/4 K=7 punctured λΉ„ν„°λΉ„ + EEP/FIC depuncture βœ… done
dab-descramble μ—λ„ˆμ§€ λΆ„μ‚° PRBS (x⁹ + x⁡ + 1) βœ… done
dab-msc MSC sub-channel byte μΆ”μΆœ βœ… done
dab-eti ETI(NI, G.703) frame parser (ETSI EN 300 799) βœ… done
dab-fec T-DMB outer FEC: sync-aligned Forney + RS(204,188) βœ… done
dab-cli dab fic-iq + dab msc-iq CLI λ°”μ΄λ„ˆλ¦¬ βœ… done

검증 κ²°κ³Ό (μ‹€μΈ‘)

캑처 경둜 FIB CRC RS(204,188) MP4 frames
60s live capture cs16le β†’ LinearResampler β†’ 전체 chain 98.7% (7424/7524) 98.7% (7240/7332) 1784 @ 30 FPS
k8b_v4.iq (20s) cs16le β†’ LinearResampler 87.4% (2193/2508) 92.8% (3383/3646) 475 @ 23.75 FPS
sim5_resampled.cf32 (12s) cf32 bypass 90.4% (1704/1884) 96.8% (1381/1426) β€”

λ‘œλ“œλ§΅

상ν–₯식 ꡬ좕: 각 λ‹¨κ³„λŠ” λ‹€μŒ 단계가 μŒ“μ΄κΈ° 전에 reference 와 κ²€μ¦λœλ‹€. Oracle 은 C++ eti-stuff (byte-identical λͺ©ν‘œ) 와 Python airspy-mini-dmb.

βœ… μ™„λ£Œ

  • Week 1 β€” Outer FEC + ETI plumbing. dab-eti (ETI(NI) frame parser), dab-msc (sub-channel extraction), dab-fec (sync-aligned Forney deinterleaver + RS(204,188)). Python reference 와 byte-identical.
  • Week 2 β€” Inner FEC. dab-viterbi (rate-1/4 K=7 λΉ„ν„°λΉ„ + EEP depuncturing), dab-descramble (energy-dispersal PRBS). eti-stuff verbatim ν¬νŒ….
  • Week 3a-d β€” dab-iq + OFDM stages 1-7. 3MSPS β†’ 2.048 MSPS resampler, null detect, CP autocorrelation sync, NCO + integer CFO, FFT, differential reference, Ο€/4-DQPSK demap.
  • Week 3e β€” Fudge-free FIC chain. 25개 슬라이슀 의 진단 β†’ integer-CFO λ₯Ό rotate_spectrum λŒ€μ‹  μ‹œκ°„-μ˜μ—­ NCO 둜 톡합 (eti-stuff 와 μ •ν™•νžˆ λ™μΌν•œ ꡬ쑰). λͺ¨λ“  magic number 제거. K8B μ—μ„œ FIB CRC 87-90%, EId 0xE040, 5 services 식별. eti-stuff linear-interp resampler 이식 (+2.5 dB SNR).
  • Week 4 β€” MSC sub-channel decode. 75 data symbol demap, 16-슬둯 Forney μ‹œκ°„ 디인터리버 (eti-generator.cpp:207 verbatim), EepProtection + KoreanTDmbOuterFec wiring. K8B SubCh 1 β†’ MPEG-TS β†’ H.264 β†’ MP4 (320Γ—240, 1784 frames, 30 FPS). 라이브 60초 캑처 검증 μ™„λ£Œ.

πŸ”¨ μ§„ν–‰ μ˜ˆμ •

  • Week 5 β€” libairspy FFI + streaming. 파일 I/O λŒ€μ‹  Airspy Mini 직결. Manual gain L14/M15/V12 default (μ‹€μΈ‘ -G 0 λŒ€λΉ„ 20Γ— RS κ°œμ„ ). 영ꡬ ring buffer 기반 streaming pipeline (ν˜„μž¬λŠ” batch).
  • Week 6 β€” Performance margin. SNR threshold νŠœλ‹, lock μ‹œκ°„ μ΅œμ ν™”, criterion 벀치마크 (throughput, latency, memory).

🌐 μ°¨κΈ° 핡심: WebAssembly μ›Ή DMB

  • Week 7 β€” WASM 웹버전 (β˜… 세계 졜초). wasm-pack λΉŒλ“œ + λΈŒλΌμš°μ € SDR μ—°κ²° (WebUSB 둜 Airspy Mini 직결, λ˜λŠ” 미리 λ…Ήν™”λœ .iq 파일 μ—…λ‘œλ“œ).
    • 라이브 데λͺ¨ νŽ˜μ΄μ§€: μ‚¬μš©μžκ°€ URL 만 μ—΄λ©΄ μ•±/ν”ŒλŸ¬κ·ΈμΈ 없이 λΈŒλΌμš°μ €μ—μ„œ DMB μ‹œμ²­ κ°€λŠ₯
    • λͺ¨λ°”일 PWA: μ•ˆλ“œλ‘œμ΄λ“œ/iOS Chrome μ—μ„œ WebUSB 지원 μ‹œ SDR dongle μ—°κ²° κ°€λŠ₯ (ν•œκ΅­ K-block 6 채널 μŠ€μΊ” β†’ 채널 선택 β†’ 라이브 μ˜μƒ)
    • ν•™μˆ /ꡐ윑 자료 ν™œμš©: spec.html β†’ μ‹€μ‹œκ°„ OFDM constellation/FFT bin μ‹œκ°ν™” (signal-processing κ°•μ˜μš©)
  • Week 8 β€” λ…Όλ¬Έ. SoftwareX / JOSS / IEEE BMSB 타깃 β€” "dab-rs: A Memory-Safe Software-Defined DAB Mode I Demodulator in Rust with WebAssembly Deployment."

DAB Mode I νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° κ°’
λ‚΄λΆ€ sample rate 2.048 MSPS
Useful symbol 2048 samples (1 ms)
Guard interval 504 samples
Sub-carriers 1536 (βˆ’768..+768, DC null)
Modulation Ο€/4-DQPSK (differential)
Inner FEC rate-1/4 conv, K=7, polys (0o133, 0o171, 0o145, 0o133)
Outer FEC (T-DMB) RS(204,188) DVB params + Forney TI (N=12, M=17)

발견된 함정 (Discovered subtleties)

ν¬νŒ… κ³Όμ •μ—μ„œ 마주친 λ―Έμ„Έν•œ κ΅¬ν˜„ λ””ν…ŒμΌλ“€. λ‹€μŒ κΈ°μ—¬μž(λ˜λŠ” λ‹€μŒ λ…Όλ¬Έ reviewer)κ°€ 미리 μ•Œμ•„λ‘¬μ•Ό ν•  사항듀.

1. Integer-CFO λŠ” μ‹œκ°„μ˜μ—­ NCO 에 ν¬ν•¨μ‹œμΌœμ•Ό ν•œλ‹€ (Week 3e slice 25)

eti-stuff λŠ” coarseCorrector + fineCorrector λ₯Ό ν•˜λ‚˜μ˜ μ‹œκ°„μ˜μ—­ NCO 둜 ν•¨κ»˜ mix ν•œλ‹€ (getSamples(coarseCorrector + fineCorrector)). μ •μˆ˜-CFO Ξ΄ κ°€ λ§€ μ‹¬λ³Όλ§ˆλ‹€ NCO μœ„μƒ 진폭 2π·δ·T_s/T_u β‰ˆ 1.55 rad/sym (Ξ΄=+1) λ₯Ό λΆ€μ—¬ν•˜λŠ”λ°, dab-rs 의 초기 κ΅¬ν˜„μ€ μ •μˆ˜-CFO λ₯Ό rotate_spectrum 으둜 post-FFT μ²˜λ¦¬ν•΄μ„œ κ·Έ per-symbol μœ„μƒμ΄ λΉ μ‘Œλ‹€. μˆ˜ν•™μ μœΌλ‘œ κ°™μ•„ λ³΄μ΄μ§€λ§Œ, Ο€/4-DQPSK 차동 볡쑰 λ‹¨κ³„μ—μ„œ constellation 이 1.55 rad νšŒμ „λœ μ±„λ‘œ 도착 β†’ λΉ„νŠΈ λ§€ν•‘ wrong.

fix: 데이터 심볼은 cfo_hz + δ·1000 을 NCO 에 톡합해 mix, rotate μƒλž΅. PRS 만 rotate (Ξ΄ λ₯Ό μΈ‘μ •ν•˜λ €λ©΄ PRS FFT κ°€ λ¨Όμ € ν•„μš”ν•˜κΈ° λ•Œλ¬Έ). κ²°κ³Ό: sim5 FIB CRC 4% β†’ 90.4%, fudge-free.

2. Linear-interpolation resampler κ°€ polyphase FIR 보닀 +2.5 dB (Week 3e slice 17)

dab-ofdm 의 초기 resampler λŠ” polyphase FIR (Blackman-sinc) μ΄μ—ˆλŠ”λ° ν•œκ³„ SNR(11 dB K8B) μ—μ„œ decoding 절벽 μ•„λž˜. eti-stuff 의 airspy-handler.cpp:157-162 linear-interp resampler λ₯Ό verbatim 이식 (LinearResampler struct). 같은 μž…λ ₯μ—μ„œ band_ratio +2.5 dB, k8b_v4.iq cs16le FIB 0/2496 β†’ 2193/2508.

3. DAB MSC μ‹œκ°„ λ””μΈν„°λ¦¬λ²„λŠ” λΉ„νŠΈ-μœ„μΉ˜ 기반 16-슬둯 Forney (Week 4 slice 1)

eti-stuff eti-generator.cpp:207:

const int16_t interleaveMap[] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};

λΉ„νŠΈ μœ„μΉ˜ i & 0xF λ§ˆλ‹€ 16-슬둯 ring buffer 의 μ„œλ‘œ λ‹€λ₯Έ μŠ¬λ‘―μ—μ„œ 읽어와 λ””μΈν„°λ¦¬λΈŒν•œλ‹€. CIF 1κ°œλ‹Ή 55296 λΉ„νŠΈ, 18개 OFDM 심볼 β†’ 1 CIF, 1 DAB frame = 4 CIFs. 15-CIF warmup ν›„ 좜λ ₯이 valid.

4. Airspy AGC (-G 0) λŠ” ν•œκ³„ SNR μ—μ„œ sub-optimal

같은 indoor K8B μ•ˆν…Œλ‚˜ μ…‹μ—…μ—μ„œ 17-config gain sweep κ²°κ³Ό: hardware AGC (-G 0) λŠ” small-signal regime μ—μ„œ under-amplify ν•œλ‹€ (firmware κ°€ mixer λŠ” working level 둜 μž‘μ§€λ§Œ VGA λŠ” default 10 으둜 κ³ μ • β†’ ~20 dB headroom λ―Έμ‚¬μš©). Manual LNA 14 / Mixer 15 / VGA 12 κ°€ 25μ΄ˆλ‹Ή 4619-4852 RS corrected blocks λ₯Ό μ‚°μΆœ, -G 0 의 241 λŒ€λΉ„ 20Γ— κ°œμ„ . fibquality 100 포화. Live air-receive μ‹œ manual L14/M15/V12 κΈ°λ³Έ ꢌμž₯.

5. SFN multipath κ°€ null symbol 을 μ–•κ²Œ λ§Œλ“ λ‹€ (Week 3a)

DAB null symbol 은 96ms frame λ§ˆλ‹€ ~0 envelope 이어야 ν•˜λŠ”λ°, K8B λŠ” single-frequency network ν™˜κ²½μ—μ„œ 5개 솑신탑(남산/κ΄€μ•…μ‚°/μš©λ¬Έμ‚°/광ꡐ산/μš΄μ •μ‚°) 이 μ‹œκ°„μ°¨λ‘œ 도착해 null 간격이 λ‹€λ₯Έ μ†‘μ‹ νƒ‘μ˜ μ‹ ν˜Έλ‘œ μ±„μ›Œμ§„λ‹€. min/Β΅ β‰ˆ 0.72-0.78 (κ΅κ³Όμ„œ κ°’ 0.1-0.3 와 닀름). κ³ μ • threshold λŠ” λͺ¨λ“  null 을 놓침. NullDetector λŠ” adaptive threshold p1 + 0.30Β·(p99 βˆ’ p1) λ₯Ό smoothed envelope 에 μ μš©ν•΄ ~7 dB SNR μ—μ„œλ„ 96ms cadence 회볡.

6. Viterbi soft-bit polarity convention (Week 2)

eti-stuff 의 viterbi-handler.cpp 주석은 +255 β†’ bit 0 이라 μ ν˜€μžˆμ§€λ§Œ trellis metric update λ₯Ό μΆ”μ ν•˜λ©΄ +255 β†’ bit 1 κ°€ μ‹€μ œ λ™μž‘. dab-rs λŠ” verbatim ν¬νŒ…μ΄λΌ μ‹€μ œ λ™μž‘μ„ κ·ΈλŒ€λ‘œ 따라가며, λ”°λΌμ„œ OFDM 디맀퍼도 같은 + β‡’ 1 convention 으둜 soft bit λ₯Ό emit ν•΄μ•Ό ν•œλ‹€.

7. Airspy 12-bit packed sample format (Week 5 μ˜ˆμ •)

Airspy Mini 의 native USB 좜λ ₯은 12-bit ADC μƒ˜ν”Œμ„ 16-bit μŒμ— packed ν•œλ‹€ (zero-extended Complex<i16> κ°€ μ•„λ‹˜). libairspy κ°€ transparent ν•˜κ²Œ unpack ν•΄μ„œ Complex<f32> λ˜λŠ” Complex<i16> 둜 λ…ΈμΆœν•œλ‹€. λ”°λΌμ„œ dab-iq-airspy (Week 5) λŠ” pure-Rust libusb λŒ€μ‹  bindgen FFI 둜 libairspy λ₯Ό ν˜ΈμΆœν•œλ‹€. 직접 libusb κ²½λ‘œλŠ” unpacking + 게인 stage μ œμ–΄ (LNA/Mixer/VGA)

  • bias-tee ν† κΈ€ λͺ¨λ‘ μƒˆλ‘œ derive ν•΄μ•Ό ν•˜λ―€λ‘œ 뢀담이 크닀.

λΉŒλ“œ & ν…ŒμŠ€νŠΈ

Rust stable (1.83+). macOS κΈ°μ€€:

brew install fftw libsndfile libsamplerate pkg-config libusb airspy
cargo build --workspace --release
cargo test  --workspace --release

CLI μ‚¬μš© 예

# 라이브 캑처
airspy_rx -f 183.008 -a 3000000 -t 2 -l 14 -m 15 -v 12 -n 180000000 -r live.iq

# FIC (앙상블 + μ„œλΉ„μŠ€ λͺ©λ‘)
dab fic-iq live.iq

# MSC (νŠΉμ • sub-channel β†’ MPEG-TS)
dab msc-iq --scid 1 --ts mYTN.ts live.iq

# λ‹€λ₯Έ format/rate
dab fic-iq --format cf32le --rate 2048000 pre_resampled.cf32

검증 captures

airspy-mini-dmb repo (Git LFS) 에 reference captures (k8b_v4.iq + k8b_v4.eti + sim5_resampled.cf32 λ“±) 쑴재. 둜컬 경둜 μ§€μ • ν›„ 톡합 ν…ŒμŠ€νŠΈ:

export DAB_RS_K8B_V4_IQ=/path/to/k8b_v4.iq
cargo test -p dab-cli --test k8b_v4_fic_iq -- --include-ignored

κΈ°λŒ€ μˆ˜μΉ˜λŠ” tests/golden/ 에 κΈ°λ‘λ˜μ–΄ μžˆλ‹€.


ν•™μˆ /ꡐ윑 ν™œμš©

dab-rs λŠ” production-quality SDR μ†Œν”„νŠΈμ›¨μ–΄μΈ λ™μ‹œμ— ν•™λΆ€/λŒ€ν•™μ› 톡신·방솑· μ‹ ν˜Έμ²˜λ¦¬ κ°•μ˜μš© ꡐ재둜 섀계됐닀. 각 단계가 isolated crate + unit tests + diagnostic dump 둜 λΆ„λ¦¬λ˜μ–΄, λ‹€μŒ μ‹œμ—°μ΄ λͺ¨λ‘ κ°€λŠ₯ν•˜λ‹€:

  • OFDM 동기 μ‹œκ°ν™”: docs/diag/cp_autocorr_manual.py κ°€ CP μžκΈ°μƒκ΄€ metric 을 plot ν•΄ fine timing + fractional CFO μΆ”μ •μ˜ λ™μž‘ 원리 λ…ΈμΆœ
  • FFT bin alignment: docs/diag/fft_by_pos.py κ°€ sync_pos 기반으둜 dab-rs FFT 와 eti-stuff FFT λ₯Ό sample-aligned 비ꡐ (|cc|=0.9994 도달 검증)
  • μ‹œκ°„ 디인터리버 μœ„μƒ 뢄석: docs/diag/sync_pos_compare.py κ°€ PRS alignment 의 Β±T_s oscillation 을 dump
  • outer FEC visualization: crates/dab-viterbi/examples/encode_dump.rs
    • verify_expected_soft.rs κ°€ Python ↔ Rust convolutional encode 의 bit-identity 확인

각 발견(Discovered subtlety #1-#7) 은 reference μ™€μ˜ μΈ‘μ • β†’ κ°€μ„€ β†’ μ •ν™•ν•œ μˆ˜μ • cycle 둜 λ„μΆœλλ‹€ (Week 3e 의 25 슬라이슀 = docs/diag/ 의 진단 인프라).


λΌμ΄μ„ΌμŠ€

μ½”λ“œ: MIT. Reference captures λŠ” 별도 link, redistribute ν•˜μ§€ μ•ŠμŒ (CC-BY-4.0).


인용

이 ν”„λ‘œμ νŠΈλ₯Ό ν•™μˆ  μžλ£Œμ—μ„œ μΈμš©ν•œλ‹€λ©΄:

@software{dab_rs_2026,
  author  = {{zobithecat}},
  title   = {{dab-rs: A Memory-Safe Software-Defined DAB Mode I Demodulator
              in Rust with WebAssembly Deployment}},
  year    = {2026},
  url     = {https://github.com/zobithecat/dab-rs},
  note    = {The first pure-Rust DAB OFDM decoder and the first
             T-DMB receiver implementation in Rust.}
}

πŸ‡°πŸ‡· ν•œκ΅­ 졜초 Β· 🌐 세계 졜초 β€” Made with ❀️ and cargo build.

About

Memory-safe DAB Mode I OFDM demodulator and T-DMB receiver in pure Rust

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages