determine_basal.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoir, clock, pumphistory) {
  2. // This middleware only works if you have added pumphistory to middleware in FreeAPS X code (my pumphistory branch).
  3. const BG = glucose[0].glucose;
  4. // Change to false to turn off Chris Wilson's formula
  5. var chrisFormula = true;
  6. var TDD = 0.00;
  7. const minLimitChris = profile.autosens_min;
  8. const maxLimitChris = profile.autosens_max;
  9. const adjustmentFactor = 1;
  10. // Your current target, lower limit
  11. const currentMinTarget = profile.min_bg;
  12. var exerciseSetting = false;
  13. var log = "";
  14. var logTDD = "";
  15. var logBasal = "";
  16. var logBolus = "";
  17. var logTempBasal = "";
  18. var current = 0;
  19. // If you have not set this to 0.05 in FAX settings (Omnipod), this will be set to 0.1 in code.
  20. var minimalDose = profile.bolus_increment;
  21. var insulin = 0.00;
  22. var tempInsulin = 0.00;
  23. var bolusInsulin = 0.00;
  24. var scheduledBasalInsulin = 0,00;
  25. var incrementsRaw = 0.00;
  26. var incrementsRounded = 0.00;
  27. var quota = 0;
  28. if (profile.high_temptarget_raises_sensitivity == true || profile.exercise_mode == true) {
  29. exerciseSetting = true;
  30. }
  31. // Turn off Chris' formula (and AutoISF) when using a temp target >= 118 (6.5 mol/l) and if an exercise setting is enabled.
  32. // If using AutoISF uncomment the profile.use_autoisf = false
  33. if (currentMinTarget >= 118 && exerciseSetting == true) {
  34. // profile.use_autoisf = false;
  35. chrisFormula = false;
  36. log = "Chris' formula off due to a high temp target/exercising. Current min target: " + currentMinTarget;
  37. }
  38. // Calculate TDD --------------------------------------
  39. //Bolus:
  40. for (let i = 0; i < pumphistory.length; i++) {
  41. if (pumphistory[i]._type == "Bolus") {
  42. // Bolus delivered
  43. bolusInsulin += pumphistory[i].amount;
  44. TDD += bolusInsulin;
  45. }
  46. }
  47. // Temp basals:
  48. for (let j = 1; j < pumphistory.length; j++) {
  49. if (pumphistory[j]._type == "TempBasal" && pumphistory[j].rate > 0) {
  50. current = j;
  51. quota = pumphistory[j].rate;
  52. var duration = pumphistory[j-1]['duration (min)'] / 60;
  53. var origDur = duration;
  54. var pastTime = new Date(pumphistory[j-1].timestamp);
  55. // If temp basal hasn't yet ended, use now as end date for calculation
  56. do {
  57. j--;
  58. if (j <= 0) {
  59. var morePresentTime = new Date();
  60. break;
  61. } else if (pumphistory[j]._type == "TempBasal" || pumphistory[j]._type == "PumpSuspend") {
  62. var morePresentTime = new Date(pumphistory[j].timestamp);
  63. break;
  64. }
  65. }
  66. while (j >= 0);
  67. var diff = (morePresentTime - pastTime) / 36e5;
  68. if (diff < origDur) {
  69. duration = diff;
  70. }
  71. insulin = quota * duration;
  72. // Account for smallest possible pump dosage
  73. if (minimalDose != 0.05) {
  74. minimalDose = 0.1;
  75. }
  76. incrementsRaw = insulin / minimalDose;
  77. if (incrementsRaw >= 1) {
  78. incrementsRounded = Math.floor(incrementsRaw);
  79. insulin = incrementsRounded * minimalDose;
  80. tempInsulin += insulin;
  81. } else { insulin = 0}
  82. // Add temp basal delivered to TDD
  83. TDD += insulin;
  84. j = current;
  85. }
  86. }
  87. // Calculate basals delivered with scheduled basal rates or Autotuned basal rates.
  88. // Check for 0 temp basals with 0 min duration. Take that timestamp and compare to next enacted temp basal. The difference should be duration of current scheduled basal (which can be retrieved from profile.json). This is for when ending a manual temp basal and (perhaps) continueing in open loop.
  89. // Also check for temp basals with a real duration < (next.basal.timestamp - orig.basal.timestamp). This is a temp basal that completes. Then calculate (following.basal.timestamp - next.basal.timestamp)h * the basal rate scheduled at that time.
  90. ///
  91. var scheduledBasalInsulin = 0;
  92. for (let i = 0; i < pumphistory.length; i++) {
  93. // Check for 0 temp basals with 0 min duration.
  94. if (pumphistory[i]['duration (min)'] == 0) {
  95. let time1 = new Date(pumphistory[i].timestamp);
  96. let time2 = time1;
  97. let j = i;
  98. do {
  99. j--;
  100. if (pumphistory[j]._type == "TempBasal" && j >= 0) {
  101. time2 = new Date(pumphistory[j].timestamp);
  102. break;
  103. }
  104. } while (j >= 0);
  105. // duration of current scheduled basal in h
  106. let basDuration = (time2 - time1) / 36e5;
  107. if (basDuration > 0) {
  108. let hour = time1.getHours();
  109. let minutes = time1.getMinutes();
  110. let seconds = "00";
  111. string = "" + hour + ":" + minutes + ":" + seconds;
  112. let basalScheduledRate = 0;
  113. for (let k = 0; k < profile.basalprofile.length; k++) {
  114. if (profile.basalprofile[k].start = string) {
  115. basalScheduledRate = profile.basalprofile[k].rate;
  116. // This is the scheduled insulin amount delivered after ending a manual temp basal and (perhaps) when continuing in open loop or if disconnected
  117. scheduledBasalInsulin += basalScheduledRate * basDuration;
  118. break;
  119. } else if (profile.basalprofile[k].start < string && profile.basalprofile[k+1].start > string){
  120. basalScheduledRate = profile.basalprofile[k].rate;
  121. scheduledBasalInsulin += basalScheduledRate * basDuration;
  122. break;
  123. }
  124. }
  125. }
  126. }
  127. // Check for temp basals that completes
  128. if (pumphistory[i]._type == "TempBasal") {
  129. let time1 = new Date(pumphistory[i].timestamp);
  130. let time2 = time1;
  131. for (let m = i; m < pumphistory.length; m--) {
  132. if (pumphistory[m]._type == "TempBasal") {
  133. let time2 = new Date(pumphistory[m].timestamp);
  134. break;
  135. }
  136. }
  137. let basDuration = (time2 - time1) / 36e5;
  138. if ((pumphistory[i-1]['duration (min)'] / 60 ) < basDuration) {
  139. let timeOrig = new Date(pumphistory[i-1].timestamp);
  140. for (let l = i-1; l < pumphistory.length; l++) {
  141. if (pumphistory[l]._type == "TempBasal") {
  142. let timeNext = new Date(timeOrig = pumphistory[l].timestamp);
  143. break;
  144. }
  145. }
  146. let durationOfSheduledBasal = (timeNext - timeOrig) / 36e5;
  147. let hour = time1.getHours();
  148. let minutes = time1.getMinutes();
  149. let seconds = "00";
  150. let string = "" + hour + ":" + minutes + ":" + seconds;
  151. let basalScheduledRate = 0;
  152. for (let k = 0; k < profile.basalprofile.length; k++) {
  153. if (profile.basalprofile[k].start = string) {
  154. basalScheduledRate = profile.basalprofile[k].rate;
  155. // This is the scheduled insulin amount delivered after a fully completed temp basal
  156. scheduledBasalInsulin += basalScheduledRate * basDuration;
  157. break;
  158. } else if (profile.basalprofile[k].start < string && profile.basalprofile[k+1].start > string){
  159. basalScheduledRate = profile.basalprofile[k].rate;
  160. // This is the scheduled insulin amount delivered after a fully completed temp basal
  161. scheduledBasalInsulin += basalScheduledRate * basDuration;
  162. break;
  163. }
  164. }
  165. }
  166. }
  167. }
  168. TDD += scheduledBasalInsulin;
  169. logBolus = ". Delivered bolus insulin: " + bolusInsulin.toPrecision(5) + " U";
  170. logTempBasal = ". Delivered temporary basal insulin: " + tempInsulin.toPrecision(5) + " U";
  171. logBasal = ". Delivered scheduled basal rate insulin: " + scheduledBasalInsulin.toPrecision(5) + " U";
  172. logTDD = ". TDD past 24h is: " + TDD.toPrecision(3) + " U";
  173. // ----------------------------------------------------
  174. // Chris' formula:
  175. if (chrisFormula == true && TDD > 0) {
  176. var newRatio = profile.sens / (277700 / (adjustmentFactor * TDD * BG));
  177. log = "New ratio using Chris' formula is " + newRatio.toPrecision(3) + " with ISF: " + (profile.sens / newRatio).toPrecision(3) + " (" + ((profile.sens / newRatio) * 0.0555).toPrecision(3) + " mmol/l/U)";
  178. // Respect autosens.max and autosens.min limits
  179. if (newRatio > maxLimitChris) {
  180. newRatio = maxLimitChris;
  181. log = "Chris' formula hit limit by autosens_max setting: " + maxLimitChris + ". ISF: " + (profile.sens / newRatio).toPrecision(3) + " (" + ((profile.sens / newRatio) * 0.0555).toPrecision(3) + " mmol/l/U)";
  182. } else if (newRatio < minLimitChris) {
  183. newRatio = minLimitChris;
  184. log = "Chris' formula hit limit by autosens_min setting: " + minLimitChris + ". ISF: " + (profile.sens / newRatio).toPrecision(3) + " (" + ((profile.sens / newRatio) * 0.0555).toPrecision(3) + " mmol/l/U)";
  185. }
  186. // Set the new ratio
  187. autosens.ratio = newRatio;
  188. return log + logTDD + logBolus + logTempBasal + logBasal;
  189. } else { return "Chris' formula is off." }
  190. }