From 481f94d5115ab7992fb40914971c1a63ba69eba9 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Mon, 26 Jan 2026 23:11:07 +0100 Subject: [PATCH] feat: added jitter to the network module that is calculated from the ICMP signals --- Modules/Net/main.swift | 1 + Modules/Net/popup.swift | 17 ++++++++++++++++- Modules/Net/readers.swift | 26 ++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Modules/Net/main.swift b/Modules/Net/main.swift index 6e43baae..9c1b05e6 100644 --- a/Modules/Net/main.swift +++ b/Modules/Net/main.swift @@ -107,6 +107,7 @@ public struct Network_Usage: Codable, RemoteType { public struct Network_Connectivity: Codable { var status: Bool = false var latency: Double = 0 + var jitter: Double = 0 } public struct Network_Process: Codable, Process_p { diff --git a/Modules/Net/popup.swift b/Modules/Net/popup.swift index e2117fb6..4815c943 100644 --- a/Modules/Net/popup.swift +++ b/Modules/Net/popup.swift @@ -38,6 +38,7 @@ internal class Popup: PopupWrapper { private var statusField: ValueField? = nil private var connectivityField: ValueField? = nil private var latencyField: ValueField? = nil + private var jitterField: ValueField? = nil private var interfaceView: NSStackView? = nil private var interfaceField: ValueField? = nil @@ -82,6 +83,7 @@ internal class Popup: PopupWrapper { private var lastReset: Date = Date() private var latency: [Double] = [] + private var jitter: [Double] = [] private var base: DataSizeBase { DataSizeBase(rawValue: Store.shared.string(key: "\(self.title)_base", defaultValue: "byte")) ?? .byte @@ -274,6 +276,7 @@ internal class Popup: PopupWrapper { self.statusField = popupRow(view, title: "\(localizedString("Status")):", value: localizedString("Unknown")).1 self.connectivityField = popupRow(view, title: "\(localizedString("Internet connection")):", value: localizedString("Unknown")).1 self.latencyField = popupRow(view, title: "\(localizedString("Latency")):", value: "0 ms").1 + self.jitterField = popupRow(view, title: "\(localizedString("Jitter")):", value: "0 ms").1 return view } @@ -602,18 +605,30 @@ internal class Popup: PopupWrapper { } self.latency.append(value?.latency ?? 0) + if self.jitter.count >= 90 { + self.jitter.remove(at: 0) + } + self.jitter.append(value?.jitter ?? 0) + DispatchQueue.main.async(execute: { if (self.window?.isVisible ?? false) || !self.connectionInitialized { var text = "Unknown" var latency = localizedString("Unknown") + var jitter = localizedString("Unknown") + if let v = value { text = v.status ? "UP" : "DOWN" if v.status && !self.latency.isEmpty { latency = "\((self.latency.reduce(0, +) / Double(self.latency.count)).rounded(toPlaces: 2)) ms" } + if v.status && !self.jitter.isEmpty { + jitter = "\((self.jitter.reduce(0, +) / Double(self.jitter.count)).rounded(toPlaces: 2)) ms" + } } - self.connectivityField?.stringValue = localizedString(text) self.latencyField?.stringValue = latency + self.jitterField?.stringValue = jitter + + self.connectivityField?.stringValue = localizedString(text) self.connectionInitialized = true } diff --git a/Modules/Net/readers.swift b/Modules/Net/readers.swift index 1187f480..c9af6487 100644 --- a/Modules/Net/readers.swift +++ b/Modules/Net/readers.swift @@ -742,6 +742,18 @@ internal class ConnectivityReader: Reader { set { self.variablesQueue.sync { self._latency = newValue } } } + private var _previousLatency: Double? = nil + private var previousLatency: Double? { + get { self.variablesQueue.sync { self._previousLatency } } + set { self.variablesQueue.sync { self._previousLatency = newValue } } + } + + private var _jitter: Double? = nil + private var jitter: Double? { + get { self.variablesQueue.sync { self._jitter } } + set { self.variablesQueue.sync { self._jitter = newValue } } + } + var start: DispatchTime? = nil private struct ICMPHeader { @@ -817,6 +829,9 @@ internal class ConnectivityReader: Reader { if let l = self.latency { self.wrapper.latency = l } + if let j = self.jitter { + self.wrapper.jitter = j + } self.callback(self.wrapper) } } @@ -831,6 +846,17 @@ internal class ConnectivityReader: Reader { let end = DispatchTime.now() self.latency = Double(end.uptimeNanoseconds - (self.start?.uptimeNanoseconds ?? 0)) / 1_000_000 + + if let prev = self.previousLatency { + let d = abs((self.latency ?? 0) - prev) + if self.jitter == nil { + self.jitter = d + } else { + self.jitter! += (d - self.jitter!) / 16.0 + } + } + self.previousLatency = self.latency + self.status = error == nil self.isPinging = false self.timeoutTimer?.invalidate()