feat: added jitter to the network module that is calculated from the ICMP signals

This commit is contained in:
Serhiy Mytrovtsiy
2026-01-26 23:11:07 +01:00
parent 9671269b39
commit 481f94d511
3 changed files with 43 additions and 1 deletions

View File

@@ -107,6 +107,7 @@ public struct Network_Usage: Codable, RemoteType {
public struct Network_Connectivity: Codable { public struct Network_Connectivity: Codable {
var status: Bool = false var status: Bool = false
var latency: Double = 0 var latency: Double = 0
var jitter: Double = 0
} }
public struct Network_Process: Codable, Process_p { public struct Network_Process: Codable, Process_p {

View File

@@ -38,6 +38,7 @@ internal class Popup: PopupWrapper {
private var statusField: ValueField? = nil private var statusField: ValueField? = nil
private var connectivityField: ValueField? = nil private var connectivityField: ValueField? = nil
private var latencyField: ValueField? = nil private var latencyField: ValueField? = nil
private var jitterField: ValueField? = nil
private var interfaceView: NSStackView? = nil private var interfaceView: NSStackView? = nil
private var interfaceField: ValueField? = nil private var interfaceField: ValueField? = nil
@@ -82,6 +83,7 @@ internal class Popup: PopupWrapper {
private var lastReset: Date = Date() private var lastReset: Date = Date()
private var latency: [Double] = [] private var latency: [Double] = []
private var jitter: [Double] = []
private var base: DataSizeBase { private var base: DataSizeBase {
DataSizeBase(rawValue: Store.shared.string(key: "\(self.title)_base", defaultValue: "byte")) ?? .byte 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.statusField = popupRow(view, title: "\(localizedString("Status")):", value: localizedString("Unknown")).1
self.connectivityField = popupRow(view, title: "\(localizedString("Internet connection")):", 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.latencyField = popupRow(view, title: "\(localizedString("Latency")):", value: "0 ms").1
self.jitterField = popupRow(view, title: "\(localizedString("Jitter")):", value: "0 ms").1
return view return view
} }
@@ -602,18 +605,30 @@ internal class Popup: PopupWrapper {
} }
self.latency.append(value?.latency ?? 0) 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: { DispatchQueue.main.async(execute: {
if (self.window?.isVisible ?? false) || !self.connectionInitialized { if (self.window?.isVisible ?? false) || !self.connectionInitialized {
var text = "Unknown" var text = "Unknown"
var latency = localizedString("Unknown") var latency = localizedString("Unknown")
var jitter = localizedString("Unknown")
if let v = value { if let v = value {
text = v.status ? "UP" : "DOWN" text = v.status ? "UP" : "DOWN"
if v.status && !self.latency.isEmpty { if v.status && !self.latency.isEmpty {
latency = "\((self.latency.reduce(0, +) / Double(self.latency.count)).rounded(toPlaces: 2)) ms" 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.latencyField?.stringValue = latency
self.jitterField?.stringValue = jitter
self.connectivityField?.stringValue = localizedString(text)
self.connectionInitialized = true self.connectionInitialized = true
} }

View File

@@ -742,6 +742,18 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
set { self.variablesQueue.sync { self._latency = newValue } } 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 var start: DispatchTime? = nil
private struct ICMPHeader { private struct ICMPHeader {
@@ -817,6 +829,9 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
if let l = self.latency { if let l = self.latency {
self.wrapper.latency = l self.wrapper.latency = l
} }
if let j = self.jitter {
self.wrapper.jitter = j
}
self.callback(self.wrapper) self.callback(self.wrapper)
} }
} }
@@ -831,6 +846,17 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
let end = DispatchTime.now() let end = DispatchTime.now()
self.latency = Double(end.uptimeNanoseconds - (self.start?.uptimeNanoseconds ?? 0)) / 1_000_000 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.status = error == nil
self.isPinging = false self.isPinging = false
self.timeoutTimer?.invalidate() self.timeoutTimer?.invalidate()