{"article":{"slug":"turn-a-wifi-7-router-into-a-320msps-software-defined-radio-receiver","title":"Turn a WiFi 7 Router into a 320Msps Software Defined Radio Receiver","subtitle":null,"summary":"A GitHub project that repurposes a WiFi 7 router as a ~320 Msps software-defined radio receiver, documenting the hardware path, sampling approach, and how to get RF samples out of commodity WLAN silicon.","content_type":"tutorial","language":"en","canonical_url":"https://github.com/h0m3us3r/w1700k-sdr","author":{"name":"h0m3us3r","url":"https://github.com/h0m3us3r","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"GitHub","url":"https://github.com/h0m3us3r/w1700k-sdr","listing_slug":null,"listing":null},"topics":[{"name":"Hardware","slug":"hardware","url":"https://listedarticles.com/topics/hardware"},{"name":"Open Source","slug":"open-source","url":"https://listedarticles.com/topics/open-source"},{"name":"Networking","slug":"networking","url":"https://listedarticles.com/topics/networking"},{"name":"Engineering","slug":"engineering","url":"https://listedarticles.com/topics/engineering"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":1059,"reading_minutes":5,"published_at":"2026-10-12T00:00:00.000Z","added_at":"2026-10-14T02:17:06.688Z","updated_at":"2026-10-14T02:17:06.688Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/turn-a-wifi-7-router-into-a-320msps-software-defined-radio-receiver","markdown_url":"https://listedarticles.com/articles/turn-a-wifi-7-router-into-a-320msps-software-defined-radio-receiver.md","example":false,"citation":"h0m3us3r, GitHub. \"Turn a WiFi 7 Router into a 320Msps Software Defined Radio Receiver.\" 12 Oct 2026. https://github.com/h0m3us3r/w1700k-sdr (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://github.com/h0m3us3r/w1700k-sdr"},"body_markdown":"Turns a Gemtek W1700K Wi-Fi 7 router into a wideband software-defined receiver. The router's MediaTek MT7996 radio captures raw 14-bit IQ at up to 320 Msps, its Airoha AN7581 SoC streams it over 10 GbE, and a Linux host shows it as a live spectrum and waterfall in the browser, with the receiver's controls next to it.\n\n*The whole 2.4 GHz band at 320 Msps, live, with the decimation's roll-off\ncorrected in the stream (\"flatten\"): a 262,144-point FFT of every sample, 100\nspectra a second (this recording runs at 50 frames a second).*\n\nThe MT7996 runs only our own firmware: a ROM patch, WM and WA built from this repository, with no MediaTek code. It brings the radio up from a cold power-on by itself, from MediaTek's initialisation and calibration decoded register by register against a trace of MediaTek's own firmware, and takes its commands (LO, sample rate, capture, stream, gain, filter) from the host. See docs/architecture.md and docs/roadmap.md.\n\n| Sample rates | 40, 80, 160 or 320 Msps of 14-bit complex IQ on every band, independent of the LO and the filter; on band 2 also 640/2 (every other sample of 640 Msps: flat to ±155 MHz, but signals 160–320 MHz away fold in) | \n| Bands | 2.4 GHz (MT7976), 5 GHz and 6 GHz (two MT7977s); switching bands is a register write inside a running stream | \n| Tuning | No channels: the LO is any Hz a band's synthesizer locks at (unit A: 2.081–2.748, 4.647–7.509 and 4.707–7.576 GHz), on its grid of 2.98 Hz (2.4 GHz) or 11.9–14.3 Hz steps; retuned mid-stream (about 1 ms) | \n| Front end | AGC or manual LNA/baseband gain; analog filter 20–320 MHz | \n| Transport | the radio DMAs into a 1 GiB ring in SoC DRAM; the kernel sends every 8 KiB slice as one UDP datagram, zero-copy, on the 10 GbE port; at 320 Msps half the slices are packed to 28 bits per sample pair on the radio's own cores to fit the link (lossless, automatic) | \n| Host | `w1700k-stream` receives the export and serves the IQ stream to every consumer on the host (shared memory, complex int16, gap-tagged), with an optional correction (\"flatten\") of the decimation's roll-off applied to the samples themselves;`w1700k-spectrum` : 262,144-point FFT at 100 frames/s over every sample, browser UI, live controls;`sdr-recv` records the stream | \n\n*2.4 GHz at 320 Msps, flattened: Wi-Fi frames and shorter hopping bursts on a\nfloor that stays level to the band's edges. The Stream panel shows the link\ncarrying 9.75 Gbit/s, half of the slices packed, and every sample transformed.*\n\n*Two busy 80 MHz Wi-Fi channels at 5170–5330 MHz in a flattened 320 MHz view.*\n\n*Zoomed to 24 MHz around 5250 MHz: the two channels' edges and the guard gap\nbetween them, in 1.22 kHz bins.*\n\n| Path |  | \n|---|---|\n| `platform/` | What runs on the SoC: our U-Boot, Linux 6.18 with board patches, a read-only BusyBox root | \n| `kernel/w1700k_sdr/` | The MT7996 driver (firmware download, the DMA ring, the 10 GbE export, the firmware's command mailbox) and its unit-side clients | \n| `firmware/` | Our MT7996 firmware: the ROM patch, the WM (radio bring-up and band set-up, streams, RF control) and the WA (packer) | \n| `host/w1700k/` | Host control: firmware load, streaming, RF control as firmware commands; `w1700k serve` for the web UI | \n| `host/stream/` | `w1700k-stream` , the host's one receiver of the export: the IQ stream in shared memory for every consumer, and the roll-off correction | \n| `host/spectrum/` | The spectrum/waterfall web server | \n| `host/recv/` | `sdr-recv` , the stream recorder | \n| `init/` | Installation from stock, updates, recovery, return to stock | \n| `include/hw/` | Register reference: every known MT7996, A-die and AN7581 register, as C headers | \n| `docs/` | Architecture, roadmap | \n\nYou need a W1700K (HW 1.x) or W1700K2 (HW 2.1), a 3.3 V USB serial adapter on\nits console header, an Ethernet cable to its `lan3` port for installation, and\na 10 GbE NIC on the host cabled to its `wan` port (10GBASE-T; an X710 with a\ncopper SFP+ module works) for the IQ stream. Both revisions run the same images.\nMost units carry a W25N04KV NAND; some W1700K2s carry a W25N04LV (ID `ef 8b 23`),\nwhich is supported but has never been run on a unit: `init/install` stops on\none until you pass `--i-dont-care-about-nand-corruption`, and asks for a raw\ndump of it (`init/rawdump`) for the maintainers.\n\n```\nmake                                   # everything, from public sources (~10 min plus downloads)\nmake check                             # the test suite\nsudo init/host/link up IFACE           # 192.168.1.10/24 on the NIC cabled to lan3\nsudo init/host/tftp-forward enable IFACE\ninit/install                           # back up the NAND, install, save the radio calibration\nsudo init/host/link up IFACE10G 10.77.0.10/24 9216    # the 10 GbE stream link\nhost/stream/build/w1700k-stream &                      # the stream, for every consumer on this host\nhost/spectrum/build/w1700k-spectrum --controller \"host/bin/w1700k serve\" --listen 0.0.0.0:8073\n```\nThen open `http://HOST:8073/` and press ▶. The first start loads the radio\nfirmware and brings the radio up (about 2 s). Without the browser, the same\noperations are `host/bin/w1700k load|start|band|tune|gain|filter|stop|status`.\n\nBuild requirements: a C/C++ compiler, make, git, Python 3.10+, the usual\nBuildroot prerequisites, clang, ld.lld, llvm-objcopy and llvm-readobj (RISC-V\nfirmware), openssl (firmware container encryption), and\n`libfftw3-dev` and `zlib` for the stream service and the spectrum server. No vendor firmware is\ndownloaded or needed.\n\n- **Our firmware is receive-only.** It runs none of MediaTek's transmit\ncalibrations and keeps the MAC's transmit path disabled; a gapless watch at\n5250 MHz saw nothing during a load, where MediaTek's firmware transmits a\nfull-power burst. Still, do not cable an antenna port straight into a\nsensitive instrument without an attenuator.\n- **Our ROM patch writes no eFuse.** MediaTek's ROM patches since 2025\npermanently program one eFuse gate bit on their first run (also with\nMediaTek's own driver); a unit that ever ran one has that bit set.\n- **Installation replaces the stock operating system.**`init/install` first\ncopies the NAND's data to the host; keep that copy.`init/restore` writes it\nback and returns the unit to its stock firmware.\n- Accessing undocumented MT7996 or AN7581 addresses can hang the SoC or wedge\nthe radio until a power cycle. `include/hw/` marks every known hazard.\n\nMIT, except the Linux driver and kernel patches (GPL-2.0-only), U-Boot patches (GPL-2.0+), the driver's user-space header (GPL-2.0 WITH Linux-syscall-note) and files derived from MediaTek's mt76 driver (BSD-3-Clause-Clear). See LICENSE.","body_html":"<p>Turns a Gemtek W1700K Wi-Fi 7 router into a wideband software-defined receiver. The router&#39;s MediaTek MT7996 radio captures raw 14-bit IQ at up to 320 Msps, its Airoha AN7581 SoC streams it over 10 GbE, and a Linux host shows it as a live spectrum and waterfall in the browser, with the receiver&#39;s controls next to it.</p>\n<p>*The whole 2.4 GHz band at 320 Msps, live, with the decimation&#39;s roll-off\ncorrected in the stream (&quot;flatten&quot;): a 262,144-point FFT of every sample, 100\nspectra a second (this recording runs at 50 frames a second).*</p>\n<p>The MT7996 runs only our own firmware: a ROM patch, WM and WA built from this repository, with no MediaTek code. It brings the radio up from a cold power-on by itself, from MediaTek&#39;s initialisation and calibration decoded register by register against a trace of MediaTek&#39;s own firmware, and takes its commands (LO, sample rate, capture, stream, gain, filter) from the host. See docs/architecture.md and docs/roadmap.md.</p>\n<p>| Sample rates | 40, 80, 160 or 320 Msps of 14-bit complex IQ on every band, independent of the LO and the filter; on band 2 also 640/2 (every other sample of 640 Msps: flat to ±155 MHz, but signals 160–320 MHz away fold in) | \n| Bands | 2.4 GHz (MT7976), 5 GHz and 6 GHz (two MT7977s); switching bands is a register write inside a running stream | \n| Tuning | No channels: the LO is any Hz a band&#39;s synthesizer locks at (unit A: 2.081–2.748, 4.647–7.509 and 4.707–7.576 GHz), on its grid of 2.98 Hz (2.4 GHz) or 11.9–14.3 Hz steps; retuned mid-stream (about 1 ms) | \n| Front end | AGC or manual LNA/baseband gain; analog filter 20–320 MHz | \n| Transport | the radio DMAs into a 1 GiB ring in SoC DRAM; the kernel sends every 8 KiB slice as one UDP datagram, zero-copy, on the 10 GbE port; at 320 Msps half the slices are packed to 28 bits per sample pair on the radio&#39;s own cores to fit the link (lossless, automatic) | \n| Host | <code>w1700k-stream</code> receives the export and serves the IQ stream to every consumer on the host (shared memory, complex int16, gap-tagged), with an optional correction (&quot;flatten&quot;) of the decimation&#39;s roll-off applied to the samples themselves;<code>w1700k-spectrum</code> : 262,144-point FFT at 100 frames/s over every sample, browser UI, live controls;<code>sdr-recv</code> records the stream | </p>\n<p>*2.4 GHz at 320 Msps, flattened: Wi-Fi frames and shorter hopping bursts on a\nfloor that stays level to the band&#39;s edges. The Stream panel shows the link\ncarrying 9.75 Gbit/s, half of the slices packed, and every sample transformed.*</p>\n<p><em>Two busy 80 MHz Wi-Fi channels at 5170–5330 MHz in a flattened 320 MHz view.</em></p>\n<p>*Zoomed to 24 MHz around 5250 MHz: the two channels&#39; edges and the guard gap\nbetween them, in 1.22 kHz bins.*</p>\n<div class=\"table-wrap\"><table><thead><tr><th>Path</th><th></th></tr></thead><tbody><tr><td><code>platform/</code></td><td>What runs on the SoC: our U-Boot, Linux 6.18 with board patches, a read-only BusyBox root</td></tr><tr><td><code>kernel/w1700k_sdr/</code></td><td>The MT7996 driver (firmware download, the DMA ring, the 10 GbE export, the firmware&#39;s command mailbox) and its unit-side clients</td></tr><tr><td><code>firmware/</code></td><td>Our MT7996 firmware: the ROM patch, the WM (radio bring-up and band set-up, streams, RF control) and the WA (packer)</td></tr><tr><td><code>host/w1700k/</code></td><td>Host control: firmware load, streaming, RF control as firmware commands; <code>w1700k serve</code> for the web UI</td></tr><tr><td><code>host/stream/</code></td><td><code>w1700k-stream</code> , the host&#39;s one receiver of the export: the IQ stream in shared memory for every consumer, and the roll-off correction</td></tr><tr><td><code>host/spectrum/</code></td><td>The spectrum/waterfall web server</td></tr><tr><td><code>host/recv/</code></td><td><code>sdr-recv</code> , the stream recorder</td></tr><tr><td><code>init/</code></td><td>Installation from stock, updates, recovery, return to stock</td></tr><tr><td><code>include/hw/</code></td><td>Register reference: every known MT7996, A-die and AN7581 register, as C headers</td></tr><tr><td><code>docs/</code></td><td>Architecture, roadmap</td></tr></tbody></table></div>\n<p>You need a W1700K (HW 1.x) or W1700K2 (HW 2.1), a 3.3 V USB serial adapter on\nits console header, an Ethernet cable to its <code>lan3</code> port for installation, and\na 10 GbE NIC on the host cabled to its <code>wan</code> port (10GBASE-T; an X710 with a\ncopper SFP+ module works) for the IQ stream. Both revisions run the same images.\nMost units carry a W25N04KV NAND; some W1700K2s carry a W25N04LV (ID <code>ef 8b 23</code>),\nwhich is supported but has never been run on a unit: <code>init/install</code> stops on\none until you pass <code>--i-dont-care-about-nand-corruption</code>, and asks for a raw\ndump of it (<code>init/rawdump</code>) for the maintainers.</p>\n<pre><code>make                                   # everything, from public sources (~10 min plus downloads)\nmake check                             # the test suite\nsudo init/host/link up IFACE           # 192.168.1.10/24 on the NIC cabled to lan3\nsudo init/host/tftp-forward enable IFACE\ninit/install                           # back up the NAND, install, save the radio calibration\nsudo init/host/link up IFACE10G 10.77.0.10/24 9216    # the 10 GbE stream link\nhost/stream/build/w1700k-stream &amp;                      # the stream, for every consumer on this host\nhost/spectrum/build/w1700k-spectrum --controller &quot;host/bin/w1700k serve&quot; --listen 0.0.0.0:8073</code></pre>\n<p>Then open <code>http://HOST:8073/</code> and press ▶. The first start loads the radio\nfirmware and brings the radio up (about 2 s). Without the browser, the same\noperations are <code>host/bin/w1700k load|start|band|tune|gain|filter|stop|status</code>.</p>\n<p>Build requirements: a C/C++ compiler, make, git, Python 3.10+, the usual\nBuildroot prerequisites, clang, ld.lld, llvm-objcopy and llvm-readobj (RISC-V\nfirmware), openssl (firmware container encryption), and\n<code>libfftw3-dev</code> and <code>zlib</code> for the stream service and the spectrum server. No vendor firmware is\ndownloaded or needed.</p>\n<ul><li><p><strong>Our firmware is receive-only.</strong> It runs none of MediaTek&#39;s transmit</p><p>calibrations and keeps the MAC&#39;s transmit path disabled; a gapless watch at\n5250 MHz saw nothing during a load, where MediaTek&#39;s firmware transmits a\nfull-power burst. Still, do not cable an antenna port straight into a\nsensitive instrument without an attenuator.</p></li><li><p><strong>Our ROM patch writes no eFuse.</strong> MediaTek&#39;s ROM patches since 2025</p><p>permanently program one eFuse gate bit on their first run (also with\nMediaTek&#39;s own driver); a unit that ever ran one has that bit set.</p></li><li><p><strong>Installation replaces the stock operating system.</strong><code>init/install</code> first</p><p>copies the NAND&#39;s data to the host; keep that copy.<code>init/restore</code> writes it\nback and returns the unit to its stock firmware.</p></li><li><p>Accessing undocumented MT7996 or AN7581 addresses can hang the SoC or wedge</p><p>the radio until a power cycle. <code>include/hw/</code> marks every known hazard.</p></li></ul>\n<p>MIT, except the Linux driver and kernel patches (GPL-2.0-only), U-Boot patches (GPL-2.0+), the driver&#39;s user-space header (GPL-2.0 WITH Linux-syscall-note) and files derived from MediaTek&#39;s mt76 driver (BSD-3-Clause-Clear). See LICENSE.</p>","headings":[]}}