btfz-sdr/gnuradio_demo/pluto-ble-adv-tx

Claude Code and agent skill by btfz-sdr/gnuradio_demo

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

PlutoSDR(AD9361) 纯 C 发射 BLE LE 1M 广播包 —— PDU/CRC/白化组包、查表积分调制器、信道=频率=白化种子、广播周期必须交给硬件循环缓冲区(软件 usleep 实测差 3.5~4 倍)、空口验证配方与避坑清单。改 pluto_ble_adv_tx.c/ble_phy.c、排查"手机收不到/断续"或"广播间隔不准"时用本技能。

Install

npx vskill@latest install btfz-sdr/gnuradio_demo/pluto-ble-adv-tx

SKILL.md

PlutoSDR BLE 广播包发射(LE 1M)

工程根目录:本文件所在目录往上两级(即 pluto_ble_adv_tx.c 所在目录)。 编译+上机:make run CH=37 INTERVAL=200 ATTN=30(scp 到 pluto.local 再 ssh 上板跑)。 密码不写在本文件里:从环境变量 PLUTO_PW(或工程脚本里 source 的库)取;非交互 scp 必须加 -O。

触发条件

  • 改 pluto_ble_adv_tx.c(组包、射频配置、缓冲区与发射循环)或 ble_phy.c(调制器)
  • 手机 / nRF Connect 看不到设备;或只有自家接收机能解、标准设备一个包都收不到
  • 怀疑广播间隔不准(既指"实测与设定不符",也指"不合规范")
  • 换信道、改采样率、调发射衰减之后现象发生变化

工程文件地图

文件作用
ble_phy.c / ble_phy.h调制器(比特 → 高斯成形 → 相位查表 → int16 IQ)+ 收发共用函数(CRC-24 / 白化 / LSB 位展开)
pluto_ble_adv_tx.c主程序:getopt、AD9361 配置、PDU/CRC/白化、循环缓冲区创建与发射
makefilebuild / run;四个旋钮 CH / INTERVAL / ATTN / SAMPLE(都有默认值,make run 直接可用)

发射链路

PDU → CRC-24 → 白化(种子 = 信道号) → LSB 位展开 → 高斯成形(BT≈0.5) → 相位累加查表
→ int16 IQ → [包 1296 样点 + 留白 4000 样点] 填进硬件循环缓冲区(长度 = 采样率 × 间隔)
→ DMA 按采样时钟无限循环

硬规则(按踩坑代价排序)

  1. 广播周期必须交给硬件循环缓冲区,不能用软件"睡"。

iio_buffer_push()(阻塞)+ usleep(间隔−包长) 在板子上做不准:同一版代码指令 10/20/100 ms, Ellisys 实测 35/75/395 ms(三个点严格落在 实测 ≈ 4×指令 − 5ms 这条线上,是比例性偏差不是抖动); 同一份代码改成从宿主机推流跑,实测又是 201.2 ms(≈ 指令值)——周期会随运行环境漂移。 正确做法:把"一个广播周期"整体做成一个缓冲区,长度 = 采样率 × 间隔 (4 MHz × 200 ms = 800000 样点),前段填包、后段全填 0(静默),用 iio_device_create_buffer(tx, target_samples, true)(末参数 true = 硬件循环)创建, 只 push 一次,之后硬件按采样时钟无限循环,进程用 while (!stop) sleep(1); 活着。 代价:① 间隔启动时定死,改间隔 = 重启;② 缓冲区内存 = 采样率 × 间隔(200 ms @4 MHz ≈ 3.2 MB); ③ 进程退出/被杀即停发。

  1. 白化种子与射频信道绑死:LE 1M 的白化种子 = 信道号(37/38/39),种子写死而频率可自由改时,

换信道后任何标准接收机都解不出(实测:同一基带按 37 解 CRC OK,按 38 解一个包都没有)。 信道 → 频率 → 种子必须同源,只暴露一个 -c <信道>;-f 手动覆盖频率要告警。

  1. `-s` 采样率不是越大越好:符号率 = 采样率 ÷ SAMPLE_PER_SYMBOL,而 sps 是编译期常量。

-s 10 只是把同一串样点播快 2.5 倍 → 2.5 Msym/s、占用带宽 ≈2.8 MHz、标准接收机一个包都收不到。 要么让速率参与调制器参数,要么不要暴露 -s。

  1. 发射衰减必须显式写 `hardwaregain`:硬件上电默认是 −30 dB(不是注释里假设的 0 dB)。

实测过"调了衰减后手机端现象全变、被误判成别的故障"。写失败(部分固件返回 EINVAL)要只告警不致命, 并把"仍是硬件默认值"打在屏幕上。改任何射频电平前后都读回来记档。

  1. Pluto 上 RX/TX 采样率是联动的:只写 TX 的 sampling_frequency,读回 RX 也是同一个值。

别在同一块板上一头发射、一头用 10 MHz 跑接收程序(那会把发射采样率一起带成 10 MHz); 要同时收发用两块板,或让接收也降到 4 MHz。

  1. 打印提示里不要写死期望值:代码里曾留 Calculated CRC (应为 E8 7D 36),MAC/设备名一改就变成误导

(当前配置实测是 F4 0F D3)。提示只打印算法真实算出来的值。

  1. 调制度要实测,不要从系数推导:16 抽头高斯核抽头和 256 极易被误读成 ±1 MHz;

实测每符号频偏 ±250 kHz、h = 0.50(256 分摊在 4 个样点上 = 每符号 64/1024 = 0.25 周期)。

  1. 规范下限别踩:advInterval 范围 20 ms~10.24 s,是 0.625 ms 的整数倍(4 MHz 下正好 = 2500 样点);

其中不可连接广播 ADV_NONCONN_IND 下限 100 ms(可连接 20 ms)。本工程用的是 ADV_NONCONN_IND。

  1. 别猜采样率可行范围,直接读设备:sampling_frequency_available: [2083333 1 61440000],

下限 2.083 MHz —— "采样率设太低导致波形不对"这个方向本身不成立。

  1. 单信道是已知缺口:规范要求广播事件在 37/38/39 轮换(Vol 6 Part B 4.4.2),本工程只发单信道。

低占空比扫描会把单信道广播"看成断续",且与间隔无关(与"间隔 20/100/200 ms 现象一样"吻合)。

空口验证:证明"发出去的东西在空口上连续且按时"

基带对只说明"包造得对"。用户报"手机看到断续"时不要拿手机当第一裁判,按下面顺序用独立观察者定量:

  1. 发射循环有没有卡:给推流循环加计数与间隔统计(宿主机最省事)。基线 -i 1:44.93 s / 25956 次 push,

平均 1730 µs、最大 2.6 ms、异常 0 次。

  1. 空口能量包络有没有洞(不解调、抗削顶):抓 15~20 s,按 1 ms 分辨率算功率包络,门限取"底噪中位数 + 6 dB"。

基线:每秒活跃比例 3.2%~4.6% 一条平线,>500 ms 空洞 0 个。

  1. "我们自己的包"有没有洞(最硬):整段 IQ 逐包解调后按 AdvA 过滤再算间隔。

同一段里永远混着周围手机/家电的广播(15 s 里 9 个广播源、495 个包),不过滤 MAC 的统计没有意义。 基线:73 个自包全 CRC OK,间隔中位 ≈ 指令值,>500 ms 空洞 0 个。

少一个就白抓的前提:

  • 接收 LO 必须对到发射信道(ch37 → 2402 MHz)。LO 停在 2400 时,2402 落在 4 MHz 带宽的奈奎斯特边缘:

能量包络还看得见,一个包都解不出来。

  • 按 4 MHz 抓(RX/TX 采样率联动,见硬规则 5)。
  • 抓完把 LO 改回原值并确认没有残留发射进程。

判读:三步都干净 → 节奏产生在"空口之后"(手机/App 的扫描窗口、系统扫描节流、与 WiFi 分时)。 有专业分析仪(Ellisys 等)再做一次复核:本工程最终结论就是"手机断续 / Pluto 接收机连续 / Ellisys 连续" —— 三个观察者、两份独立接收机证据。再回到硬规则 10 看单信道缺口。

实测基线(改动后可对比)

项值
包结构40 字节 = 320 bit,空中 1.324 ms(1296 样点)+ 1 ms 留白(4000 样点)= 5296 样点 @4 MHz
广播周期改前(软件定时):板上 Ellisys 10/20/100 ms → 35/75/395 ms;宿主推流 201.2 ms;改后 = target_samples / fs(按构造精确,如 800000/4 MHz = 200.000000 ms)
基带量化频偏 ±250 kHz、h = 0.50、−20 dB 带宽 1098 kHz、±1 MHz −30.9 dB、±2 MHz −71.6 dB、位重建 320/320
空口(自包)15 s 内 73 个包全 CRC OK,时间连续 14.74 s,>500 ms 空洞 0 个
空口(能量)20 s 每秒活跃比 3.2%~4.6% 平线,>500 ms 空洞 0 个
组包自检PDU 42 1E 06 05 04 03 23 DC 17 09 68 65 6C 6C 6F ...(MAC DC:23:03:04:05:06),CRC-24 空中字节序 F4 0F D3

已知局限

  • 单信道:只发 37/38/39 中的一个,不轮换(下一集补)。
  • 间隔启动时定死:改间隔要重启进程(换来运行期的绝对稳定);缓冲区内存 = 采样率 × 间隔。
  • 无功率斜坡:恒定包络但边沿是硬跳变,实测频谱掩码仍轻松通过,优先级最低。
  • 1024 点相位表(量化 4 kHz vs 频偏 250 kHz):够用,不要为此加复杂度。

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
201
Repository forks
45
7-day trend score
0

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/btfz-sdr/gnuradio_demo/pluto-ble-adv-tx/badge)](https://verified-skill.com/skills/btfz-sdr/gnuradio_demo/pluto-ble-adv-tx)

Something wrong with this skill or its page? Tell us.