From SpenchWiki
Jump to: navigation, search

DFS with ath5k

  • With a view to Dynamic Frequency Selection powered by RADAR PHY errors reported to the driver.
  • ath5k aims to support the Atheros WiFi chipset on Linux.

Visualisation of RADAR activity

Visualisation app
  1. Patched ath5k driver reports PHY error details to userspace via debugfs.
  2. Python daemon on Linux collects details, sweeps frequency ranges on-demand, and reports details to any connected client over the network.
  3. .NET GUI visualises legitimate WiFi packets and RADAR errors over frequency/time, plots histograms of pulse width, RSSI distribution, time between pulses, and draws time-series graphic to help uncover repetition rate within a sample of pulses.




I use a Ubiquiti Networks SR4C (802.11a) with the ath5k driver in compat-wireless-2011-10-11.

It appears as:

ath5k phy0: Atheros AR5414 chip found (MAC: 0xa5, PHY: 0x61) version: 2

With some extra 'printk's, the full frequency range is revealed:

AR5K_EEPROM_HDR_11A: 0xa61 (min: 4920, max: 6100)

Important changes

There are some extra changes which enable the whole system to work. These are contained in the driver patch, and are described below.

Channel list

The patch enables all channels between 4.9 GHz and 6.1 GHz (depending on channel width), with 5 MHz spacing. A future improvement would be another mechanism where a specific frequency could be set that is not contained in the channel list that is constructed upon driver initialisation (i.e. currently, the setting of frequencies not in this list will fail).

all_channels: Default is 1

Take note of the follow changes:

static unsigned int
ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
                unsigned int mode, unsigned int max)    // ATH_CHAN_MAX when !CHAN_DEBUG
        unsigned int count, size, freq, ch;
        enum ieee80211_band band;

        switch (mode) {
        case AR5K_MODE_11A:
                /* 1..220, but 2GHz frequencies are filtered by check_channel */
                size = 220 + (196-182+1) + 9+3 + 4+4;
                band = IEEE80211_BAND_5GHZ;
        case AR5K_MODE_11B:
        case AR5K_MODE_11G:
                size = 26;
                band = IEEE80211_BAND_2GHZ;
                ATH5K_WARN(ah, "bad mode, not copying channels\n");
                return 0;

        count = 0;
        for (ch = 1; ch <= size && count < max; ch++) {
                if (ch <= 220)
                        freq = ieee80211_channel_to_frequency(ch, band);
                else if (ch <= (220 + 15)) {    // 5910 -> 5980 (15)
                        freq = 5000 + (ch - 221 + 182) * 5;
                else if (ch <= (220 + 15 + 9+3)) {      // 4920+2.5 -> 4985+2.5 but not whole (9)
                        int i = ch - (220 + 15);
                        freq = 49200;
                        do {
                                freq += 25;
                                if (((freq % 100) == 0) || ((freq % 100) == 50))
                                        freq += 25;
                        } while (--i);
                        freq /= 10;
                else if (ch <= (220 + 15 + 9+3 + 4+4)) {        // 4990+2.5 -> 5000 (4)
                        freq = (49800 + (25 * (ch - (220 + 15 + 9+3)))) / 10;
                else    // Fallback
                        freq = ieee80211_channel_to_frequency(ch, band);

                if (freq == 0) { /* mapping failed - not a standard channel */
printk("Not a standard channel: %d\n", ch);

                /* Write channel info, needed for ath5k_channel_ok() */
                channels[count].center_freq = freq;
                channels[count].band = band;
                channels[count].hw_value = mode;

                /* Check if channel is supported by the chipset */
                if (!ath5k_channel_ok(ah, &channels[count])) {
//if (ah->debug.level & ATH5K_DEBUG_DUMPBANDS)
        printk("Channel NOT OK: %d  %d\n", ch, channels[count].center_freq);

                if (!modparam_all_channels &&
                    !ath5k_is_standard_channel(ch, band))

printk("Set up %d channels\n", count);
        return count;

Regulatory domain

There are other changes, but these main ones open up all the channels for use:

#define ATH5K_ALL      REG_RULE(4920-10, 6100+10, 20, 0, /*23*//*0*/40, /*NL80211_RRF_PASSIVE_SCAN*/NL80211_RRF_DFS)

static const struct ieee80211_regdomain ath_all = {
       .n_reg_rules = 2,
       .alpha2 =  "99",
       .reg_rules = {

static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
       /* this is the most restrictive */
       return &/*ath_world_regdom_64*/ath_all;

Additional module arguments

 0: 20MHz
 1: 10MHz
 2: 5MHz
 Values from CTRY_* enum.

Kismet compatability

This patched driver works well with Kismet. Just use one of the following 'channellist's:

All channels:


Normal channels (not Public Safety):


Only Public Safety:



ath5k Linux kernel driver patch

This is a diff against compat-wireless-2011-10-11. I had to apply some additional patches/fixes to enable it to compile against my existing kernel (2.6.26 x86):

  • 022-atomic64_backport.patch is in the root directory in case you need it too (but is not applied by my driver patch).

My config.mk:

diff compat-wireless-2011-10-11.orig/config.mk compat-wireless-2011-10-11/config.mk

NOTE: Debug mode is necessary for the information to be passed to userland via debugfs!

Also add to fstab:

none            /sys/kernel/debug debugfs

And make sure debugfs is enabled in your kernel!

To ready the interface:

sudo iwconfig wlan0 mode Monitor
sudo ifconfig wlan0 up

You can optionally (manually) set a frequency if you want to watch /sys/kernel/debug/ieee80211/phy0/ath5k/radar:

sudo iwconfig wlan0 freq 5800M

To specify alternate channel width:

sudo modprobe ath5k default_bwmode=[0: 20MHz, 1: 10MHz, 2: 5MHz]

To remove it for restart:

sudo modprobe -r ath5k ath mac80211 cfg80211 compat


Two types:

  1. TCP server for GUI
  2. Stand-alone for console

They have plenty of command-line arguments, so remember to check them with '-h'.

Both scripts rely on knowning how to decode the RADAR error struct passed down from the kernel. If this is different for you from the default, make sure you update these scripts!

The RADAR queue length must match the driver's one (2048 by default, can be change as command-line argument).


struct ath5k_radar_error {
	u32 tsf;
	u8 rssi;
	u8 width;   // 0 for WiFi frame
	u8 type;    // 0: WiFi frame (no error), 1: RADAR PHY error
	u8 subtype; // For type 0: 0; type 1: rs_rate

must match:

sizeof_radar_error = 4+1+1+1+1


item = struct.unpack("Icccc"...

(and also the way it is used by the radar_error class).

Usage: radar_server.py: [options]

  -h, --help            show this help message and exit
  -P PORT, --port=PORT  server port [default=5256]
  -l QUEUE_LENGTH, --queue-length=QUEUE_LENGTH
                        radar error queue length [default=2048]
  -d DEV, --dev=DEV     device [default=wlan0]
  -p PHY, --phy=PHY     device [default=phy0]
  --progress            show progress [default=False]

All frequencies are in MHz (e.g. 5500). Sweep range can go backwards.

Simple plain-text line-based protocol:

FREQ <freq>

Thresholds (as in driver, see patch for details, or here (AR5212 radar detection notes on FreeBSD ath-hal)):

FIRPWR <pwr>
RSSI <rssi>
PHEIGHT <height>
PRSSI <rssi>
INBAND <threshold>

START [start freq: 4920] [end freq: 6100] [freq step: 5] [sampling interval in seconds, can be floating point: 1]

To continually scan one frequency, use same start and end frequency.

To terminate (will stop active sweep):


or just disconnect.

Details are sent back as:

DATA <freq> <Base64 encoded list of RADAR error structs>
Stand-alone console
Usage: radar.py: [options]

  -h, --help            show this help message and exit
  -s START, --start=START
                         start freq [default=4920]
  -e END, --end=END     end freq [default=6100]
  -S STEP, --step=STEP  freq step [default=5]
  -d DEV, --dev=DEV     device [default=wlan0]
  -p PHY, --phy=PHY     device [default=phy0]
  -i INTERVAL, --interval=INTERVAL
                        sample interval [default=2]
  -c, --current         don't change current WiFi settings [default=False]
  --short               show statistics in short form [default=False]

Will scan and output statistics to console (see Python script for output format). Also dumps various histograms to disk (CSV files).

Excerpt from output:

5620 MHz: -
5625 MHz: -
5630 MHz: 0039 in 1063604 us - RSSI: 20.4 +/- 7.2 [015-030],    Width: 1st: 0000 #0000, 2nd: 0000 #0000 [imm: 0039, unk: 0000 (0.0 +/- 0.0 [256--01]),  Time Diff: 1st: 3004 #0004, 2nd: 1006 #0002 (27989.6 +/- 79650.4 [994-382388])
5635 MHz: 0006 in 135130 us - RSSI: 28.8 +/- 4.3 [021-035],     Width: 1st: 0004 #0004, 2nd: 0011 #0001 [imm: 0000, unk: 0000 (5.3 +/- 2.6 [004-011]),  Time Diff: 1st: 3010 #0001, 2nd: 12014 #0001 (27026.0 +/- 39315.9 [1502-105082])
5640 MHz: -
5645 MHz: -


The GUI runs under .NET and uses ZedGraph (reference DLL included). You will need to run the Python server on your Linux box with the WiFi card and be able to connect to it over your network.

Screenshots may be seen below.

Pre-compiled binaries are included in the download, so you don't need to compile the source. However, if you decide to compile the project, you will have to point the ZedGraph and TehLib.NET references to the DLLs in the '_References' directory.

Monitoring RADAR detection in the console


watch -n 1 "cat /sys/kernel/debug/ieee80211/phy0/ath5k/radar"

to get:

Every 1.0s: cat /sys/kernel/debug/ieee80211/phy0/ath/radar

RADAR detection enabled:        1
RADAR PHY error filter:         0

Filter output power threshold:  44 (44)
Pulse height threshold:         28

RADAR RSSI threshold:           15
Pulse RSSI threshold:           58

In-band threshold:              9

RADAR error queue usage:        0/2048 (start: 0, overflows: 0)

RADAR errors:   0
PHY errors:     0
Other errors:   0
Frames:         0

0:              0
1-50:           0
51-100:         0
101-150:        0
151-200:        0
201-250:        0
251-254:        0
255:            0

Manually setting RADAR detection parameters

For example (adjust depending on your interface index):

echo "firpwr -60" > /sys/kernel/debug/ieee80211/phy0/ath5k/radar
echo "rssi 1" > /sys/kernel/debug/ieee80211/phy0/ath5k/radar
echo "pheight 1" > /sys/kernel/debug/ieee80211/phy0/ath5k/radar
echo "prssi 1" > /sys/kernel/debug/ieee80211/phy0/ath5k/radar
echo "inband 31" > /sys/kernel/debug/ieee80211/phy0/ath5k/radar

Spectrum Sweep

20 MHz channel width

  • Notice how RADAR errors are reported either side of a legitimate WiFi channel:
Counts vs. Frequency
RSSI vs. Frequency

10 MHz channel width

  • No WiFi operating at this channel width:
Counts vs. Time
RSSI vs. Frequency

5 MHz channel width

  • Some WiFi operating at this narrow channel width:
Counts vs. Frequency
RSSI vs. Frequency

Kurnell Weather RADAR

  • Registered on RFMap:
    • 5.625 GHz (1.1 MHz bandwidth at 250 kW)

Photo taken from the air while flying down Victor One

  • Following data was received on ~5500/~5600 MHz
  • One can infer the rotation rate of the RADAR by examining the difference in time, especially in the 'Counts' (number of reported RADAR PHY errors) over time:
Counts vs. Time
  • Average RSSI decreases a little in each 'transmission section' (separated by the pause in-between), however decrease is most noticable in the maximum value per sample-group:
RSSI vs. Time
  • Not sure why the distribution of widths changes later (was this when my laptop was knocked off its perch by the wind?!):
Pulse width vs. Time
  • Whatever the change above, it is also reflected here:
Time between pulses vs. Time
  • 1kHz PRF
    • You can see that the five highest bins (which hold counts for the distinct times between detected RADAR pulses) have values of ~1000. The time-series plot shows the regular repetition:
Time-series plot of pulses
  • 5kHz PRF
    • As above, however this time it's ~5000:
Time-series plot of pulses
  • This is also reflected in the time-between-pulses histogram:
Histogram of time between pulses


  • Indoors, the sweep looks quite different:
    • Notice the significant response at 5800MHz. Could this be DECT nearby?
RSSI vs. Frequency
  • Examing the time-series plot of RSSI for 5800MHz clearly shows a regular, repeating group of pulses (not as apparent due to the colouring on the width plot as the pulse widths seem to be changing moreso, however the time-between pulses is identical):
Time-series plot of pulses

5140 MHz

  • No idea what this is, however it appears rather regular (also note the regular pauses between groups of RADAR errors):
Counts vs. Time
RSSI vs. Time
Pulse width vs. Time
RADAR ath5k 5140 TimeTime.png