@ -31,14 +31,13 @@ function calculate_expected_delta(target_bg, eventual_bg, bgi) {
// (hours * mins_per_hour) / 5 = how many 5 minute periods in 2h = 24
var five _min _blocks = ( 2 * 60 ) / 5 ;
var target _delta = target _bg - eventual _bg ;
var expectedDelta = round ( bgi + ( target _delta / five _min _blocks ) , 1 ) ;
return expectedDelta ;
return /* expectedDelta */ round ( bgi + ( target _delta / five _min _blocks ) , 1 ) ;
}
function convert _bg ( value , profile )
{
if ( profile . out _units == "mmol/L" )
if ( profile . out _units == = "mmol/L" )
{
return round ( value / 18 , 1 ) . toFixed ( 1 ) ;
}
@ -48,10 +47,76 @@ function convert_bg(value, profile)
}
}
var determine _basal = function determine _basal ( glucose _status , currenttemp , iob _data , profile , autosens _data , meal _data , tempBasalFunctions , microBolusAllowed , reservoir _data ) {
function enable _smb (
profile ,
microBolusAllowed ,
meal _data ,
target _bg
) {
// disable SMB when a high temptarget is set
if ( ! microBolusAllowed ) {
console . error ( "SMB disabled (!microBolusAllowed)" ) ;
return false ;
} else if ( ! profile . allowSMB _with _high _temptarget && profile . temptargetSet && target _bg > 100 ) {
console . error ( "SMB disabled due to high temptarget of" , target _bg ) ;
return false ;
} else if ( meal _data . bwFound === true && profile . A52 _risk _enable === false ) {
console . error ( "SMB disabled due to Bolus Wizard activity in the last 6 hours." ) ;
return false ;
}
// enable SMB/UAM if always-on (unless previously disabled for high temptarget)
if ( profile . enableSMB _always === true ) {
if ( meal _data . bwFound ) {
console . error ( "Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard" ) ;
} else {
console . error ( "SMB enabled due to enableSMB_always" ) ;
}
return true ;
}
// enable SMB/UAM (if enabled in preferences) while we have COB
if ( profile . enableSMB _with _COB === true && meal _data . mealCOB ) {
if ( meal _data . bwCarbs ) {
console . error ( "Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard" ) ;
} else {
console . error ( "SMB enabled for COB of" , meal _data . mealCOB ) ;
}
return true ;
}
// enable SMB/UAM (if enabled in preferences) for a full 6 hours after any carb entry
// (6 hours is defined in carbWindow in lib/meal/total.js)
if ( profile . enableSMB _after _carbs === true && meal _data . carbs ) {
if ( meal _data . bwCarbs ) {
console . error ( "Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard" ) ;
} else {
console . error ( "SMB enabled for 6h after carb entry" ) ;
}
return true ;
}
// enable SMB/UAM (if enabled in preferences) if a low temptarget is set
if ( profile . enableSMB _with _temptarget === true && ( profile . temptargetSet && target _bg < 100 ) ) {
if ( meal _data . bwFound ) {
console . error ( "Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard" ) ;
} else {
console . error ( "SMB enabled for temptarget of" , convert _bg ( target _bg , profile ) ) ;
}
return true ;
}
console . error ( "SMB disabled (no enableSMB preferences active or no condition satisfied)" ) ;
return false ;
}
var determine _basal = function determine _basal ( glucose _status , currenttemp , iob _data , profile , autosens _data , meal _data , tempBasalFunctions , microBolusAllowed , reservoir _data , currentTime ) {
var rT = { } ; //short for requestedTemp
var deliverAt = new Date ( ) ;
if ( currentTime ) {
deliverAt = new Date ( currentTime ) ;
}
if ( typeof profile === 'undefined' || typeof profile . current _basal === 'undefined' ) {
rT . error = 'Error: could not get current basal rate' ;
@ -61,26 +126,41 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
var basal = profile _current _basal ;
var systemTime = new Date ( ) ;
if ( currentTime ) {
systemTime = currentTime ;
}
var bgTime = new Date ( glucose _status . date ) ;
var minAgo = round ( ( systemTime - bgTime ) / 60 / 1000 , 1 ) ;
var bg = glucose _status . glucose ;
if ( bg < 39 ) { //Dexcom is in ??? mode or calibrating
rT . reason = "CGM is calibrating or in ??? state" ;
var noise = glucose _status . noise ;
// 38 is an xDrip error state that usually indicates sensor failure
// all other BG values between 11 and 37 mg/dL reflect non-error-code BG values, so we should zero temp for those
if ( bg <= 10 || bg === 38 || noise >= 3 ) { //Dexcom is in ??? mode or calibrating, or xDrip reports high noise
rT . reason = "CGM is calibrating, in ??? state, or noise is high" ;
}
if ( minAgo > 12 || minAgo < - 5 ) { // Dexcom data is too old, or way in the future
rT . reason = "If current system time " + systemTime + " is correct, then BG data is too old. The last BG data was read " + minAgo + "m ago at " + bgTime ;
// if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps
//cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc
} else if ( bg > 60 && glucose _status . delta == 0 && glucose _status . short _avgdelta > - 1 && glucose _status . short _avgdelta < 1 && glucose _status . long _avgdelta > - 1 && glucose _status . long _avgdelta < 1 ) {
if ( glucose _status . last _cal && glucose _status . last _cal < 3 ) {
rT . reason = "CGM was just calibrated" ;
} else {
rT . reason = "Error: CGM data is unchanged for the past ~45m" ;
}
}
if ( bg < 39 || minAgo > 12 || minAgo < - 5 ) {
if ( currenttemp . rate >= basal ) { // high temp is running
rT . reason += ". Canceling high temp basal of " + currenttemp . rate ;
//cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc
if ( bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < - 5 || ( bg > 60 && glucose _status . delta == 0 && glucose _status . short _avgdelta > - 1 && glucose _status . short _avgdelta < 1 && glucose _status . long _avgdelta > - 1 && glucose _status . long _avgdelta < 1 ) ) {
if ( currenttemp . rate > basal ) { // high temp is running
rT . reason += ". Replacing high temp basal of " + currenttemp . rate + " with neutral temp of " + basal ;
rT . deliverAt = deliverAt ;
rT . temp = 'absolute' ;
rT . duration = 0 ;
rT . rate = 0 ;
rT . duration = 3 0;
rT . rate = basal ;
return rT ;
//return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
} else if ( currenttemp . rate == 0 && currenttemp . duration > 30 ) { //shorten long zero temps to 30m
} else if ( currenttemp . rate == = 0 && currenttemp . duration > 30 ) { //shorten long zero temps to 30m
rT . reason += ". Shortening " + currenttemp . duration + "m long zero temp to 30m. " ;
rT . deliverAt = deliverAt ;
rT . temp = 'absolute' ;
@ -115,14 +195,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
var sensitivityRatio ;
var high _temptarget _raises _sensitivity = profile . exercise _mode || profile . high _temptarget _raises _sensitivity ;
var normalTarget = 100 ; // evaluate high/low temptarget against 100, not scheduled basal (which might change)
var normalTarget = 100 ; // evaluate high/low temptarget against 100, not scheduled target (which might change)
if ( profile . half _basal _exercise _target ) {
var halfBasalTarget = profile . half _basal _exercise _target ;
} else {
var halfBasalTarget = 160 ; // when temptarget is 160 mg/dL, run 50% basal (120 = 75%; 140 = 60%)
halfBasalTarget = 160 ; // when temptarget is 160 mg/dL, run 50% basal (120 = 75%; 140 = 60%)
// 80 mg/dL with low_temptarget_lowers_sensitivity would give 1.5x basal, but is limited to autosens_max (1.2x by default)
}
if ( high _temptarget _raises _sensitivity && profile . temptargetSet && target _bg > normalTarget + 10
if ( high _temptarget _raises _sensitivity && profile . temptargetSet && target _bg > normalTarget
|| profile . low _temptarget _lowers _sensitivity && profile . temptargetSet && target _bg < normalTarget ) {
// w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44
// e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6
@ -132,36 +212,36 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// limit sensitivityRatio to profile.autosens_max (1.2x by default)
sensitivityRatio = Math . min ( sensitivityRatio , profile . autosens _max ) ;
sensitivityRatio = round ( sensitivityRatio , 2 ) ;
console . error ( "Sensitivity ratio set to " + sensitivityRatio + " based on temp target of " + target _bg + "; " ) ;
} else if ( typeof autosens _data !== 'undefined' ) {
console . log ( "Sensitivity ratio set to " + sensitivityRatio + " based on temp target of " + target _bg + "; " ) ;
} else if ( typeof autosens _data !== 'undefined' && autosens _data ) {
sensitivityRatio = autosens _data . ratio ;
console . error ( "Autosens ratio: " + sensitivityRatio + "; " ) ;
console . log ( "Autosens ratio: " + sensitivityRatio + "; " ) ;
}
if ( sensitivityRatio ) {
basal = profile . current _basal * sensitivityRatio ;
basal = round _basal ( basal , profile ) ;
if ( basal != profile _current _basal ) {
console . error ( "Adjusting basal from " + profile _current _basal + " to " + basal + "; " ) ;
if ( basal != = profile _current _basal ) {
console . log ( "Adjusting basal from " + profile _current _basal + " to " + basal + "; " ) ;
} else {
console . error ( "Basal unchanged: " + basal + "; " ) ;
console . log ( "Basal unchanged: " + basal + "; " ) ;
}
}
// adjust min, max, and target BG for sensitivity, such that 50% increase in ISF raises target from 100 to 120
if ( profile . temptargetSet ) {
//console. error ("Temp Target set, not adjusting with autosens; ");
} else if ( typeof autosens _data !== 'undefined' ) {
//console. log ("Temp Target set, not adjusting with autosens; ");
} else if ( typeof autosens _data !== 'undefined' && autosens _data ) {
if ( profile . sensitivity _raises _target && autosens _data . ratio < 1 || profile . resistance _lowers _target && autosens _data . ratio > 1 ) {
// with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range
min _bg = round ( ( min _bg - 60 ) / autosens _data . ratio ) + 60 ;
max _bg = round ( ( max _bg - 60 ) / autosens _data . ratio ) + 60 ;
new _target _bg = round ( ( target _bg - 60 ) / autosens _data . ratio ) + 60 ;
var new _target _bg = round ( ( target _bg - 60 ) / autosens _data . ratio ) + 60 ;
// don't allow target_bg below 80
new _target _bg = Math . max ( 80 , new _target _bg ) ;
if ( target _bg == new _target _bg ) {
console . error ( "target_bg unchanged: " + new _target _bg + "; " ) ;
if ( target _bg == = new _target _bg ) {
console . log ( "target_bg unchanged: " + new _target _bg + "; " ) ;
} else {
console . error ( "target_bg from " + target _bg + " to " + new _target _bg + "; " ) ;
console . log ( "target_bg from " + target _bg + " to " + new _target _bg + "; " ) ;
}
target _bg = new _target _bg ;
}
@ -197,34 +277,33 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
var profile _sens = round ( profile . sens , 1 )
var sens = profile . sens ;
if ( typeof autosens _data !== 'undefined' ) {
if ( typeof autosens _data !== 'undefined' && autosens _data ) {
sens = profile . sens / sensitivityRatio ;
sens = round ( sens , 1 ) ;
if ( sens != profile _sens ) {
console . error ( "ISF from " + profile _sens + " to " + sens ) ;
if ( sens != = profile _sens ) {
console . log ( "ISF from " + profile _sens + " to " + sens ) ;
} else {
console . error ( "ISF unchanged: " + sens ) ;
console . log ( "ISF unchanged: " + sens ) ;
}
//console. error (" (autosens ratio "+sensitivityRatio+")");
//console. log (" (autosens ratio "+sensitivityRatio+")");
}
console . error ( "; CR:" , profile . carb _ratio ) ;
// compare currenttemp to iob_data.lastTemp and cancel temp if they don't match
var lastTempAge ;
if ( typeof iob _data . lastTemp !== 'undefined' ) {
lastTempAge = round ( ( new Date ( ) . getTime ( ) - iob _data . lastTemp . date ) / 60000 ) ; // in minutes
// } ---- added to not produce errors
lastTempAge = round ( ( new Date ( systemTime ) . getTime ( ) - iob _data . lastTemp . date ) / 60000 ) ; // in minutes
} else {
lastTempAge = 0 ;
}
//console.error("currenttemp:",currenttemp,"lastTemp:",JSON.stringify(iob_data.lastTemp),"lastTempAge:",lastTempAge,"m");
tempModulus = ( lastTempAge + currenttemp . duration ) % 30 ;
var tempModulus = ( lastTempAge + currenttemp . duration ) % 30 ;
console . error ( "currenttemp:" , currenttemp , "lastTempAge:" , lastTempAge , "m" , "tempModulus:" , tempModulus , "m" ) ;
rT . temp = 'absolute' ;
rT . deliverAt = deliverAt ;
if ( microBolusAllowed && currenttemp && iob _data . lastTemp && currenttemp . rate != iob _data . lastTemp . rate ) {
rT . reason = "Warning: currenttemp rate " + currenttemp . rate + " != lastTemp rate " + iob _data . lastTemp . rate + " from pumphistory; setting neutral temp of "+ basal + ". ";
return tempBasalFunctions . setTempBasal ( basal , 3 0, profile , rT , currenttemp ) ;
if ( microBolusAllowed && currenttemp && iob _data . lastTemp && currenttemp . rate != = iob _data . lastTemp . rate && lastTempAge > 10 && currenttemp . duration ) {
rT . reason = "Warning: currenttemp rate " + currenttemp . rate + " != lastTemp rate " + iob _data . lastTemp . rate + " from pumphistory; canceling temp ";
return tempBasalFunctions . setTempBasal ( 0 , 0, profile , rT , currenttemp ) ;
}
if ( currenttemp && iob _data . lastTemp && currenttemp . duration > 0 ) {
// TODO: fix this (lastTemp.duration is how long it has run; currenttemp.duration is time left
@ -234,10 +313,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
//}
//console.error(lastTempAge, round(iob_data.lastTemp.duration,1), round(lastTempAge - iob_data.lastTemp.duration,1));
var lastTempEnded = lastTempAge - iob _data . lastTemp . duration
if ( lastTempEnded > 5 ) {
rT . reason = "Warning: currenttemp running but lastTemp from pumphistory ended " + lastTempEnded + "m ago; setting neutral temp of "+ basal + ". ";
if ( lastTempEnded > 5 && lastTempAge > 10 ) {
rT . reason = "Warning: currenttemp running but lastTemp from pumphistory ended " + lastTempEnded + "m ago; canceling temp ";
//console.error(currenttemp, round(iob_data.lastTemp,1), round(lastTempAge,1));
return tempBasalFunctions . setTempBasal ( basal , 3 0, profile , rT , currenttemp ) ;
return tempBasalFunctions . setTempBasal ( 0 , 0, profile , rT , currenttemp ) ;
}
// TODO: figure out a way to do this check that doesn't fail across basal schedule boundaries
//if ( tempModulus < 25 && tempModulus > 5 ) {
@ -264,37 +343,44 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
if ( iob _data . iob > 0 ) {
var naive _eventualBG = round ( bg - ( iob _data . iob * sens ) ) ;
} else { // if IOB is negative, be more conservative and use the lower of sens, profile.sens
var naive _eventualBG = round ( bg - ( iob _data . iob * Math . min ( sens , profile . sens ) ) ) ;
naive _eventualBG = round ( bg - ( iob _data . iob * Math . min ( sens , profile . sens ) ) ) ;
}
// and adjust it for the deviation above
var eventualBG = naive _eventualBG + deviation ;
// calculate what portion of that is due to bolussnooze
//var bolusContrib = iob_data.bolussnooze * sens;
// and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime
//var naive_snoozeBG = round( naive_eventualBG + 1.5 * bolusContrib );
// adjust that for deviation like we did eventualBG
//var snoozeBG = naive_snoozeBG + deviation;
// adjust target BG range if needed to safely bring down high BG faster without causing lows
if ( bg > max _bg && profile . adv _target _adjustments && ! profile . temptargetSet ) {
// raise target for noisy / raw CGM data
if ( glucose _status . noise >= 2 ) {
// increase target at least 10% (default 30%) for raw / noisy data
var noisyCGMTargetMultiplier = Math . max ( 1.1 , profile . noisyCGMTargetMultiplier ) ;
// don't allow maxRaw above 250
var maxRaw = Math . min ( 250 , profile . maxRaw ) ;
var adjustedMinBG = round ( Math . min ( 200 , min _bg * noisyCGMTargetMultiplier ) ) ;
var adjustedTargetBG = round ( Math . min ( 200 , target _bg * noisyCGMTargetMultiplier ) ) ;
var adjustedMaxBG = round ( Math . min ( 200 , max _bg * noisyCGMTargetMultiplier ) ) ;
console . log ( "Raising target_bg for noisy / raw CGM data, from " + target _bg + " to " + adjustedTargetBG + "; " ) ;
min _bg = adjustedMinBG ;
target _bg = adjustedTargetBG ;
max _bg = adjustedMaxBG ;
// adjust target BG range if configured to bring down high BG faster
} else if ( bg > max _bg && profile . adv _target _adjustments && ! profile . temptargetSet ) {
// with target=100, as BG rises from 100 to 160, adjustedTarget drops from 100 to 80
var adjustedMinBG = round ( Math . max ( 80 , min _bg - ( bg - min _bg ) / 3 ) , 0 ) ;
var adjustedTargetBG = round ( Math . max ( 80 , target _bg - ( bg - target _bg ) / 3 ) , 0 ) ;
var adjustedMaxBG = round ( Math . max ( 80 , max _bg - ( bg - max _bg ) / 3 ) , 0 ) ;
adjustedMinBG = round ( Math . max ( 80 , min _bg - ( bg - min _bg ) / 3 ) , 0 ) ;
adjustedTargetBG = round ( Math . max ( 80 , target _bg - ( bg - target _bg ) / 3 ) , 0 ) ;
adjustedMaxBG = round ( Math . max ( 80 , max _bg - ( bg - max _bg ) / 3 ) , 0 ) ;
// if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedMinBG, don’ t use it
//console.error("naive_eventualBG:",naive_eventualBG+", eventualBG:",eventualBG);
if ( eventualBG > adjustedMinBG && naive _eventualBG > adjustedMinBG && min _bg > adjustedMinBG ) {
console . error ( "Adjusting targets for high BG: min_bg from " + min _bg + " to " + adjustedMinBG + "; " ) ;
console . log ( "Adjusting targets for high BG: min_bg from " + min _bg + " to " + adjustedMinBG + "; " ) ;
min _bg = adjustedMinBG ;
} else {
console . error ( "min_bg unchanged: " + min _bg + "; " ) ;
console . log ( "min_bg unchanged: " + min _bg + "; " ) ;
}
// if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedTargetBG, don’ t use it
if ( eventualBG > adjustedTargetBG && naive _eventualBG > adjustedTargetBG && target _bg > adjustedTargetBG ) {
console . error ( "target_bg from " + target _bg + " to " + adjustedTargetBG + "; " ) ;
console . log ( "target_bg from " + target _bg + " to " + adjustedTargetBG + "; " ) ;
target _bg = adjustedTargetBG ;
} else {
console . error ( "target_bg unchanged: " + target _bg + "; " ) ;
console . log ( "target_bg unchanged: " + target _bg + "; " ) ;
}
// if eventualBG, naive_eventualBG, and max_bg aren't all above adjustedMaxBG, don’ t use it
if ( eventualBG > adjustedMaxBG && naive _eventualBG > adjustedMaxBG && max _bg > adjustedMaxBG ) {
@ -321,7 +407,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
, 'bg' : bg
, 'tick' : tick
, 'eventualBG' : eventualBG
//, 'snoozeBG': snoozeBG
, 'insulinReq' : 0
, 'reservoir' : reservoir _data // The expected reservoir volume at which to deliver the microbolus (the reservoir volume from right before the last pumphistory run)
, 'deliverAt' : deliverAt // The time at which the microbolus should be delivered
@ -341,71 +426,13 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
ZTpredBGs . push ( bg ) ;
UAMpredBGs . push ( bg ) ;
// enable SMB whenever we have COB or UAM is enabled
// SMB is disabled by default, unless explicitly enabled in preferences.json
var enableSMB = false ;
// disable SMB when a high temptarget is set
if ( ! microBolusAllowed ) {
console . error ( "SMB disabled (!microBolusAllowed)" )
} else if ( ! profile . allowSMB _with _high _temptarget && profile . temptargetSet && target _bg > 100 ) {
console . error ( "SMB disabled due to high temptarget of" , target _bg ) ;
enableSMB = false ;
// enable SMB/UAM (if enabled in preferences) while we have COB
} else if ( profile . enableSMB _with _COB === true && meal _data . mealCOB ) {
if ( meal _data . bwCarbs ) {
if ( profile . A52 _risk _enable ) {
console . error ( "Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard" )
enableSMB = true ;
} else {
console . error ( "SMB not enabled for Bolus Wizard COB" ) ;
}
} else {
console . error ( "SMB enabled for COB of" , meal _data . mealCOB ) ;
enableSMB = true ;
}
// enable SMB/UAM (if enabled in preferences) for a full 6 hours after any carb entry
// (6 hours is defined in carbWindow in lib/meal/total.js)
} else if ( profile . enableSMB _after _carbs === true && meal _data . carbs ) {
if ( meal _data . bwCarbs ) {
if ( profile . A52 _risk _enable ) {
console . error ( "Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard" )
enableSMB = true ;
} else {
console . error ( "SMB not enabled for Bolus Wizard carbs" ) ;
}
} else {
console . error ( "SMB enabled for 6h after carb entry" ) ;
enableSMB = true ;
}
// enable SMB/UAM (if enabled in preferences) if a low temptarget is set
} else if ( profile . enableSMB _with _temptarget === true && ( profile . temptargetSet && target _bg < 100 ) ) {
if ( meal _data . bwFound ) {
if ( profile . A52 _risk _enable ) {
console . error ( "Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard" )
enableSMB = true ;
} else {
console . error ( "enableSMB_with_temptarget not supported within 6h of using Bolus Wizard" ) ;
}
} else {
console . error ( "SMB enabled for temptarget of" , convert _bg ( target _bg , profile ) ) ;
enableSMB = true ;
}
// enable SMB/UAM if always-on (unless previously disabled for high temptarget)
} else if ( profile . enableSMB _always === true ) {
if ( meal _data . bwFound ) {
if ( profile . A52 _risk _enable === true ) {
console . error ( "Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard" )
enableSMB = true ;
} else {
console . error ( "enableSMB_always not supported within 6h of using Bolus Wizard" ) ;
}
} else {
console . error ( "SMB enabled due to enableSMB_always" ) ;
enableSMB = true ;
}
} else {
console . error ( "SMB disabled (no enableSMB preferences active)" ) ;
}
var enableSMB = enable _smb (
profile ,
microBolusAllowed ,
meal _data ,
target _bg
) ;
// enable UAM (if enabled in preferences)
var enableUAM = ( profile . enableUAM ) ;
@ -417,43 +444,48 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// calculate current carb absorption rate, and how long to absorb all carbs
// CI = current carb impact on BG in mg/dL/5m
ci = round ( ( minDelta - bgi ) , 1 ) ;
uci = round ( ( minDelta - bgi ) , 1 ) ;
var uci = round ( ( minDelta - bgi ) , 1 ) ;
// ISF (mg/dL/U) / CR (g/U) = CSF (mg/dL/g)
if ( profile . temptargetSet ) {
// TODO: remove commented-out code for old behavior
//if (profile.temptargetSet) {
// if temptargetSet, use unadjusted profile.sens to allow activity mode sensitivityRatio to adjust CR
var csf = profile . sens / profile . carb _ratio ;
} else {
//var csf = profile.sens / profile.carb_ratio;
//} else {
// otherwise, use autosens-adjusted sens to counteract autosens meal insulin dosing adjustments
// so that autotuned CR is still in effect even when basals and ISF are being adjusted by autosens
var csf = sens / profile . carb _ratio ;
}
//var csf = sens / profile.carb_ratio;
//}
// use autosens-adjusted sens to counteract autosens meal insulin dosing adjustments so that
// autotuned CR is still in effect even when basals and ISF are being adjusted by TT or autosens
// this avoids overdosing insulin for large meals when low temp targets are active
csf = sens / profile . carb _ratio ;
console . error ( "profile.sens:" , profile . sens , "sens:" , sens , "CSF:" , csf ) ;
var maxCarbAbsorptionRate = 30 ; // g/h; maximum rate to assume carbs will absorb if no CI observed
// limit Carb Impact to maxCarbAbsorptionRate * csf in mg/dL per 5m
maxCI = round ( maxCarbAbsorptionRate * csf * 5 / 60 , 1 )
var maxCI = round ( maxCarbAbsorptionRate * csf * 5 / 60 , 1 )
if ( ci > maxCI ) {
console . error ( "Limiting carb impact from" , ci , "to" , maxCI , "mg/dL/5m (" , maxCarbAbsorptionRate , "g/h )" ) ;
ci = maxCI ;
}
// set meal_carbimpact high enough to absorb all meal carbs over 6 hours
// total_impact (mg/dL) = CSF (mg/dL/g) * carbs (g)
//console.error(csf * meal_data.carbs);
// meal_carbimpact (mg/dL/5m) = CSF (mg/dL/g) * carbs (g) / 6 (h) * (1h/60m) * 5 (m/5m) * 2 (for linear decay)
//var meal_carbimpact = round((csf * meal_data.carbs / 6 / 60 * 5 * 2),1)
var remainingCATimeMin = 3 ; // h; before carb absorption starts
// adjust remainingCATime (instead of CR) for autosens
remainingCATimeMin = remainingCATimeMin / sensitivityRatio ;
var remainingCATimeMin = 3 ; // h; duration of expected not-yet-observed carb absorption
// adjust remainingCATime (instead of CR) for autosens if sensitivityRatio defined
if ( sensitivityRatio ) {
remainingCATimeMin = remainingCATimeMin / sensitivityRatio ;
}
// 20 g/h means that anything <= 60g will get a remainingCATimeMin, 80g will get 4h, and 120g 6h
// when actual absorption ramps up it will take over from remainingCATime
var assumedCarbAbsorptionRate = 20 ; // g/h; maximum rate to assume carbs will absorb if no CI observed
var remainingCATime = remainingCATimeMin ; // added by mike https://github.com/openaps/oref0/issues/884
var remainingCATime = remainingCATimeMin ;
if ( meal _data . carbs ) {
// if carbs * assumedCarbAbsorptionRate > remainingCATimeMin, raise it
// so <= 90g is assumed to take 3h, and 120g=4h
remainingCATimeMin = Math . max ( remainingCATimeMin , meal _data . mealCOB / assumedCarbAbsorptionRate ) ;
var lastCarbAge = round ( ( new Date ( ) . getTime ( ) - meal _data . lastCarbTime ) / 60000 ) ;
var lastCarbAge = round ( ( new Date ( systemTime ) . getTime ( ) - meal _data . lastCarbTime ) / 60000 ) ;
//console.error(meal_data.lastCarbTime, lastCarbAge);
fractionCOBAbsorbed = ( meal _data . carbs - meal _data . mealCOB ) / meal _data . carbs ;
var fractionCOBAbsorbed = ( meal _data . carbs - meal _data . mealCOB ) / meal _data . carbs ;
remainingCATime = remainingCATimeMin + 1.5 * lastCarbAge / 60 ;
remainingCATime = round ( remainingCATime , 1 ) ;
//console.error(fractionCOBAbsorbed, remainingCATimeAdjustment, remainingCATime)
@ -478,7 +510,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// remainingCIpeak (mg/dL/5m) = remainingCarbs (g) * CSF (mg/dL/g) * 5 (m/5m) * 1h/60m / (remainingCATime/2) (h)
var remainingCIpeak = remainingCarbs * csf * 5 / 60 / ( remainingCATime / 2 ) ;
//console.error(profile.min_5m_carbimpact,ci,totalCI,totalCA,remainingCarbs,remainingCI,remainingCATime);
//if (meal_data.mealCOB * 3 > meal_data.carbs) { }
// calculate peak deviation in last hour, and slope from that to current deviation
var slopeFromMaxDeviation = round ( meal _data . slopeFromMaxDeviation , 2 ) ;
@ -488,17 +519,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
var slopeFromDeviations = Math . min ( slopeFromMaxDeviation , - slopeFromMinDeviation / 3 ) ;
//console.error(slopeFromMaxDeviation);
aci = 10 ;
var aci = 10 ;
//5m data points = g * (1U/10g) * (40mg/dL/1U) / (mg/dL/5m)
// duration (in 5m data points) = COB (g) * CSF (mg/dL/g) / ci (mg/dL/5m)
// limit cid to remainingCATime hours: the reset goes to remainingCI
if ( ci == 0 ) {
if ( ci == = 0 ) {
// avoid divide by zero
cid = 0 ;
} else {
cid = Math . min ( remainingCATime * 60 / 5 / 2 , Math . max ( 0 , meal _data . mealCOB * csf / ci ) ) ;
}
acid = Math . max ( 0 , meal _data . mealCOB * csf / aci ) ;
var acid = Math . max ( 0 , meal _data . mealCOB * csf / aci ) ;
// duration (hours) = duration (5m) * 5 / 60 * 2 (to account for linear decay)
console . error ( "Carb Impact:" , ci , "mg/dL per 5m; CI Duration:" , round ( cid * 5 / 60 * 2 , 1 ) , "hours; remaining CI (~2h peak):" , round ( remainingCIpeak , 1 ) , "mg/dL per 5m" ) ;
//console.error("Accel. Carb Impact:",aci,"mg/dL per 5m; ACI Duration:",round(acid*5/60*2,1),"hours");
@ -529,18 +560,18 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
try {
iobArray . forEach ( function ( iobTick ) {
//console.error(iobTick);
predBGI = round ( ( - iobTick . activity * sens * 5 ) , 2 ) ;
predZTBGI = round ( ( - iobTick . iobWithZeroTemp . activity * sens * 5 ) , 2 ) ;
var predBGI = round ( ( - iobTick . activity * sens * 5 ) , 2 ) ;
var predZTBGI = round ( ( - iobTick . iobWithZeroTemp . activity * sens * 5 ) , 2 ) ;
// for IOBpredBGs, predicted deviation impact drops linearly from current deviation down to zero
// over 60 minutes (data points every 5m)
predDev = ci * ( 1 - Math . min ( 1 , IOBpredBGs . length / ( 60 / 5 ) ) ) ;
var predDev = ci * ( 1 - Math . min ( 1 , IOBpredBGs . length / ( 60 / 5 ) ) ) ;
IOBpredBG = IOBpredBGs [ IOBpredBGs . length - 1 ] + predBGI + predDev ;
// calculate predBGs with long zero temp without deviations
ZTpredBG = ZTpredBGs [ ZTpredBGs . length - 1 ] + predZTBGI ;
var ZTpredBG = ZTpredBGs [ ZTpredBGs . length - 1 ] + predZTBGI ;
// for COBpredBGs, predicted carb impact drops linearly from current carb impact down to zero
// eventually accounting for all carbs (if they can be absorbed over DIA)
predCI = Math . max ( 0 , Math . max ( 0 , ci ) * ( 1 - COBpredBGs . length / Math . max ( cid * 2 , 1 ) ) ) ;
predACI = Math . max ( 0 , Math . max ( 0 , aci ) * ( 1 - COBpredBGs . length / Math . max ( acid * 2 , 1 ) ) ) ;
var predCI = Math . max ( 0 , Math . max ( 0 , ci ) * ( 1 - COBpredBGs . length / Math . max ( cid * 2 , 1 ) ) ) ;
var predACI = Math . max ( 0 , Math . max ( 0 , aci ) * ( 1 - COBpredBGs . length / Math . max ( acid * 2 , 1 ) ) ) ;
// if any carbs aren't absorbed after remainingCATime hours, assume they'll absorb in a /\ shaped
// bilinear curve peaking at remainingCIpeak at remainingCATime/2 hours (remainingCATime/2*12 * 5m)
// and ending at remainingCATime h (remainingCATime*12 * 5m intervals)
@ -549,18 +580,18 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
remainingCItotal += predCI + remainingCI ;
remainingCIs . push ( round ( remainingCI , 0 ) ) ;
predCIs . push ( round ( predCI , 0 ) ) ;
//console. error (round(predCI,1)+"+"+round(remainingCI,1)+" ");
//console. log (round(predCI,1)+"+"+round(remainingCI,1)+" ");
COBpredBG = COBpredBGs [ COBpredBGs . length - 1 ] + predBGI + Math . min ( 0 , predDev ) + predCI + remainingCI ;
aCOBpredBG = aCOBpredBGs [ aCOBpredBGs . length - 1 ] + predBGI + Math . min ( 0 , predDev ) + predACI ;
var aCOBpredBG = aCOBpredBGs [ aCOBpredBGs . length - 1 ] + predBGI + Math . min ( 0 , predDev ) + predACI ;
// for UAMpredBGs, predicted carb impact drops at slopeFromDeviations
// calculate predicted CI from UAM based on slopeFromDeviations
predUCIslope = Math . max ( 0 , uci + ( UAMpredBGs . length * slopeFromDeviations ) ) ;
var predUCIslope = Math . max ( 0 , uci + ( UAMpredBGs . length * slopeFromDeviations ) ) ;
// if slopeFromDeviations is too flat, predicted deviation impact drops linearly from
// current deviation down to zero over 3h (data points every 5m)
predUCImax = Math . max ( 0 , uci * ( 1 - UAMpredBGs . length / Math . max ( 3 * 60 / 5 , 1 ) ) ) ;
var predUCImax = Math . max ( 0 , uci * ( 1 - UAMpredBGs . length / Math . max ( 3 * 60 / 5 , 1 ) ) ) ;
//console.error(predUCIslope, predUCImax);
// predicted CI from UAM is the lesser of CI based on deviationSlope or DIA
predUCI = Math . min ( predUCIslope , predUCImax ) ;
var predUCI = Math . min ( predUCIslope , predUCImax ) ;
if ( predUCI > 0 ) {
//console.error(UAMpredBGs.length,slopeFromDeviations, predUCI);
UAMduration = round ( ( UAMpredBGs . length + 1 ) * 5 / 60 , 1 ) ;
@ -582,7 +613,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// set minPredBGs starting when currently-dosed insulin activity will peak
// look ahead 60m (regardless of insulin type) so as to be less aggressive on slower insulins
var insulinPeakTime = 60 ;
// add 30m to allow for ins l uin delivery (SMBs or temps)
// add 30m to allow for ins ul in delivery (SMBs or temps)
insulinPeakTime = 90 ;
var insulinPeak5m = ( insulinPeakTime / 60 ) * 12 ;
//console.error(insulinPeakTime, insulinPeak5m, profile.insulinPeakTime, profile.curve);
@ -599,19 +630,18 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// set eventualBG to include effect of carbs
//console.error("PredBGs:",JSON.stringify(predBGs));
} catch ( e ) {
console . error ( "Problem with iobArray. Optional feature Advanced Meal Assist disabled :", e ) ;
console . error ( "Problem with iobArray. Optional feature Advanced Meal Assist disabled " ) ;
}
if ( meal _data . mealCOB ) {
console . error ( "predCIs (mg/dL/5m):" , predCIs . join ( " " ) ) ;
console . error ( "remainingCIs: " , remainingCIs . join ( " " ) ) ;
}
//,"totalCA:",round(totalCA,2),"remainingCItotal/csf+totalCA:",round(remainingCItotal/csf+totalCA,2));
rT . predBGs = { } ;
IOBpredBGs . forEach ( function ( p , i , theArray ) {
theArray [ i ] = round ( Math . min ( 401 , Math . max ( 39 , p ) ) ) ;
} ) ;
for ( var i = IOBpredBGs . length - 1 ; i > 12 ; i -- ) {
if ( IOBpredBGs [ i - 1 ] != IOBpredBGs [ i ] ) { break ; }
if ( IOBpredBGs [ i - 1 ] != = IOBpredBGs [ i ] ) { break ; }
else { IOBpredBGs . pop ( ) ; }
}
rT . predBGs . IOB = IOBpredBGs ;
@ -619,10 +649,9 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
ZTpredBGs . forEach ( function ( p , i , theArray ) {
theArray [ i ] = round ( Math . min ( 401 , Math . max ( 39 , p ) ) ) ;
} ) ;
for ( var i = ZTpredBGs . length - 1 ; i > 6 ; i -- ) {
//if (ZTpredBGs[i-1] != ZTpredBGs[i]) { break; }
for ( i = ZTpredBGs . length - 1 ; i > 6 ; i -- ) {
// stop displaying ZTpredBGs once they're rising and above target
if ( ZTpredBGs [ i - 1 ] >= ZTpredBGs [ i ] || ZTpredBGs [ i ] < target _bg ) { break ; }
if ( ZTpredBGs [ i - 1 ] >= ZTpredBGs [ i ] || ZTpredBGs [ i ] < = target _bg ) { break ; }
else { ZTpredBGs . pop ( ) ; }
}
rT . predBGs . ZT = ZTpredBGs ;
@ -631,19 +660,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
aCOBpredBGs . forEach ( function ( p , i , theArray ) {
theArray [ i ] = round ( Math . min ( 401 , Math . max ( 39 , p ) ) ) ;
} ) ;
for ( var i = aCOBpredBGs . length - 1 ; i > 12 ; i -- ) {
if ( aCOBpredBGs [ i - 1 ] != aCOBpredBGs [ i ] ) { break ; }
for ( i = aCOBpredBGs . length - 1 ; i > 12 ; i -- ) {
if ( aCOBpredBGs [ i - 1 ] != = aCOBpredBGs [ i ] ) { break ; }
else { aCOBpredBGs . pop ( ) ; }
}
// disable for now. may want to add a preference to re-enable
//rT.predBGs.aCOB = aCOBpredBGs;
}
if ( meal _data . mealCOB > 0 && ( ci > 0 || remainingCIpeak > 0 ) ) {
COBpredBGs . forEach ( function ( p , i , theArray ) {
theArray [ i ] = round ( Math . min ( 401 , Math . max ( 39 , p ) ) ) ;
} ) ;
for ( var i = COBpredBGs . length - 1 ; i > 12 ; i -- ) {
if ( COBpredBGs [ i - 1 ] != COBpredBGs [ i ] ) { break ; }
for ( i = COBpredBGs . length - 1 ; i > 12 ; i -- ) {
if ( COBpredBGs [ i - 1 ] != = COBpredBGs [ i ] ) { break ; }
else { COBpredBGs . pop ( ) ; }
}
rT . predBGs . COB = COBpredBGs ;
@ -655,8 +682,8 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
UAMpredBGs . forEach ( function ( p , i , theArray ) {
theArray [ i ] = round ( Math . min ( 401 , Math . max ( 39 , p ) ) ) ;
} ) ;
for ( var i = UAMpredBGs . length - 1 ; i > 12 ; i -- ) {
if ( UAMpredBGs [ i - 1 ] != UAMpredBGs [ i ] ) { break ; }
for ( i = UAMpredBGs . length - 1 ; i > 12 ; i -- ) {
if ( UAMpredBGs [ i - 1 ] != = UAMpredBGs [ i ] ) { break ; }
else { UAMpredBGs . pop ( ) ; }
}
rT . predBGs . UAM = UAMpredBGs ;
@ -666,7 +693,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
}
}
// set eventualBG and snoozeBG based on COB or UAM predBGs
// set eventualBG based on COB or UAM predBGs
rT . eventualBG = eventualBG ;
}
@ -733,14 +760,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
//console.error("minUAMPredBG:",minUAMPredBG,"minZTGuardBG:",minZTGuardBG,"minZTUAMPredBG:",minZTUAMPredBG);
// if any carbs have been entered recently
if ( meal _data . carbs ) {
// average the minIOBPredBG and minUAMPredBG if available
/ *
if ( minUAMPredBG < 999 ) {
avgMinPredBG = round ( ( minIOBPredBG + minUAMPredBG ) / 2 ) ;
} else {
avgMinPredBG = minIOBPredBG ;
}
* /
// if UAM is disabled, use max of minIOBPredBG, minCOBPredBG
if ( ! enableUAM && minCOBPredBG < 999 ) {
@ -748,30 +767,29 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// if we have COB, use minCOBPredBG, or blendedMinPredBG if it's higher
} else if ( minCOBPredBG < 999 ) {
// calculate blendedMinPredBG based on how many carbs remain as COB
//blendedMinPredBG = fractionCarbsLeft*minCOBPredBG + (1-fractionCarbsLeft)*minUAMPredBG;
blendedMinPredBG = fractionCarbsLeft * minCOBPredBG + ( 1 - fractionCarbsLeft ) * minZTUAMPredBG ;
var blendedMinPredBG = fractionCarbsLeft * minCOBPredBG + ( 1 - fractionCarbsLeft ) * minZTUAMPredBG ;
// if blendedMinPredBG > minCOBPredBG, use that instead
minPredBG = round ( Math . max ( minIOBPredBG , minCOBPredBG , blendedMinPredBG ) ) ;
// if carbs have been entered, but have expired, use minUAMPredBG
} else {
//minPredBG = minUAMPredBG;
} else if ( enableUAM ) {
minPredBG = minZTUAMPredBG ;
} else {
minPredBG = minGuardBG ;
}
// in pure UAM mode, use the higher of minIOBPredBG,minUAMPredBG
} else if ( enableUAM ) {
//minPredBG = round(Math.max(minIOBPredBG,minUAMPredBG));
minPredBG = round ( Math . max ( minIOBPredBG , minZTUAMPredBG ) ) ;
}
// make sure minPredBG isn't higher than avgPredBG
minPredBG = Math . min ( minPredBG , avgPredBG ) ;
console . error ( "minPredBG: " + minPredBG + " minIOBPredBG: " + minIOBPredBG + " minZTGuardBG: " + minZTGuardBG ) ;
console . log ( "minPredBG: " + minPredBG + " minIOBPredBG: " + minIOBPredBG + " minZTGuardBG: " + minZTGuardBG ) ;
if ( minCOBPredBG < 999 ) {
console . error ( " minCOBPredBG: " + minCOBPredBG ) ;
console . log ( " minCOBPredBG: " + minCOBPredBG ) ;
}
if ( minUAMPredBG < 999 ) {
console . error ( " minUAMPredBG: " + minUAMPredBG ) ;
console . log ( " minUAMPredBG: " + minUAMPredBG ) ;
}
console . error ( " avgPredBG:" , avgPredBG , "COB:" , meal _data . mealCOB , "/" , meal _data . carbs ) ;
// But if the COB line falls off a cliff, don't trust UAM too much:
@ -790,9 +808,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
rT . reason += ", UAMpredBG " + convert _bg ( lastUAMpredBG , profile )
}
rT . reason += "; " ;
//var bgUndershoot = threshold - Math.min(minGuardBG, Math.max( naive_eventualBG, eventualBG ));
// use naive_eventualBG if above 40, but switch to minGuardBG if both eventualBGs hit floor of 39
//var carbsReqBG = Math.max( naive_eventualBG, eventualBG );
var carbsReqBG = naive _eventualBG ;
if ( carbsReqBG < 40 ) {
carbsReqBG = Math . min ( minGuardBG , carbsReqBG ) ;
@ -802,14 +818,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
var minutesAboveMinBG = 240 ;
var minutesAboveThreshold = 240 ;
if ( meal _data . mealCOB > 0 && ( ci > 0 || remainingCIpeak > 0 ) ) {
for ( var i = 0 ; i < COBpredBGs . length ; i ++ ) {
for ( i = 0 ; i < COBpredBGs . length ; i ++ ) {
//console.error(COBpredBGs[i], min_bg);
if ( COBpredBGs [ i ] < min _bg ) {
minutesAboveMinBG = 5 * i ;
break ;
}
}
for ( var i = 0 ; i < COBpredBGs . length ; i ++ ) {
for ( i = 0 ; i < COBpredBGs . length ; i ++ ) {
//console.error(COBpredBGs[i], threshold);
if ( COBpredBGs [ i ] < threshold ) {
minutesAboveThreshold = 5 * i ;
@ -817,14 +833,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
}
}
} else {
for ( var i = 0 ; i < IOBpredBGs . length ; i ++ ) {
for ( i = 0 ; i < IOBpredBGs . length ; i ++ ) {
//console.error(IOBpredBGs[i], min_bg);
if ( IOBpredBGs [ i ] < min _bg ) {
minutesAboveMinBG = 5 * i ;
break ;
}
}
for ( var i = 0 ; i < IOBpredBGs . length ; i ++ ) {
for ( i = 0 ; i < IOBpredBGs . length ; i ++ ) {
//console.error(IOBpredBGs[i], threshold);
if ( IOBpredBGs [ i ] < threshold ) {
minutesAboveThreshold = 5 * i ;
@ -848,9 +864,8 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
if ( minutesAboveThreshold < 240 || minutesAboveMinBG < 60 ) {
console . error ( "BG projected to remain above" , convert _bg ( threshold , profile ) , "for" , minutesAboveThreshold , "minutes" ) ;
}
// include at least minutesAbove MinBG worth of zero temps in calculating carbsReq
// include at least minutesAbove Threshold worth of zero temps in calculating carbsReq
// always include at least 30m worth of zero temp (carbs to 80, low temp up to target)
//var zeroTempDuration = Math.max(30,minutesAboveMinBG);
var zeroTempDuration = minutesAboveThreshold ;
// BG undershoot, minus effect of zero temps until hitting min_bg, converted to grams, minus COB
var zeroTempEffect = profile . current _basal * sens * zeroTempDuration / 60 ;
@ -871,7 +886,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// predictive low glucose suspend mode: BG is / is projected to be < threshold
} else if ( bg < threshold || minGuardBG < threshold ) {
rT . reason += "minGuardBG " + convert _bg ( minGuardBG , profile ) + "<" + convert _bg ( threshold , profile ) ;
var bgUndershoot = target _bg - minGuardBG ;
bgUndershoot = target _bg - minGuardBG ;
var worstCaseInsulinReq = bgUndershoot / sens ;
var durationReq = round ( 60 * worstCaseInsulinReq / profile . current _basal ) ;
durationReq = round ( durationReq / 30 ) * 30 ;
@ -880,6 +895,13 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
return tempBasalFunctions . setTempBasal ( 0 , durationReq , profile , rT , currenttemp ) ;
}
// if not in LGS mode, cancel temps before the top of the hour to reduce beeping/vibration
// console.error(profile.skip_neutral_temps, rT.deliverAt.getMinutes());
if ( profile . skip _neutral _temps && rT . deliverAt . getMinutes ( ) >= 55 ) {
rT . reason += "; Canceling temp at " + rT . deliverAt . getMinutes ( ) + "m past the hour. " ;
return tempBasalFunctions . setTempBasal ( 0 , 0 , profile , rT , currenttemp ) ;
}
if ( eventualBG < min _bg ) { // if eventual BG is below target:
rT . reason += "Eventual BG " + convert _bg ( eventualBG , profile ) + " < " + convert _bg ( min _bg , profile ) ;
// if 5m or 30m avg BG is rising faster than expected delta
@ -904,9 +926,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
}
// calculate 30m low-temp required to get projected BG up to target
// use snoozeBG to more gradually ramp in any counteraction of the user's boluses
// multiply by 2 to low-temp faster for increased hypo safety
//var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / sens);
var insulinReq = 2 * Math . min ( 0 , ( eventualBG - target _bg ) / sens ) ;
insulinReq = round ( insulinReq , 2 ) ;
// calculate naiveInsulinReq based on naive_eventualBG
@ -914,7 +934,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
naiveInsulinReq = round ( naiveInsulinReq , 2 ) ;
if ( minDelta < 0 && minDelta > expectedDelta ) {
// if we're barely falling, newinsulinReq should be barely negative
//rT.reason += ", Snooze BG " + convert_bg(snoozeBG, profile);
var newinsulinReq = round ( ( insulinReq * ( minDelta / expectedDelta ) ) , 2 ) ;
//console.error("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq);
insulinReq = newinsulinReq ;
@ -922,6 +941,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// rate required to deliver insulinReq less insulin over 30m:
var rate = basal + ( 2 * insulinReq ) ;
rate = round _basal ( rate , profile ) ;
// if required temp < existing temp basal
var insulinScheduled = currenttemp . duration * ( currenttemp . rate - basal ) / 60 ;
// if current temp would deliver a lot (30% of basal) less than the required insulin,
@ -937,18 +957,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
} else {
// calculate a long enough zero temp to eventually correct back up to target
if ( rate <= 0 ) {
var bgUndershoot = target _bg - naive _eventualBG ;
var worstCaseInsulinReq = bgUndershoot / sens ;
var durationReq = round ( 60 * worstCaseInsulinReq / profile . current _basal ) ;
bgUndershoot = target _bg - naive _eventualBG ;
worstCaseInsulinReq = bgUndershoot / sens ;
durationReq = round ( 60 * worstCaseInsulinReq / profile . current _basal ) ;
if ( durationReq < 0 ) {
durationReq = 0 ;
// don't set a n SMB zero temp longer than 60 minutes s
// don't set a temp longer than 120 minute s
} else {
durationReq = round ( durationReq / 30 ) * 30 ;
durationReq = Math . min ( 6 0, Math . max ( 0 , durationReq ) ) ;
durationReq = Math . min ( 12 0, Math . max ( 0 , durationReq ) ) ;
}
//console.error(durationReq);
//rT.reason += "insulinReq " + insulinReq + "; "
if ( durationReq > 0 ) {
rT . reason += ", setting " + durationReq + "m zero temp. " ;
return tempBasalFunctions . setTempBasal ( rate , durationReq , profile , rT , currenttemp ) ;
@ -995,8 +1014,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// eventual BG is at/above target
// if iob is over max, just cancel any temps
// if we're not here because of SMB, eventual BG is at/above target
if ( ! ( microBolusAllowed && rT . COB ) ) {
if ( eventualBG >= max _bg ) {
rT . reason += "Eventual BG " + convert _bg ( eventualBG , profile ) + " >= " + convert _bg ( max _bg , profile ) + ", " ;
}
if ( iob _data . iob > max _iob ) {
@ -1012,15 +1030,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// insulinReq is the additional insulin required to get minPredBG down to target_bg
//console.error(minPredBG,eventualBG);
//var insulinReq = round( (Math.min(minPredBG,eventualBG) - target_bg) / sens, 2);
var insulinReq = round ( ( Math . min ( minPredBG , eventualBG ) - target _bg ) / sens , 2 ) ;
// when dropping, but not as fast as expected, reduce insulinReq proportionally
// to the what fraction of expectedDelta we're dropping at
//if (minDelta < 0 && minDelta > expectedDelta) {
//var newinsulinReq = round(( insulinReq * (1 - (minDelta / expectedDelta)) ), 2);
//console.error("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq + " for minDelta " + minDelta + " vs. expectedDelta " + expectedDelta);
//insulinReq = newinsulinReq;
//}
insulinReq = round ( ( Math . min ( minPredBG , eventualBG ) - target _bg ) / sens , 2 ) ;
// if that would put us over max_iob, then reduce accordingly
if ( insulinReq > max _iob - iob _data . iob ) {
rT . reason += "max_iob " + max _iob + ", " ;
@ -1028,49 +1038,56 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
}
// rate required to deliver insulinReq more insulin over 30m:
var rate = basal + ( 2 * insulinReq ) ;
rate = basal + ( 2 * insulinReq ) ;
rate = round _basal ( rate , profile ) ;
insulinReq = round ( insulinReq , 3 ) ;
rT . insulinReq = insulinReq ;
//console.error(iob_data.lastBolusTime);
// minutes since last bolus
var lastBolusAge = round ( ( new Date ( ) . getTime ( ) - iob _data . lastBolusTime ) / 60000 , 1 ) ;
var lastBolusAge = round ( ( new Date ( systemTime ) . getTime ( ) - iob _data . lastBolusTime ) / 60000 , 1 ) ;
//console.error(lastBolusAge);
//console.error(profile.temptargetSet, target_bg, rT.COB);
// only allow microboluses with COB or low temp targets, or within DIA hours of a bolus
if ( microBolusAllowed && enableSMB && bg > threshold ) {
// never bolus more than maxSMBBasalMinutes worth of basal
mealInsulinReq = round ( meal _data . mealCOB / profile . carb _ratio , 3 ) ;
if ( typeof profile . maxSMBBasalMinutes == 'undefined' ) {
maxBolus = round ( profile . current _basal * 30 / 60 , 1 ) ;
var mealInsulinReq = round ( meal _data . mealCOB / profile . carb _ratio , 3 ) ;
if ( typeof profile . maxSMBBasalMinutes == = 'undefined' ) {
var maxBolus = round ( profile . current _basal * 30 / 60 , 1 ) ;
console . error ( "profile.maxSMBBasalMinutes undefined: defaulting to 30m" ) ;
// if IOB covers more than COB, limit maxBolus to 30m of basal
} else if ( iob _data . iob > mealInsulinReq && iob _data . iob > 0 ) {
console . error ( "IOB" , iob _data . iob , "> COB" , meal _data . mealCOB + "; mealInsulinReq =" , mealInsulinReq ) ;
maxBolus = round ( profile . current _basal * 30 / 60 , 1 ) ;
if ( profile . maxUAMSMBBasalMinutes ) {
console . error ( "profile.maxUAMSMBBasalMinutes:" , profile . maxUAMSMBBasalMinutes , "profile.current_basal:" , profile . current _basal ) ;
maxBolus = round ( profile . current _basal * profile . maxUAMSMBBasalMinutes / 60 , 1 ) ;
} else {
console . error ( "profile.maxUAMSMBBasalMinutes undefined: defaulting to 30m" ) ;
maxBolus = round ( profile . current _basal * 30 / 60 , 1 ) ;
}
} else {
console . error ( "profile.maxSMBBasalMinutes:" , profile . maxSMBBasalMinutes , "profile.current_basal:" , profile . current _basal ) ;
maxBolus = round ( profile . current _basal * profile . maxSMBBasalMinutes / 60 , 1 ) ;
}
// bolus 1/2 the insulinReq, up to maxBolus, rounding down to nearest 0.1U
microBolus = Math . floor ( Math . min ( insulinReq / 2 , maxBolus ) * 10 ) / 10 ;
// bolus 1/2 the insulinReq, up to maxBolus, rounding down to nearest bolus increment
var roundSMBTo = 1 / profile . bolus _increment ;
var microBolus = Math . floor ( Math . min ( insulinReq / 2 , maxBolus ) * roundSMBTo ) / roundSMBTo ;
// calculate a long enough zero temp to eventually correct back up to target
var smbTarget = target _bg ;
var worstCaseInsulinReq = ( smbTarget - ( naive _eventualBG + minIOBPredBG ) / 2 ) / sens ;
var durationReq = round ( 60 * worstCaseInsulinReq / profile . current _basal ) ;
worstCaseInsulinReq = ( smbTarget - ( naive _eventualBG + minIOBPredBG ) / 2 ) / sens ;
durationReq = round ( 60 * worstCaseInsulinReq / profile . current _basal ) ;
// if insulinReq > 0 but not enough for a microBolus, don't set an SMB zero temp
if ( insulinReq > 0 && microBolus < 0.1 ) {
if ( insulinReq > 0 && microBolus < profile . bolus _increment ) {
durationReq = 0 ;
}
var smbLowTempReq = 0 ;
if ( durationReq <= 0 ) {
durationReq = 0 ;
// don't set a temp longer than 12 0 minutes
// don't set a n SMB zero temp longer than 6 0 minutes
} else if ( durationReq >= 30 ) {
durationReq = round ( durationReq / 30 ) * 30 ;
durationReq = Math . min ( 12 0, Math . max ( 0 , durationReq ) ) ;
durationReq = Math . min ( 6 0, Math . max ( 0 , durationReq ) ) ;
} else {
// if SMB durationReq is less than 30m, set a nonzero low temp
smbLowTempReq = round ( basal * durationReq / 30 , 2 ) ;
@ -1085,17 +1102,23 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
}
rT . reason += ". " ;
//allow SMBs every 3 minutes
var nextBolusMins = round ( 3 - lastBolusAge , 1 ) ;
//allow SMBs every 3 minutes by default
var SMBInterval = 3 ;
if ( profile . SMBInterval ) {
// allow SMBIntervals between 1 and 10 minutes
SMBInterval = Math . min ( 10 , Math . max ( 1 , profile . SMBInterval ) ) ;
}
var nextBolusMins = round ( SMBInterval - lastBolusAge , 0 ) ;
var nextBolusSeconds = round ( ( SMBInterval - lastBolusAge ) * 60 , 0 ) % 60 ;
//console.error(naive_eventualBG, insulinReq, worstCaseInsulinReq, durationReq);
console . error ( "naive_eventualBG" , naive _eventualBG + "," , durationReq + "m " + smbLowTempReq + "U/h temp needed; last bolus" , lastBolusAge + "m ago; maxBolus: " + maxBolus ) ;
if ( lastBolusAge > 3 ) {
if ( lastBolusAge > SMBInterval ) {
if ( microBolus > 0 ) {
rT . units = microBolus ;
rT . reason += "Microbolusing " + microBolus + "U. " ;
}
} else {
rT . reason += "Waiting " + nextBolusMins + "m to microbolus again. ";
rT . reason += "Waiting " + nextBolusMins + "m " + nextBolusSeconds + "s to microbolus again. ";
}
//rT.reason += ". ";
@ -1106,11 +1129,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
return rT ;
}
// if insulinReq is negative, snoozeBG > target_bg, and lastCOBpredBG > target_bg, set a neutral temp
//if (insulinReq < 0 && snoozeBG > target_bg && lastCOBpredBG > target_bg) {
//rT.reason += "; SMB bolus snooze: setting current basal of " + basal + " as temp. ";
//return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
//}
}
var maxSafeBasal = tempBasalFunctions . getMaxSafeBasal ( profile ) ;
@ -1120,13 +1138,13 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
rate = round _basal ( maxSafeBasal , profile ) ;
}
var insulinScheduled = currenttemp . duration * ( currenttemp . rate - basal ) / 60 ;
insulinScheduled = currenttemp . duration * ( currenttemp . rate - basal ) / 60 ;
if ( insulinScheduled >= insulinReq * 2 ) { // if current temp would deliver >2x more than the required insulin, lower the rate
rT . reason += currenttemp . duration + "m@" + ( currenttemp . rate ) . toFixed ( 2 ) + " > 2 * insulinReq. Setting temp basal of " + rate + "U/hr. " ;
return tempBasalFunctions . setTempBasal ( rate , 30 , profile , rT , currenttemp ) ;
}
if ( typeof currenttemp . duration == 'undefined' || currenttemp . duration == 0 ) { // no temp is set
if ( typeof currenttemp . duration == = 'undefined' || currenttemp . duration == = 0 ) { // no temp is set
rT . reason += "no temp, setting " + rate + "U/hr. " ;
return tempBasalFunctions . setTempBasal ( rate , 30 , profile , rT , currenttemp ) ;
}