Skip to content

Advanced energy scan - #49

Draft
puddly wants to merge 6 commits into
zigpy:devfrom
puddly:puddly/advanced-energy-scan
Draft

Advanced energy scan#49
puddly wants to merge 6 commits into
zigpy:devfrom
puddly:puddly/advanced-energy-scan

Conversation

@puddly

@puddlypuddly commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

To run it:

# Collect the data
pip install 'git+https://github.com/puddly/zigpy-cli@puddly/advanced-energy-scan'
zigpy radio ezsp /dev/cu.SLAB_USBtoUART advanced-energy-scan \
--num-energy-scans 1000 \
--num-network-scans 20 \
advanced-energy-scan.json
# Plot it with the script below
pip install pandas matplotlib
python plot.py advanced-energy-scan.json

Inspired by zigpy/zha#51.

I'm thinking of taking a slightly different approach to pick channels. Instead of performing a "long" scan with a high exponent, we instead perform many short scans. The theory is that the one long scan is just max(short_scans) for the same time period so we're just discarding data.

This allows for a much more granular view of the spectrum that can be combined with a beacon scan to identify real noise as opposed to a Zigbee router right next to the coordinator:

advanced-energy-scan

Script to generate the above plot:

importsysimportjsonimportpathlibimportpandasaspdimportmatplotlib.pyplotaspltimportmatplotlib.patchesaspatchespath=pathlib.Path(sys.argv[1])
data=json.loads(path.read_text())
current_channel=data["current_channel"]
energy_scan=pd.DataFrame(data=data["energy_scan"])
energy_scan.timestamp=pd.to_datetime(energy_scan.timestamp, unit="s")
network_scan=pd.DataFrame(data["network_scan"])
ifenergy_scan.rssi.isna().any():
# LQI only scanvmin=0vmax=255energy_scan["scan"] =energy_scan.energyscan_type="lqi"else:
# Scan with RSSIvmin=-100vmax=10energy_scan["scan"] =energy_scan.rssiscan_type="rssi"# Define the colormap for histogramsplt.rcParams["figure.dpi"] =50energy_cmap=plt.get_cmap("viridis")
network_cmap=plt.get_cmap("hsv")
norm=plt.Normalize(vmin=vmin, vmax=vmax)
# Set up the plotnum_channels=len(energy_scan.channel.drop_duplicates().sort_values().to_list())
fig, axes=plt.subplots(nrows=num_channels, figsize=(10, 20), sharex=True)
fig.suptitle(
(
"Energy Distribution Across 802.15.4 Channels"+f" ({len(energy_scan) //num_channels} samples)\n"+path.name
),
fontsize=20,
)
forax, ((channel,), scans) inzip(axes, energy_scan.groupby(["channel"])):
counts, bins, hist_patches=ax.hist(
scans["scan"],
bins=100,
range=(vmin, vmax),
color="black",
edgecolor="black",
)
forpatch, bin_leftinzip(hist_patches, bins):
patch.set_facecolor(energy_cmap(norm(bin_left)))
networks=Noneifnotnetwork_scan.empty:
networks=network_scan[network_scan.channel==channel].copy()
# Draw vertical lines according to each network beacon, colored by extended_pan_idifnotnetworks.empty:
network_indexes=pd.factorize(networks.extended_pan_id)[0]
networks["color"] =network_indexes/ (network_indexes.max() +1)
for_, networkinnetworks.iterrows():
ax.axvline(
(network.rssiifscan_type=="rssi"elsenetwork.lqi),
color=network_cmap(network.color),
linestyle="dotted",
linewidth=3,
alpha=0.5,
zorder=0,
)
title="$\\bf{"+f"Channel\\{channel}"+"}$"ifnetworksisnotNoneandnotnetworks.empty:
title+=f"\n{len(networks)} networks"ax.set_frame_on(False)
ax.set_ylabel(title, rotation=0)
ax.set_ylim(0, None)
ax.set_xlim(vmin, vmax)
ax.yaxis.set_label_coords(-0.1, 0.4)
ax.yaxis.set_ticks([])
# Show the x axis on the last channelifchannel==26:
ax.set_xlabel("RSSI (dBm)"ifscan_type=="rssi"else"LQI")
else:
ax.xaxis.set_visible(False)
# Highlight the plot for the current channelifchannel==current_channel:
ax.set_facecolor("lightblue")
plt.tight_layout()
plt.savefig(f"{path.stem}.png", dpi=200)
plt.show()

@Hedda

Hedda commented Aug 4, 2024

Copy link
Copy Markdown

Inspired by zigpy/zha#51

Suggest also check out ideas discussed in this related forum thread -> https://community.home-assistant.io/t/i-tracked-channel-utilization-with-zha-to-find-the-best-zigbee-channel/656139

@codecov

codecovBot commented Aug 19, 2024

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 52 lines in your changes missing coverage. Please review.
✅ Project coverage is 2.02%. Comparing base (ab58a1b) to head (ebc953d).
⚠️ Report is 7 commits behind head on dev.

Files with missing linesPatch %Lines
zigpy_cli/radio.py0.00%52 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## dev #49 +/- ##
========================================
- Coverage 2.23% 2.02% -0.21% 
========================================
Files 8 8 Lines 492 543 +51 ========================================
Hits 11 11 - Misses 481 532 +51 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Hedda

This comment was marked as off-topic.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@puddly@Hedda