> Generate Bluetooth Low Energy (BLE) PHY waveforms. Read BEFORE writing any BLE waveform code to avoid hallucinating API patterns. Covers bleWaveformGenerator, bleIdealReceiver, bleCTEIQSample, bleAngleEstimate, bluetoothTestWaveform for LE1M/LE2M/LE500K/LE125K. Bluetooth Toolbox R2022a+.
npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-generate-ble-waveform
| Intent | Cues | Pattern |
|--------|------|---------|
| Basic generation | "generate", "waveform" | Single-Mode |
| Compare modes | "compare", "all modes" | All-Mode |
| Direction finding | "CTE", "AoA", "AoD" | CTE |
| RF-PHY compliance | "compliance", "RF-PHY" | RF-PHY Test |
| Coexistence | "WLAN", "interference" | WLAN Coexistence |
| TX measurements | "PAPR", "OBW", "power" | TX Measurements |
| Localization | "RSSI", "beacon", "distance" | RSSI Localization |
| BER/receiver | "BER", "noise", "sensitivity" | BER Simulation |
| Multi-packet | "burst", "IFS" | Multi-Packet |
| Spectrum | "spectrum", "spectral mask" | Spectral Analysis |
| Whitening | "whitening", "scrambling" | Whitening Comparison |
| Impairments | "CFO", "practical", "realistic" | End-to-End |
Default (ambiguous request): LE1M, 37-byte payload, channel 37.
Use this skill when the user asks to generate, simulate, or analyze BLE PHY-layer waveforms — including mode comparisons, CTE/direction finding, RF-PHY compliance testing, BER simulations, TX measurements, WLAN coexistence, or RSSI localization. Applies to any task involving bleWaveformGenerator, bleIdealReceiver, bleCTEIQSample, bleAngleEstimate, or bluetoothTestWaveform.
bluetoothWaveformGenerator (covered by a separate BR/EDR skill)bleCSWaveform(bleCSConfig) (R2024b+); CS has its own ranging/distance-measurement pipeline and is not part of PHY waveform generationbluetoothLENode for link-layer/network modelingbluetoothPathLoss for propagation/channel modeling| Mode | symbolRate | Data Rate | Coding | Duration (37 B) |
|------|:----------:|:---------:|--------|:---------------:|
| "LE1M" | 1e6 | 1 Mbps | None | ~336 us |
| "LE2M" | 2e6 | 2 Mbps | None | ~172 us |
| "LE500K" | 1e6 | 500 kbps | FEC S=2 | ~974 us |
| "LE125K" | 1e6 | 125 kbps | FEC S=8 | ~2768 us |
For detailed guidance beyond what's in this file, see:
| Param | Default | Range |
|-------|---------|-------|
| Mode | "LE1M" | LE1M, LE2M, LE500K, LE125K |
| ChannelIndex | 37 | 0-39 |
| SamplesPerSymbol | 8 | >=1 (use >=4 for plots) |
| WhitenStatus | "On" | On, Off |
| DFPacketType | "Disabled" | Disabled, ConnectionCTE, ConnectionlessCTE |
| AccessAddress | adv default | 32-bit binary col vector |
| ModulationIndex | 0.5 | [0.45, 0.55] |
| PulseLength | 1 | [1, 4] |
messageBits = randi([0 1], payloadLenBytes*8, 1);
waveform = bleWaveformGenerator(messageBits, ...
Mode=phyMode, SamplesPerSymbol=sps, ...
ChannelIndex=ch, WhitenStatus="On");
symbolRate = 1e6 + 1e6*(phyMode=="LE2M");
fs = symbolRate * sps;
phyModes = ["LE1M","LE2M","LE500K","LE125K"];
sps = 8;
messageBits = randi([0 1], 37*8, 1);
figure; tl = tiledlayout(4,2,TileSpacing="compact",Padding="compact");
title(tl,"BLE - All PHY Modes")
for idx = 1:4
wf = bleWaveformGenerator(messageBits, ...
Mode=phyModes(idx), SamplesPerSymbol=sps, ChannelIndex=37, WhitenStatus="On");
sr = 1e6 + 1e6*(phyModes(idx)=="LE2M");
fs = sr*sps; t = (0:length(wf)-1)/fs*1e6;
nexttile; plot(t,real(wf),t,imag(wf))
xlabel("Time (\mus)"); ylabel("Amplitude"); title(phyModes(idx)+" - IQ")
legend("I","Q"); grid on; xlim([0 min(80,t(end))])
nexttile; N=length(wf); f=(-N/2:N/2-1)*(fs/N)/1e6;
plot(f, 20*log10(abs(fftshift(fft(wf)))/N+eps))
xlabel("Frequency (MHz)"); ylabel("dB"); title(phyModes(idx)+" - Spectrum")
grid on; xlim([-3 3]); ylim([-80 0])
end
sps = 8; fs = 1e6*sps;
messageBits = randi([0 1], 37*8, 1);
ifsGap = complex(zeros(round(150e-6*fs), 1));
burst = complex(zeros(0,1));
for pkt = 1:3
wf = bleWaveformGenerator(messageBits, Mode="LE1M", SamplesPerSymbol=sps, ChannelIndex=37);
burst = [burst; wf; ifsGap]; %#ok<AGROW>
end
messageBits = randi([0 1], 37*8, 1); sps = 8;
wf = bleWaveformGenerator(messageBits, Mode="LE1M", SamplesPerSymbol=sps, ChannelIndex=37, WhitenStatus="On");
rxBits = bleIdealReceiver(wf, Mode="LE1M", SamplesPerSymbol=sps, ChannelIndex=37, WhitenStatus="On");
assert(isequal(rxBits, messageBits))
accessAddr = randi([0 1], 32, 1);
wf = bleWaveformGenerator(randi([0 1],27*8,1), ...
Mode="LE2M", SamplesPerSymbol=8, ChannelIndex=9, ...
AccessAddress=accessAddr, WhitenStatus="On");
fs = 2e6 * 8;
messageBits = randi([0 1], 20*8, 1); sps = 8; fs = 1e6*sps;
wf_noCTE = bleWaveformGenerator(messageBits, Mode="LE1M", ...
SamplesPerSymbol=sps, ChannelIndex=10, WhitenStatus="On");
wf_conn = bleWaveformGenerator(messageBits, Mode="LE1M", ...
SamplesPerSymbol=sps, ChannelIndex=10, WhitenStatus="On", DFPacketType="ConnectionCTE");
wf_cless = bleWaveformGenerator(messageBits, Mode="LE1M", ...
SamplesPerSymbol=sps, ChannelIndex=10, WhitenStatus="On", DFPacketType="ConnectionlessCTE");
% CTE appends unmodulated constant tone for antenna switching (typ. 72-216 us)
cteDelta_conn_us = (length(wf_conn) - length(wf_noCTE)) / fs * 1e6;
cteDelta_cless_us = (length(wf_cless) - length(wf_noCTE)) / fs * 1e6;
pduHex = '02049B03270102030405'; % 10-byte PDU (>=6 bytes needed for >=11 IQ samples)
pdu = int2bit(hex2dec(reshape(pduHex, 2, [])'), 8, false);
pdu = pdu(:);
cfgCRC = crcConfig(Polynomial="z^24+z^10+z^9+z^6+z^4+z^3+z+1", ...
InitialConditions=int2bit(hex2dec('555551'),24), DirectMethod=true);
pduCRC = crcGenerate(pdu, cfgCRC);
txWf = bleWaveformGenerator(pduCRC, ChannelIndex=36, DFPacketType="ConnectionlessCTE");
[~, ~, iqSamples] = bleIdealReceiver(txWf, ChannelIndex=36, ...
DFPacketType="ConnectionlessCTE", SlotDuration=2);
cfgAngle = bleAngleEstimateConfig;
cfgAngle.ArraySize = 4; cfgAngle.SlotDuration = 2; cfgAngle.SwitchingPattern = [1 2 3 4];
angle = bleAngleEstimate(iqSamples, cfgAngle);
Alternative (R2022a+): iqSamples = bleCTEIQSample(cteSamples, Mode="LE1M", SlotDuration=2);
messageBits = randi([0 1], 100*8, 1); sps = 8; phyMode = "LE1M";
snrValues = 0:4:20; berResults = zeros(size(snrValues));
for idx = 1:length(snrValues)
wf = bleWaveformGenerator(messageBits, Mode=phyMode, SamplesPerSymbol=sps, ChannelIndex=5, WhitenStatus="On");
sigPower = mean(abs(wf).^2);
rxWf = awgn(wf, snrValues(idx), 10*log10(sigPower));
rxBits = bleIdealReceiver(rxWf, Mode=phyMode, SamplesPerSymbol=sps, ChannelIndex=5, WhitenStatus="On");
n = min(length(messageBits), length(rxBits));
berResults(idx) = sum(messageBits(1:n) ~= double(rxBits(1:n))) / n;
end
semilogy(snrValues, berResults, "-o"); xlabel("SNR (dB)"); ylabel("BER"); grid on
bleIdealReceiver returns int8 — cast with double(). For Eb/No conversion: snr = convertSNR(EbNo,"ebno","snr",SamplesPerSymbol=sps).
cfg = bluetoothTestWaveformConfig;
cfg.Mode = "LE1M"; cfg.PayloadLength = 37;
cfg.PacketType = "Disabled"; cfg.ModulationIndex = 0.5;
testWf = bluetoothTestWaveform(cfg);
rfCfg = bluetoothRFPHYTestConfig;
rfCfg.Test = "Output power"; rfCfg.Mode = "LE1M";
rfCfg.PayloadLength = 37; rfCfg.OutputPower = 0; rfCfg.CenterFrequency = "Mid";
sps = 8; fs = 1e6*sps;
bleWf = bleWaveformGenerator(randi([0 1],37*8,1), Mode="LE1M", SamplesPerSymbol=sps, ChannelIndex=37, WhitenStatus="On");
t = (0:length(bleWf)-1)'/fs;
wlanInterferer = 0.1*complex(randn(length(bleWf),1),randn(length(bleWf),1)) .* exp(1j*2*pi*3e6*t);
combined = bleWf + wlanInterferer;
CIR_dB = 10*log10(mean(abs(bleWf).^2) / mean(abs(wlanInterferer).^2));
sps = 8; wf = bleWaveformGenerator(randi([0 1],255*8,1), Mode="LE1M", ...
SamplesPerSymbol=sps, ChannelIndex=37, WhitenStatus="On", ModulationIndex=0.5);
fs = 1e6*sps; N = length(wf);
avgPower_dBm = 10*log10(mean(abs(wf).^2)) + 30;
spec = abs(fftshift(fft(wf))).^2; cumP = cumsum(spec)/sum(spec);
f = (-N/2:N/2-1)*(fs/N);
occBW_MHz = (f(find(cumP>=0.995,1)) - f(find(cumP>=0.005,1))) / 1e6;
papr_dB = 10*log10(max(abs(wf).^2) / mean(abs(wf).^2));
bits = randi([0 1], 37*8, 1); sps = 8;
wfOn = bleWaveformGenerator(bits, Mode="LE1M", SamplesPerSymbol=sps, ChannelIndex=37, WhitenStatus="On");
wfOff = bleWaveformGenerator(bits, Mode="LE1M", SamplesPerSymbol=sps, ChannelIndex=37, WhitenStatus="Off");
cfgAdv = bleLLAdvertisingChannelPDUConfig;
cfgAdv.PDUType = "Advertising indication";
cfgAdv.AdvertiserAddress = "A1B2C3D4E5F6";
cfgAdv.AdvertiserAddressType = "Random";
cfgAdv.AdvertisingData = "0201060709546573744245020A00";
pduBits = bleLLAdvertisingChannelPDU(cfgAdv);
wf = bleWaveformGenerator(pduBits, Mode="LE1M", SamplesPerSymbol=8, ChannelIndex=37);
PDUType values: "Advertising indication", "Scan request", "Scan response", "Connection indication", "Advertising direct indication", "Advertising non connectable indication".
Addresses: 12-char hex string (no colons). AdvertisingData: hex string (length-type-value AD structs).
cfgData = bleLLDataChannelPDUConfig;
cfgData.LLID = "Data (start fragment/complete)";
cfgData.SequenceNumber = 1;
cfgData.NESN = 0;
cfgData.MoreData = false;
payload = dec2hex(randi([0 255], 50, 1))'; % 50-byte hex payload
payload = reshape(payload', 1, []);
pduBits = bleLLDataChannelPDU(cfgData, payload);
wf = bleWaveformGenerator(pduBits, Mode="LE2M", SamplesPerSymbol=8, ChannelIndex=15);
LLID values: "Data (continuation fragment/empty)", "Data (start fragment/complete)", "Control".
SequenceNumber (not SN). Payload: hex string, numeric vector [0,255], or n×2 char array.
Uses helperBLEPracticalReceiver (AGC + CFO + timing recovery). Requires: openExample('bluetooth/BLEPracticalReceiverExample'). SNR conversion for coded modes: SNR = EbNo + 10*log10(codeRate) - 10*log10(sps).
| Mode | ConnectionCTE | ConnectionlessCTE |
|------|:---:|:---:|
| LE1M | Yes | Yes |
| LE2M | Yes | No |
| LE500K | No | No |
| LE125K | No | No |
CTE requires data channels (0-36). Never use ChannelIndex 37/38/39 with DFPacketType≠"Disabled". CTE is a data-channel-only feature.
'8E89BED6' (fixed); data = random.bleIdealReceiver: Hard-decision only, returns int8.BN × IRC × Sub_Interval <= ISO_Interval. If math exceeds, REFUSE.| Request | Why Invalid |
|---------|-------------|
| LE2M + FEC coding | FEC only exists in coded PHY (LE500K/LE125K) |
| LE2M on ch 37/38/39 | Primary advertising = LE1M or Coded only |
| CTE + LE500K/LE125K | CTE restricted to LE1M/LE2M per BLE 5.1 spec |
| ConnectionlessCTE + LE2M | ConnectionlessCTE = LE1M only |
| "3-slot" or "5-slot" BLE | Slot-based = BR/EDR; BLE uses single packets |
| CTE on ch 37/38/39 | CTE = data channels (0-36) only |
| ISO params where BN×IRC×Sub_Int > ISO_Interval | Sub-events exceed interval = impossible scheduling |
fs = (1e6 + 1e6*(mode=="LE2M")) * sps — never hardcodetiledlayout/nexttile (not subplot)20*log10(abs(fftshift(fft(wf)))/N + eps) — do NOT use periodogram, pspectrum, or pwelch for BLE spectral plotsxlim([0 min(80,t(end))]) for coded modesplot(t, real(wf), t, imag(wf))complex(zeros(N,1)) (output is complex)SamplesPerSymbol>=4 for spectrum plots; SPS=1 is valid for generation but insufficient for visualization or accurate spectral analysis (at Nyquist limit)Generated scripts must follow MathWorks example conventions. Use %% section headers, softcode all parameters, and comment non-obvious logic.
Divide scripts with %% headers: Configuration → Waveform Generation → Processing → Analysis → Visualization. All user-configurable values as named variables at the top; no magic numbers in processing logic.
%% Configuration
% Specify BLE waveform generation parameters.
phyMode = "LE1M"; % PHY transmission mode
payloadLength = 37; % Payload length in bytes
sps = 8; % Samples per symbol
channelIndex = 37; % BLE channel index (0-39)
% Derived parameters
symbolRate = 1e6 + 1e6*(phyMode=="LE2M"); % 2 Msym/s for LE2M, 1 Msym/s otherwise
fs = sps * symbolRate; % Sample rate in Hz
numBits = payloadLength * 8; % Payload length in bits
%% Waveform Generation
% Generate BLE waveform from random payload bits.
messageBits = randi([0 1], numBits, 1);
waveform = bleWaveformGenerator(messageBits, ...
Mode=phyMode, SamplesPerSymbol=sps, ChannelIndex=channelIndex);
% Eb/No in dB% IFS gap must be complex (bleWaveformGenerator output is complex)% LE2M uses 2 Msym/s (only mode with 2x symbol rate)end statementsmessageBits, txWaveform, symbolRate, channelIndexcountMode, pktIdx (not i, j)bitsPerByte = 8, tIFS = 150e-6[numErrors, perCount] = deal(0, 0)| Mistake | Fix |
|---------|-----|
| bleWaveformConfig(...) | Does not exist. Use bleWaveformGenerator(bits, NV...) directly |
| bluetoothWaveformGenerator for BLE | BR/EDR only. Use bleWaveformGenerator |
| comm.BLEReceiver | Does not exist. Use bleIdealReceiver |
| symbolRate=1e6 for LE2M | LE2M = 2e6 |
| 'PHYMode' as param name | Use 'Mode' |
| 'LECODED' as mode | Use "LE500K" or "LE125K" |
| Config object as 1st arg | Must be binary col vector randi([0 1],N,1) |
| Empty [] bits | Minimum 1 byte |
| Real zeros() for IFS | Use complex(zeros(N,1)) |
| CTE + coded modes | CTE only on LE1M/LE2M |
| ConnectionlessCTE + LE2M | LE1M only |
| LE2M on ch 37/38/39 | Spec violation. Use LE1M or Coded |
| CTELength/CTEType as NV args | Not valid. Use DFPacketType |
| bleAngleEstimate(wf, NV...) | Use bleAngleEstimate(iqSamples, bleAngleEstimateConfig) |
| bleWaveformGenerator for RF-PHY | Use bluetoothTestWaveformConfig + bluetoothTestWaveform |
| AWGN without oversampling correction | Use convertSNR(EbNo,"ebno","snr",SamplesPerSymbol=sps); pass explicit power to awgn (never use "measured") |
| bleIdealReceiver with impairments | Use helperBLEPracticalReceiver |
| Eb/No for coded modes without code rate | SNR = EbNo + 10*log10(codeRate) - 10*log10(sps) |
| SPS=1 without warning | SPS=1 is at Nyquist limit — warn user: no spectral analysis, marginal decode performance |
| Timing ppm offset without resample | Apply clock drift via resample(wf, 1e6+ppm, 1e6) or fractional delay filter |
| Conformance Df1/Df2 without PRBS9 | Use comm.PNSequence (z^9+z^5+1) for TP/TRM/CA/BV-05-C; separate stable-bit (Df1) from alternating-bit (Df2) patterns |
| periodogram/pwelch for BLE spectrum | Use manual fftshift(fft(wf)) — skill convention |
| SN as property name | Use SequenceNumber on bleLLDataChannelPDUConfig |
| Address with colons "A1:B2:..." | Use 12-char hex string without colons: "A1B2C3D4E5F6" |
| PDUType="ADV_IND" | Use full string: "Advertising indication" |
| Binary vector as AdvertisingData | Use hex string: "0201060709..." (length-type-value) |
----
Copyright 2026 The MathWorks, Inc.
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Use when the user asks to run Codex CLI (codex exec, codex resume) or references OpenAI Codex for code analysis, refactoring, or automated editing. Uses GPT-5.2 by default for state-of-the-art software engineering.
Implement memory-safe programming with RAII, ownership, smart pointers, and resource management across Rust, C++, and C. Use when writing safe systems code, managing resources, or preventing memory bugs.
Python/HTSlib workflows for genomic files. Use when reading, querying, filtering, or writing SAM/BAM/CRAM, VCF/BCF, FASTA/FASTQ, or tabix data with pysam, including pileup, coverage, indexing, and CRAM references.
Evaluate scientific claims and evidence quality. Use for assessing experimental design validity, identifying biases and confounders, applying evidence grading frameworks (GRADE, Cochrane Risk of Bias), or teaching critical analysis. Best for understanding evidence quality, identifying flaws. For formal peer review writing use peer-review.
Use when a user asks to debug or fix failing GitHub PR checks that run in GitHub Actions; use `gh` to inspect checks and logs, summarize failure context, draft a fix plan, and implement only after explicit approval. Treat external providers (for example Buildkite) as out of scope and report only the details URL.
> Create, build, deploy, and localize declarative agents for M365 Copilot and Teams. USE THIS SKILL for ANY task involving a declarative agent — including localization, scaffolding, editing manifests, adding capabilities, and deploying. Localization requires tokenized manifests and language files that only this skill knows how to produce. "scaffold an agent", "new agent project", "add a capability", "add a plugin", "configure my agent", "deploy my agent", "fix my agent manifest", "edit my agent", "localize my agent", "add localization", "translate my agent", "multi-language agent", "add an API plugin", "add an MCP plugin", "add OAuth to my plugin", "review instructions", "improve instructions", "fix my instructions"
Documentation generation workflow covering API docs, architecture docs, README files, code comments, and technical writing.
Take matlab/matlab-generate-ble-waveform from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.