[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-93386":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":13,"contributorsCount":13,"subscribersCount":13,"size":13,"stars1d":13,"stars7d":14,"stars30d":14,"stars90d":13,"forks30d":13,"starsTrendScore":13,"compositeScore":15,"rankGlobal":8,"rankLanguage":8,"license":16,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":19,"hasPages":17,"topics":20,"createdAt":8,"pushedAt":8,"updatedAt":21,"readmeContent":22,"aiSummary":23,"trendingCount":13,"starSnapshotCount":13,"syncStatus":24,"lastSyncTime":25,"discoverSource":26},93386,"famicom-rf-hackrf-decoder","GOROman\u002Ffamicom-rf-hackrf-decoder","GOROman",null,"C++",144,8,102,0,31,58.46,"MIT License",false,"main",true,[],"2026-07-22 04:02:09","# famicom-rf-hackrf-decoder\n\n[日本語 README はこちら](README.ja.md)\n\nA software decoder that receives the Famicom's VHF RF output (NTSC-J) with a\nHackRF One and displays it on your PC in real time — full NTSC color decoding\nplus FM intercarrier audio. C++20 + libhackrf + SDL2. No GNU Radio required.\n\nLive decode of a real Famicom (Super Mario Bros.):\n\n![real Famicom decode](docs\u002Fscreenshot.png)\n\nSynthetic color-bar golden test output:\n\n![color bars](docs\u002Fcolorbars.png)\n\n## Supported channels\n\n| Channel | Video carrier | Audio carrier (FM) |\n|---|---|---|\n| Japan VHF ch1 | 91.25 MHz | 95.75 MHz |\n| Japan VHF ch2 | 97.25 MHz | 101.75 MHz |\n\nThe HackRF tunes 2.0 MHz above the video carrier to keep its DC spike out of\nthe signal (ch1 → 93.25 MHz) and shifts back down in software.\n\n> **Note: real RF modulators drift.** The unit this was developed against\n> outputs its ch1 video carrier at 90.83 MHz (420 kHz below nominal). If you\n> can't get sync, run `--spectrum` first to find the actual carrier and pass\n> it with `--freq`. Envelope detection tolerates a residual offset of\n> ±100 kHz or so.\n\n## Hardware: HackRF One\n\n[HackRF One](https:\u002F\u002Fgreatscottgadgets.com\u002Fhackrf\u002Fone\u002F) (Great Scott\nGadgets) is an open-source SDR covering 1 MHz–6 GHz with 8-bit IQ sampling\nup to 20 MSPS over USB 2.0. This project uses it receive-only at 10 MSPS.\n\n![hardware setup](docs\u002Fhardware-setup.jpg)\n\nConnection: Famicom RF output (75 Ω RCA, the cable that normally goes to\nthe TV's antenna terminal) into the HackRF's **ANTENNA** SMA port. A proper\nRCA→SMA adapter is nicer, but as the photo shows, simply joining the\ncoax center conductor to the SMA pin works fine — the modulator output is\nstrong, so cable losses are a non-issue. Keep the connection wired\n(no over-the-air radiation) and start with AMP off \u002F moderate LNA gain;\nwatch the clip warning and adjust with `l`\u002F`g` keys.\n\nThe LEDs on the board: 3V3\u002F1V8\u002FRF = power rails, USB = host connected,\nRX flashes while famidec is streaming.\n\n## Build\n\n```sh\nbrew install hackrf sdl2 cmake pkg-config   # macOS; Linux needs the same libs\ncmake -B build -DCMAKE_BUILD_TYPE=Release\ncmake --build build -j\n```\n\nDeveloped and tested on macOS (Apple Silicon); the code has no\nplatform-specific dependencies beyond libhackrf and SDL2.\n\n## Usage\n\n```sh\n# 1. Check the spectrum first (find the real carrier position)\n.\u002Fbuild\u002Ffamidec --channel 1 --spectrum\n\n# 2. Live display (channel preset, or the measured frequency)\n.\u002Fbuild\u002Ffamidec --channel 1\n.\u002Fbuild\u002Ffamidec --freq 90.83e6\n\n# Record raw IQ while decoding\n.\u002Fbuild\u002Ffamidec --freq 90.83e6 --record cap.cs8\n\n# Decode from a recording (hackrf_transfer .cs8 files work too)\n.\u002Fbuild\u002Ffamidec --input file --file cap.cs8 --loop\n\n# Headless: dump decoded frames as PPM (debug \u002F verification)\n.\u002Fbuild\u002Ffamidec --input file --file cap.cs8 --dump-frames out_ --frames 30\n```\n\n### Options\n\n| Option | Description |\n|---|---|\n| `--channel 1\\|2` | Japan VHF channel preset (default 1) |\n| `--freq HZ` | explicit video carrier frequency |\n| `--input hackrf\\|file` | input source (default hackrf) |\n| `--file PATH` \u002F `--loop` | .cs8 playback \u002F loop |\n| `--rate HZ` \u002F `--offset HZ` | sample rate (default 10e6) \u002F tuning offset (default 2e6) |\n| `--lna N` \u002F `--vga N` \u002F `--amp` | LNA 0-40 (default 24) \u002F VGA 0-62 (default 20) \u002F RF amp |\n| `--mode color\\|gray` | color \u002F grayscale (default color) |\n| `--detector envelope\\|sync` | envelope \u002F carrier-PLL synchronous detection |\n| `--sat F` \u002F `--hue DEG` | saturation \u002F hue trim |\n| `--overscan F` | horizontal crop per side, 0..0.15 (default 0.047 ~ the NES 256-px picture) |\n| `--no-audio` \u002F `--volume F` | disable FM audio \u002F volume 0..1 (default 0.7) |\n| `--record PATH` | tee raw IQ to .cs8 while decoding |\n| `--dump-frames PREFIX` \u002F `--frames N` | headless PPM frame dump |\n| `--dump-composite PATH` | dump post-AGC composite as f32 (debug) |\n| `--spectrum` | print PSD and exit (no video) |\n\n### Keys \u002F on-screen display\n\n- `q` \u002F ESC: quit, `l` \u002F `L`: LNA ±8 dB, `g` \u002F `G`: VGA ±2 dB,\n  `c`: color\u002Fgray toggle, `s`: screenshot (BMP), `h`: help overlay\n- `v`: start\u002Fstop IQ recording to `famidec_rec_NNN.cs8` (a red `REC`\n  counter shows while recording); replay later with\n  `famidec --input file --file famidec_rec_001.cs8 --loop`.\n  Raw IQ at 10 MSPS is ~20 MB\u002Fs (~72 GB\u002Fhour) — record short clips\n- `←` \u002F `→`: tune ±50 kHz, `↑` \u002F `↓`: tune ±1 MHz (live retuning)\n- `r`: CRT emulation (barrel distortion + scanlines + vignette)\n- **Top left (big green)**: retro-TV style channel number (`CH1`)\n- **Top right (yellow)**: sync lock states and decoded FPS\n  (`V-SYNC:OK H-SYNC:OK 60.0FPS`), video\u002Faudio carriers\n  (`VHF:90.83MHz AUD:95.33MHz`), and measured latency\n  (`DELAY V:35ms A:88ms`)\n- While unlocked the display free-runs and shows **snow**, like a real TV;\n  when the vsync pulses are lost in noise, frames keep flowing on the\n  line-PLL flywheel\n\n## How it works\n\n```\nHackRF One (10 MSPS, tuned video carrier +2 MHz)\n  → complex DC blocker (removes the tuner DC spike)\n  → NCO mixer shifts the video carrier to 0 Hz\n  → 4.3 MHz complex LPF (rejects FM audio & adjacent channel)\n  → AM detection (envelope, or carrier-PLL synchronous)\n  → AGC (sync-tip \u002F back-porch tracking → IRE normalization)\n  → sync separation + flywheel line PLL \u002F vsync detection\n  → Y\u002FC band split (3.58 MHz BPF, Y = composite − chroma)\n  → per-line color burst phase measurement → chroma QAM demod (U\u002FV)\n  → YUV→RGB, 640×480 (240p line-doubled)\n  → triple buffer → SDL2 display\naudio tap (pre-LPF) → −4.5 MHz mix → ÷25 decimating FIR (400 kHz)\n  → FM discriminator → 75 µs de-emphasis → ÷8 → 50 kHz → SDL audio\n```\n\nThe Famicom is not broadcast-compliant (non-interlaced 240p, chroma phase\nadvancing 120° per line, one short line per frame), so the decoder:\n\n- splits Y\u002FC by **frequency band**, not a line comb\n- detects vsync as a **long-pulse region** without requiring equalizing pulses\n- measures the color burst **independently on every line** instead of\n  relying on a burst PLL\n\nThreads: USB callback (push-only into an SPSC ring) → DSP thread\n(demod → frame assembly) → main thread (SDL render). Measured ~13× real-time\nheadroom at 10 MSPS on Apple Silicon.\n\n## Tests\n\n```sh\n.\u002Fbuild\u002Fsynth_ntsc            # synthesize color bars → decode → assert RGB\n.\u002Fbuild\u002Fsynth_ntsc bars.cs8   # write synthetic IQ as .cs8 (for E2E tests)\nctest --test-dir build\n```\n\n## Troubleshooting\n\n| Symptom | Fix |\n|---|---|\n| Snow, never syncs | run `--spectrum`, find the real carrier, pass `--freq` |\n| Too dark \u002F blown out | adjust gain with `l`\u002F`L` `g`\u002F`G` (watch for clipping) |\n| No color (grayscale) | burst indicator stays yellow → improve signal\u002Fgain\u002Ffrequency |\n| Wrong hues | trim with `--hue DEG` (e.g. `--hue 10`) |\n| Picture shifted vertically | tune `kActiveStartLine` in `src\u002Fdsp\u002Fntsc_decoder.hpp` |\n| No sound | check `--volume`, or the RF modulator's audio carrier may be off-frequency |\n\n## License \u002F disclaimer\n\nMIT License — see [LICENSE](LICENSE).\n\nReceive-only tool. It never transmits with the HackRF One.\n","这是一个用于实时解码任天堂红白机（Famicom）RF射频输出信号的软件工具，支持NTSC-J制式视频与FM伴音的完整软解。项目基于C++20实现，依赖HackRF One SDR硬件接收VHF频段（91.25\u002F97.25 MHz）信号，通过软件完成载波偏移校正、NTSC色度解调、亮度恢复及音频FM解调，无需GNU Radio。适用于复古游戏硬件调试、模拟器验证、视频信号分析及嵌入式音视频逆向研究等场景。",2,"2026-07-17 02:30:09","CREATED_QUERY"]