TIRCalculator.swift 5.0 KB

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