removed Kalman, no added value

This commit is contained in:
Johan Degraeve 2021-01-03 08:21:50 +01:00
parent 0e14b56ad7
commit dea60b95f3
5 changed files with 2 additions and 138 deletions

View File

@ -437,9 +437,6 @@
F8EEDD6423020FAD00D2D610 /* NoCalibrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EEDD6323020FAD00D2D610 /* NoCalibrator.swift */; };
F8F7B8E6259A6EBF00C47B04 /* LibreSmoothing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F7B8E5259A6EBF00C47B04 /* LibreSmoothing.swift */; };
F8F7B8EB259A7B1C00C47B04 /* SavitzkyGolaySmoothableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F7B8EA259A7B1C00C47B04 /* SavitzkyGolaySmoothableArray.swift */; };
F8F7B8F0259A850100C47B04 /* Double+KalmanFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F7B8EF259A850000C47B04 /* Double+KalmanFilter.swift */; };
F8F7B8F6259A852D00C47B04 /* KalmanFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F7B8F5259A852D00C47B04 /* KalmanFilter.swift */; };
F8F7B8FA259A857400C47B04 /* KalmanFilterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F7B8F9259A857400C47B04 /* KalmanFilterType.swift */; };
F8F971B623A5914D00C3F17D /* M5Stack+BluetoothPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F971B123A5914C00C3F17D /* M5Stack+BluetoothPeripheral.swift */; };
F8F971B723A5914D00C3F17D /* BluetoothPeripheralType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F971B423A5914C00C3F17D /* BluetoothPeripheralType.swift */; };
F8F971B823A5914D00C3F17D /* BluetoothPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F971B523A5914C00C3F17D /* BluetoothPeripheral.swift */; };
@ -1073,9 +1070,6 @@
F8EEDD6323020FAD00D2D610 /* NoCalibrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoCalibrator.swift; sourceTree = "<group>"; };
F8F7B8E5259A6EBF00C47B04 /* LibreSmoothing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibreSmoothing.swift; sourceTree = "<group>"; };
F8F7B8EA259A7B1C00C47B04 /* SavitzkyGolaySmoothableArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavitzkyGolaySmoothableArray.swift; sourceTree = "<group>"; };
F8F7B8EF259A850000C47B04 /* Double+KalmanFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+KalmanFilter.swift"; sourceTree = "<group>"; };
F8F7B8F5259A852D00C47B04 /* KalmanFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KalmanFilter.swift; sourceTree = "<group>"; };
F8F7B8F9259A857400C47B04 /* KalmanFilterType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KalmanFilterType.swift; sourceTree = "<group>"; };
F8F971B123A5914C00C3F17D /* M5Stack+BluetoothPeripheral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "M5Stack+BluetoothPeripheral.swift"; sourceTree = "<group>"; };
F8F971B423A5914C00C3F17D /* BluetoothPeripheralType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralType.swift; sourceTree = "<group>"; };
F8F971B523A5914C00C3F17D /* BluetoothPeripheral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheral.swift; sourceTree = "<group>"; };
@ -2252,9 +2246,6 @@
F8F7B8EE259A84A300C47B04 /* KalmanFilter */ = {
isa = PBXGroup;
children = (
F8F7B8EF259A850000C47B04 /* Double+KalmanFilter.swift */,
F8F7B8F5259A852D00C47B04 /* KalmanFilter.swift */,
F8F7B8F9259A857400C47B04 /* KalmanFilterType.swift */,
);
path = KalmanFilter;
sourceTree = "<group>";
@ -3047,7 +3038,6 @@
F8C97850242A9FD500A09483 /* MiaoMiaoBluetoothPeripheralViewModel.swift in Sources */,
F816E10E2437EAC9009EE65B /* BlueReader+CoreDataProperties.swift in Sources */,
F8B3A830227F085A004BA588 /* SettingsTableViewCell.swift in Sources */,
F8F7B8F6259A852D00C47B04 /* KalmanFilter.swift in Sources */,
F82436FC24BE014000BED341 /* TextsLibreStates.swift in Sources */,
F8F7B8EB259A7B1C00C47B04 /* SavitzkyGolaySmoothableArray.swift in Sources */,
F830991C23C2909E005741DF /* Watlaa+CoreDataClass.swift in Sources */,
@ -3088,7 +3078,6 @@
F8297F4F238DCAD800D74D66 /* BluetoothPeripheralNavigationController.swift in Sources */,
F8A1585322EDB602007F5B5D /* ConstantsBloodGlucose.swift in Sources */,
F85FB769255DE14600D1C39E /* ConstantsLibreSmoothing.swift in Sources */,
F8F7B8FA259A857400C47B04 /* KalmanFilterType.swift in Sources */,
F8DF766023E38FC100063910 /* BLEPeripheral+CoreDataClass.swift in Sources */,
F80ED2EE236F68F90005C035 /* SettingsViewM5StackWiFiSettingsViewModel.swift in Sources */,
F8FDD6CB2553385000625B49 /* Array.swift in Sources */,
@ -3215,7 +3204,6 @@
F80859272364355F00F3829D /* ConstantsGlucoseChart.swift in Sources */,
F825286E2443C1000067AF77 /* BluetoothPeripheralManager+CGMG6TransmitterDelegate.swift in Sources */,
F816E10324367389009EE65B /* GNSEntryBluetoothPeripheralViewModel.swift in Sources */,
F8F7B8F0259A850100C47B04 /* Double+KalmanFilter.swift in Sources */,
F8E51D5D2448D8B5001C9E5A /* LoopManager.swift in Sources */,
F8DF766D23ED9B0900063910 /* DexcomG5BluetoothPeripheralViewModel.swift in Sources */,
F8E51D65244BA790001C9E5A /* WatlaaBluetoothPeripheralViewModel.swift in Sources */,

View File

@ -5,8 +5,8 @@ class LibreSmoothing {
// MARK: - public functions
/// - smooths trend array of GlucoseData, ie per minute values using SavitzkyGolayQuaDratic filter
/// - first per minute SavitzkyGolayQuaDratic, then Kalman filter
/// - after Kalman filter, back the per 5 minutes : using values of 5 minutes before , 10 minutes before, 5 minutes after and 10 minutes after, ... the number of values taken into account depends on the filterWidth
/// - first per minute SavitzkyGolayQuaDratic
/// - then per 5 minutes : using values of 5 minutes before , 10 minutes before, 5 minutes after and 10 minutes after, ... the number of values taken into account depends on the filterWidth
public static func smooth(trend: inout [GlucoseData], repeatPerMinuteSmoothingSavitzkyGolay: Int, filterWidthPerMinuteValuesSavitzkyGolay: Int, filterWidthPer5MinuteValuesSavitzkyGolay: Int, repeatPer5MinuteSmoothingSavitzkyGolay:Int) {
// smooth the trend values, filterWidth 5, 2 iterations
@ -16,9 +16,6 @@ class LibreSmoothing {
}
// apply Kalman filter
LibreSmoothing.smoothWithKalmanFilter(trend: &trend, filterNoise: 2.5)
// now smooth per 5 minutes
LibreSmoothing.smoothPer5Minutes(trend: trend, withFilterWidth: filterWidthPer5MinuteValuesSavitzkyGolay, iterations: repeatPer5MinuteSmoothingSavitzkyGolay)
@ -33,41 +30,6 @@ class LibreSmoothing {
// MARK: - private functions
/// trend must be list of non 0 values, equal time distance from each other (ie every minute)
private static func smoothWithKalmanFilter(trend: inout [GlucoseData], filterNoise: Double) {
// there must be at least one element
guard trend.count > 0 else {return}
// all values must be > 0.0
guard trend.filter({return $0.glucoseLevelRaw > 0.0}).count > 0 else {return}
// copy glucoseLevelRaw for each element in trend to array of Double
let trendAsDoubleArray = trend.map({$0.glucoseLevelRaw})
var filter = KalmanFilter(stateEstimatePrior: trendAsDoubleArray[0], errorCovariancePrior: filterNoise)
// iterate through the items reversed, because the first item is actually the most recent
for (index, item) in trendAsDoubleArray.enumerated().reversed() {
let prediction = filter.predict(stateTransitionModel: 1, controlInputModel: 0, controlVector: 0, covarianceOfProcessNoise: filterNoise)
let update = prediction.update(measurement: item, observationModel: 1, covarienceOfObservationNoise: filterNoise)
filter = update
let glucose = filter.stateEstimatePrior
guard (glucose > 0.0) else {
break
}
trend[index].glucoseLevelRaw = glucose
}
}
/// - smooths each value, using values of 5 minutes before , 10 minutes before, 5 minutes after and 10 minutes after, ... the number of values taken into account depends on the filterWidth
/// - parameters :
/// - withFilterWidth : filter width to use

View File

@ -1,6 +0,0 @@
// MARK: Double as Kalman input
extension Double: KalmanInput {
public var transposed: Double { self }
public var inversed: Double { 1 / self }
public var additionToUnit: Double { 1 - self }
}

View File

@ -1,61 +0,0 @@
/**
Conventional Kalman Filter
*/
public struct KalmanFilter<Type: KalmanInput>: KalmanFilterType {
/// x̂_k|k-1
public let stateEstimatePrior: Type
/// P_k|k-1
public let errorCovariancePrior: Type
public init(stateEstimatePrior: Type, errorCovariancePrior: Type) {
self.stateEstimatePrior = stateEstimatePrior
self.errorCovariancePrior = errorCovariancePrior
}
/**
Predict step in Kalman filter.
- parameter stateTransitionModel: F_k
- parameter controlInputModel: B_k
- parameter controlVector: u_k
- parameter covarianceOfProcessNoise: Q_k
- returns: Another instance of Kalman filter with predicted x̂_k and P_k
*/
public func predict(stateTransitionModel: Type, controlInputModel: Type, controlVector: Type, covarianceOfProcessNoise: Type) -> KalmanFilter {
// x̂_k|k-1 = F_k * x̂_k-1|k-1 + B_k * u_k
let predictedStateEstimate = stateTransitionModel * stateEstimatePrior + controlInputModel * controlVector
// P_k|k-1 = F_k * P_k-1|k-1 * F_k^t + Q_k
let predictedEstimateCovariance = stateTransitionModel * errorCovariancePrior * stateTransitionModel.transposed + covarianceOfProcessNoise
return KalmanFilter(stateEstimatePrior: predictedStateEstimate, errorCovariancePrior: predictedEstimateCovariance)
}
/**
Update step in Kalman filter. We update our prediction with the measurements that we make
- parameter measurement: z_k
- parameter observationModel: H_k
- parameter covarienceOfObservationNoise: R_k
- returns: Updated with the measurements version of Kalman filter with new x̂_k and P_k
*/
public func update(measurement: Type, observationModel: Type, covarienceOfObservationNoise: Type) -> KalmanFilter {
// H_k^t transposed. We cache it improve performance
let observationModelTransposed = observationModel.transposed
// _k = z_k - H_k * x̂_k|k-1
let measurementResidual = measurement - observationModel * stateEstimatePrior
// S_k = H_k * P_k|k-1 * H_k^t + R_k
let residualCovariance = observationModel * errorCovariancePrior * observationModelTransposed + covarienceOfObservationNoise
// K_k = P_k|k-1 * H_k^t * S_k^-1
let kalmanGain = errorCovariancePrior * observationModelTransposed * residualCovariance.inversed
// x̂_k|k = x̂_k|k-1 + K_k * _k
let posterioriStateEstimate = stateEstimatePrior + kalmanGain * measurementResidual
// P_k|k = (I - K_k * H_k) * P_k|k-1
let posterioriEstimateCovariance = (kalmanGain * observationModel).additionToUnit * errorCovariancePrior
return KalmanFilter(stateEstimatePrior: posterioriStateEstimate, errorCovariancePrior: posterioriEstimateCovariance)
}
}

View File

@ -1,19 +0,0 @@
public protocol KalmanInput {
var transposed: Self { get }
var inversed: Self { get }
var additionToUnit: Self { get }
static func + (lhs: Self, rhs: Self) -> Self
static func - (lhs: Self, rhs: Self) -> Self
static func * (lhs: Self, rhs: Self) -> Self
}
public protocol KalmanFilterType {
associatedtype Input: KalmanInput
var stateEstimatePrior: Input { get }
var errorCovariancePrior: Input { get }
func predict(stateTransitionModel: Input, controlInputModel: Input, controlVector: Input, covarianceOfProcessNoise: Input) -> Self
func update(measurement: Input, observationModel: Input, covarienceOfObservationNoise: Input) -> Self
}