mobile-ios/CovidSafe/Herald/Sensor/BLE/BLEUtilities.swift
2020-12-19 16:13:44 +11:00

195 lines
5.3 KiB
Swift

//
// BLEUtilities.swift
//
// Copyright 2020 VMware, Inc.
// SPDX-License-Identifier: MIT
//
import Foundation
import CoreBluetooth
/**
Extension to make the state human readable in logs.
*/
@available(iOS 10.0, *)
extension CBManagerState: CustomStringConvertible {
/**
Get plain text description of state.
*/
public var description: String {
switch self {
case .poweredOff: return ".poweredOff"
case .poweredOn: return ".poweredOn"
case .resetting: return ".resetting"
case .unauthorized: return ".unauthorized"
case .unknown: return ".unknown"
case .unsupported: return ".unsupported"
@unknown default: return "undefined"
}
}
}
extension CBPeripheralManagerState : CustomStringConvertible {
/**
Get plain text description of state.
*/
public var description: String {
switch self {
case .poweredOff: return ".poweredOff"
case .poweredOn: return ".poweredOn"
case .resetting: return ".resetting"
case .unauthorized: return ".unauthorized"
case .unknown: return ".unknown"
case .unsupported: return ".unsupported"
@unknown default: return "undefined"
}
}
}
extension CBCentralManagerState : CustomStringConvertible {
/**
Get plain text description of state.
*/
public var description: String {
switch self {
case .poweredOff: return ".poweredOff"
case .poweredOn: return ".poweredOn"
case .resetting: return ".resetting"
case .unauthorized: return ".unauthorized"
case .unknown: return ".unknown"
case .unsupported: return ".unsupported"
@unknown default: return "undefined"
}
}
}
/**
Extension to make the state human readable in logs.
*/
extension CBPeripheralState: CustomStringConvertible {
/**
Get plain text description fo state.
*/
public var description: String {
switch self {
case .connected: return ".connected"
case .connecting: return ".connecting"
case .disconnected: return ".disconnected"
case .disconnecting: return ".disconnecting"
@unknown default: return "undefined"
}
}
}
/**
Extension to make the time intervals more human readable in code.
*/
extension TimeInterval {
static var day: TimeInterval { get { TimeInterval(86400) } }
static var hour: TimeInterval { get { TimeInterval(3600) } }
static var minute: TimeInterval { get { TimeInterval(60) } }
static var never: TimeInterval { get { TimeInterval(Int.max) } }
}
/**
Sample statistics.
*/
class Sample {
private var n:Int64 = 0
private var m1:Double = 0.0
private var m2:Double = 0.0
private var m3:Double = 0.0
private var m4:Double = 0.0
/**
Minimum sample value.
*/
var min:Double? = nil
/**
Maximum sample value.
*/
var max:Double? = nil
/**
Sample size.
*/
var count:Int64 { get { n } }
/**
Mean sample value.
*/
var mean:Double? { get { n > 0 ? m1 : nil } }
/**
Sample variance.
*/
var variance:Double? { get { n > 1 ? m2 / Double(n - 1) : nil } }
/**
Sample standard deviation.
*/
var standardDeviation:Double? { get { n > 1 ? sqrt(m2 / Double(n - 1)) : nil } }
/**
String representation of mean, standard deviation, min and max
*/
var description: String { get {
let sCount = n.description
let sMean = (mean == nil ? "-" : mean!.description)
let sStandardDeviation = (standardDeviation == nil ? "-" : standardDeviation!.description)
let sMin = (min == nil ? "-" : min!.description)
let sMax = (max == nil ? "-" : max!.description)
return "count=" + sCount + ",mean=" + sMean + ",sd=" + sStandardDeviation + ",min=" + sMin + ",max=" + sMax
} }
/**
Add sample value.
*/
func add(_ x:Double) {
// Sample value accumulation algorithm avoids reiterating sample to compute variance.
let n1 = n
n += 1
let d = x - m1
let d_n = d / Double(n)
let d_n2 = d_n * d_n;
let t = d * d_n * Double(n1);
m1 += d_n;
m4 += t * d_n2 * Double(n * n - 3 * n + 3) + 6 * d_n2 * m2 - 4 * d_n * m3;
m3 += t * d_n * Double(n - 2) - 3 * d_n * m2;
m2 += t;
if min == nil || x < min! {
min = x;
}
if max == nil || x > max! {
max = x;
}
}
}
/**
Time interval samples for collecting elapsed time statistics.
*/
class TimeIntervalSample : Sample {
private var startTime: Date?
private var timestamp: Date?
var period: TimeInterval? { get {
(startTime == nil ? nil : timestamp?.timeIntervalSince(startTime!))
}}
override var description: String { get {
let sPeriod = (period == nil ? "-" : period!.description)
return super.description + ",period=" + sPeriod
}}
/**
Add elapsed time since last call to add() as sample.
*/
func add() {
guard timestamp != nil else {
timestamp = Date()
startTime = timestamp
return
}
let now = Date()
if let timestamp = timestamp {
add(now.timeIntervalSince(timestamp))
}
timestamp = now
}
}