华为云码道实战:你家 WiFi 卡在哪堵墙上:我用香农公式和 40 万组对拍算了张热力图

发布时间:2026/10/11 3:44:53
华为云码道实战:你家 WiFi 卡在哪堵墙上:我用香农公式和 40 万组对拍算了张热力图
你家 WiFi 卡在哪堵墙上我用香农公式和 40 万组对拍算了张热力图一键开通华为云码道 CodeArts 代码智能体https://developer.huaweicloud.com/codeartsco.html?sourcedmzntgwatomgit1sourceaddmzntgwatomgithd仓库https://gitcode.com/ZuoDa01/wifi-wall-map一、这东西是什么一句话把一张户型图变成 WiFi 信号热力图。你在画布上拖着路由器走屏幕上每个格子的接收功率、信噪比、香农速率上限实时重算还会告诉你这个点位该用 2.4G 还是 5G。不是测速工具也不是装饰性的动效。它每一个数字都能从两个 SI 精确定义常量真空光速 c 299792458 m/s、玻尔兹曼常数 k 1.380649×10⁻²³ J/K一路推到标准文档里印死的速率表中间没有一处是看着像。初始界面一室一厅户型二、为什么做这个上个月朋友装修完问我说我家书房就一格信号是不是运营商的锅。我让他把路由器从客厅挪到走廊他挪完说好像差不多——差不多这三个字最气人因为没人能告诉他差了多少 dB、差在哪堵墙上。市面上一切WiFi 分析App 干的事都是站在原地测一下告诉你现在多少 dBm。它们回答不了我把路由器往左挪一米会怎样。而这个问题恰恰是闭式可解的自由空间损耗是对数函数墙是加法容量上限是香农公式。一个能拖动、能实时重算的仿真器比十个测速器有用。于是就有了这个题目。下面全部代码由华为云码道 CodeArts Agent 生成我做的是选题、定规格、钉锚点、复核、以及被它反过来纠错。三、先跑起来看零依赖Node 18gitclone https://gitcode.com/ZuoDa01/wifi-wall-map.gitcdwifi-wall-mapnpmtest# 35 个测试npmstart# node scripts/serve.mjs默认 8130浏览器打开http://127.0.0.1:8130/。三套内置户型一室一厅 9×6、南北通透三居 12×9、长条公寓 15×4。南北通透三居承重墙后面肉眼可见地暗下去这张是三居。两条混凝土承重墙2.4G 各算 18 dB后面色带直接掉两档——不用懂射频也知道那间屋子为什么卡。长条公寓一堵金属龙骨隔墙把整个房间劈成两半悬停任意点位出结论悬停读数rx / SNR / 香农上界 / 穿了几堵墙按住白点拖动路由器热力图实时重算把 AP 从左上拖到中部之后四、提示词必须钉到函数级这是用码道最容易翻车的地方。你写帮我做个 WiFi 热力图它会给你一堆看起来很唬人但没法验证的东西。我每一轮的第一句都是同一句话禁止在对话里贴任何代码正文你的输出只能是仓库里的文件。然后才是规格。规格不写实现自由空间损耗计算写成这样必须导出下面 7 个函数名字一字不差 fsplDb(distM, freqHz) // 自由空间路径损耗 20*log10(4*PI*distM*freqHz/c)返回 dB dbmToMw(dbm) // 10^(dbm/10) mwToDbm(mw) // 10*log10(mw) noiseFloorDbm(bwHz, tempK290) // 10*log10(k*tempK*bwHz*1000) snrDb(rxDbm, noiseDbm) // rxDbm - noiseDbm shannonBps(bwHz, snrDb) // bwHz * log2(1 10^(snrDb/10)) linkBudgetDb({txEirpDbm, distM, freqHz, wallLossDb})边界条件也不能含糊。几何穿墙那一层我把五条约定编号写死在提示词里E1 线段与墙只在墙的端点处相碰 —— 算穿过 E2 交点落在墙线段端点之外 —— 不算 E3 连线与墙共线重叠 —— 不算防同一堵墙重复计数 E4 观察点恰好压在墙上 —— 不算 E5 crossCount(A,B) 必须恒等于 crossCount(B,A)不写 E5它就会写出一个不对称的实现然后你拖动 AP 时热力图会出现往左走掉一格、往右回不来的怪事。五、架构四层谁也不许越界data/materials.js 八种材质穿透损耗 三套户型 纯数据 src/rf.js FSPL / kTB / 香农 / 链路预算 纯公式 src/geometry.js 线段相交、穿墙计数、损耗求和 纯几何 src/engine.js 格点场、闭式与数值、频段推荐 只做编排 src/ui.js index.html 只做 DOM 与绘制分层是不是真成立不能靠我说。引擎的测试里有一条是这么写的// 不重实现cellField(...).rxDbm linkBudgetDb(同样入参)用逐位相等assert.equal(cf.rxDbm,direct);而不是近似。只要 engine 里偷偷自己减了一次 FSPL浮点末位就会露馅。六、核心算法以及一个会骗人的上界自由空间损耗exportfunctionfsplDb(distM,freqHz){return20*Math.log10((4*Math.PI*distM*freqHz)/C);}1 米、2.417 GHz 处它等于40.113316 dB距离翻倍恒加 6.0205999133 dB跟频率无关频率翻倍也恒加同一个数跟距离无关。这两条不变式我钉进了测试。热噪声底用 SI 定义常量直接算290K 下每赫兹−173.975187 dBm教科书上写的 −174 就是它。20 MHz 带宽下是 −100.964887 dBm。然后是关键的一步只用香农上界会骗你。香农公式 C B·log₂(1S/N) 里5 GHz 通常配 80 MHz 带宽2.4 GHz 只有 20 MHz。4 倍带宽直接压过了 5G 多出来的那几 dB 损耗。我实测跑 plan-a 远角2.4G rx-42.2 snr58.7 香农 390 Mbps 5G rx-49.4 snr45.6 香农 1212 Mbps ← 5G 完胜跟常识相反所以必须再叠一层 802.11 标准里的单空间流 PHY 天花板exportconstPHY_CEILING{20:72.2,40:144.4,80:433.3,160:866.7};// HT20 MCS7 / HT40 MCS7 / VHT80 MCS9 / VHT160 MCS9标准文档定值加上天花板之后plan-b 沿对角线扫描才出现常识里的反转t0.1 5G t0.2 5G t0.3 5G t0.4 5G t0.5 5G t0.6 5G t0.7 2.4G t0.8 2.4G t0.9 2.4G t1.0 2.4G ← 反转恰好一次而在远角还出现一个更好玩的结论翻转plan-b 远角 香农2.4G 193.5 5G 126.4 → 判 2.4G 封顶后2.4G 72.2 5G 126.4 → 判 5G同一个点位用不用标准天花板给你的建议是反的。这玩意儿不是5G 穿墙差那种烂大街的科普它是你买路由器时真会踩的坑。同一套点位手动锁定 2.4G 与锁定 5G 的差别锁定 2.4G七、可验证护城河40 万组随机差分对拍上面这些数字都是我自己算出来的凭什么信我凭我再写了一份跟码道那份互不引用的参考实现然后拿随机输入去撞。几何这一层我故意做得不可能有浮点误差// 毫米定点 BigInt 方向积全程无浮点判等constorient(a,b,c){constlhs(BigInt(b.x)-BigInt(a.x))*(BigInt(c.y)-BigInt(a.y));constrhs(BigInt(b.y)-BigInt(a.y))*(BigInt(c.x)-BigInt(a.x));returnlhsrhs?-1:lhsrhs?1:0;};RF 那层换一条数学路径用波长 λc/f 改写 FSPL、用exp(x·ln10)代替Math.pow、用ln/ln2代替log2。然后跑对拍组数: {fspl:50000,dbmToMw:50000,noise:50000,shannon:50000, link:50000,crossCount:50000,symmetry:50000,wallLoss:50000} 合计 400000 一致400000 组差分零分歧锚点数量只有几十个随机输入 40 万组。这才是能挡住你是不是自己对自己的东西。七点五、对拍第一次开火打的是我自己差分脚本第一版跑出来 12 例分歧全在dbmToMw上。我差点就把它记成码道实现有问题。先别定罪回头验自己的参照。把分歧点的相对误差换算成 ULPx-97.91515877936035 pow1.61615913776545416e-10 exp1.61615913776544925e-10 rel3.039e-15 ulp14 x-94.66329358750954 rel3.026e-15 ulp14 worst rel: 3.039e-15 1 ULP 2.220446049250313e-16最大分歧 14 个 ULP。这不是逻辑错误是Math.pow(10, x)和Math.exp(x * Math.LN10)两条 libm 路径本来就有的实现噪声。而我给差分设的容差是 1e-15 相对——比数学路径的噪声底还紧等于要求两条不同的库函数实现逐位一致那必然报假案。改成 1e-13 相对并在注释里把这个数值的来历钉死// 容差取 1e-13 相对不是随手写的实测两条 libm 路径在最坏点相差 14 ULP 3.04e-15。// 再往紧就是拿实现噪声当 bug 定罪。第二批分歧更有意思60000 组几何对拍里冒出来一批got 0 ref 1。逐例看下去发现这些点都是观察点恰好落在墙的端点上。规格里 E1 说墙端点相碰算穿过E4 说观察点压在墙上不算两条在这种情形下正面冲突而我没写优先级。码道选的是 E4 优先人站在门口还没穿过这堵墙。我复核之后认为它取的方向比我的规格合理——这次该改的是参照实现那一边。改完40 万组零分歧。这个顺序很重要差分对拍报分歧时先假设是自己的参照错了。我一天之内两次差点冤枉被测方一次是容差比 libm 噪声紧一次是规格自己没写完优先级。八、测试锚点之外还要有不变式35 个测试全绿ℹ tests 35 ℹ pass 35 ℹ fail 0 ℹ duration_ms 469.4除了具体数字我更在意这几条不变式它们专抓浮点漂移和 off-by-one// I1 距离翻倍恒等于 6.0205999133 dB与频率无关for(constdof[1,5,33])for(constfof[2417e6,5805e6])approxAbs(fsplDb(2*d,f)-fsplDb(d,f),6.0205999133,1e-9);// I3 SNR0dB 时 C 恒等于 B —— 用严格相等不给容差assert.equal(shannonBps(20e6,0)/1e6,20);// E5 穿墙对称性随机 2000 组assert.equal(crossCount(A,B,walls),crossCount(B,A,walls));九、真实的坑包括我自己挖的坑 1我给的锚点不可复现码道直接指出我错了我在提示词里写snrDb(-84.0721, -100.964887) 16.8928 shannonBps(20e6, 16.8928)/1e6 112.8173它跑完回我这两行在 1e-4 容差下自相矛盾。用 16.8928 算出来是112.8175而 112.8173 对应的是没取整的 snr16.892787。它是对的。我把中间量的展示值当成输入值递给了它。教训是链式锚点要么全程给全精度要么只给公式别给中间数。坑 2numericField 硬编码了 1 米差点让我吹出一个假精度规格里我让引擎提供一条数值路径格内 4×4 子采样取平均来跟闭式对拍但我忘了把格子尺寸cellM传给它。它只能猜猜成了 1 米conststep1/sub;constsycell.y-0.5(j0.5)*step;// ← 这个 0.5 就是 1 米格的一半于是网格越细子采样盒越会跑到隔壁格甚至穿墙。实测闭式与数值的偏差plan-b cellM1.0 - 1.24998 dB cellM0.5 - 8.37489 dB cellM0.25 - 15.37505 dB曲率误差本该随网格减半而缩 4 倍实测反而涨。如果我只在 cellM1 上测一次就会拿到吻合到 1.25 dB的漂亮结论写进文章——那才是真的丢人。修完之后cellM 1.0 / 0.5 / 0.25 - 0.009103 / 0.001784 / 0.000278 dB 比值≈0.2O(cellM²) 成立坑 3跨墙格的偏差永远不会消失这一点必须说清楚否则就是骗人。闭式与数值的偏差在无墙区按 O(cellM²) 收敛但在墙正好穿过的那个格子上plan-a 跨墙格最大偏差 9.750 dB 比值 1.000 / 1.000 plan-b 跨墙格最大偏差 13.000 dB 比值 1.000 / 1.000恒定不随网格加密改善。原因很直白格子再细也总有格子压在墙上格心和子点会分到墙两侧差的就是一堵墙的损耗。所以网格越细一定越准是错的页面必须把当前分辨率标出来。坑 4E1 和 E4 打架码道的解法比我的规格好我一边写E1 墙端点相碰算穿过一边又写E4 观察点压在墙上不算还举了个(0,0)-(5,5)终点落在墙中段、要求算穿过的例子。这三条自相矛盾。它的处理是分成两层segmentsCross是纯线段相交原语E1 返回 true满足我的例子crossCount在其上叠加 E4观察点压墙不计。我拿 40 万组随机差分去撞这一层零分歧。坑 5上下文一满它就开始空转第三轮跑到 525 秒界面还显示执行中但正文已经 62000 字、工具调用数从第 25 秒起就卡在 167 一动不动、一个文件都没建。状态栏写着100% · 707K / 198K 上下文已使用。判据很简单工具数长时间不变 正文持续增长 在对话里空转。开新会话重发同一条自包含提示词250 秒交付。坑 6它向我要 Personal Access Token第四轮跑了 10 分 16 秒自报 35 测试全绿、serve 返回 200最后一句是等你给 token 我立刻 push。沙箱丢了推送凭据。没给。凭据不外流是底线。整轮产物随沙箱没了只能重发。从那以后我在提示词里加了一条纪律每建完一个文件单独 commit别攒到最后一次性提交——这样即使推送失败损失也只有一轮。坑 7仓库选择器是个开关码道会话框上的选择仓库是 toggle。仓库已经绑定时你再走一遍搜索 点结果等于把它取消绑定。掉绑定的表现特别阴险消息发得进去、界面正常、但永远不执行。我同一晚踩了两次第二次直接把 R2 整轮废掉。最后给驱动脚本加了幂等护栏if(beforefull||before.endsWith(/short)){console.log(ALREADY_BOUND 幂等退出不做任何点击);process.exit(0);}坑 8首版界面横向溢出1280×900 下页面比视口宽右侧读数区被裁掉良好覆盖的值显示成半截。这种截图拿去参赛是不能用的。修完之后scrollWidth innerWidth 1280控制台错误清零。十、我请了三个评审让它们互相拆台代码跑通之后我用三个独立评审角色去挑刺各自打分并给 top5 硬伤。结果技术 87、产品 78、UI 74。最狠的是产品评审这一刀我原样抄过来对拍证明的是没写错代码不是热力图准。整套模型里唯一非真值的输入——八种墙体材质的穿透损耗——每一条都自标工程经验中值非标准规定值。而卡在哪堵墙这个标题结论恰恰 100% 由这张表决定。懂 RF 的评委会当场反问真实世界那堵 240mm 砖墙到底是 12 dB 还是 20 dB你一个字没证。我认。所以 README 里专门写了一节《这套数值能信到什么程度》五条边界一条没删并且把 ±5 dB 敏感性和不含多径/衰落/干扰/天线方向图写成了显式声明而不是藏在角落。UI 评审给的三条我也认热力图最低档#0b1120跟页面底色同色死角跟卡片底糊成一片房间名和墙体材质根本没画出来悬停结论不回收换了点位还留着上一条文案。十一、提效数据轮次交付内容码道耗时本地复核R1src/rf.js 锚点测试7m20s10/10 绿R2asrc/geometry.js 相交测试5m00s19/19 绿R2bdata/materials.js 八材质三户型7m28s26/26 绿R3asrc/engine.js 格点场4m10s导出齐全R3b修 cellM 缺陷 PHY 天花板 引擎测试6m15s35/35 绿R4index.html src/ui.js serve.mjs10m16s推送失败→ 重发 7m50s界面实跑R6aREADME MIT LICENSE6m30s条款齐全合计 15 个 commit、1120 行 JS、35 个测试、40 万组差分对拍零分歧。人工投入主要花在四件事上定选题和可验证护城河、算锚点并交叉验算、写函数级规格与边界编号、以及被码道反过来纠错。纯敲代码的时间接近于零但判断哪句话是错的时间占了大头。码道会话R1 交付与三轮工具调用十二、五维自检架构设计30%五层单向依赖engine 只做编排UI 零物理公式用逐位相等断言锁死。代码质量30%零依赖、35 测试全绿、锚点 不变式双轨、独立参考实现做差分。安全合规20%静态服务器实测../、%2e%2e%2f、反斜杠、绝对路径、%00全部 404只绑 127.0.0.1全程未向工具提交任何凭据。UI/UX15%纯深底 单一冷蓝热力图是单色相明度阶梯无彩虹无发光但图例、房间名、墙材质分档这三项还没落地。博文内容5%就是这篇。十三、还没做完的如实说不支持导入你自己家的户型只有三套内置——这是产品评审指出的一次性 demo要害。不支持多 AP / Mesh 布点建议。墙体只能画直线段。材质表缺 ITU-R P.1238 的正式引用与 ±5 dB 敏感性指标bandFlipMargin我写进了规格但那一轮我中途停了代码里没有不吹。页面上自动频段的策略还没完全统一到引擎的同一个函数视图层仍有一处自己做的 max。移动端没有触摸拖拽。写在最后这个项目里最有价值的不是热力图是那一串被反复证伪的过程我的锚点不可复现、我的判据太弱、我的规格漏了参数、我的容差比 libm 的噪声底还紧。每一次都是独立参考实现或者被测方自己把这些揪出来的。让 AI 写代码不难难的是有一套东西能让它说全绿了这句话变得可以证伪。有了那套东西它写得越快你越省心没有的话它写得越快你埋的雷越多。工具入口https://atomgit.com/dashboard