TIRCalculator.swift 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // LoopFollow
  2. // TIRCalculator.swift
  3. import Foundation
  4. class TIRCalculator {
  5. static func calculate(bgData: [ShareGlucoseData]) -> [TIRDataPoint] {
  6. guard !bgData.isEmpty else { return [] }
  7. let thresholds = UnitSettingsStore.shared.effectiveThresholds()
  8. let veryLowThreshold = 54.0
  9. let lowThreshold = thresholds.low
  10. let highThreshold = thresholds.high
  11. let veryHighThreshold = 250.0
  12. var periodData: [TIRPeriod: [Double]] = [:]
  13. let calendar = dateTimeUtils.displayCalendar()
  14. for reading in bgData {
  15. let date = Date(timeIntervalSince1970: reading.date)
  16. let components = calendar.dateComponents([.hour], from: date)
  17. let hour = components.hour ?? 0
  18. let glucose = Double(reading.sgv)
  19. var period: TIRPeriod?
  20. if let hourRange = TIRPeriod.night.hourRange, hour >= hourRange.start, hour < hourRange.end {
  21. period = .night
  22. } else if let hourRange = TIRPeriod.morning.hourRange, hour >= hourRange.start, hour < hourRange.end {
  23. period = .morning
  24. } else if let hourRange = TIRPeriod.day.hourRange, hour >= hourRange.start, hour < hourRange.end {
  25. period = .day
  26. } else if let hourRange = TIRPeriod.evening.hourRange, hour >= hourRange.start, hour < hourRange.end {
  27. period = .evening
  28. }
  29. if let period = period {
  30. if periodData[period] == nil {
  31. periodData[period] = []
  32. }
  33. periodData[period]?.append(glucose)
  34. }
  35. }
  36. var tirPoints: [TIRDataPoint] = []
  37. for period in [TIRPeriod.night, .morning, .day, .evening] {
  38. guard let readings = periodData[period], !readings.isEmpty else {
  39. tirPoints.append(TIRDataPoint(
  40. period: period,
  41. veryLow: 0.0,
  42. low: 0.0,
  43. inRange: 0.0,
  44. high: 0.0,
  45. veryHigh: 0.0
  46. ))
  47. continue
  48. }
  49. let percentages = calculatePercentages(readings: readings,
  50. veryLowThreshold: veryLowThreshold,
  51. lowThreshold: lowThreshold,
  52. highThreshold: highThreshold,
  53. veryHighThreshold: veryHighThreshold)
  54. tirPoints.append(TIRDataPoint(
  55. period: period,
  56. veryLow: percentages.veryLow,
  57. low: percentages.low,
  58. inRange: percentages.inRange,
  59. high: percentages.high,
  60. veryHigh: percentages.veryHigh
  61. ))
  62. }
  63. let allReadings = bgData.map { Double($0.sgv) }
  64. let averagePercentages = calculatePercentages(readings: allReadings,
  65. veryLowThreshold: veryLowThreshold,
  66. lowThreshold: lowThreshold,
  67. highThreshold: highThreshold,
  68. veryHighThreshold: veryHighThreshold)
  69. tirPoints.append(TIRDataPoint(
  70. period: .average,
  71. veryLow: averagePercentages.veryLow,
  72. low: averagePercentages.low,
  73. inRange: averagePercentages.inRange,
  74. high: averagePercentages.high,
  75. veryHigh: averagePercentages.veryHigh
  76. ))
  77. return tirPoints
  78. }
  79. private static func calculatePercentages(readings: [Double],
  80. veryLowThreshold: Double,
  81. lowThreshold: Double,
  82. highThreshold: Double,
  83. veryHighThreshold: Double) -> (veryLow: Double, low: Double, inRange: Double, high: Double, veryHigh: Double)
  84. {
  85. let total = Double(readings.count)
  86. guard total > 0 else {
  87. return (0.0, 0.0, 0.0, 0.0, 0.0)
  88. }
  89. var veryLowCount = 0
  90. var lowCount = 0
  91. var inRangeCount = 0
  92. var highCount = 0
  93. var veryHighCount = 0
  94. for glucose in readings {
  95. if glucose < veryLowThreshold {
  96. veryLowCount += 1
  97. } else if glucose < lowThreshold {
  98. lowCount += 1
  99. } else if glucose > veryHighThreshold {
  100. veryHighCount += 1
  101. } else if glucose > highThreshold {
  102. highCount += 1
  103. } else {
  104. inRangeCount += 1
  105. }
  106. }
  107. return (
  108. veryLow: (Double(veryLowCount) / total) * 100.0,
  109. low: (Double(lowCount) / total) * 100.0,
  110. inRange: (Double(inRangeCount) / total) * 100.0,
  111. high: (Double(highCount) / total) * 100.0,
  112. veryHigh: (Double(veryHighCount) / total) * 100.0
  113. )
  114. }
  115. }