Skip to content

Commit

Permalink
current working version
Browse files Browse the repository at this point in the history
  • Loading branch information
jantman committed Oct 30, 2018
1 parent 0de3ae8 commit 6d1f605
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 73 deletions.
11 changes: 6 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ include/
.idea/
result.json
*.json
jitter_*.png
quality_*.png
rssi_*.png
tcp_*.png
udp_*.png
/jitter_*.png
/quality_*.png
/rssi_*.png
/tcp_*.png
/udp_*.png
/channels*.png
80 changes: 79 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
python-wifi-survey-heatmap
==========================

.. image:: https://www.repostatus.org/badges/latest/wip.svg
:alt: Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.
:target: https://www.repostatus.org/#wip

A Python application for Linux machines to perform WiFi site surveys and present
the results as a heatmap overlayed on a floorplan.

Expand Down Expand Up @@ -49,7 +53,7 @@ First connect to the network that you want to survey. Then, run ``sudo wifi-surv

* ``INTERFACE`` is the name of your Wireless interface (e.g. ``wlp3s0``)
* ``SERVER`` is the IP address or hostname of the iperf3 server
* ``PNG`` is the path to a floorplan PNG file to use as the background for the map; see `example_floorplan.png <example_floorplan.png>`_ for an example. In order to compare multiple surveys it may be helpful to pre-mark your measurement points on the floorplan, like `example_with_marks.png <example_with_marks.png`_. The UI currently loads the PNG at exact size, so it may help to scale your PNG file to your display.
* ``PNG`` is the path to a floorplan PNG file to use as the background for the map; see `examples/example_floorplan.png <examples/example_floorplan.png>`_ for an example. In order to compare multiple surveys it may be helpful to pre-mark your measurement points on the floorplan, like `examples/example_with_marks.png <examples/example_with_marks.png`_. The UI currently loads the PNG at exact size, so it may help to scale your PNG file to your display.
* ``Title`` is the title for the survey (such as the network name or AP location), which will also be used to name the data file and output files.

If ``Title.json`` already exists, the data from it will be pre-loaded into the application; this can be used to resume a survey.
Expand All @@ -62,3 +66,77 @@ Heatmap Generation
++++++++++++++++++

Once you've performed a survey with a given title and the results are saved in ``Title.json``, run ``wifi-heatmap PNG Title`` to generate heatmap files in the current directory. This process does not require (and shouldn't have) root/sudo and operates only on the JSON data file. For this, it will look better if you use a PNG without the measurement location marks.

The end result of this process for a given survey (Title) should be XX ``.png`` images in your current directory:

* **channels24_TITLE.png** - Bar graph of average signal quality of APs seen on 2.4 GHz channels, by channel. Useful for visualizing channel contention. (Based on 20 MHz channel bandwidth)
* **channels5_TITLE.png** - Bar graph of average signal quality of APs seen on 5 GHz channels, by channel. Useful for visualizing channel contention. (Based on per-channel bandwidth from 20 to 160 MHz)
* **jitter_TITLE.png** - Heatmap based on UDP jitter measurement in milliseconds.
* **quality_TITLE.png** - Heatmap based on iwconfig's "quality" metric.
* **rssi_TITLE.png** - Heatmap based on iwconfig's signal strength (rssi) metric.
* **tcp_download_Mbps_TITLE.png** - Heatmap of iperf3 transfer rate, TCP, downloading from server to client.
* **tcp_upload_Mbps_TITLE.png** - Heatmap of iperf3 transfer rate, TCP, uploading from client to server.
* **udp_Mbps_TITLE.png** - Heatmap of iperf3 transfer rate, UDP, uploading from client to server.

Examples
--------

Floorplan
+++++++++

.. image:: examples/example_floorplan.png
:alt: example floorplan image

Floorplan with Measurement Marks
++++++++++++++++++++++++++++++++

.. image:: examples/example_with_marks.png
:alt: example floorplan image with measurement marks

2.4 GHz Channels
++++++++++++++++

.. image:: examples/channels24_WAP1.png
:alt: example 2.4 GHz channel usage

5 GHz Channels
++++++++++++++

.. image:: examples/channels5_WAP1.png
:alt: example 5 GHz channel usage

Jitter
++++++

.. image:: examples/jitter_WAP1.png
:alt: example jitter heatmap

Quality
+++++++

.. image:: examples/quality_WAP1.png
:alt: example quality heatmap

RSSI / Signal Strength
++++++++++++++++++++++

.. image:: examples/rssi_WAP1.png
:alt: example rssi heatmap

TCP Download Speed (Mbps)
+++++++++++++++++++++++++

.. image:: examples/tcp_download_Mbps_WAP1.png
:alt: example tcp download heatmap

TCP Upload Speed (Mbps)
+++++++++++++++++++++++

.. image:: examples/tcp_upload_Mbps_WAP1.png
:alt: example tcp upload heatmap

UDP Upload Speed (Mbps)
+++++++++++++++++++++++

.. image:: examples/udp_Mbps_WAP1.png
:alt: example udp upload heatmap
Binary file added examples/channels24_WAP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/channels5_WAP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
Binary file added examples/jitter_WAP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/quality_WAP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/rssi_WAP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/tcp_download_Mbps_WAP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/tcp_upload_Mbps_WAP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/udp_Mbps_WAP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
195 changes: 128 additions & 67 deletions wifi_survey_heatmap/heatmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,69 +58,69 @@

WIFI_CHANNELS = {
# center frequency to (channel, bandwidth MHz)
2412: (1, 20),
2417: (2, 20),
2422: (3, 20),
2427: (4, 20),
2432: (5, 20),
2437: (6, 20),
2442: (7, 20),
2447: (8, 20),
2452: (9, 20),
2457: (10, 20),
2462: (11, 20),
2467: (12, 20),
2472: (13, 20),
2484: (14, 20),
5160: (32, 20),
5170: (34, 40),
5180: (36, 20),
5190: (38, 40),
5200: (40, 20),
5210: (42, 80),
5220: (44, 20),
5230: (46, 40),
5240: (48, 20),
5250: (50, 160),
5260: (52, 20),
5270: (54, 40),
5280: (56, 20),
5290: (58, 80),
5300: (60, 20),
5310: (62, 40),
5320: (64, 20),
5340: (68, 20),
5480: (96, 20),
5500: (100, 20),
5510: (102, 40),
5520: (104, 20),
5530: (106, 80),
5540: (108, 20),
5550: (110, 40),
5560: (112, 20),
5570: (114, 160),
5580: (116, 20),
5590: (118, 40),
5600: (120, 20),
5610: (122, 80),
5620: (124, 20),
5630: (126, 40),
5640: (128, 20),
5660: (132, 20),
5670: (134, 40),
5680: (136, 20),
5690: (138, 80),
5700: (140, 20),
5710: (142, 40),
5720: (144, 20),
5745: (149, 20),
5755: (151, 40),
5765: (153, 20),
5775: (155, 80),
5785: (157, 20),
5795: (159, 40),
5805: (161, 20),
5825: (165, 20)
2412.0: (1, 20.0),
2417.0: (2, 20.0),
2422.0: (3, 20.0),
2427.0: (4, 20.0),
2432.0: (5, 20.0),
2437.0: (6, 20.0),
2442.0: (7, 20.0),
2447.0: (8, 20.0),
2452.0: (9, 20.0),
2457.0: (10, 20.0),
2462.0: (11, 20.0),
2467.0: (12, 20.0),
2472.0: (13, 20.0),
2484.0: (14, 20.0),
5160.0: (32, 20.0),
5170.0: (34, 40.0),
5180.0: (36, 20.0),
5190.0: (38, 40.0),
5200.0: (40, 20.0),
5210.0: (42, 80.0),
5220.0: (44, 20.0),
5230.0: (46, 40.0),
5240.0: (48, 20.0),
5250.0: (50, 160.0),
5260.0: (52, 20.0),
5270.0: (54, 40.0),
5280.0: (56, 20.0),
5290.0: (58, 80.0),
5300.0: (60, 20.0),
5310.0: (62, 40.0),
5320.0: (64, 20.0),
5340.0: (68, 20.0),
5480.0: (96, 20.0),
5500.0: (100, 20.0),
5510.0: (102, 40.0),
5520.0: (104, 20.0),
5530.0: (106, 80.0),
5540.0: (108, 20.0),
5550.0: (110, 40.0),
5560.0: (112, 20.0),
5570.0: (114, 160.0),
5580.0: (116, 20.0),
5590.0: (118, 40.0),
5600.0: (120, 20.0),
5610.0: (122, 80.0),
5620.0: (124, 20.0),
5630.0: (126, 40.0),
5640.0: (128, 20.0),
5660.0: (132, 20.0),
5670.0: (134, 40.0),
5680.0: (136, 20.0),
5690.0: (138, 80.0),
5700.0: (140, 20.0),
5710.0: (142, 40.0),
5720.0: (144, 20.0),
5745.0: (149, 20.0),
5755.0: (151, 40.0),
5765.0: (153, 20.0),
5775.0: (155, 80.0),
5785.0: (157, 20.0),
5795.0: (159, 40.0),
5805.0: (161, 20.0),
5825.0: (165, 20.0)
}


Expand Down Expand Up @@ -167,8 +167,9 @@ def generate(self):
for k in a.keys():
if k in ['x', 'y']:
continue
a[k] = [0 if x is None else x for x in a[k]]
a[k].append(min(a[k]))
self._plot_channels()
self._channel_graphs()
num_x = int(self._image_width / 4)
num_y = int(num_x / (self._image_width / self._image_height))
x = np.linspace(0, self._image_width, num_x)
Expand All @@ -187,7 +188,13 @@ def generate(self):
a, k, '%s - %s' % (self._title, ptitle), gx, gy, num_x, num_y
)

def _plot_channels(self):
def _channel_to_signal(self):
"""
Return a dictionary of 802.11 channel number to combined "quality" value
for all APs seen on the given channel. This includes interpolation to
overlapping channels based on channel width of each channel.
"""
# build a dict of frequency (GHz) to list of quality values
channels = defaultdict(list)
for row in self._data:
for scan in row['result']['iwscan']:
Expand All @@ -196,12 +203,66 @@ def _plot_channels(self):
channels[scan['Frequency'] / 1000000].append(
scan['stats']['quality']
)
# collapse down to dict of frequency (GHz) to average quality (float)
for freq in channels.keys():
channels[freq] = sum(channels[freq]) / len(channels[freq])
print(channels)
raise NotImplementedError()
# build the full dict of frequency to quality for all channels
freq_qual = {x: 0.0 for x in WIFI_CHANNELS.keys()}
# then, update to account for full bandwidth of each channel
for freq, qual in channels.items():
freq_qual[freq] += qual
for spread in range(
int(freq - (WIFI_CHANNELS[freq][1] / 2.0)),
int(freq + (WIFI_CHANNELS[freq][1] / 2.0) + 1.0)
):
if spread in freq_qual and spread != freq:
freq_qual[spread] += qual
return {
WIFI_CHANNELS[x][0]: freq_qual[x] for x in freq_qual.keys()
}

def _plot_channels(self, names, values, title, fname, ticks):
pp.rcParams['figure.figsize'] = (
self._image_width / 300, self._image_height / 300
)
fig, ax = pp.subplots()
ax.set_title(title)
ax.bar(names, values)
ax.set_xlabel('Channel')
ax.set_ylabel('Mean Quality')
ax.set_xticks(ticks)
#ax.set_xticklabels(names)
logger.info('Writing plot to: %s', fname)
pp.savefig(fname, dpi=300)
pp.close('all')

def _channel_graphs(self):
c2s = self._channel_to_signal()
names24 = []
values24 = []
names5 = []
values5 = []
for ch, val in c2s.items():
if ch < 15:
names24.append(ch)
values24.append(val)
else:
names5.append(ch)
values5.append(val)
self._plot_channels(
names24, values24, '2.4GHz Channel Utilization',
'%s_%s.png' % ('channels24', self._title),
names24
)
ticks5 = [
38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159
]
self._plot_channels(
names5, values5, '5GHz Channel Utilization',
'%s_%s.png' % ('channels5', self._title),
ticks5
)

def _add_inner_title(self, ax, title, loc, size=None, **kwargs):
if size is None:
size = dict(size=pp.rcParams['legend.fontsize'])
Expand Down

0 comments on commit 6d1f605

Please sign in to comment.