removed Kalman, no added value
This commit is contained in:
parent
0e14b56ad7
commit
dea60b95f3
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue