Skip to main content
COSMICBYTEZLABS
NewsSecurityHOWTOsToolsTraining
StudyProjectsNewsletterHire MeAbout
Subscribe

Press Enter to search or Esc to close

News
Security
HOWTOs
Tools
Training
Study
Projects
Newsletter
Hire Me
About
RSS Feed
Reading List
Subscribe

Stay in the Loop

Get the latest security alerts, tutorials, and tech insights delivered to your inbox.

Subscribe NowFree forever. No spam.
COSMICBYTEZLABS

Your trusted source for IT intelligence, cybersecurity insights, and hands-on technical guides.

2647+ Articles
165+ Guides

CONTENT

  • Latest News
  • Security Alerts
  • HOWTOs
  • Checklists
  • Projects
  • Exam Prep

RESOURCES

  • Search
  • Browse Tags
  • Newsletter Archive
  • Reading List
  • RSS Feed

COMPANY

  • About Us
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CosmicBytez Labs. All rights reserved.

System Status: Operational
  1. Home
  2. Projects
  3. Build a WiFi Site Survey Tool with Heatmap Generation
Build a WiFi Site Survey Tool with Heatmap Generation
PROJECTAdvanced

Build a WiFi Site Survey Tool with Heatmap Generation

Build a WiFi site survey tool in C#: read RSSI from the Native WiFi API, click a floor plan to sample, and render an IDW-interpolated coverage heatmap.

Dylan H.

Infrastructure Engineer

September 2, 2026
7 min read
6-8 hours

Tools & Technologies

Visual Studio 2022.NET 8 SDKA WiFi adapter supporting the Native WiFi API

Why Build a Survey Tool

Commercial WiFi survey software starts around a thousand dollars per seat and assumes you are surveying a campus. For a warehouse, a clinic, or a two-storey office, you need far less: a floor plan, a way to record signal strength where you are standing, and a picture at the end that shows management where the dead zones are.

That is a weekend project. This guide builds one in C# — reading real RSSI from the Windows Native WiFi API, sampling by clicking a floor plan, and interpolating a coverage heatmap between the points you measured.

The finished reference implementation is WifiSurvey on GitHub.

Project Overview

What you'll build:

  • A floor-plan canvas with pan, zoom, and click-to-sample
  • Live RSSI, link quality, channel, frequency, and band capture
  • An IDW-interpolated heatmap that fills the space between samples
  • Saveable survey projects and CSV / image / HTML export

Time to complete: 6-8 hours

Prerequisites: Solid C#, comfort with P/Invoke, and a Windows 10/11 machine with a WiFi adapter. This one genuinely needs real hardware — you cannot fake a survey in a VM without a passthrough adapter.

Step 1: Talk to the Native WiFi API

.NET has no managed WiFi API. You call wlanapi.dll directly. Three functions do almost everything:

[DllImport("wlanapi.dll")]
private static extern uint WlanOpenHandle(
    uint dwClientVersion, IntPtr pReserved,
    out uint pdwNegotiatedVersion, out IntPtr phClientHandle);
 
[DllImport("wlanapi.dll")]
private static extern uint WlanEnumInterfaces(
    IntPtr hClientHandle, IntPtr pReserved, out IntPtr ppInterfaceList);
 
[DllImport("wlanapi.dll")]
private static extern uint WlanGetAvailableNetworkList(
    IntPtr hClientHandle, ref Guid pInterfaceGuid,
    uint dwFlags, IntPtr pReserved, out IntPtr ppAvailableNetworkList);

Every one of these allocates unmanaged memory you are responsible for. Wrap the handle in a SafeHandle and free every list with WlanFreeMemory, or a long survey will leak steadily as you sample.

public sealed class WlanClient : IDisposable
{
    private readonly IntPtr _handle;
 
    public WlanClient()
    {
        if (WlanOpenHandle(2, IntPtr.Zero, out _, out _handle) != 0)
            throw new InvalidOperationException("WlanOpenHandle failed");
    }
 
    public void Dispose() => WlanCloseHandle(_handle, IntPtr.Zero);
}

Step 2: Signal Quality Is Not dBm

This is the detail that trips up most first attempts. The Native WiFi API reports wlanSignalQuality as an integer from 0 to 100. That is not RSSI — it is a linear scale across a fixed dBm window, and Microsoft documents the mapping:

// wlanSignalQuality 0   => -100 dBm
// wlanSignalQuality 100 =>  -50 dBm
public static int QualityToDbm(uint signalQuality)
    => (int)(signalQuality / 2.0) - 100;

Two consequences worth internalising before you trust your own data:

  • Resolution is 0.5 dBm per quality point. You cannot distinguish -47 from -47.3 dBm; do not render a heatmap that implies you can.
  • The scale saturates. Anything stronger than -50 dBm reads as 100, and anything weaker than -100 dBm reads as 0. Standing next to the AP, every sample looks identical — which is fine, because that is not where coverage problems live.

Step 3: The Floor Plan Canvas

Load any bitmap as the backdrop and keep a transform between screen space and image space. Do not store sample positions in screen coordinates — the moment someone zooms, every point you recorded is wrong.

private float _zoom = 1.0f;
private PointF _pan = PointF.Empty;
 
private PointF ScreenToImage(Point screen) => new(
    (screen.X - _pan.X) / _zoom,
    (screen.Y - _pan.Y) / _zoom);
 
protected override void OnMouseClick(MouseEventArgs e)
{
    if (e.Button != MouseButtons.Left) return;
    var imagePoint = ScreenToImage(e.Location);
    var reading = _wlan.SampleCurrent();
    _survey.Add(new Measurement(imagePoint, reading));
    Invalidate();
}

Store measurements in image coordinates, render them through the transform. Pan on right-drag, zoom on the mouse wheel around the cursor position.

Step 4: Interpolate the Heatmap

You have scattered points and need a continuous surface. Inverse Distance Weighting is the right tool here — simple, no fitting step, and it honours your measurements exactly.

Each output pixel is a weighted average of nearby samples, where weight falls off with the square of distance:

public static double Interpolate(PointF target, IReadOnlyList<Measurement> samples, int k = 8)
{
    const double Power = 2.0;
 
    var nearest = samples
        .Select(s => (s, dist: Distance(target, s.Position)))
        .OrderBy(t => t.dist)
        .Take(k)
        .ToList();
 
    // Standing exactly on a sample: return it, and avoid dividing by zero
    if (nearest[0].dist < 0.0001) return nearest[0].s.Rssi;
 
    double weightedSum = 0, weightTotal = 0;
    foreach (var (s, dist) in nearest)
    {
        double w = 1.0 / Math.Pow(dist, Power);
        weightedSum += w * s.Rssi;
        weightTotal += w;
    }
 
    return weightedSum / weightTotal;
}

Three practical notes:

  • Limit to the k nearest samples. Weighting every point against every pixel is O(pixels × samples) and will crawl on a large floor plan. Eight neighbours is plenty.
  • Guard the zero-distance case. Without that early return, a pixel landing exactly on a sample divides by zero.
  • IDW cannot see walls. It will happily interpolate strong signal straight through a lift shaft. Sample densely near anything that attenuates, and treat the space between rooms as an estimate rather than a measurement.

Step 5: Map Values to Colour

Interpolate on a green-to-red ramp so the picture reads instantly. Anchor the ends at values that mean something operationally rather than at your dataset's min and max — otherwise a survey of a uniformly good floor renders alarming red patches.

RangeQualityColour
-30 to -50 dBmExcellentGreen
-50 to -60 dBmGoodYellow-green
-60 to -70 dBmMarginalAmber
-70 to -90 dBmPoorRed

Anything weaker than -70 dBm is where voice and roaming start to suffer, so that is the boundary worth making visually obvious.

Render the heatmap to an off-screen bitmap once after each sample, then blit it under the markers. Recomputing interpolation inside OnPaint will make panning unusable.

Step 6: Export Something You Can Send

The survey is only useful if it leaves your laptop. Three formats cover nearly every request:

  • CSV — one row per sample: position, RSSI, quality, SSID, BSSID, channel, band, timestamp. This is what goes into a spreadsheet when someone wants to argue about coverage.
  • PNG — the composited floor plan plus heatmap plus legend. This is what goes into the report.
  • HTML — both of the above in one self-contained file you can email.

Keep the project file itself boring. Serialise the sample list and the floor-plan path to JSON under your own extension:

var json = JsonSerializer.Serialize(_survey, new JsonSerializerOptions
{
    WriteIndented = true
});
File.WriteAllText(path, json);

Embedding the floor-plan image as base64 makes the project file self-contained and much easier to hand to a colleague — at the cost of size. Worth it.

Surveying Well

The tool is the easy half. Getting data worth trusting:

  • Walk a grid, not a path. Sample every 5 to 10 metres in open space, tighter near walls and lifts.
  • Hold still and let it settle. Signal quality fluctuates; sample after a second of standing, not mid-stride.
  • Survey at working height. A reading taken at floor level is not what a laptop on a desk sees.
  • Repeat the worst areas at a different time of day. Interference from microwaves, neighbouring networks, and simple occupancy moves the numbers more than people expect.

Where to Take It Next

  • Multi-AP attribution — record BSSID per sample and colour by which AP is serving, which exposes roaming problems a pure signal map hides
  • Channel overlap — chart co-channel interference from the available network list
  • Predictive mode — place APs on the plan and model expected coverage before buying hardware
  • 5 GHz versus 6 GHz overlays — same walk, two surfaces, side by side

Reference Implementation

Complete source, including the WinForms canvas, the P/Invoke layer, and the export pipeline, is at CosmicBytez/WifiSurvey. MIT licensed.

#CSharp#WiFi#Site Survey#Heatmap#PInvoke

Related Articles

Build a Real-Time Network Latency Monitor in C#

Build a Windows ping monitor in C# with live latency statistics, a bounded result buffer that cannot leak memory, and CSV export for capacity reports.

6 min read

CVE-2026-10187: Totolink N300RH Stack Buffer Overflow in WiFi Config

A critical-severity stack buffer overflow in the Totolink N300RH wireless router allows remote attackers to execute arbitrary code via a crafted KeyStr…

5 min read

Automating Report Generation with Python and Jinja2

Build an automated report generation system using Python, Jinja2 templates, and data extraction from multiple sources. Covers multi-tenant data...

5 min read
Back to all Projects