determine_basal.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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. const minLimitChris = profile.autosens_min;
  7. const maxLimitChris = profile.autosens_max;
  8. const adjustmentFactor = 1;
  9. // Your current target, lower limit
  10. const currentMinTarget = profile.min_bg;
  11. var exerciseSetting = false;
  12. var log = "";
  13. var logTDD = "";
  14. var logBasal = "";
  15. var logBolus = "";
  16. var logTempBasal = "";
  17. var current = 0;
  18. // If you have not set this to 0.05 in FAX settings (Omnipod), this will be set to 0.1 in code.
  19. var minimalDose = profile.bolus_increment;
  20. var TDD = 0;
  21. var insulin = 0;
  22. var tempInsulin = 0;
  23. var bolusInsulin = 0;
  24. var scheduledBasalInsulin = 0;
  25. var incrementsRaw = 0;
  26. var incrementsRounded = 0;
  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. TDD += pumphistory[i].amount;
  44. }
  45. }
  46. bolusInsulin = TDD;
  47. // Temp basals:
  48. if (minimalDose != 0.05) {
  49. minimalDose = 0.1;
  50. }
  51. for (let j = 1; j < pumphistory.length; j++) {
  52. if (pumphistory[j]._type == "TempBasal" && pumphistory[j].rate > 0) {
  53. current = j;
  54. quota = pumphistory[j].rate;
  55. var duration = pumphistory[j-1]['duration (min)'] / 60;
  56. var origDur = duration;
  57. var pastTime = new Date(pumphistory[j-1].timestamp);
  58. // If temp basal hasn't yet ended, use now as end date for calculation
  59. do {
  60. j--;
  61. if (j <= 0) {
  62. var morePresentTime = new Date();
  63. break;
  64. } else if (pumphistory[j]._type == "TempBasal" || pumphistory[j]._type == "PumpSuspend") {
  65. var morePresentTime = new Date(pumphistory[j].timestamp);
  66. break;
  67. }
  68. }
  69. while (j >= 0);
  70. var diff = (morePresentTime - pastTime) / 36e5;
  71. if (diff < origDur) {
  72. duration = diff;
  73. }
  74. insulin = quota * duration;
  75. // Account for smallest possible pump dosage
  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. j = current;
  83. }
  84. // Add temp basal delivered to TDD
  85. }
  86. // Check and count for exceptions when
  87. // basals are delivered with a scheduled basal rate or an Autotuned basal rate.
  88. // 1. 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) continuing in open loop for a while.
  89. // 2. 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. for (let i = 0; i < pumphistory.length; i++) {
  92. // Check for 0 temp basals with 0 min duration.
  93. insulin = 0;
  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. insulin = basalScheduledRate * basDuration;
  118. // Account for smallest possible pump dosage
  119. incrementsRaw = insulin / minimalDose;
  120. if (incrementsRaw >= 1) {
  121. incrementsRounded = Math.floor(incrementsRaw);
  122. insulin = incrementsRounded * minimalDose;
  123. scheduledBasalInsulin += insulin;
  124. } else { insulin = 0}
  125. } else if (k + 1 < profile.basalprofile.length) {
  126. if (profile.basalprofile[k].start < string && profile.basalprofile[k+1].start > string){
  127. // This is also the scheduled insulin amount delivered after ending a manual temp basal and (perhaps) when continuing in open loop or if disconnected
  128. basalScheduledRate = profile.basalprofile[k].rate;
  129. insulin = basalScheduledRate * basDuration;
  130. // Account for smallest possible pump dosage
  131. incrementsRaw = insulin / minimalDose;
  132. if (incrementsRaw >= 1) {
  133. incrementsRounded = Math.floor(incrementsRaw);
  134. insulin = incrementsRounded * minimalDose;
  135. scheduledBasalInsulin += insulin;
  136. } else { insulin = 0}
  137. }
  138. }
  139. }
  140. }
  141. }
  142. // Check for temp basals that completes
  143. if (pumphistory[i]._type == "TempBasal") {
  144. let time1 = new Date(pumphistory[i].timestamp);
  145. let time2 = time1;
  146. for (let m = i; m < pumphistory.length; m--) {
  147. if (pumphistory[m]._type == "TempBasal") {
  148. let time2 = new Date(pumphistory[m].timestamp);
  149. break;
  150. }
  151. }
  152. let basDuration = (time2 - time1) / 36e5;
  153. if ((pumphistory[i-1]['duration (min)'] / 60 ) < basDuration) {
  154. let timeOrig = new Date(pumphistory[i-1].timestamp);
  155. for (let l = i-1; l < pumphistory.length; l++) {
  156. if (pumphistory[l]._type == "TempBasal") {
  157. let timeNext = new Date(timeOrig = pumphistory[l].timestamp);
  158. break;
  159. }
  160. }
  161. let durationOfSheduledBasal = (timeNext - timeOrig) / 36e5;
  162. let hour = time1.getHours();
  163. let minutes = time1.getMinutes();
  164. let seconds = "00";
  165. let string = "" + hour + ":" + minutes + ":" + seconds;
  166. let basalScheduledRate = 0;
  167. for (let k = 0; k < profile.basalprofile.length; k++) {
  168. if (profile.basalprofile[k].start == string) {
  169. basalScheduledRate = profile.basalprofile[k].rate;
  170. // This is the scheduled insulin amount delivered after a fully completed temp basal
  171. scheduledBasalInsulin += basalScheduledRate * basDuration;
  172. break;
  173. } else if (k + 1 < profile.basalprofile.length) {
  174. if (profile.basalprofile[k].start < string && profile.basalprofile[k+1].start > string){
  175. basalScheduledRate = profile.basalprofile[k].rate;
  176. // This is the scheduled insulin amount delivered after a fully completed temp basal
  177. scheduledBasalInsulin += basalScheduledRate * basDuration;
  178. break;
  179. }
  180. }
  181. }
  182. }
  183. }
  184. // Account for smallest possible pump dosage
  185. incrementsRaw = scheduledBasalInsulin / minimalDose;
  186. if (incrementsRaw >= 1) {
  187. incrementsRounded = Math.floor(incrementsRaw);
  188. scheduledBasalInsulin = incrementsRounded * minimalDose;
  189. tempInsulin += insulin;
  190. } else { insulin = 0}
  191. }
  192. TDD = bolusInsulin + tempInsulin + scheduledBasalInsulin;
  193. logBolus = ". Delivered bolus insulin: " + bolusInsulin.toPrecision(5) + " U";
  194. logTempBasal = ". Delivered temporary basal insulin: " + tempInsulin.toPrecision(5) + " U";
  195. logBasal = ". Delivered scheduled basal rate insulin: " + scheduledBasalInsulin.toPrecision(5) + " U";
  196. logTDD = ". TDD past 24h is: " + TDD.toPrecision(5) + " U";
  197. // ----------------------------------------------------
  198. // Chris' formula with added adjustmentFactor for tuning:
  199. if (chrisFormula == true && TDD > 0) {
  200. var newRatio = profile.sens / (277700 / (adjustmentFactor * TDD * BG));
  201. 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)";
  202. // Respect autosens.max and autosens.min limits
  203. if (newRatio > maxLimitChris) {
  204. newRatio = maxLimitChris;
  205. 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)";
  206. } else if (newRatio < minLimitChris) {
  207. newRatio = minLimitChris;
  208. 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)";
  209. }
  210. // Set the new ratio
  211. autosens.ratio = newRatio;
  212. // Print to logs
  213. return log + logTDD + logBolus + logTempBasal + logBasal;
  214. } else { return "Chris' formula is off." }
  215. }