Skip to content

Commit

Permalink
Merge pull request #6 from DL6ER/master
Browse files Browse the repository at this point in the history
Further enhancements
  • Loading branch information
jantman committed Dec 22, 2020
2 parents e59341c + 39f4d98 commit 8dbf419
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 129 deletions.
18 changes: 11 additions & 7 deletions Dockerfile
Expand Up @@ -6,22 +6,26 @@ ARG repo_ref
USER root

RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
DEBIAN_FRONTEND=noninteractive \
apt-get install -y --no-install-recommends \
iperf3 \
gcc \
git \
pulseaudio-utils \
python3 \
python3-dev \
python3-matplotlib \
python3-pip \
python3-scipy \
python3-setuptools \
python3-wheel \
python3-wxgtk4.0 \
wireless-tools && \
pip3 install \
iperf3
wireless-tools \
&& rm -rf /var/lib/apt/lists/*

RUN pip3 install iperf3 matplotlib scipy wheel

# Install libnl from DL6ER's fork because the python3.6+
# compatibility fixes weren't included in the upstream
# project in 12/2020
RUN pip3 install --upgrade --user git+https://github.com/DL6ER/libnl

COPY . /app

Expand Down
51 changes: 26 additions & 25 deletions README.rst
Expand Up @@ -20,7 +20,7 @@ Quick start

Check out the **Running In Docker** steps below to get single-line commands that run without the need to install *anything* on your computer (thanks to using `docker`).
Creating a heatmap using the software consists of the following three essential steps:
1. Start an `iperf3` server on any machine in your local network. This server is used for bandwidth measurements to be independent of your Internet connection.
1. Start an `iperf3` server on any machine in your local network. This server is used for bandwidth measurements to be independent of your Internet connection. When omitting the `--server` option, this may be skipped, however, be aware that the performance heatmaps tpyically are the icing on the cake of your measurement and are very useful in determining the *real* performance of your WiFi.
2. Use the `wifi-survey` tool to record a measurement. You can load a floorplan and click on your current location ot record signal strength and determine the achievable bandwidth.
3. Once done with all the measurements, use the `wifi-heatmap` tool to compute a high-resolution heatmap from your recorded data. In case your data turns out to be too coarse, you can always go back to step 2 and delete or move old and also add new measurements at any time.

Expand All @@ -32,7 +32,7 @@ Installation and Dependencies
* The Python `iperf3 <https://pypi.org/project/iperf3/>`_ package, which needs `iperf3 <http://software.es.net/iperf/>`_ installed on your system.
* The Python `libiw <https://pypi.org/project/libiw/>`_ package.
* `wxPython Phoenix <https://wiki.wxpython.org/How%20to%20install%20wxPython>`_, which unfortunately must be installed using OS packages or built from source.
* An iperf3 server running on another system on the LAN, as described below.
* An iperf3 server running on another system on the LAN, as described below is recommended but optional.

Recommended installation is via ``python setup.py develop`` in a virtualenv setup with ``--system-site-packages`` (for the above dependencies).

Expand All @@ -43,15 +43,15 @@ Data Collection

At each survey location, data collection should take 45-60 seconds. The data collected is currently:

* 10-second iperf3 measurement, TCP, client (this app) sending to server, default iperf3 options
* 10-second iperf3 measurement, TCP, server sending to client, default iperf3 options
* 10-second iperf3 measurement, UDP, client (this app) sending to server, default iperf3 options
* 10-second iperf3 measurement, TCP, client (this app) sending to server, default iperf3 options [optional, enable with `--server`]
* 10-second iperf3 measurement, TCP, server sending to client, default iperf3 options [optional, enable with `--server`]
* 10-second iperf3 measurement, UDP, client (this app) sending to server, default iperf3 options [optional, enable with `--server`]
* Recording of various WiFi details such as advertised channel bandwidth, bitrate, or signal strength
* Scan of all visible access points in the vicinity
* Scan of all visible access points in the vicinity [optional, enable with `--scan`]

Hints:
- The duration of the bandwidth measurement can be changed using the `--duration` argument of `wifi-survey`. This has great influence on the actual length of the individual data collections.
- Scanning for other network takes rather long. As this isn't required in most cases, you can skip this using `wifi-survey --no-scan`
- Scanning for other network takes rather long. As this isn't required in most cases, it is not enabled by default

Usage
-----
Expand All @@ -68,20 +68,19 @@ Performing a Survey

The survey tool (``wifi-survey``) must be run as root or via ``sudo`` in order to use iwconfig/iwlist.

First connect to the network that you want to survey. Then, run ``sudo wifi-survey INTERFACE SERVER PNG Title`` where:
First connect to the network that you want to survey. Then, run ``sudo wifi-survey`` where:

* ``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 `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.
Command-line options include:

If ``Title.json`` already exists, the data from it will be pre-loaded into the application; this can be used to resume a survey.
* ``-i INTERFACE`` / ``--interface INTERFACE`` is the name of your Wireless interface (e.g. ``wlp3s0``)
* ``-p PICTURE`` / ``--picture PICTURE`` 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.
* ``-t TITLE`` / ``--title 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.
* ``-s IPERF3_SERVER`` / ``--server IPERF3_SERVER`` to enable ``iperf3`` scans. The generated speed heatmaps are very useful (much more useful than signal strength) in visualizing the *real* performance of your network as they are live measurements with real data (instead of only theoretical values).
* ``-S`` / ``--scan`` to enable wireless scaning at the end of each measurement. This may take a lot of time, however, generates data used later for generating channel utilization graphs. If you're using a modern wireless product that allows running RF scans, it makes sense to use that data instead of these scans.
* ``-b BSSID`` / ``--bssid BSSID`` allows you to specify a single desired BSSID for your survey. This will be checked several times during of every measurement, and the measurement will be discarded if you're connected to the wrong BSSID. This can be useful as a safeguard to make sure you don't accidentally roam to a different AP.
* ``-d 123`` / ``--duration 123`` allows you to change the duration of each individual `iperf3` test run (default is 10 seconds as mentioned above)

Some other command-line options include:

* ``-S`` / ``--no-scan`` to disable running iwlist scans at the end of each measurement. This greatly speeds up survey time but loses the data used for channel utilization graphs. If you're using a modern wireless product that allows running RF scans, it makes sense to use that data instead of iw scans.
* ``-b`` / ``--bssid`` allows you to specify a single desired BSSID for your survey. This will be checked several times during of every measurement, and the measurement will be discarded if you're connected to the wrong BSSID. This can be useful as a safeguard to make sure you don't accidentally roam to a different AP.
* ``-d`` / ``--duration`` allows you to change the duration of each individual `iperf3` test run (default is 10 seconds as mentioned above)
If ``TITLE.json`` already exists, the data from it will be pre-loaded into the application; this can be used to **resume a survey**.

When the UI loads, you should see your PNG file displayed. The UI is really simple:

Expand Down Expand Up @@ -113,22 +112,24 @@ Inside Docker, however, this becomes quite a bit more difficult. Currently Pulse
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.
Once you've performed a survey with a given title and the results are saved in ``Title.json``, run ``wifi-heatmap 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.

You can optionally pass the path to a JSON file mapping the access point MAC addresses (BSSIDs) to friendly names via the ``-a`` / ``--ap-names`` argument. If specified, this will annotate each measurement dot on the heatmap with the name (mapping value) and frequency band of the AP that was connected when the measurement was taken. This can be useful in multi-AP roaming environments.

The end result of this process for a given survey (Title) should be some ``.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.
* `rss_TITLE.png` - Heatmap based on the received signal strength.
* `signal_quality_TITLE.png` - Heatmap based on the received signal strength.
* `tx_power_TITLE.png` - Heatmap based on the transmitter power your WiFi card used. If your WiFi card doe snot support adaptive power management, this number will stay constant.
* `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_download_Mbps_TITLE.png` - Heatmap of `iperf3` transfer rate, UDP, downloading from server to client.
* `udp_upload_Mbps_TITLE.png` - Heatmap of `iperf3` transfer rate, UDP, uploading from client to server.
* `jitter_download_TITLE.png` - Heatmap based on UDP jitter measurement in milliseconds.
* `jitter_upload_TITLE.png` - Heatmap based on UDP jitter measurement in milliseconds.
* `frequency_TITLE.png` - Heatmap of used frequency. May reveal zones in which Wi-Fi steering moved the device onto a different band (2.4GHz / 5 GHz co-existance).
* `channel_rx_bitrate_TITLE.png` - Heatmap of advertised channel bandwidth in RX direction (AP to client)
* `channel_tx_bitrate_TITLE.png` - Heatmap of advertised channel bandwidth in TX direction (client to AP)
* `channel_bitrate_TITLE.png` - Heatmap of negotiated channel bandwidth

If you'd like to synchronize the colors/thresholds across multiple heatmaps, such as when comparing different AP placements, you can run ``wifi-heatmap-thresholds`` passing it each of the titles / output JSON filenames. This will generate a ``thresholds.json`` file in the current directory, suitable for passing to the ``wifi-heatmap`` ``-t`` / ``--thresholds`` option.

Expand All @@ -153,14 +154,14 @@ Survey
-e DISPLAY=$DISPLAY \
-v "$HOME/.Xauthority:/root/.Xauthority:ro" \
jantman/python-wifi-survey-heatmap \
wifi-survey INTERFACE SERVER FLOORPLAN.png TITLE
wifi-survey
Note that running with ``--net="host"`` and ``--privileged`` is required in order to manipulate the host's wireless interface.

Heatmap
+++++++

``docker run -it --rm -v $(pwd):/pwd -w /pwd jantman/python-wifi-survey-heatmap:23429a4 wifi-heatmap floorplan.png DeckTest``
``docker run -it --rm -v $(pwd):/pwd -w /pwd jantman/python-wifi-survey-heatmap:23429a4 wifi-heatmap Example``

iperf3 server
+++++++++++++
Expand Down
23 changes: 14 additions & 9 deletions wifi_survey_heatmap/collector.py
Expand Up @@ -47,36 +47,41 @@

class Collector(object):

def __init__(self, interface_name, server_addr, duration, scan=True):
def __init__(self, server_addr, duration, scanner, scan=True):
super().__init__()
logger.debug(
'Initializing Collector for interface: %s; iperf server: %s',
interface_name, server_addr
scanner.interface_name, server_addr
)
# ensure interface_name is a wireless interfaces
self._interface_name = interface_name
self._iperf_server = server_addr
self._scan = scan
self._duration = duration

self.scanner = Scanner(interface_name, scan)
self.scanner = scanner

def run_iperf(self, udp=False, reverse=False):
client = iperf3.Client()
client.duration = self._duration
client.server_hostname = self._iperf_server
client.port = 5201

server_parts = self._iperf_server.split(":")
if len(server_parts) == 2:
client.server_hostname = server_parts[0]
client.port = int(server_parts[1])
else:
client.server_hostname = self._iperf_server
client.port = 5201 # substitute some default port

client.protocol = 'udp' if udp else 'tcp'
client.reverse = reverse
logger.debug(
logger.info(
'Running iperf to %s; udp=%s reverse=%s', self._iperf_server,
udp, reverse
)
for retry in range(0, 4):
res = client.run()
if res.error is None:
break
logger.error('iperf error: %s; retrying', res.error)
logger.error('iperf error %s; retrying', res.error)
logger.debug('iperf result: %s', res)
return res

Expand Down

0 comments on commit 8dbf419

Please sign in to comment.