var percentile = require('../percentile'); //rounding of basals for pumps //20 for omnipod to 0.05 //40 for medtronic to 0.025 var basal_scale = 20; // does three things - tunes basals, ISF, and CSF function tuneAllTheThings (inputs) { var previousAutotune = inputs.previousAutotune; //console.error(previousAutotune); var pumpProfile = inputs.pumpProfile; var pumpBasalProfile = pumpProfile.basalprofile; //console.error(pumpBasalProfile); var basalProfile = previousAutotune.basalprofile; //console.error(basalProfile); var isfProfile = previousAutotune.isfProfile; //console.error(isfProfile); var ISF = isfProfile.sensitivities[0].sensitivity; //console.error(ISF); var carbRatio = previousAutotune.carb_ratio; //console.error(carbRatio); var CSF = ISF / carbRatio; var DIA = previousAutotune.dia; var peak = previousAutotune.insulinPeakTime; if (! previousAutotune.useCustomPeakTime === true) { if ( previousAutotune.curve === "ultra-rapid" ) { peak = 55; } else { peak = 75; } } //console.error(DIA, peak); // conditional on there being a pump profile; if not then skip if (pumpProfile) { var pumpISFProfile = pumpProfile.isfProfile; } if (pumpISFProfile && pumpISFProfile.sensitivities[0]) { var pumpISF = pumpISFProfile.sensitivities[0].sensitivity; var pumpCarbRatio = pumpProfile.carb_ratio; var pumpCSF = pumpISF / pumpCarbRatio; } if (! carbRatio) { carbRatio = pumpCarbRatio; } if (! CSF) { CSF = pumpCSF; } if (! ISF) { ISF = pumpISF; } //console.error(CSF); var preppedGlucose = inputs.preppedGlucose; var CSFGlucose = preppedGlucose.CSFGlucoseData; //console.error(CSFGlucose[0]); var ISFGlucose = preppedGlucose.ISFGlucoseData; //console.error(ISFGlucose[0]); var basalGlucose = preppedGlucose.basalGlucoseData; //console.error(basalGlucose[0]); var CRData = preppedGlucose.CRData; //console.error(CRData); var diaDeviations = preppedGlucose.diaDeviations; //console.error(diaDeviations); var peakDeviations = preppedGlucose.peakDeviations; //console.error(peakDeviations); // tune DIA var newDIA = DIA; if (diaDeviations) { var currentDIAMeanDev = diaDeviations[2].meanDeviation; var currentDIARMSDev = diaDeviations[2].RMSDeviation; //console.error(DIA,currentDIAMeanDev,currentDIARMSDev); var minMeanDeviations = 1000000; var minRMSDeviations = 1000000; var meanBest = 2; var RMSBest = 2; for (var i=0; i < diaDeviations.length; i++) { var meanDeviations = diaDeviations[i].meanDeviation; var RMSDeviations = diaDeviations[i].RMSDeviation; if (meanDeviations < minMeanDeviations) { minMeanDeviations = Math.round(meanDeviations*1000)/1000; meanBest = i; } if (RMSDeviations < minRMSDeviations) { minRMSDeviations = Math.round(RMSDeviations*1000)/1000; RMSBest = i; } } console.error("Best insulinEndTime for meanDeviations:",diaDeviations[meanBest].dia,"hours"); console.error("Best insulinEndTime for RMSDeviations:",diaDeviations[RMSBest].dia,"hours"); if ( meanBest < 2 && RMSBest < 2 ) { if ( diaDeviations[1].meanDeviation < currentDIAMeanDev * 0.99 && diaDeviations[1].RMSDeviation < currentDIARMSDev * 0.99 ) { newDIA = diaDeviations[1].dia; } } else if ( meanBest > 2 && RMSBest > 2 ) { if ( diaDeviations[3].meanDeviation < currentDIAMeanDev * 0.99 && diaDeviations[3].RMSDeviation < currentDIARMSDev * 0.99 ) { newDIA = diaDeviations[3].dia; } } if ( newDIA > 12 ) { console.error("insulinEndTime maximum is 12h: not raising further"); newDIA=12; } if ( newDIA !== DIA ) { console.error("Adjusting insulinEndTime from",DIA,"to",newDIA,"hours"); } else { console.error("Leaving insulinEndTime unchanged at",DIA,"hours"); } } // tune insulinPeakTime var newPeak = peak; if (peakDeviations && peakDeviations[2]) { var currentPeakMeanDev = peakDeviations[2].meanDeviation; var currentPeakRMSDev = peakDeviations[2].RMSDeviation; //console.error(currentPeakMeanDev); minMeanDeviations = 1000000; minRMSDeviations = 1000000; meanBest = 2; RMSBest = 2; for (i=0; i < peakDeviations.length; i++) { meanDeviations = peakDeviations[i].meanDeviation; RMSDeviations = peakDeviations[i].RMSDeviation; if (meanDeviations < minMeanDeviations) { minMeanDeviations = Math.round(meanDeviations*1000)/1000; meanBest = i; } if (RMSDeviations < minRMSDeviations) { minRMSDeviations = Math.round(RMSDeviations*1000)/1000; RMSBest = i; } } console.error("Best insulinPeakTime for meanDeviations:",peakDeviations[meanBest].peak,"minutes"); console.error("Best insulinPeakTime for RMSDeviations:",peakDeviations[RMSBest].peak,"minutes"); if ( meanBest < 2 && RMSBest < 2 ) { if ( peakDeviations[1].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[1].RMSDeviation < currentPeakRMSDev * 0.99 ) { newPeak = peakDeviations[1].peak; } } else if ( meanBest > 2 && RMSBest > 2 ) { if ( peakDeviations[3].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[3].RMSDeviation < currentPeakRMSDev * 0.99 ) { newPeak = peakDeviations[3].peak; } } if ( newPeak !== peak ) { console.error("Adjusting insulinPeakTime from",peak,"to",newPeak,"minutes"); } else { console.error("Leaving insulinPeakTime unchanged at",peak); } } // Calculate carb ratio (CR) independently of CSF and ISF // Use the time period from meal bolus/carbs until COB is zero and IOB is < currentBasal/2 // For now, if another meal IOB/COB stacks on top of it, consider them together // Compare beginning and ending BGs, and calculate how much more/less insulin is needed to neutralize // Use entered carbs vs. starting IOB + delivered insulin + needed-at-end insulin to directly calculate CR. var CRTotalCarbs = 0; var CRTotalInsulin = 0; CRData.forEach(function(CRDatum) { var CRBGChange = CRDatum.CREndBG - CRDatum.CRInitialBG; var CRInsulinReq = CRBGChange / ISF; var CRIOBChange = CRDatum.CREndIOB - CRDatum.CRInitialIOB; CRDatum.CRInsulinTotal = CRDatum.CRInitialIOB + CRDatum.CRInsulin + CRInsulinReq; //console.error(CRDatum.CRInitialIOB, CRDatum.CRInsulin, CRInsulinReq, CRDatum.CRInsulinTotal); var CR = Math.round( CRDatum.CRCarbs / CRDatum.CRInsulinTotal * 1000 )/1000; //console.error(CRBGChange, CRInsulinReq, CRIOBChange, CRDatum.CRInsulinTotal); //console.error("CRCarbs:",CRDatum.CRCarbs,"CRInsulin:",CRDatum.CRInsulin,"CRDatum.CRInsulinTotal:",CRDatum.CRInsulinTotal,"CR:",CR); if (CRDatum.CRInsulinTotal > 0) { CRTotalCarbs += CRDatum.CRCarbs; CRTotalInsulin += CRDatum.CRInsulinTotal; //console.error("CRTotalCarbs:",CRTotalCarbs,"CRTotalInsulin:",CRTotalInsulin); } }); CRTotalInsulin = Math.round(CRTotalInsulin*1000)/1000; var totalCR = Math.round( CRTotalCarbs / CRTotalInsulin * 1000 )/1000; console.error("CRTotalCarbs:",CRTotalCarbs,"CRTotalInsulin:",CRTotalInsulin,"totalCR:",totalCR); // convert the basal profile to hourly if it isn't already var hourlyBasalProfile = []; var hourlyPumpProfile = []; for (i=0; i < 24; i++) { // autotuned basal profile for (var j=0; j < basalProfile.length; ++j) { if (basalProfile[j].minutes <= i * 60) { if (basalProfile[j].rate === 0) { console.error("ERROR: bad basalProfile",basalProfile[j]); return; } hourlyBasalProfile[i] = JSON.parse(JSON.stringify(basalProfile[j])); } } hourlyBasalProfile[i].i=i; hourlyBasalProfile[i].minutes=i*60; var zeroPadHour = ("000"+i).slice(-2); hourlyBasalProfile[i].start=zeroPadHour + ":00:00"; hourlyBasalProfile[i].rate=Math.round(hourlyBasalProfile[i].rate*1000)/1000 // pump basal profile if (pumpBasalProfile && pumpBasalProfile[0]) { for (j=0; j < pumpBasalProfile.length; ++j) { //console.error(pumpBasalProfile[j]); if (pumpBasalProfile[j].rate === 0) { console.error("ERROR: bad pumpBasalProfile",pumpBasalProfile[j]); return; } if (pumpBasalProfile[j].minutes <= i * 60) { hourlyPumpProfile[i] = JSON.parse(JSON.stringify(pumpBasalProfile[j])); } } hourlyPumpProfile[i].i=i; hourlyPumpProfile[i].minutes=i*60; hourlyPumpProfile[i].rate=Math.round(hourlyPumpProfile[i].rate*1000)/1000 } } //console.error(hourlyPumpProfile); //console.error(hourlyBasalProfile); var newHourlyBasalProfile = JSON.parse(JSON.stringify(hourlyBasalProfile)); // look at net deviations for each hour for (var hour=0; hour < 24; hour++) { var deviations = 0; for (i=0; i < basalGlucose.length; ++i) { var BGTime; if (basalGlucose[i].date) { BGTime = new Date(basalGlucose[i].date); } else if (basalGlucose[i].displayTime) { BGTime = new Date(basalGlucose[i].displayTime.replace('T', ' ')); } else if (basalGlucose[i].dateString) { BGTime = new Date(basalGlucose[i].dateString); } else { console.error("Could not determine last BG time"); } var myHour = BGTime.getHours(); if (hour === myHour) { //console.error(basalGlucose[i].deviation); deviations += parseFloat(basalGlucose[i].deviation); } } deviations = Math.round( deviations * 1000 ) / 1000 console.error("Hour",hour.toString(),"total deviations:",deviations,"mg/dL"); // calculate how much less or additional basal insulin would have been required to eliminate the deviations // only apply 20% of the needed adjustment to keep things relatively stable var basalNeeded = 0.2 * deviations / ISF; basalNeeded = Math.round( basalNeeded * 100 ) / 100 // if basalNeeded is positive, adjust each of the 1-3 hour prior basals by 10% of the needed adjustment console.error("Hour",hour,"basal adjustment needed:",basalNeeded,"U/hr"); if (basalNeeded > 0 ) { for (var offset=-3; offset < 0; offset++) { var offsetHour = hour + offset; if (offsetHour < 0) { offsetHour += 24; } //console.error(offsetHour); newHourlyBasalProfile[offsetHour].rate += basalNeeded / 3; newHourlyBasalProfile[offsetHour].rate=Math.round(newHourlyBasalProfile[offsetHour].rate*1000)/1000 } // otherwise, figure out the percentage reduction required to the 1-3 hour prior basals // and adjust all of them downward proportionally } else if (basalNeeded < 0) { var threeHourBasal = 0; for (offset=-3; offset < 0; offset++) { offsetHour = hour + offset; if (offsetHour < 0) { offsetHour += 24; } threeHourBasal += newHourlyBasalProfile[offsetHour].rate; } var adjustmentRatio = 1.0 + basalNeeded / threeHourBasal; //console.error(adjustmentRatio); for (offset=-3; offset < 0; offset++) { offsetHour = hour + offset; if (offsetHour < 0) { offsetHour += 24; } newHourlyBasalProfile[offsetHour].rate = newHourlyBasalProfile[offsetHour].rate * adjustmentRatio; newHourlyBasalProfile[offsetHour].rate=Math.round(newHourlyBasalProfile[offsetHour].rate*1000)/1000 } } } if (pumpBasalProfile && pumpBasalProfile[0]) { for (hour=0; hour < 24; hour++) { //console.error(newHourlyBasalProfile[hour],hourlyPumpProfile[hour].rate*1.2); // cap adjustments at autosens_max and autosens_min if (typeof pumpProfile.autosens_max !== 'undefined') { var autotuneMax = pumpProfile.autosens_max; } else { var autotuneMax = 1.2; } if (typeof pumpProfile.autosens_min !== 'undefined') { var autotuneMin = pumpProfile.autosens_min; } else { var autotuneMin = 0.7; } var maxRate = hourlyPumpProfile[hour].rate * autotuneMax; var minRate = hourlyPumpProfile[hour].rate * autotuneMin; if (newHourlyBasalProfile[hour].rate > maxRate ) { console.error("Limiting hour",hour,"basal to",maxRate.toFixed(2),"(which is",autotuneMax,"* pump basal of",hourlyPumpProfile[hour].rate,")"); //console.error("Limiting hour",hour,"basal to",maxRate.toFixed(2),"(which is 20% above pump basal of",hourlyPumpProfile[hour].rate,")"); newHourlyBasalProfile[hour].rate = maxRate; } else if (newHourlyBasalProfile[hour].rate < minRate ) { console.error("Limiting hour",hour,"basal to",minRate.toFixed(2),"(which is",autotuneMin,"* pump basal of",hourlyPumpProfile[hour].rate,")"); //console.error("Limiting hour",hour,"basal to",minRate.toFixed(2),"(which is 20% below pump basal of",hourlyPumpProfile[hour].rate,")"); newHourlyBasalProfile[hour].rate = minRate; } newHourlyBasalProfile[hour].rate = Math.round(newHourlyBasalProfile[hour].rate*basal_scale)/basal_scale; } } // some hours of the day rarely have data to tune basals due to meals. // when no adjustments are needed to a particular hour, we should adjust it toward the average of the // periods before and after it that do have data to be tuned var lastAdjustedHour = 0; // scan through newHourlyBasalProfile and find hours where the rate is unchanged for (hour=0; hour < 24; hour++) { if (hourlyBasalProfile[hour].rate === newHourlyBasalProfile[hour].rate) { var nextAdjustedHour = 23; for (var nextHour = hour; nextHour < 24; nextHour++) { if (! (hourlyBasalProfile[nextHour].rate === newHourlyBasalProfile[nextHour].rate)) { nextAdjustedHour = nextHour; break; //} else { //console.error(nextHour, hourlyBasalProfile[nextHour].rate, newHourlyBasalProfile[nextHour].rate); } } //console.error(hour, newHourlyBasalProfile); newHourlyBasalProfile[hour].rate = Math.round( (0.8*hourlyBasalProfile[hour].rate + 0.1*newHourlyBasalProfile[lastAdjustedHour].rate + 0.1*newHourlyBasalProfile[nextAdjustedHour].rate)*basal_scale)/basal_scale; if (newHourlyBasalProfile[hour].untuned) newHourlyBasalProfile[hour].untuned++; else newHourlyBasalProfile[hour].untuned = 1; console.error("Adjusting hour",hour,"basal from",hourlyBasalProfile[hour].rate,"to",newHourlyBasalProfile[hour].rate,"based on hour",lastAdjustedHour,"=",newHourlyBasalProfile[lastAdjustedHour].rate,"and hour",nextAdjustedHour,"=",newHourlyBasalProfile[nextAdjustedHour].rate); } else { lastAdjustedHour = hour; } } console.error(newHourlyBasalProfile); basalProfile = newHourlyBasalProfile; // Calculate carb ratio (CR) independently of CSF and ISF // Use the time period from meal bolus/carbs until COB is zero and IOB is < currentBasal/2 // For now, if another meal IOB/COB stacks on top of it, consider them together // Compare beginning and ending BGs, and calculate how much more/less insulin is needed to neutralize // Use entered carbs vs. starting IOB + delivered insulin + needed-at-end insulin to directly calculate CR. // calculate net deviations while carbs are absorbing // measured from carb entry until COB and deviations both drop to zero var deviations = 0; var mealCarbs = 0; var totalMealCarbs = 0; var totalDeviations = 0; var fullNewCSF; //console.error(CSFGlucose[0].mealAbsorption); //console.error(CSFGlucose[0]); for (i=0; i < CSFGlucose.length; ++i) { //console.error(CSFGlucose[i].mealAbsorption, i); if ( CSFGlucose[i].mealAbsorption === "start" ) { deviations = 0; mealCarbs = parseInt(CSFGlucose[i].mealCarbs); } else if (CSFGlucose[i].mealAbsorption === "end") { deviations += parseFloat(CSFGlucose[i].deviation); // compare the sum of deviations from start to end vs. current CSF * mealCarbs //console.error(CSF,mealCarbs); var csfRise = CSF * mealCarbs; //console.error(deviations,ISF); //console.error("csfRise:",csfRise,"deviations:",deviations); totalMealCarbs += mealCarbs; totalDeviations += deviations; } else { deviations += Math.max(0*previousAutotune.min_5m_carbimpact,parseFloat(CSFGlucose[i].deviation)); mealCarbs = Math.max(mealCarbs, parseInt(CSFGlucose[i].mealCarbs)); } } // at midnight, write down the mealcarbs as total meal carbs (to prevent special case of when only one meal and it not finishing absorbing by midnight) // TODO: figure out what to do with dinner carbs that don't finish absorbing by midnight if (totalMealCarbs === 0) { totalMealCarbs += mealCarbs; } if (totalDeviations === 0) { totalDeviations += deviations; } //console.error(totalDeviations, totalMealCarbs); if (totalMealCarbs === 0) { // if no meals today, CSF is unchanged fullNewCSF = CSF; } else { // how much change would be required to account for all of the deviations fullNewCSF = Math.round( (totalDeviations / totalMealCarbs)*100 )/100; } // only adjust by 20% var newCSF = ( 0.8 * CSF ) + ( 0.2 * fullNewCSF ); // safety cap CSF if (typeof(pumpCSF) !== 'undefined') { var maxCSF = pumpCSF * autotuneMax; var minCSF = pumpCSF * autotuneMin; if (newCSF > maxCSF) { console.error("Limiting CSF to",maxCSF.toFixed(2),"(which is",autotuneMax,"* pump CSF of",pumpCSF,")"); newCSF = maxCSF; } else if (newCSF < minCSF) { console.error("Limiting CSF to",minCSF.toFixed(2),"(which is",autotuneMin,"* pump CSF of",pumpCSF,")"); newCSF = minCSF; } //else { console.error("newCSF",newCSF,"is close enough to",pumpCSF); } } var oldCSF = Math.round( CSF * 1000 ) / 1000; newCSF = Math.round( newCSF * 1000 ) / 1000; totalDeviations = Math.round ( totalDeviations * 1000 )/1000; console.error("totalMealCarbs:",totalMealCarbs,"totalDeviations:",totalDeviations,"oldCSF",oldCSF,"fullNewCSF:",fullNewCSF,"newCSF:",newCSF); // this is where CSF is set based on the outputs if (newCSF) { CSF = newCSF; } if (totalCR === 0) { // if no meals today, CR is unchanged var fullNewCR = carbRatio; } else { // how much change would be required to account for all of the deviations fullNewCR = totalCR; } // don't tune CR out of bounds var maxCR = pumpCarbRatio * autotuneMax; if (maxCR > 150) { maxCR = 150 } var minCR = pumpCarbRatio * autotuneMin; if (minCR < 1) { minCR = 1 } // safety cap fullNewCR if (typeof(pumpCarbRatio) !== 'undefined') { if (fullNewCR > maxCR) { console.error("Limiting fullNewCR from",fullNewCR,"to",maxCR.toFixed(2),"(which is",autotuneMax,"* pump CR of",pumpCarbRatio,")"); fullNewCR = maxCR; } else if (fullNewCR < minCR) { console.error("Limiting fullNewCR from",fullNewCR,"to",minCR.toFixed(2),"(which is",autotuneMin,"* pump CR of",pumpCarbRatio,")"); fullNewCR = minCR; } //else { console.error("newCR",newCR,"is close enough to",pumpCarbRatio); } } // only adjust by 20% var newCR = ( 0.8 * carbRatio ) + ( 0.2 * fullNewCR ); // safety cap newCR if (typeof(pumpCarbRatio) !== 'undefined') { if (newCR > maxCR) { console.error("Limiting CR to",maxCR.toFixed(2),"(which is",autotuneMax,"* pump CR of",pumpCarbRatio,")"); newCR = maxCR; } else if (newCR < minCR) { console.error("Limiting CR to",minCR.toFixed(2),"(which is",autotuneMin,"* pump CR of",pumpCarbRatio,")"); newCR = minCR; } //else { console.error("newCR",newCR,"is close enough to",pumpCarbRatio); } } newCR = Math.round( newCR * 1000 ) / 1000; console.error("oldCR:",carbRatio,"fullNewCR:",fullNewCR,"newCR:",newCR); // this is where CR is set based on the outputs //var ISFFromCRAndCSF = ISF; if (newCR) { carbRatio = newCR; //ISFFromCRAndCSF = Math.round( carbRatio * CSF * 1000)/1000; } // calculate median deviation and bgi in data attributable to ISF var deviations = []; var BGIs = []; var avgDeltas = []; var ratios = []; for (i=0; i < ISFGlucose.length; ++i) { deviation = parseFloat(ISFGlucose[i].deviation); deviations.push(deviation); var BGI = parseFloat(ISFGlucose[i].BGI); BGIs.push(BGI); var avgDelta = parseFloat(ISFGlucose[i].avgDelta); avgDeltas.push(avgDelta); var ratio = 1 + deviation / BGI; //console.error("Deviation:",deviation,"BGI:",BGI,"avgDelta:",avgDelta,"ratio:",ratio); ratios.push(ratio); } avgDeltas.sort(function(a, b){return a-b}); BGIs.sort(function(a, b){return a-b}); deviations.sort(function(a, b){return a-b}); ratios.sort(function(a, b){return a-b}); var p50deviation = percentile(deviations, 0.50); var p50BGI = percentile(BGIs, 0.50); var p50ratios = Math.round( percentile(ratios, 0.50) * 1000)/1000; var fullNewISF = ISF; if (ISFGlucose.length < 10) { // leave ISF unchanged if fewer than 10 ISF data points console.error ("Only found",ISFGlucose.length,"ISF data points, leaving ISF unchanged at",ISF); } else { // calculate what adjustments to ISF would have been necessary to bring median deviation to zero fullNewISF = ISF * p50ratios; } fullNewISF = Math.round( fullNewISF * 1000 ) / 1000; //console.error("p50ratios:",p50ratios,"fullNewISF:",fullNewISF,ratios[Math.floor(ratios.length/2)]); // adjust the target ISF to be a weighted average of fullNewISF and pumpISF var adjustmentFraction; if (typeof(pumpProfile.autotune_isf_adjustmentFraction) !== 'undefined') { adjustmentFraction = pumpProfile.autotune_isf_adjustmentFraction; } else { adjustmentFraction = 1.0; } // low autosens ratio = high ISF var maxISF = pumpISF / autotuneMin; // high autosens ratio = low ISF var minISF = pumpISF / autotuneMax; if (typeof(pumpISF) !== 'undefined') { if ( fullNewISF < 0 ) { var adjustedISF = ISF; } else { adjustedISF = adjustmentFraction*fullNewISF + (1-adjustmentFraction)*pumpISF; } // cap adjustedISF before applying 10% //console.error(adjustedISF, maxISF, minISF); if (adjustedISF > maxISF) { console.error("Limiting adjusted ISF of",adjustedISF.toFixed(2),"to",maxISF.toFixed(2),"(which is pump ISF of",pumpISF,"/",autotuneMin,")"); adjustedISF = maxISF; } else if (adjustedISF < minISF) { console.error("Limiting adjusted ISF of",adjustedISF.toFixed(2),"to",minISF.toFixed(2),"(which is pump ISF of",pumpISF,"/",autotuneMax,")"); adjustedISF = minISF; } // and apply 20% of that adjustment var newISF = ( 0.8 * ISF ) + ( 0.2 * adjustedISF ); if (newISF > maxISF) { console.error("Limiting ISF of",newISF.toFixed(2),"to",maxISF.toFixed(2),"(which is pump ISF of",pumpISF,"/",autotuneMin,")"); newISF = maxISF; } else if (newISF < minISF) { console.error("Limiting ISF of",newISF.toFixed(2),"to",minISF.toFixed(2),"(which is pump ISF of",pumpISF,"/",autotuneMax,")"); newISF = minISF; } } newISF = Math.round( newISF * 1000 ) / 1000; //console.error(avgRatio); //console.error(newISF); p50deviation = Math.round( p50deviation * 1000 ) / 1000; p50BGI = Math.round( p50BGI * 1000 ) / 1000; adjustedISF = Math.round( adjustedISF * 1000 ) / 1000; console.error("p50deviation:",p50deviation,"p50BGI",p50BGI,"p50ratios:",p50ratios,"Old ISF:",ISF,"fullNewISF:",fullNewISF,"adjustedISF:",adjustedISF,"newISF:",newISF,"newDIA:",newDIA,"newPeak:",newPeak); if (newISF) { ISF = newISF; } // reconstruct updated version of previousAutotune as autotuneOutput var autotuneOutput = previousAutotune; autotuneOutput.basalprofile = basalProfile; isfProfile.sensitivities[0].sensitivity = ISF; autotuneOutput.isfProfile = isfProfile; autotuneOutput.sens = ISF; autotuneOutput.csf = CSF; //carbRatio = ISF / CSF; carbRatio = Math.round( carbRatio * 1000 ) / 1000; autotuneOutput.carb_ratio = carbRatio; autotuneOutput.dia = newDIA; autotuneOutput.insulinPeakTime = newPeak; if (diaDeviations || peakDeviations) { autotuneOutput.useCustomPeakTime = true; } return autotuneOutput; } exports = module.exports = tuneAllTheThings;