How the engine counts¶
The exact definitions behind the numbers and the events: what a Block Ack success counts, when a roam is a roam, which BSS serves the client, how Mist's polls line up with the air.
SensorLink:capturereturns aCaptureStream: the pcap bytes plus the status lines oflinkside-cap. Those contain the drops, and the app always shows them.linesstreams the output of a long-running command, such as a sweep.- tshark reads with
-r -: the pcap comes in via stdin, and tshark processes it frame by frame.-i -makes dumpcap write a temporary capture file on the Mac;-r -does not. - Two tshark processes per stream: the fixed field list (
TsharkField, the column order) for every frame, and the elements of the management frames as a JSON tree (ManagementDecoder). The JSON tshark gets only the management frames whose body the engine needs decoded (ManagementSelection, which reads the pcap records): a beacon or probe response that repeats one of the last 256 bodies decoded of the same transmitter and subtype, but for its timestamp, the TIM and the MME of beacon protection, takes that decode; sounding feedback gets no body; frames of other types do not go. A body that changed goes again (its BSS Load, the count of a channel switch, the profiles of an EMA set), so every beacon keeps its elements. The JSON tshark numbers its frames from 1, and the selection ties each to its number in the stream; a stream that is not pcap of 802.11 frames goes as it is (decode-cost.md).TsharkDecoderattaches the body toFrame.managementby frame number, so a management frame and every frame after it wait for its body. The JSON tshark holds the newline after a packet's closing}back until the next packet;JSONPacketSplitterends a packet at that brace, so a body does not wait for the next management frame, which in a context sweep at one or two frames a second heldcapBup to 4 s behind. A live session's decode numbers each capture's frames from 1, while the radio's pcap holds every capture of the session: the recorder numbers each record it writes (CaptureRecorder,RecordNumbers, kept as runs of consecutive records), and a frame takes the number its record has in the pcap, so an event's frame is the same number live, in the report and in a replay. - Raw values: RSSI per chain, with the frequency; rates per PHY from radiotap, normalized in
RateInfo. What the mt7925 driver fills in is recorded indocs/measurements/eht-capture.md. - Block Ack success: per BA the bits up to and including the highest acknowledged bit count; a 0 bit before it is a loss, bits after it do not count (those MPDUs may never have been sent). Each sequence number counts once, in the first BA whose acknowledged range covers it. A Block Ack Request whose SSN moves past sequence numbers no BA acknowledged counts them as lost (
givenUp): from the one after the newest a BA settled up to the SSN, at most the flow's BA window. Interference at the recipient takes whole A-MPDUs, which no BA reports; the originator's BAR is then the only trace. The BA that answers a BAR, from its recipient with its SSN withinctsWindow(200 µs) on the radio's TSF,blockAckAnswer(5 ms) on the capture time without one, goes on the BAR's flow whatever TID it carries: the AP45 labels its answer to each of the phone's BARs TID 0 (testlab-aps.md). The gap counts only across a stretch the sensor heard, from the BA or request that set its start to the BAR: a radio listened within the channel of the BAR's link all along, tuned there before it and never without a frame forradioSilence(1 s: a restart of its capture or of its USB device, or a cut in a recording; in focus the AP's beacons come every 102.4 ms), after a request with a Multi-Link element also within the channel of every link of the mapping the AP did not refuse, and the client's last search ended before it. Otherwise MPDUs acknowledged where no radio listened, on another link of the MLD or while capA searched or a radio tuned elsewhere, move the sequence numbers on unseen, and the BAR only sets where the flow goes on. A pause alone stops nothing: intestlab-intermittent-lossthe AP's requests 6.4 and 2.6 s after the flow's last BA gave up 6 and 3 MPDUs, and the capture holds the AP's retransmissions of some of them. A capture switch on the same channel left at most 0.2 s without a frame in the recordings, within which the gap still counts; without the client's (re)association the engine knows no other link of an MLD, and an MLO client's BAR then counts as on one link. In the recordings it held back none of the 93 BARs with a gap within a window (2026-10-29). What a BAR gives up withinflushWindow(0.2 s) before a deauthentication or disassociation of the client by the same AP, either way, is booked back from failed toflushed, in its own interval: the AP dropping its queue before it disconnects. One on another BSS, such as one the client had left, flushes nothing. The downlink after each wake of the client from power save (a frame with the PM bit clear after a doze of at leastminimumDoze, 1 ms, until it dozes again, at mostwakeBurst, 0.5 s; with MLO the client dozes when every link it was heard on has the PM bit set,PowerSaveTracker) is kept per wake (WakeBurst): the MPDUs its BAs settle, the AP's data frames and their retries. The first BA of a flow, and the first after a gap or a jump, counts from its first acknowledged bit: after a (re)association the AP starts a new agreement at sequence 0, while the MT7925 reports the 256 sequence numbers up to its newest, 254 of them never sent. The gap also covers a new agreement whose setup the sensor cannot read; in the recordings the first BA after a gap left no loss out (power-save.md). A successful (re)association response to the client ends its agreements with the AP: their flows start over, a BAR of the new agreement only sets where its BAs start, and its first BA counts from its first acknowledged bit, also where it starts up to a window behind the old agreement's newest (the MT7925 after a kick intestlab-ap-kick). The disassociation before it ends nothing yet: the client's BA for the AP's last A-MPDU can come after it. A BA that starts less than a window behind the flow's newest start, withinblockAckFlowTimeout, is for an older window: the U7, the AP45, the phone and the MacBook start a BA at the first sequence number of the A-MPDU that elicited it, with only that A-MPDU's MPDUs in the bitmap, so an A-MPDU that leads with a retransmission gets a BA from there whose 0 bits include MPDUs the BAs before it acknowledged (the MT7925 reports its whole window). The flow goes on from its newest start and keeps a window of outcomes behind it, so those sequence numbers keep their first outcome; in the recordings up to 0.9 % of a client's BAs start so. The window is the largest bitmap the flow's BAs have had, the BA's own included, 64 before its first, not its last BA's: an HE recipient can answer one agreement with 64 or 256 bits, and a Multi-STA entry can be shorter than the compressed BAs of its flow. With the last one's, a 256-bit BA 80 behind the newest start after a 64-bit one read as a jump and counted its MPDUs again. In the recordings no flow of compressed BAs changes its bitmap length (2026-10-28). The window holds across a gap and a jump, since a new agreement between the same two stations keeps its buffer, until a (re)association drops the flow; an agreement set up again with a smaller buffer, which PMF hides, keeps the larger window, and a BAR can then give up more than the agreement holds. An MPDU that comes back with the retry bit confirms the loss. With MLO the sequence numbers belong to the MLD: the BAs of all links count in one series. Below a minimum number of BAs, or of MPDUs they settle (20 and 50,HintConfiguration.success, for the hints, the Overview's readouts, Link metrics and the report; the time axis plots a step from the engine's 5 BAs), the result is "too little traffic": twenty empty BAs and five MPDUs a BAR gave up are no 0 %. A Multi-STA entry with Ack Type 1 has no bitmap: TID 14 is an all-ack, every MPDU of the A-MPDU in the TB PPDU arrived; TID 0–7 acknowledges a single MPDU of that TID; TID 15 a management frame, which is no data and does not count. Each all-ack and single ack counts as one delivered MPDU when it comes and is pending on the client's flows to the AP, an all-ack on every TID, until a flow's next BA with an acknowledged bit: the sequence numbers from the one after the newest a BA settled up to that BA's start were theirs, delivered, each in the interval of the ack that held it (a single ack one, the all-acks an even share of the rest, an all-ack's own MPDU placed once over its flows). They are theirs as long as the acks can hold them, an all-ack at most a BA window and a single ack one MPDU; as long as the BA starts ahead, at most 4096 less a window on, since a recipient's window starts at most one window behind what it acknowledged; and as long as the flow did not go quiet, no step longer thanblockAckFlowTimeoutfrom its last BA through the acks, which keep it going. Otherwise each ack stays one MPDU. The gap is the least of what they held: the true count can be laps of 4096 more, in a run of all-acks without a loss. A BAR gives up the sequence numbers no BA settled less what the acks since that BA can hold, and a BA without an acknowledged bit settles no gap: neither tells which part the acks held. What a client gives up without a BAR the sensor sees, such as one in a TB PPDU, counts as delivered in a gap. How close it comes on the BAs of a recording turned into all-acks: all-ack.md. The capture share per direction is the share of delivered MPDUs the sensor decoded as a data frame: capture quality, not loss. - UL OFDMA: a trigger frame and a Multi-STA Block Ack name a station by AID only, and an AID belongs to the AP that gave it (
AssociationID). From the client's successful (re)association response, triggers and Multi-STA entries with its AID are the client's only from that AP: with MLO from every link of the AP MLD, which has one AID space (after a request with a Multi-Link element; one without it associates one link), and with Multiple BSSID from the transmitted BSSID of the set too, as the BSS table knows it at the response, since it triggers and acknowledges the stations of every BSS in the set. A Multi-STA entry counts on the client's flow with the AP that gave the AID, so one from the transmitted BSSID goes on with the flow of the client's own BSS. The AID is the client's while it is associated with that AP (ConnectionTracker.associated): a disconnect or its data with another AP ends it, another AP's successful response gives the next one, a refused one leaves it. After a roam the old AP's AID is another station's, and a station of the roam target with the same number is not the client. Without a response in the capture the client's AID is unknown, as for a client connected before the capture, and of a trigger or a Multi-STA Block Ack to the broadcast address nothing counts. A Multi-STA Block Ack addressed to the client acknowledges the client alone: its entries count, whatever their AID, also without the response, on the client's flow with the AP that sent it, as a compressed Block Ack does, or with the AP that gave the AID where that AP sent it; after a roam the sensor did not see, the new AP's goes on the client's flow with the new AP. Intestlab-co-channelthe U7 sent all 255 of its Multi-STA Block Acks with a bitmap to the phone, AID 5. BA success and Link's count of Multi-STA entries take the same entries (Frame.multiSTAEntries). - Airtime and sequence gaps: the airtime of a frame in a PPDU of its own at a legacy rate, in HT and in VHT is its TXTIME as 802.11 gives it (
Frame.airtime), of the frame with its FCS, which the mt7925 leaves off: DSSS and HR/DSSS with the long or the short preamble, OFDM, and on 2.4 GHz ERP-OFDM and HT with the 6 µs signal extension; HT in the mixed format and VHT with an LTF per stream and BCC, with the short GI the last symbol ending on 4 µs. Wireshark's estimate (wlan_radio.duration) differs by tshark, 5.0 counting the FCS and 4.6 not, neither the extension, and rounds the short GI to the µs; the engine's equals each where they agree (test data). The airtime of an HE or EHT frame, for which there is none of either, is the length at the data rate plus a preamble of 40 µs (LinkMetrics.preamble); the length is the 802.11 frame's, without the radiotap header (Frame.mpduLength). An MPDU in the PPDU of the radio's frame before it counts its length at the rate alone. That is an HT, VHT, HE or EHT MPDU (a legacy PPDU carries one) from the same transmitter to the same receiver, its TSF later than the one before by its own length at the rate within 60 µs (ppduSlack), plus the MPDUs between them the sensor missed: k skipped sequence numbers of a non-retry QoS data frame of the same TID allow k of the longer of the two (eht-capture.md). A known difference, left as it is (2026-10-29): live, a frame the reorder buffer lets out of order, more than its window late or across a step of the clock model, comes before an MPDU with an earlier TSF and starts a PPDU of its own, so a live session can count a preamble of 40 µs more than its replay, which plays each radio in its own order. The sequence gaps are the sequence numbers skipped between a transmitter's non-retry data frames to one receiver on one TID, by its MLD address with MLO, without Null and QoS Null, whose sequence number the standard leaves free; a jump of 256 or more is a new start (sequenceJump). Both are shown only, in Link and in the report; no rule reads them. - Data frames and the retry bit: a direction's data frames are those with a payload, not Null or QoS Null, and not EAPOL in the clear: that is the login's, and its resends the handshake's (
m3-missed, the Handshakes pane), not the link's frames sent again. The retry bit, the rates and the median MCS, the data bytes, the data airtime and the frames after a wake (WakeBurst) count them; EAPOL counts as a frame, in the airtime, and as a frame decoded in the share of sequence numbers missed: its sequence numbers are no gap (DirectionMetrics.eapolFrames). An EAPOL frame the keys encrypt, of a reauthentication or a rekey, is not known as one. Since 2026-10-31: intestlab-4way-ackthe AP's M3, sent eight times per 4-way to the A9000 as client, made the window with two 4-ways a note "Sent again". - TXOPs:
TxopTrackerpairs the control frames around an RTS on the radio's TSF (radiotap.mactime; the capture time comes over USB in batches). An RTS answered with a Block Ack or ACK within its duration is delivered. Uplink: without a CTS to the client unanswered, with a CTS and no answer the data lost whole. Downlink: the CTS and ACK to the AP carry no transmitter address; the one within the window after the AP's RTS to the client is the client's, and the downlink splits like the uplink. Only one to an AP the client exchanges RTS with counts: the CTS and ACK to the reference client are in the focus filter too. In a recording without the CTS and ACK to the AP (the focus filter before 2026-10-20), the AP's RTS again within 2 ms is unanswered, otherwise no Block Ack. Only TXOPs that start with an RTS count, and how many do changes per device and over time. It counts next to BA success and is never mixed with it: BA success counts a PPDU lost whole and delivered on a retry as delivered. An unanswered RTS is also a busy, dozing or absent receiver, so the hints show the failed share as evidence of the loss rules andcongestion, fromminimumRTS(20) RTS per direction, without a threshold of its own (txop-loss.md). - Serving BSS: as of a time, the BSSID of the client's last (re)association request up to then, with MLO that of the association link; before its first request, the BSS of its data then, the first back to the start; without data before it, the first request's BSS from the start; without a request or data, the AP of its authentication then, as when the AP refuses its SAE commit (
ConnectionTracker.serving(at:),LinkAnalyzer.servingBSSIDat the end). Data on another link of the same AP MLD is the same AP (BSSTable.sameAP), not a change, and the data with the first request's AP right before it is one period with it. The header, the hints, the report and the weaker-roam anomaly use this one definition; the AP's lane on the time axis takes its periods (servingPeriods), which name the same BSS at every moment. "Roamed" stays separate: a later request to another BSS than the first. - The client's connection: in one place per client, the
ConnectionTrackerof itsLinkAnalyzer, frame by frame: the AP it is associated with (of its last accepted (re)association, of its data, or the current AP its reassociation request names; before anything else a Block Ack says where; nil after a disconnect from it), the AP it left by a disconnect and when, whether the capture saw it connected, its joins and the serving BSS over time (a search that found the client on another AP moves it too). A join runs from the client's first authentication or (re)association request to an AP until the keys are in place (the accepted response with FT or without RSN, M4, or its data with the AP after the acceptance, whose messages the sensor missed), the AP refuses it, the login fails, a disconnect ends it or the client joins another AP; one that got no further than authentication ends when the client sends that AP nothing more withinauthenticationWindow(5 s). A join is a move (Join.from, see Roams) when the client leaves another AP with it. The roam tracker takes its roams from here, each a join that is a move with the join's outcome, and keeps their timing; the story its join phases (a join that got to a request, was refused or still goes on; an attempt that came to nothing more is a line of the phase it falls in), their title (a roam is Moved, a join after the client was seen connected Reconnected, else Connected) and the end of the login, from which on the lines are the connection's (an FT roam's at its response); the hints, the panes and the export the serving BSS and the disconnects. - Comparing signal across BSSs: raw plus the per-band offset from the configuration, plus 10·log10(width/20) for a 6 GHz duplicate beacon (6, 9 and 12 dB at 80, 160 and 320 MHz), by default within the same band (
BSSEntry.comparableSignal). The raw values stay stored. - 802.1X: a timeline per EAP exchange with the method and EAP-Success or Failure; the RADIUS latency is the time between an EAP-Response and the next EAP-Request; with PMK caching (PMKID in the (re)association) there is no EAP, shown as "EAP skipped".
- Roams: a roam is a join of the client's connection that is a move (
ConnectionTracker.Join.from): at an authentication or (re)association request of the join the client is associated with another AP (ConnectionTracker.associated); the links of one AP MLD are one AP. A join within 2 s after the client left an AP by a disconnect is a roam too, one per departure; a reconnect after a longer pause is not. The current AP a reassociation request names is the AP the roam leaves, as the client fills it: the iPhone named the AP of its roam before, the one between seen by Mist alone (roam-ap45.md); a join to the AP the sensor last saw the client on becomes a roam by it, from the join's first frame. The outcome is the join's, by its rules in one place: completed once the keys are in place, refused with the AP's status, failed when the join ended otherwise (the login failed, a disconnect, a join elsewhere, or an authentication given up afterauthenticationWindow), incomplete at the end of the capture. Roam time: from the client's last frame to the old AP to the keys at the new AP (the (re)association response with FT, otherwise M4,Join.keys); a roam whose keys only the client's data showed has none. The time off the air counts only when the client exchanged data with the old AP in the second before, at least five data frames (minimumActivity), each sequence number once, so a frame both radios heard or sent again counts once, the last of them at most 100 ms before its last frame to the old AP (recentData). These windows, those of the TXOPs and the event frames both radios heard are inEngineConfiguration, which a session records insession.json; a data frame without payload (Null, QoS Null) isFrame.isNullDataeverywhere. A frame to the old AP during the handshake moves that moment. The wait before each handshake message goes to its sender; the AP's EAP message after an EAP-Response of the client goes to the RADIUS server; a request the client repeats without an answer goes to the AP. The time until the first data frame depends on traffic and is shown, not judged. - The client logged off: a deauthentication or disassociation from the AP that the client began, by the frames (its EAPOL-Logoff within 1 s before, in the clear before the keys or encrypted after them and known by its size, 16 bytes of payload, and by the AP's answer,
encryptedLogoff) or by its vendor's measured use of the reason (a Mist AP's 23 and 36: reason codes), is the AP ending the 802.1X session the client ended (HintFacts.endedByClient, kept asSessionSnapshot.logoffs). The AP45 sent it 15 to 20 ms after the logoff, in the clear with the keys dropped first or protected with them kept (mist-events.md). The client turning Wi-Fi off and the reconnect a session asks for both give it. The timeline marks it "after the client logged off" and as no failure, with the client's own disconnect within a second after it and Mist's lines of the moment (its events with a reason in the same second, its reason 6 after them); the story opens the phase Disconnected with the logoff and reads the logoff and the AP's disconnect as one line, "The client logged off; the AP ended the session", with the rest of that disconnect (below). Reason 23 before the keys, without a logoff before it, is a login that failed. Within one second, a Mist event with a time in whole seconds follows the air's lines of that second. - Encrypted action frames: with PMF a protected action frame shows only who sent it and its length. With the MIC of the client's cipher (from its (re)association request, otherwise the BSS's) the engine names two by the size of their body (
EventExtractor.protectedAction, mist-events.md): the client's 4 bytes withinsaQueryWindow(10 ms) after the AP's deauthentication or disassociation in the clear are its SA Query request, PMF's check that the AP sent it, and the AP's 4 bytes withinsaQueryTimeoutafter it its response (encryptedSAQuery); a frame of 9 or 12 bytes answered by one of 9 or 12 from the other side withinblockAckAnswer(5 ms) is a Block Ack setup, both frames (encryptedBlockAckSetup). The rest keeps its length, unnamed (protectedAction): a frame of 12 bytes alone, which on eduroam is also the client's neighbor report request. A Block Ack setup is no steering forweaker-roamandping-pongand stays out of the story; an unnamed frame from the AP in the 5 s before a disconnect or a new connection is a line, "Encrypted action frame from the AP", "may be a BTM request (PMF)", and one from the client "content encrypted (PMF)". The AP's deauthentication or disassociation in the clear reads with its rest as one line: the client's SA Query and the AP's answer, then the client's own disconnect within a second after its SA Query or after its logoff ("reason 36: …, SA Query from the client unanswered, then the client disassociated",Story.tail(of:in:)). - A disconnect on a BSS the client had left: a deauthentication or disassociation from or to a BSS other than the one the client is associated with (
ConnectionTracker.associated) and the one it is joining is the AP clearing an entry it kept, not the end of the connection. The tracker makes it an event of its own kind (Event.Kind.staleDisconnect), so the hints, the handshakes, the roams and the story read no disconnect in it without a check of their own; the timeline and the report show it with its BSS, the Block Ack flush leaves out a disconnect by another AP by its own rule. One from the AP the client is joining ends that join (mist-events.md). - Reference client: one of our own devices at the same location and on the same AP, in focus at the same time; New Session takes its MAC and remembers it. Finding the client's channel looks at the client's frames only. The hints count it only when it was heard in the window: a remembered reference that is not there would hold back the rules that compare with it.
- Mist: first a site (
orgs/{org_id}/sites, or the sites in the privileges of a site-scoped token), thensites/{site_id}/clients/search; the org has more than 50,000 Wi-Fi users, so an org-wide search is too slow. AP names fromsites/{site_id}/stats/devices?type=ap. Stats polled about every 30 s, events per client, the serving AP's radio from the device stats.MistRateBudgetthrottles from 2500 calls per hour and refuses from 4000; Mist's limit is 5000. EveryHTTPMistClientspends from its token's budget (MistRateBudget.shared(token:)), so the search, the poller and the AP stats of one process count together; another process with the same token, the CLI beside the app, counts apart. The poller stops only when Mist refuses the token; after a network error it polls again at its interval, after a 429 or the budget's refusal when they say. Each poll's error goes intomist.jsonas aMistFailure, its kind only (not reachable, token refused, the role refused, client not found, rate limit, own budget), from the first failed poll to the next that answered: the Overview's chip while it lasts, a row per failure in the report's Session table. A failed poll keeps the stats Mist gave before (MistState.stats): the AP's name, the client's RSSI at the AP and Mist's place for a search stay; only the Mist pane's stats of the poll say none. A live session writes each poll tomist.json(MistSample: the client's RSSI and SNR, the retry deltas, the AP's frames to the client sent the first time (txPackets) and the client's frames to the AP (rxPackets, the weight of the uplink's retry delta), the serving radio'sutil_non_wifiand noise floor, the age of the stats, and the new events with the keys inMistSample.eventKeys, of their text only the codes in parentheses,text_codes); a replay reads it. The events about the client's DHCP, its address, gateway ARP and DNS areMistView.IPService, with their latency and the time since the association: the hintip-services, a line each in the timeline and the story ("Address from DHCP, 2.0 s, 2.5 s after the association"), and a table in the report next to the 4-way and EAP, the time to connect beyond the air. A snapshot takes the polls up to its own time, in both modes, and each window of the hints the last poll with stats at or before its end, with the events of every poll up to it. Before the first poll there is no Mist. After Stop the poller goes on (MistClientPoller.AfterStop): every 10 s until the events of the session's last seconds can be in, 30 s after Stop, and the stats have refreshed past Stop, or 80 s after Stop at the latest, since a client that left keeps its oldlast_seen(Mist's delays in mist-events.md; all three values are assumptions). Each of those polls judges the finished session again,mist.jsonkeeps them, and a finished session takes every poll, also those after its end (SessionSnapshot.finished); so does a replay at its end, the CLI and the golden files. A window chosen in a finished session, such as the 20 s around a hint's moment, and the report of it take every poll as well (HintFacts(finished:)), and so does a window chosen during a live session, with every poll so far: a hint found on a late event (a DNS answer, the reason of an encrypted disconnect, Mist's reason of the AP's deauthentication, 10 to 26 s late) stays in its own window. Only the moving live verdict, over the whole session, keeps the polls up to each window's end. The scan of the windows keeps each one's polls up to its end, as live had them, except for Mist's count of the AP's frames sent again. Mist counts them per refresh of its stats (MistView.SentAgain), from thelast_seenbefore to its own, about a minute, and gives that count a minute or more after the minute it counts: the facts of a window, and of the whole session, take the refreshes that overlap them from every poll they have (HintFacts.mistSentAgain), each for the part of its interval inside, weighted by the frames the AP sent, assumed even over the interval, and from at leastminimumMPDUsframes; in sessions recorded beforetxPacketsweighted by time, without a minimum. Live, a window gets its count when it comes in, as a late event.tx_retriespertx_pktsis retries per frame sent the first time;tx_retries / (tx_pkts + tx_retries)is the share of all the AP's frames sent again, what the retry bit counts. - MLO:
LinkAnalyzerstarts with a link MAC or the MLD address and learns the link MACs from the ML element in the (re)assoc request. BAs from all links count in one MLD sequence space. - EAP identity:
Framekeeps only a SHA-256 hash (16 hex digits) of the identity, not the identity itself. - Clock:
ClockSyncreads the Pi clock a few times and keeps the measurement with the shortest round trip; the uncertainty is half of that.ClockModelinterpolates between the measurements. - App: replay and live give the same
SessionSnapshot. The replay decodes the recording once and plays it into the analyzer; seeking back starts over. A window (WindowSelection) needs no seek: the facts come from the buckets of the analysis so far. Numbers in the app use the en_US format, since the app is in English. - A second of a live session: the store makes one snapshot at a time off the main thread, of the newest analysis; one a newer analysis replaced while it waited is never judged, so a snapshot slower than a second leaves no queue of copies, and the session's other updates do not wait for it. Each window of the scan looks up its own buckets and, by bisection while they are in time order, its own events and wakes; what every window takes from the whole session (
HintFacts.History: the handshakes, the EAP exchanges, the SAE failures, the session's Block Acks, the Mist polls parsed) is worked out once per report. On the main thread: the window's own body, with the toolbar, reads neither the live state nor the snapshot, since AppKit builds the toolbar again for each new body; the pipeline's reports (frames per second, the stats lines, the sweep's hops) change the live state once per tick, the Mac's Wi-Fi only when it changes; the Overview's time axis draws again only for another moment (SessionSnapshot.sameMoment), not for a change of the live state alone; the story and the Overview's time axis are made with the snapshot, off the main thread; the lines of the time axis and of Link metrics are vectorized plots (LinePlot,PointPlot), one per line; the time axis and the charts of Link metrics draw per point of their width only what shows apart: of a line the lowest and the highest point and the points where it leaves or reaches a gap, of the MCS a dot per value (PlotPoint.thinnedLine,thinnedDots), and a segment of one point, which draws no line, as a dot (withoutLonePoints,lonePoints); the charts of Link metrics join the segments of a line whose gap does not show at their width, which the line's round caps close (PlotPoint.joined), while the readout and the hover read every point; the time axis's pointer, the moment hovered and the span being dragged, is an observed object of its own (TimeAxisPointer) that only the readout and the marks behind each lane read (PointerMarks), so a move of the mouse draws those and not the lanes; the time formatters are made once (decode-cost.md). - Processes:
Subprocesswaits without blocking a GCD thread. With many tshark processes at once the pool otherwise filled up, and then the pipes stopped draining. A damaged frame does not stop tshark 4.6.8, nor the replay: recordings damaged at up to one byte in twenty went through both passes andlinkside-cli replaywithout a crash, a hang or a line the decoder cannot read, and a frame left without a type is skipped as any frame that is no 802.11. The engine takes a damaged frame that still reads as 802.11 at its word, a damaged BSSID as another AP (damaged-frames.md).