Skip to content

Latest commit

 

History

History
187 lines (148 loc) · 9.66 KB

File metadata and controls

187 lines (148 loc) · 9.66 KB

Architecture

pluto11 is a passive desktop application. It combines two different kinds of evidence without treating them as interchangeable:

  • RF observations are measurements of energy received by an ADALM-Pluto.
  • Network records are identities obtained from Windows WLAN metadata or successfully decoded management frames.

An RF peak is not an SSID, and an SSID overlay is not proof that every signal inside its advertised channel width came from that access point. Keeping those claims separate is the central design rule of the project.

Runtime data flow

Pluto / mock / replay capture
        |
        v
scanner backend -> CaptureResult -> analysis.measure -> ChannelMeasurement
        |                                          |             |
        |                                          |             +--> weighted channel/history aggregates
        |                                          +--> Welch PSD -> panorama assembler -> BandSpectrumEvent
        |
        +--> optional in-process OFDM decoder -> parsed management frames --+
                                                                         |
Windows netsh scan ------------------------------------------------------+--> NetworkDatabase -> NetworksEvent
External decoder UDP frames ---------------------------------------------+
                                                                         |
                                                                         v
                                           SurveyEngine event queue -> Tk pages / exports

SurveyEngine is the coordinator and the only component that communicates with scanner backends. Worker threads produce immutable events; the Tk main thread consumes them. Scanner workers must never touch Tk widgets.

Package map

Area Main modules Responsibility
Application lifecycle app.py, models.py, config.py, logging_setup.py Engine, events, configuration, CLI, source lifecycle.
Radio scheduling channels.py, scheduler.py, scanner/ Channel plans, dwell policy, Pluto/mock/replay devices, capture persistence.
RF analysis analysis/power.py, occupancy.py, bursts.py, spectrum.py, interference.py dBFS, short-time thresholds, burst periodicity, Welch spectra and conservative RF-pattern evidence for one capture.
Aggregation and panorama analysis/aggregation.py, panorama.py Duration/sample-weighted channel summaries, time buckets, overlapping absolute-frequency sweeps.
Calibration analysis/calibration.py, receiver_baseline.py dBFS-to-dBm calibration and separate terminated-input spectrum flattening.
Wi-Fi evidence wifi/ Windows WLAN parsing, management-frame parsing, network merge/storage, UDP and IQ OFDM decode paths.
Presentation gui/ Main window and individual pages; rendering only, never hardware ownership.
Import/export export/ CSV, schema-versioned session JSON and replay-format IQ captures.
Optional GNU Radio bridge gnuradio/ External decoder documentation and UDP bridge for a separately owned radio.

RF activity and network identity

RF activity

ChannelMeasurement.busy_percent describes the fraction of usable time blocks whose power inside a narrow cell at the nominal channel center exceeds the selected threshold. The default cell is 4 MHz. Each capture remains 20 MHz, but using its entire power for every 5 MHz-spaced 2.4 GHz channel would count the same emitter repeatedly and flatten the channel chart. LO dither is accounted for when mapping the cell to absolute RF frequency. In the UI and documentation this is called RF activity. It can represent Wi-Fi traffic, corrupted or weak Wi-Fi frames, Bluetooth, microwave leakage, proprietary devices, receiver artifacts, or short measurement bursts.

It is not equivalent to a Wi-Fi chipset's Clear Channel Assessment counter, and it is not a count of access points. The default noise-relative threshold is useful for local comparisons but can move with the local noise estimate; use a fixed threshold and a controlled setup when comparing sessions.

Aggregates use observation duration when present, otherwise usable sample count for backward compatibility. Mean and noise power are converted to linear power before weighting and converted back to dBFS afterwards. A short capture therefore cannot outweigh a long capture merely because it generated one final percentage value.

The default 512-sample energy window represents 25.6 microseconds at the default 20 MS/s sample rate. The UI reports activity as a percentage of observed time and reports observation coverage separately. Unobserved wall-clock time is never counted as idle.

A 20 MHz Wi-Fi waveform can legitimately raise several neighboring center cells. That energy shape is not used to identify its primary Wi-Fi channel. Network channel identity is accepted only from advertised DS Parameter Set or HT Operation metadata, or from an operating-system Wi-Fi scan; the tuned Pluto channel is never substituted when a decoded frame omits that metadata.

RF-pattern evidence

RfEvidence may describe captured energy as quiet, narrowband, bursty, bursty broadband, periodic broadband, continuous broadband, or mixed. It uses short-time burst structure and frequency-domain shape. The label is an engineering clue, not an emitter identity.

On 2.4 GHz, a sufficiently repeatable roughly 7--12.5 ms broadband pulse train may be described as microwave-like interference is possible, not proven. The wording is part of the data contract: it must not be shortened to "microwave detected." Other Wi-Fi or non-Wi-Fi sources and measurement artifacts can produce similar finite captures.

Identified networks

NetworkRecord is keyed by BSSID and can merge the following sources:

  • windows_wlan: the optional Windows netsh wlan scan;
  • decoder: a successfully parsed raw management frame from the integrated decoder or an external UDP producer;
  • merged: evidence from more than one source.

The database deliberately does not create a network from energy alone. Hidden networks remain hidden unless another valid frame or metadata source provides an SSID.

Decoder confidence

The built-in decoder works on finite IQ captures owned by the survey engine. It prefers GNU Radio/gr-ieee802-11 when installed and falls back to a native legacy 20 MHz OFDM decoder. Both are opportunistic, receive-only helpers; they do not turn an RF survey into a monitor-mode Wi-Fi adapter.

Current decoder limits include legacy OFDM only (6--54 Mb/s), valid-FCS frames only for identity creation, and no DSSS/CCK, HT/VHT/HE PHY decoding. Many 2.4 GHz compatibility beacons use DSSS, so an empty decoder result must not be interpreted as an empty channel. Decoder diagnostics distinguish inspected captures, preamble candidates, SIGNAL headers, unsupported rates, FCS failures and parsed frames.

Panorama and time semantics

Pluto sees one approximately 20 MHz slice at a time. A survey is sequential, not a simultaneous observation of an entire band.

For an RF panorama, the engine makes overlapping channel-centred captures and cycles the LO through nominal, negative-dither and positive-dither positions. The PanoramaAssembler maps their valid FFT bins to an absolute-frequency grid, blends overlap in linear power with tapered weights, and publishes a waterfall row only after every expected visit completes. Missing coverage is represented as missing data, not quiet spectrum.

Time history is similarly bucketed. Blank cells mean that the channel was not observed in that time bucket. They must not be interpreted as zero RF activity or connected with a line to a later asynchronous sample.

Calibration boundaries

There are two intentionally separate calibration systems:

  1. Signal calibration (analysis/calibration.py) maps dBFS to estimated dBm using known signal measurements for a particular device, frequency, gain and antenna/cable setup.
  2. 50-ohm receiver baseline (analysis/receiver_baseline.py) flattens repeatable receiver shape, DC leakage and fixed spurs for exact LO/gain/ sample-rate settings. It is not an absolute power calibration and does not subtract real RF noise.

Neither profile should be extrapolated to another band, gain, sample rate, antenna path or unmeasured LO. The application leaves values in dBFS when a matching signal-calibration point is unavailable.

Concurrency and ownership rules

  • A DeviceWorker owns operations on one scanner backend.
  • SurveyEngine owns the workers, scheduler, source threads and event queue.
  • The integrated decoder consumes already captured IQ and never opens a second IIO context.
  • A separate GNU Radio process must use a separate Pluto. Two independent processes must never open, stream from or retune the same Pluto URI.
  • netsh and UDP are optional metadata sources. Their failure must not stop RF acquisition.

These ownership rules are functional requirements, not merely style: a second LO writer or continuous stream can corrupt both spectrum measurements and packet decoding.

Persistence contracts

  • Session JSON uses an explicit schema version in export/session_export.py.
  • Measurement exports preserve observation duration, burst periodicity and RF-pattern evidence so offline analysis can reproduce the UI's qualified claim.
  • Replay directories use meta.json plus ordered capture_*.npy complex64 arrays as defined by scanner/capture_format.py.
  • UDP accepts documented raw MAC, radiotap-plus-MAC or ASCII-hex datagrams; see ../gnuradio/UDP_FORMAT.md.

When changing a persisted schema or event shape, add a compatibility test and document migration behaviour in the pull request.