A continuous-traceroute network path monitor for Windows (MTR-style): per-hop latency and packet-loss tracking with a live latency graph, multi-target summary, MOS scoring, alerts, session save/load, a world map, and ICMP/TCP/UDP probe modes. Pure Python, zero third-party dependencies, dark-themed, HiDPI-aware. Free and Open Source.
Like WinMTR or mtr, it discovers every router between you and a target,
then re-probes the whole path on an interval. Each round is effectively a full
traceroute, so you accumulate min / avg / max / current latency, jitter, and
packet loss for every hop over time — which makes it obvious where along the
path a problem lives (your LAN, your ISP's first hop, a peering point, or the
destination).
- Route discovery by walking the IP TTL upward and reading the "TTL exceeded" replies (standard traceroute primitive).
- Continuous monitoring of all hops, fanned out across a small thread pool so each round takes ~one timeout, not the sum.
- Live hop table — IP, reverse-DNS hostname, loss %, sent count, current/avg/min/max latency, jitter. Rows colour amber/red on loss or high latency.
- Single-hop latency graph (Overview tab) for the selected hop (click a row), with red ticks marking lost probes and a filled area for the trend.
- All-hops timeline (Timeline tab) — every hop drawn as a stacked strip on a shared time + latency scale, so a latency step shows up as the row where the bars get tall. This is the full MTR-style path-over-time view.
- Continuous auto-retrace + route-change detection — because every round is a full traceroute, reroutes (a hop's address changing) and route length grow/shrink are detected live and written to the Events tab.
- Sustained-degradation alerts — a red banner + Windows beep + Event-log entry when the destination shows sustained packet loss or high latency over the last N probes. Evaluated on the destination only (the one hop whose numbers aren't muddied by ICMP-deprioritising routers), and gated so a target that simply blocks ICMP never false-alarms.
- ICMP / TCP / UDP probe modes — trace with ICMP (default, no admin), or TCP SYN to a port / UDP, for paths that block ICMP or to test a specific service. See Probe modes for the admin tradeoffs.
- Multi-target — monitor many targets at once; a left summary grid shows each one's hop count, loss, latency, and MOS at a glance. Click one to drive the detail tabs. Alerts on any target surface in the banner.
- MOS (VoIP quality) score for the destination — one number (ITU-T E-model) for "is this path good enough for voice/video", shown live in the status bar.
- Session save / load to JSON for offline review, plus crash-safe CSV logging of every probe (with a per-round index, so "did every hop spike in the same round?" is a one-line filter) — size-capped with rollover, so unattended multi-week runs can't fill the disk.
- Engine options dialog — packet type & port, reply timeout, payload size, send delay (rate throttle), max hops, name resolution, final-hop-only.
- Dark theme by default, with a light/dark toggle (button, top-right).
- HiDPI-aware — crisp on scaled (150 %/200 %) displays.
- CSV export of the per-hop summary (with destination MOS), under a self-documenting header that records the interval, timeout, probe mode, packet size, sample count, and the exact time window — so two exports can actually be compared instead of guessed at.
- Pause / Resume & copy — right-click a target to pause probing without losing its history (resume picks up where it left off); right-click a hop to copy its IP or hostname.
- Per-target settings — engine and alert options are remembered per target; right-click a target → Edit settings… to inspect or change just that one.
- Webhook alerts — point the Engine dialog's Webhook URL at an http(s) endpoint to get a JSON POST when the destination alert raises or clears.
- Persists between launches — theme, engine options, alert thresholds and
your target list save to
%APPDATA%\PingerPlotand restore on start; View → Resume targets on launch re-arms the last session's monitors.
A left Targets summary grid lists every monitored target; selecting one drives the detail tabs on the right:
| Tab | Shows |
|---|---|
| Overview | Hop table (top) over a latency graph for the selected hop (bottom). Click a row to graph it; until you do, it follows the destination. |
| Timeline | All hops stacked on a shared time axis — the path-over-time view. Scrolls when there are many hops. |
| Events | Timestamped log of route changes, alert raises, and alert clears. |
| Map | Geo-located hops plotted on a world map with the path between them. See the privacy note below. |
The Map tab geo-locates each public hop via ipwho.is (free, HTTPS, no key) and plots the path on a baked-in Natural Earth coastline (no runtime dependency). This is the only part of the app that contacts a third party, and it's opt-in: nothing is sent until you open the Map tab, and private/LAN hops are never looked up (they have no public location anyway). Lookups are rate-limited and cached. If you'd rather not send hop IPs anywhere, just don't open the Map tab.
Set the thresholds in the second toolbar row: loss ≥ X%, latency ≥ Y ms,
evaluated over the last N probes (the window is what makes it "sustained" —
a single spike won't trip a 20-probe window). Set a threshold to 0 to disable
that check. Sound toggles the beep. A condition raises when it crosses the
threshold and clears automatically when it recovers; both are logged.
ICMP is sent through the Windows IP Helper API (IcmpSendEcho in
iphlpapi.dll) via ctypes. That API runs in user space, so unlike raw-socket
ping/traceroute tools it needs no administrator rights and no packet-capture
driver. The TTL is set per-probe through IP_OPTION_INFORMATION, and an
intermediate router that drops the packet answers with status
IP_TTL_EXPIRED_TRANSIT plus its own address — that is the whole trick.
IPv4, and the probe backend is Windows-only today because it's the Win32 ICMP API — but that's the only platform-specific module (see Platform). Auto-retrace, the timeline, alerts, MOS, multi-target, and save/load are all built on this same user-space primitive, so the whole app runs without administrator rights in ICMP mode.
ICMP mode is built on the Windows IP Helper API (IcmpSendEcho) — the same call
ping.exe and tracert.exe use internally, which is why it needs no admin. The
other two modes:
| Mode | Needs admin? | Use it for |
|---|---|---|
| ICMP | No | Default. Full traceroute + monitoring. |
| TCP, final-hop-only | No | "Is this service up, and how fast?" — a TCP handshake to a port (443, 3389, a device's web UI…). No per-hop trace, just the destination. |
| TCP / UDP, full traceroute | Yes | Per-hop trace when ICMP is blocked/deprioritised. Sends via a normal socket with IP_TTL, captures routers' ICMP "TTL exceeded" via a raw SIO_RCVALL socket — which requires elevation. |
Without admin, the full TCP/UDP traceroute is refused with a clear message rather than showing misleading all-timeout hops. (UDP note: an open UDP port stays silent, so it reads as a timeout — prefer TCP for a definitive "service is up".)
PingerPlot runs on Windows 10/11 today. The only platform-specific piece is
the ICMP probe backend (icmp.py), which uses the Win32 IP Helper API so it needs
no admin rights or capture driver. Everything else — the monitor engine, the
Tkinter GUI, the headless runner, settings, alerts, the map — is pure,
cross-platform Python, and the test suite runs on both Linux and Windows in CI.
Linux and macOS are a planned addition: a POSIX backend using unprivileged
SOCK_DGRAM/IPPROTO_ICMP sockets (the same technique mtr uses) would let the
same app run on all three. The engine, GUI, and headless mode are already
platform-independent — only that one module is missing.
- Windows 10/11
- Python 3.10+ (tested on 3.12). Tkinter ships with the python.org installer.
- Nothing to
pip install. - ICMP and TCP-final-hop work as a normal user. Full TCP/UDP traceroute needs an elevated (Run as administrator) session.
Download (no Git needed) — the easiest way:
- Grab the latest
Source code (zip)from the Releases page. - Right-click the
.zip→ Extract All… to anywhere (e.g. your Desktop). - Open the extracted folder and double-click
PingerPlot.vbs. That's it — no install, no build, nopip.
Or clone with Git:
git clone https://github.com/jamesccupps/PingerPlot
cd PingerPlotNo build step and nothing to pip install — it's pure standard library.
(Optional: pip install . registers a pingerplot GUI entry point.)
Double-click PingerPlot.vbs — launches the GUI with no console window
(uses pyw/pythonw). That's the everyday launcher.
Other ways:
PingerPlot.vbs |
Double-click launch, no console window (recommended). |
Setup.cmd |
Menu: launch / launch-elevated / make shortcuts / enable-or-disable auto-start / status. |
python main.py |
From a terminal (console stays open — handy for seeing errors). |
run.bat |
Same as above, double-clickable. |
Enter a hostname or IP (e.g. 8.8.8.8, 1.1.1.1, cloudflare.com, or an
internal address like your gateway), set the interval, and press Add / Start.
Add more targets the same way — they stack in the left grid. Click a hop row to
graph it; click a target to switch the detail view. Engine… opens the probe
type/port/timeout/logging options; Save…/Load… persist a session.
Run Setup.cmd (or launch.ps1 directly) for one-time setup:
.\launch.ps1 -CreateShortcuts # Desktop + Start-menu icons (normal + an "(Admin)" one)
.\launch.ps1 -InstallAutostart # auto-start at logon, elevated, with NO UAC prompt
.\launch.ps1 -UninstallAutostart # undo it
.\launch.ps1 -Status-InstallAutostart registers a Task Scheduler logon task with Run with
highest privileges, so the app starts automatically every time you log in —
already elevated (so TCP/UDP traceroute works) and without a UAC prompt,
which an on-demand elevated launch can't avoid. It's opt-in: nothing is
registered unless you run that command, and one command removes it.
Security note — where you install matters. Because that task launches PingerPlot elevated with no prompt, only enable auto-start from a folder standard users can't modify — e.g.
C:\Program Files\PingerPlot. If the program folder or your Python install sits somewhere you can write without elevation (yourDownloads, your home folder, most data drives), then any non-elevated process running as you could replace that code and have it run as Administrator at your next logon.-InstallAutostartchecks for this and warns you, but doesn't block you. If you don't need persistence, prefer the one-off (Admin) launch below.
For a one-off elevated launch instead, use the PingerPlot (Admin) shortcut,
Setup.cmd → option 2, or .\launch.ps1 -Elevated (UAC prompts once).
For unattended monitoring on a box with no desktop session, run the same engine without the GUI — each target logs to its own crash-safe, size-capped CSV.
python -m pingerplot.headless --init monitor.json # write a starter config, then edit it
python -m pingerplot.headless monitor.json # run it (Ctrl-C to stop)The config is JSON: an optional status_interval (seconds between console
status lines; 0 = silent), a defaults block, and a targets list where each
entry may override any default:
{
"status_interval": 60,
"defaults": { "interval": 2.5, "alert_loss_pct": 20, "alert_latency_ms": 250 },
"targets": [
{ "target": "8.8.8.8", "log_path": "logs/dns.csv" },
{ "target": "10.0.0.1", "log_path": "logs/gateway.csv", "final_hop_only": true,
"webhook_url": "https://hooks.example/abc" }
]
}Relative log_paths resolve next to the config file (so it works regardless of
the working directory), and the log directory is created if missing. To run it
at logon/boot under Task Scheduler, point a task at:
pythonw.exe -m pingerplot.headless C:\path\to\monitor.json
(or python.exe if you want the periodic status lines in a redirected log).
pip install . also registers a pingerplot-headless console script.
- Loss at an intermediate hop while the final hop stays healthy is normal. Many routers rate-limit or ignore the ICMP "TTL exceeded" they're asked to generate. Only loss that carries through to the destination (the last row) indicates a real problem on the path.
- A latency step that appears at one hop and persists through every hop after it is where the delay is being introduced.
- Sub-millisecond hops show as
<1; the Win32 RTT is integer-millisecond.
python -m pytestThe pure logic (statistics, address encoding, status handling, MOS, alert state
machine, TCP/UDP ICMP correlation) is covered by a focused, platform-independent
unit suite (the Win32 DLL access is guarded behind sys.platform), so
the suite runs on Linux/macOS too, and CI runs it on both Linux and Windows. The
raw-socket/Tk layers need real hardware/a display and are exercised by running the
app or python -m pingerplot.selftest <host>.
main.py app entry point (python main.py)
PingerPlot.vbs no-console double-click launcher
launch.ps1 launcher engine: launch / elevate / shortcuts / auto-start / status
Setup.cmd menu front-end for launch.ps1
run.bat console launcher (for debugging)
pyproject.toml packaging metadata + pytest config
pingerplot/
icmp.py Windows ICMP via ctypes (IcmpSendEcho), TTL control
tcpudp.py TCP/UDP probes via raw SIO_RCVALL capture (admin), parallel per round
model.py Hop / Sample / stats / MOS (pure, tested)
monitor.py trace + continuous monitor engine, alerts, logging, save/load
gui.py Tkinter UI: summary grid, hop table, graphs, events, map
geoip.py lazy IP geolocation via ipwho.is (Map tab only)
worldmap.py baked Natural Earth coastlines (zero-dependency backdrop)
selftest.py headless trace/monitor for the console
tests/ pytest for the pure layers
- Linux & macOS — a POSIX ICMP backend (unprivileged
SOCK_DGRAM/IPPROTO_ICMP+IP_RECVERR, themtrtechnique). The engine, GUI, and headless runner are already cross-platform; only the probe backend is Windows-specific today. - IPv6 (
Icmp6SendEcho2— different structs, needs a source address). - Sub-millisecond RTT (would require raw sockets + self-timing).
- Email alert actions (webhook POSTs are already built in).
Issues and pull requests are welcome — see CONTRIBUTING.md. The hard rules: keep it dependency-free, and keep the Win32 access guarded so the test suite keeps running on any OS.
MIT © James Cupps.
PingerPlot is an independent, hobby open-source project and is not affiliated with or endorsed by any commercial network-monitoring product. It is a read-only diagnostic: it sends probes and reads replies; it never writes to or reconfigures anything on the network.
