Add Tune insulin Curve option

This commit is contained in:
Philoul 2022-05-09 08:59:57 +02:00
parent bf46d8a605
commit e7da675e1e
10 changed files with 323 additions and 352 deletions

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.plugins.general.autotune package info.nightscout.androidaps.plugins.general.autotune
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
@ -28,16 +29,14 @@ class AutotuneCore @Inject constructor(
var carbRatio = previousAutotune.ic var carbRatio = previousAutotune.ic
//console.error(carbRatio); //console.error(carbRatio);
var csf = isf / carbRatio var csf = isf / carbRatio
//val dia = previousAutotune.dia var dia = previousAutotune.dia
//val insulinInterface = activePlugin.activeInsulin var peak = previousAutotune.peak
//var peak = 75
//if (insulinInterface.id == InsulinInterface.InsulinType.OREF_ULTRA_RAPID_ACTING) peak = 55 else if (insulinInterface.id == InsulinInterface.InsulinType.OREF_FREE_PEAK) peak = sp.getInt(R.string.key_insulin_oref_peak, 75)
val csfGlucose = preppedGlucose.csfGlucoseData val csfGlucose = preppedGlucose.csfGlucoseData
val isfGlucose = preppedGlucose.isfGlucoseData val isfGlucose = preppedGlucose.isfGlucoseData
val basalGlucose = preppedGlucose.basalGlucoseData val basalGlucose = preppedGlucose.basalGlucoseData
val crData = preppedGlucose.crData val crData = preppedGlucose.crData
//List<DiaDatum> diaDeviations = preppedGlucose.diaDeviations; val diaDeviations = preppedGlucose.diaDeviations
//List<PeakDatum> peakDeviations = preppedGlucose.peakDeviations; val peakDeviations = preppedGlucose.peakDeviations
val pumpISF = pumpProfile.isf val pumpISF = pumpProfile.isf
val pumpCarbRatio = pumpProfile.ic val pumpCarbRatio = pumpProfile.ic
val pumpCSF = pumpISF / pumpCarbRatio val pumpCSF = pumpISF / pumpCarbRatio
@ -46,92 +45,98 @@ class AutotuneCore @Inject constructor(
val autotuneMin = sp.getDouble(R.string.key_openapsama_autosens_min, 0.7) val autotuneMin = sp.getDouble(R.string.key_openapsama_autosens_min, 0.7)
val min5minCarbImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, 3.0) val min5minCarbImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, 3.0)
/*******Tune DIA (#57-#99) and Peak (#101-#139) disabled for the first version code below in js******************************************************************************************************** // tune DIA
* // tune DIA var newDia = dia
* var newDIA = DIA; if (diaDeviations.size > 0)
* if (diaDeviations) { {
* var currentDIAMeanDev = diaDeviations[2].meanDeviation; val currentDiaMeanDev = diaDeviations[2].meanDeviation
* var currentDIARMSDev = diaDeviations[2].RMSDeviation; val currentDiaRMSDev = diaDeviations[2].rmsDeviation
* //console.error(DIA,currentDIAMeanDev,currentDIARMSDev); //Console.WriteLine(DIA,currentDIAMeanDev,currentDIARMSDev);
* var minMeanDeviations = 1000000; var minMeanDeviations = 1000000.0
* var minRMSDeviations = 1000000; var minRmsDeviations = 1000000.0
* var meanBest = 2; var meanBest = 2
* var RMSBest = 2; var rmsBest = 2
* for (var i=0; i < diaDeviations.length; i++) { for (i in 0..diaDeviations.size-1)
* var meanDeviations = diaDeviations[i].meanDeviation; {
* var RMSDeviations = diaDeviations[i].RMSDeviation; val meanDeviations = diaDeviations[i].meanDeviation
* if (meanDeviations < minMeanDeviations) { val rmsDeviations = diaDeviations[i].rmsDeviation
* minMeanDeviations = Math.round(meanDeviations*1000)/1000; if (meanDeviations < minMeanDeviations)
* meanBest = i; {
* } minMeanDeviations = Round.roundTo(meanDeviations, 0.001)
* if (RMSDeviations < minRMSDeviations) { meanBest = i
* minRMSDeviations = Math.round(RMSDeviations*1000)/1000; }
* RMSBest = i; if (rmsDeviations < minRmsDeviations)
* } {
* } minRmsDeviations = Round.roundTo(rmsDeviations, 0.001)
* console.error("Best insulinEndTime for meanDeviations:",diaDeviations[meanBest].dia,"hours"); rmsBest = i
* 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 ) { log("Best insulinEndTime for meanDeviations: ${diaDeviations[meanBest].dia} hours")
* newDIA = diaDeviations[1].dia; log("Best insulinEndTime for RMSDeviations: ${diaDeviations[rmsBest].dia} hours")
* } if (meanBest < 2 && rmsBest < 2)
* } else if ( meanBest > 2 && RMSBest > 2 ) { {
* if ( diaDeviations[3].meanDeviation < currentDIAMeanDev * 0.99 && diaDeviations[3].RMSDeviation < currentDIARMSDev * 0.99 ) { if (diaDeviations[1].meanDeviation < currentDiaMeanDev * 0.99 && diaDeviations[1].rmsDeviation < currentDiaRMSDev * 0.99)
* newDIA = diaDeviations[3].dia; newDia = diaDeviations[1].dia
* } }
* } else if (meanBest > 2 && rmsBest > 2)
* if ( newDIA > 12 ) { {
* console.error("insulinEndTime maximum is 12h: not raising further"); if (diaDeviations[3].meanDeviation < currentDiaMeanDev * 0.99 && diaDeviations[3].rmsDeviation < currentDiaRMSDev * 0.99)
* newDIA=12; newDia = diaDeviations[3].dia
* } }
* if ( newDIA !== DIA ) { if (newDia > 12.0)
* console.error("Adjusting insulinEndTime from",DIA,"to",newDIA,"hours"); {
* } else { log("insulinEndTime maximum is 12h: not raising further")
* console.error("Leaving insulinEndTime unchanged at",DIA,"hours"); newDia = 12.0
* } }
* } if (newDia != dia)
* log("Adjusting insulinEndTime from $dia to $newDia hours")
* // tune insulinPeakTime else
* var newPeak = peak; log("Leaving insulinEndTime unchanged at $dia hours")
* if (peakDeviations && peakDeviations[2]) { }
* var currentPeakMeanDev = peakDeviations[2].meanDeviation;
* var currentPeakRMSDev = peakDeviations[2].RMSDeviation; // tune insulinPeakTime
* //console.error(currentPeakMeanDev); var newPeak = peak
* minMeanDeviations = 1000000; if (peakDeviations.size > 2)
* minRMSDeviations = 1000000; {
* meanBest = 2; val currentPeakMeanDev = peakDeviations[2].meanDeviation
* RMSBest = 2; val currentPeakRMSDev = peakDeviations[2].rmsDeviation
* for (i=0; i < peakDeviations.length; i++) { //Console.WriteLine(currentPeakMeanDev);
* meanDeviations = peakDeviations[i].meanDeviation; var minMeanDeviations = 1000000.0
* RMSDeviations = peakDeviations[i].RMSDeviation; var minRmsDeviations = 1000000.0
* if (meanDeviations < minMeanDeviations) { var meanBest = 2
* minMeanDeviations = Math.round(meanDeviations*1000)/1000; var rmsBest = 2
* meanBest = i; for (i in 0..peakDeviations.size-1)
* } {
* if (RMSDeviations < minRMSDeviations) { val meanDeviations = peakDeviations[i].meanDeviation;
* minRMSDeviations = Math.round(RMSDeviations*1000)/1000; val rmsDeviations = peakDeviations[i].rmsDeviation;
* RMSBest = i; if (meanDeviations < minMeanDeviations)
* } {
* } minMeanDeviations = Round.roundTo(meanDeviations, 0.001)
* console.error("Best insulinPeakTime for meanDeviations:",peakDeviations[meanBest].peak,"minutes"); meanBest = i
* console.error("Best insulinPeakTime for RMSDeviations:",peakDeviations[RMSBest].peak,"minutes"); }
* if ( meanBest < 2 && RMSBest < 2 ) { if (rmsDeviations < minRmsDeviations)
* if ( peakDeviations[1].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[1].RMSDeviation < currentPeakRMSDev * 0.99 ) { {
* newPeak = peakDeviations[1].peak; minRmsDeviations = Round.roundTo(rmsDeviations, 0.001)
* } rmsBest = i
* } else if ( meanBest > 2 && RMSBest > 2 ) { }
* if ( peakDeviations[3].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[3].RMSDeviation < currentPeakRMSDev * 0.99 ) { }
* newPeak = peakDeviations[3].peak; log("Best insulinPeakTime for meanDeviations: ${peakDeviations[meanBest].peak} minutes")
* } log("Best insulinPeakTime for RMSDeviations: ${peakDeviations[rmsBest].peak} minutes")
* } if (meanBest < 2 && rmsBest < 2)
* if ( newPeak !== peak ) { {
* console.error("Adjusting insulinPeakTime from",peak,"to",newPeak,"minutes"); if (peakDeviations[1].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[1].rmsDeviation < currentPeakRMSDev * 0.99)
* } else { newPeak = peakDeviations[1].peak
* console.error("Leaving insulinPeakTime unchanged at",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)
log("Adjusting insulinPeakTime from " + peak + " to " + newPeak + " minutes")
else
log("Leaving insulinPeakTime unchanged at " + peak)
}
// Calculate carb ratio (CR) independently of csf and isf // 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 // Use the time period from meal bolus/carbs until COB is zero and IOB is < currentBasal/2
@ -142,7 +147,7 @@ class AutotuneCore @Inject constructor(
//autotune-core (lib/autotune/index.js) #149-#165 //autotune-core (lib/autotune/index.js) #149-#165
var crTotalCarbs = 0.0 var crTotalCarbs = 0.0
var crTotalInsulin = 0.0 var crTotalInsulin = 0.0
for (i in crData!!.indices) { for (i in crData.indices) {
val crDatum = crData[i] val crDatum = crData[i]
val crBGChange = crDatum.crEndBG - crDatum.crInitialBG val crBGChange = crDatum.crEndBG - crDatum.crInitialBG
val crInsulinReq = crBGChange / isf val crInsulinReq = crBGChange / isf
@ -181,7 +186,7 @@ class AutotuneCore @Inject constructor(
// look at net deviations for each hour // look at net deviations for each hour
for (hour in 0..23) { for (hour in 0..23) {
var deviations = 0.0 var deviations = 0.0
for (i in basalGlucose!!.indices) { for (i in basalGlucose.indices) {
val BGTime = Calendar.getInstance() val BGTime = Calendar.getInstance()
//var BGTime: Date? = null //var BGTime: Date? = null
if (basalGlucose[i].date != 0L) { if (basalGlucose[i].date != 0L) {
@ -300,7 +305,7 @@ class AutotuneCore @Inject constructor(
//log.debug(CSFGlucose[0].mealAbsorption); //log.debug(CSFGlucose[0].mealAbsorption);
//log.debug(CSFGlucose[0]); //log.debug(CSFGlucose[0]);
//autotune-core (lib/autotune/index.js) #346-#365 //autotune-core (lib/autotune/index.js) #346-#365
for (i in csfGlucose!!.indices) { for (i in csfGlucose.indices) {
//log.debug(CSFGlucose[i].mealAbsorption, i); //log.debug(CSFGlucose[i].mealAbsorption, i);
if (csfGlucose[i].mealAbsorption === "start") { if (csfGlucose[i].mealAbsorption === "start") {
deviations = 0.0 deviations = 0.0
@ -412,7 +417,7 @@ class AutotuneCore @Inject constructor(
val avgDeltas: MutableList<Double> = ArrayList() val avgDeltas: MutableList<Double> = ArrayList()
val ratios: MutableList<Double> = ArrayList() val ratios: MutableList<Double> = ArrayList()
var count = 0 var count = 0
for (i in isfGlucose!!.indices) { for (i in isfGlucose.indices) {
val deviation = isfGlucose[i].deviation val deviation = isfGlucose[i].deviation
isfDeviations.add(deviation) isfDeviations.add(deviation)
val BGI = isfGlucose[i].bgi val BGI = isfGlucose[i].bgi
@ -497,13 +502,10 @@ class AutotuneCore @Inject constructor(
previousAutotune.isf = isf previousAutotune.isf = isf
previousAutotune.ic = Round.roundTo(carbRatio, 0.001) previousAutotune.ic = Round.roundTo(carbRatio, 0.001)
previousAutotune.basalUntuned = basalUntuned previousAutotune.basalUntuned = basalUntuned
/* code prepared for future dia/peak integration previousAutotune.dia = newDia
previousAutotune.dia=newDia; previousAutotune.peak = newPeak
previousAutotune.peak = newPeak ; val localInsulin = LocalInsulin("Ins_$newPeak-$newDia", newPeak, newDia)
if (diaDeviations || peakDeviations) { previousAutotune.localInsulin = localInsulin
autotuneOutput.useCustomPeakTime = true;
}
*/
previousAutotune.updateProfile() previousAutotune.updateProfile()
return previousAutotune return previousAutotune
} }

View file

@ -384,6 +384,7 @@ class AutotuneFragment : DaggerFragment() {
if (autotunePlugin.result.isNotBlank()) { if (autotunePlugin.result.isNotBlank()) {
var toMgDl = 1.0 var toMgDl = 1.0
if (profileFunction.getUnits() == GlucoseUnit.MMOL) toMgDl = Constants.MMOLL_TO_MGDL if (profileFunction.getUnits() == GlucoseUnit.MMOL) toMgDl = Constants.MMOLL_TO_MGDL
var isf_Format = if (profileFunction.getUnits() == GlucoseUnit.MMOL) "%.2f" else "%.1f"
binding.autotuneResults.addView( binding.autotuneResults.addView(
TableLayout(context).also { layout -> TableLayout(context).also { layout ->
layout.addView( layout.addView(
@ -395,8 +396,13 @@ class AutotuneFragment : DaggerFragment() {
}) })
autotunePlugin.tunedProfile?.let { tuned -> autotunePlugin.tunedProfile?.let { tuned ->
layout.addView(toTableRowHeader()) layout.addView(toTableRowHeader())
layout.addView(toTableRowValue(rh.gs(R.string.isf_short), Round.roundTo(autotunePlugin.pumpProfile.isf / toMgDl, 0.001), Round.roundTo(tuned.isf / toMgDl, 0.001))) val tuneInsulin = sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)
layout.addView(toTableRowValue(rh.gs(R.string.ic_short), Round.roundTo(autotunePlugin.pumpProfile.ic, 0.001), Round.roundTo(tuned.ic, 0.001))) if (tuneInsulin) {
layout.addView(toTableRowValue(rh.gs(R.string.insulin_peak), autotunePlugin.pumpProfile.localInsulin.peak.toDouble(), tuned.localInsulin.peak.toDouble(), "%.0f"))
layout.addView(toTableRowValue(rh.gs(R.string.dia), Round.roundTo(autotunePlugin.pumpProfile.localInsulin.dia, 0.1), Round.roundTo(tuned.localInsulin.dia, 0.1),"%.1f"))
}
layout.addView(toTableRowValue(rh.gs(R.string.isf_short), Round.roundTo(autotunePlugin.pumpProfile.isf / toMgDl, 0.001), Round.roundTo(tuned.isf / toMgDl, 0.001), isf_Format))
layout.addView(toTableRowValue(rh.gs(R.string.ic_short), Round.roundTo(autotunePlugin.pumpProfile.ic, 0.001), Round.roundTo(tuned.ic, 0.001), "%.2f"))
layout.addView( layout.addView(
TextView(context).apply { TextView(context).apply {
text = rh.gs(R.string.basal) text = rh.gs(R.string.basal)
@ -413,7 +419,7 @@ class AutotuneFragment : DaggerFragment() {
val time = df.format(h.toLong()) + ":00" val time = df.format(h.toLong()) + ":00"
totalPump += autotunePlugin.pumpProfile.basal[h] totalPump += autotunePlugin.pumpProfile.basal[h]
totalTuned += tuned.basal[h] totalTuned += tuned.basal[h]
layout.addView(toTableRowValue(time, autotunePlugin.pumpProfile.basal[h], tuned.basal[h], tuned.basalUntuned[h].toString())) layout.addView(toTableRowValue(time, autotunePlugin.pumpProfile.basal[h], tuned.basal[h], "%.3f", tuned.basalUntuned[h].toString()))
} }
layout.addView(toTableRowValue("", totalPump, totalTuned, " ")) layout.addView(toTableRowValue("", totalPump, totalTuned, " "))
} }
@ -456,7 +462,7 @@ class AutotuneFragment : DaggerFragment() {
}) })
} }
private fun toTableRowValue(hour: String, inputValue: Double, tunedValue: Double, missing: String = ""): TableRow = private fun toTableRowValue(hour: String, inputValue: Double, tunedValue: Double, format:String = "%.3f", missing: String = ""): TableRow =
TableRow(context).also { row -> TableRow(context).also { row ->
val percentValue = Round.roundTo(tunedValue / inputValue * 100 - 100, 1.0).toInt().toString() + "%" val percentValue = Round.roundTo(tunedValue / inputValue * 100 - 100, 1.0).toInt().toString() + "%"
val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT).apply { weight = 1f } val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT).apply { weight = 1f }
@ -469,12 +475,12 @@ class AutotuneFragment : DaggerFragment() {
row.addView(TextView(context).apply { row.addView(TextView(context).apply {
layoutParams = lp.apply { column = 1 } layoutParams = lp.apply { column = 1 }
textAlignment = TextView.TEXT_ALIGNMENT_CENTER textAlignment = TextView.TEXT_ALIGNMENT_CENTER
text = String.format("%.3f", inputValue) text = String.format(format, inputValue)
}) })
row.addView(TextView(context).apply { row.addView(TextView(context).apply {
layoutParams = lp.apply { column = 2 } layoutParams = lp.apply { column = 2 }
textAlignment = TextView.TEXT_ALIGNMENT_CENTER textAlignment = TextView.TEXT_ALIGNMENT_CENTER
text = String.format("%.3f", tunedValue) text = String.format(format, tunedValue)
}) })
row.addView(TextView(context).apply { row.addView(TextView(context).apply {
layoutParams = lp.apply { column = 3 } layoutParams = lp.apply { column = 3 }

View file

@ -31,7 +31,6 @@ import javax.inject.Singleton
* adaptation from oref0 autotune started by philoul on 2020 (complete refactoring of AutotunePlugin initialised by Rumen Georgiev on 1/29/2018.) * adaptation from oref0 autotune started by philoul on 2020 (complete refactoring of AutotunePlugin initialised by Rumen Georgiev on 1/29/2018.)
* *
* TODO: replace Thread by Worker * TODO: replace Thread by Worker
* TODO: future version (once first version validated): add DIA and Peak tune for insulin
* TODO: future version: Allow day of the week selection to tune specifics days (training days, working days, WE days) * TODO: future version: Allow day of the week selection to tune specifics days (training days, working days, WE days)
*/ */
@ -94,7 +93,7 @@ class AutotunePlugin @Inject constructor(
profileFunction.getProfile()?.let { currentProfile -> profileFunction.getProfile()?.let { currentProfile ->
profile = profileStore.getSpecificProfile(profileToTune)?.let { ProfileSealed.Pure(it) } ?: currentProfile profile = profileStore.getSpecificProfile(profileToTune)?.let { ProfileSealed.Pure(it) } ?: currentProfile
} }
var localInsulin = LocalInsulin("PumpInsulin", activePlugin.activeInsulin.peak, profile.dia) // var because localInsulin could be updated later with Tune Insulin peak/dia val localInsulin = LocalInsulin("PumpInsulin", activePlugin.activeInsulin.peak, profile.dia) // var because localInsulin could be updated later with Tune Insulin peak/dia
log("Start Autotune with $daysBack days back") log("Start Autotune with $daysBack days back")
autotuneFS.createAutotuneFolder() //create autotune subfolder for autotune files if not exists autotuneFS.createAutotuneFolder() //create autotune subfolder for autotune files if not exists
@ -120,7 +119,7 @@ class AutotunePlugin @Inject constructor(
autotuneIob.initializeData(from, to, tunedProfile) //autotuneIob contains BG and Treatments data from history (<=> query for ns-treatments and ns-entries) autotuneIob.initializeData(from, to, tunedProfile) //autotuneIob contains BG and Treatments data from history (<=> query for ns-treatments and ns-entries)
autotuneFS.exportEntries(autotuneIob) //<=> ns-entries.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine autotuneFS.exportEntries(autotuneIob) //<=> ns-entries.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine
autotuneFS.exportTreatments(autotuneIob) //<=> ns-treatments.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine (include treatments ,tempBasal and extended autotuneFS.exportTreatments(autotuneIob) //<=> ns-treatments.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine (include treatments ,tempBasal and extended
preppedGlucose = autotunePrep.categorizeBGDatums(tunedProfile, localInsulin) //<=> autotune.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine preppedGlucose = autotunePrep.categorize(tunedProfile) //<=> autotune.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine
} }
if (preppedGlucose == null || tunedProfile == null) { if (preppedGlucose == null || tunedProfile == null) {
@ -204,9 +203,14 @@ class AutotunePlugin @Inject constructor(
var strResult = line var strResult = line
strResult += rh.gs(R.string.autotune_log_title) strResult += rh.gs(R.string.autotune_log_title)
strResult += line strResult += line
val tuneInsulin = sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)
if (tuneInsulin) {
strResult += rh.gs(R.string.autotune_log_peak, rh.gs(R.string.insulin_peak), pumpProfile.localInsulin.peak, tunedProfile.localInsulin.peak)
strResult += rh.gs(R.string.autotune_log_dia, rh.gs(R.string.ic_short), pumpProfile.localInsulin.dia, tunedProfile.localInsulin.dia)
}
// show ISF and CR // show ISF and CR
strResult += rh.gs(R.string.autotune_log_isf, rh.gs(R.string.isf_short), pumpProfile.isf, tunedProfile.isf) strResult += rh.gs(R.string.autotune_log_ic_isf, rh.gs(R.string.isf_short), pumpProfile.isf, tunedProfile.isf)
strResult += rh.gs(R.string.autotune_log_ic, rh.gs(R.string.ic_short), pumpProfile.ic, tunedProfile.ic) strResult += rh.gs(R.string.autotune_log_ic_isf, rh.gs(R.string.ic_short), pumpProfile.ic, tunedProfile.ic)
strResult += line strResult += line
var totalBasal = 0.0 var totalBasal = 0.0
var totalTuned = 0.0 var totalTuned = 0.0
@ -232,7 +236,7 @@ class AutotunePlugin @Inject constructor(
val endDateString = dateUtil.toISOString(lastloopend - 24 * 60 * 60 * 1000L).substring(0,10) val endDateString = dateUtil.toISOString(lastloopend - 24 * 60 * 60 * 1000L).substring(0,10)
val nsUrl = sp.getString(R.string.key_nsclientinternal_url, "") val nsUrl = sp.getString(R.string.key_nsclientinternal_url, "")
val optCategorizeUam = if (sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false)) "-c=true" else "" val optCategorizeUam = if (sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false)) "-c=true" else ""
val optInsulinCurve = "" val optInsulinCurve = if (sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)) "-i=true" else ""
try { try {
jsonSettings.put("datestring", dateUtil.toISOString(runDate)) jsonSettings.put("datestring", dateUtil.toISOString(runDate))
jsonSettings.put("dateutc", dateUtil.toISOAsUTC(runDate)) jsonSettings.put("dateutc", dateUtil.toISOAsUTC(runDate))

View file

@ -3,14 +3,13 @@ package info.nightscout.androidaps.plugins.general.autotune
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.LocalInsulin import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile import info.nightscout.androidaps.plugins.general.autotune.data.*
import info.nightscout.androidaps.plugins.general.autotune.data.BGDatum
import info.nightscout.androidaps.plugins.general.autotune.data.CRDatum
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.Carbs import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.MidnightTime
import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.T
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -23,11 +22,121 @@ class AutotunePrep @Inject constructor(
private val autotuneFS: AutotuneFS, private val autotuneFS: AutotuneFS,
private val autotuneIob: AutotuneIob private val autotuneIob: AutotuneIob
) { ) {
fun categorize(tunedprofile: ATProfile): PreppedGlucose? {
val preppedGlucose = categorizeBGDatums(tunedprofile, tunedprofile.localInsulin)
val tuneInsulin = sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)
if (tuneInsulin) {
var minDeviations = 1000000.0
val diaDeviations: MutableList<DiaDeviation> = ArrayList()
val peakDeviations: MutableList<PeakDeviation> = ArrayList()
val currentDIA = tunedprofile.localInsulin.dia
val currentPeak = tunedprofile.localInsulin.peak
var dia = currentDIA - 2
val endDIA = currentDIA + 2
while (dia <= endDIA)
{
var sqrtDeviations = 0.0
var deviations = 0.0
var deviationsSq = 0.0
val localInsulin = LocalInsulin("Ins_$currentPeak-$dia", currentPeak, dia)
val curve_output = categorizeBGDatums(tunedprofile, localInsulin, false)
val basalGlucose = curve_output?.basalGlucoseData
basalGlucose?.let {
for (hour in 0..23) {
for (i in 0..(basalGlucose.size-1)) {
val myHour = ((basalGlucose[i].date - MidnightTime.calc(basalGlucose[i].date)) / T.hours(1).msecs()).toInt()
if (hour == myHour) {
sqrtDeviations += Math.pow(Math.abs(basalGlucose[i].deviation), 0.5)
deviations += Math.abs(basalGlucose[i].deviation)
deviationsSq += Math.pow(basalGlucose[i].deviation, 2.0)
}
}
}
val meanDeviation = Round.roundTo(Math.abs(deviations / basalGlucose.size), 0.001)
val smrDeviation = Round.roundTo(Math.pow(sqrtDeviations / basalGlucose.size, 2.0), 0.001)
val rmsDeviation = Round.roundTo(Math.pow(deviationsSq / basalGlucose.size, 0.5), 0.001)
log("insulinEndTime $dia meanDeviation: $meanDeviation SMRDeviation: $smrDeviation RMSDeviation: $rmsDeviation (mg/dL)")
diaDeviations.add(
DiaDeviation(
dia = dia,
meanDeviation = meanDeviation,
smrDeviation = smrDeviation,
rmsDeviation = rmsDeviation
)
)
}
preppedGlucose?.diaDeviations = diaDeviations
deviations = Round.roundTo(deviations, 0.001)
if (deviations < minDeviations)
minDeviations = Round.roundTo(deviations, 0.001)
dia += 1.0
}
// consoleError('Optimum insulinEndTime', newDIA, 'mean deviation:', JSMath.Round(minDeviations/basalGlucose.length*1000)/1000, '(mg/dL)');
//consoleError(diaDeviations);
minDeviations = 1000000.0
var peak = currentPeak - 10
val endPeak = currentPeak + 10
while (peak <= endPeak)
{
var sqrtDeviations = 0.0
var deviations = 0.0
var deviationsSq = 0.0
val localInsulin = LocalInsulin("Ins_$peak-$currentDIA", peak, currentDIA)
val curve_output = categorizeBGDatums(tunedprofile, localInsulin, false)
val basalGlucose = curve_output?.basalGlucoseData
basalGlucose?.let {
for (hour in 0..23) {
for (i in 0..(basalGlucose.size - 1)) {
val myHour = ((basalGlucose[i].date - MidnightTime.calc(basalGlucose[i].date)) / T.hours(1).msecs()).toInt()
if (hour == myHour) {
//console.error(basalGlucose[i].deviation);
sqrtDeviations += Math.pow(Math.abs(basalGlucose[i].deviation), 0.5)
deviations += Math.abs(basalGlucose[i].deviation)
deviationsSq += Math.pow(basalGlucose[i].deviation, 2.0)
}
}
}
val meanDeviation = Round.roundTo(deviations / basalGlucose.size, 0.001)
val smrDeviation = Round.roundTo(Math.pow(sqrtDeviations / basalGlucose.size, 2.0), 0.001)
val rmsDeviation = Round.roundTo(Math.pow(deviationsSq / basalGlucose.size, 0.5), 0.001)
log("insulinPeakTime $peak meanDeviation: $meanDeviation SMRDeviation: $smrDeviation RMSDeviation: $rmsDeviation (mg/dL)")
peakDeviations.add(
PeakDeviation
(
peak = peak,
meanDeviation = meanDeviation,
smrDeviation = smrDeviation,
rmsDeviation = rmsDeviation,
)
)
}
deviations = Round.roundTo(deviations, 0.001);
if (deviations < minDeviations)
minDeviations = Round.roundTo(deviations, 0.001)
peak += 5
}
//consoleError($"Optimum insulinPeakTime {newPeak} mean deviation: {JSMath.Round(minDeviations/basalGlucose.Count, 3)} (mg/dL)");
//consoleError(peakDeviations);
preppedGlucose?.peakDeviations = peakDeviations
}
return preppedGlucose
}
// private static Logger log = LoggerFactory.getLogger(AutotunePlugin.class); // private static Logger log = LoggerFactory.getLogger(AutotunePlugin.class);
fun categorizeBGDatums(tunedprofile: ATProfile, localInsulin: LocalInsulin): PreppedGlucose? { fun categorizeBGDatums(tunedprofile: ATProfile, localInsulin: LocalInsulin, verbose: Boolean = true): PreppedGlucose? {
//lib/meals is called before to get only meals data (in AAPS it's done in AutotuneIob) //lib/meals is called before to get only meals data (in AAPS it's done in AutotuneIob)
var treatments: MutableList<Carbs> = autotuneIob.meals val treatments: MutableList<Carbs> = autotuneIob.meals
var boluses: MutableList<Bolus> = autotuneIob.boluses val boluses: MutableList<Bolus> = autotuneIob.boluses
// Bloc between #21 and # 54 replaced by bloc below (just remove BG value below 39, Collections.sort probably not necessary because BG values already sorted...) // Bloc between #21 and # 54 replaced by bloc below (just remove BG value below 39, Collections.sort probably not necessary because BG values already sorted...)
val glucose = autotuneIob.glucose val glucose = autotuneIob.glucose
val glucoseData: MutableList<GlucoseValue> = ArrayList() val glucoseData: MutableList<GlucoseValue> = ArrayList()
@ -37,7 +146,8 @@ class AutotunePrep @Inject constructor(
} }
} }
if (glucose.size == 0 || glucoseData.size == 0 ) { if (glucose.size == 0 || glucoseData.size == 0 ) {
log("No BG value received") if (verbose)
log("No BG value received")
return null return null
} }
@ -49,11 +159,13 @@ class AutotunePrep @Inject constructor(
//val boluses = 0 //val boluses = 0
//val maxCarbs = 0 //val maxCarbs = 0
if (treatments.size == 0) { if (treatments.size == 0) {
log("No Carbs entries") if (verbose)
log("No Carbs entries")
//return null //return null
} }
if (autotuneIob.boluses.size == 0) { if (autotuneIob.boluses.size == 0) {
log("No treatment received") if (verbose)
log("No treatment received")
return null return null
} }
@ -141,7 +253,8 @@ class AutotunePrep @Inject constructor(
} }
avgDelta = (bg - bucketedData[i + 4].value) / 4 avgDelta = (bg - bucketedData[i + 4].value) / 4
} else { } else {
log("Could not find glucose data") if (verbose)
log("Could not find glucose data")
} }
avgDelta = Round.roundTo(avgDelta, 0.01) avgDelta = Round.roundTo(avgDelta, 0.01)
glucoseDatum.avgDelta = avgDelta glucoseDatum.avgDelta = avgDelta
@ -207,7 +320,8 @@ class AutotunePrep @Inject constructor(
crInitialIOB = iob.iob crInitialIOB = iob.iob
crInitialBG = glucoseDatum.value crInitialBG = glucoseDatum.value
crInitialCarbTime = glucoseDatum.date crInitialCarbTime = glucoseDatum.date
log("CRInitialIOB: " + crInitialIOB + " CRInitialBG: " + crInitialBG + " CRInitialCarbTime: " + dateUtil.toISOString(crInitialCarbTime)) if (verbose)
log("CRInitialIOB: " + crInitialIOB + " CRInitialBG: " + crInitialBG + " CRInitialCarbTime: " + dateUtil.toISOString(crInitialCarbTime))
} }
// keep calculatingCR as long as we have COB or enough IOB // keep calculatingCR as long as we have COB or enough IOB
if (mealCOB > 0 && i > 1) { if (mealCOB > 0 && i > 1) {
@ -219,7 +333,8 @@ class AutotunePrep @Inject constructor(
val crEndIOB = iob.iob val crEndIOB = iob.iob
val crEndBG = glucoseDatum.value val crEndBG = glucoseDatum.value
val crEndTime = glucoseDatum.date val crEndTime = glucoseDatum.date
log("CREndIOB: " + crEndIOB + " CREndBG: " + crEndBG + " CREndTime: " + dateUtil.toISOString(crEndTime)) if (verbose)
log("CREndIOB: " + crEndIOB + " CREndBG: " + crEndBG + " CREndTime: " + dateUtil.toISOString(crEndTime))
val crDatum = CRDatum(dateUtil) val crDatum = CRDatum(dateUtil)
crDatum.crInitialBG = crInitialBG crDatum.crInitialBG = crInitialBG
crDatum.crInitialIOB = crInitialIOB crDatum.crInitialIOB = crInitialIOB
@ -234,7 +349,8 @@ class AutotunePrep @Inject constructor(
//log.debug(CREndTime - CRInitialCarbTime, CRElapsedMinutes); //log.debug(CREndTime - CRInitialCarbTime, CRElapsedMinutes);
if (CRElapsedMinutes < 60 || i == 1 && mealCOB > 0) { if (CRElapsedMinutes < 60 || i == 1 && mealCOB > 0) {
log("Ignoring $CRElapsedMinutes m CR period.") if (verbose)
log("Ignoring $CRElapsedMinutes m CR period.")
} else { } else {
crData.add(crDatum) crData.add(crDatum)
} }
@ -262,7 +378,8 @@ class AutotunePrep @Inject constructor(
//log.debug(type); //log.debug(type);
if (type != "csf") { if (type != "csf") {
glucoseDatum.mealAbsorption = "start" glucoseDatum.mealAbsorption = "start"
log(glucoseDatum.mealAbsorption + " carb absorption") if (verbose)
log(glucoseDatum.mealAbsorption + " carb absorption")
} }
type = "csf" type = "csf"
glucoseDatum.mealCarbs = mealCarbs.toInt() glucoseDatum.mealCarbs = mealCarbs.toInt()
@ -272,7 +389,8 @@ class AutotunePrep @Inject constructor(
// check previous "type" value, and if it was csf, set a mealAbsorption end flag // check previous "type" value, and if it was csf, set a mealAbsorption end flag
if (type == "csf") { if (type == "csf") {
csfGlucoseData[csfGlucoseData.size - 1].mealAbsorption = "end" csfGlucoseData[csfGlucoseData.size - 1].mealAbsorption = "end"
log(csfGlucoseData[csfGlucoseData.size - 1].mealAbsorption + " carb absorption") if (verbose)
log(csfGlucoseData[csfGlucoseData.size - 1].mealAbsorption + " carb absorption")
} }
if (iob.iob > 2 * currentBasal || deviation > 6 || uam) { if (iob.iob > 2 * currentBasal || deviation > 6 || uam) {
uam = if (deviation > 0) { uam = if (deviation > 0) {
@ -282,13 +400,15 @@ class AutotunePrep @Inject constructor(
} }
if (type != "uam") { if (type != "uam") {
glucoseDatum.uamAbsorption = "start" glucoseDatum.uamAbsorption = "start"
log(glucoseDatum.uamAbsorption + " unannnounced meal absorption") if (verbose)
log(glucoseDatum.uamAbsorption + " unannnounced meal absorption")
} }
type = "uam" type = "uam"
uamGlucoseData.add(glucoseDatum) uamGlucoseData.add(glucoseDatum)
} else { } else {
if (type == "uam") { if (type == "uam") {
log("end unannounced meal absorption") if (verbose)
log("end unannounced meal absorption")
} }
// Go through the remaining time periods and divide them into periods where scheduled basal insulin activity dominates. This would be determined by calculating the BG impact of scheduled basal insulin // Go through the remaining time periods and divide them into periods where scheduled basal insulin activity dominates. This would be determined by calculating the BG impact of scheduled basal insulin
@ -313,7 +433,8 @@ class AutotunePrep @Inject constructor(
} }
} }
// debug line to print out all the things // debug line to print out all the things
log((if (absorbing) 1 else 0).toString() + " mealCOB: " + Round.roundTo(mealCOB, 0.1) + " mealCarbs: " + Math.round(mealCarbs) + " basalBGI: " + Round.roundTo(basalBGI, 0.1) + " BGI: " + Round.roundTo(BGI, 0.1) + " IOB: " + iob.iob+ " Activity: " + iob.activity + " at " + dateUtil.timeStringWithSeconds(BGTime) + " dev: " + deviation + " avgDelta: " + avgDelta + " " + type) if (verbose)
log((if (absorbing) 1 else 0).toString() + " mealCOB: " + Round.roundTo(mealCOB, 0.1) + " mealCarbs: " + Math.round(mealCarbs) + " basalBGI: " + Round.roundTo(basalBGI, 0.1) + " BGI: " + Round.roundTo(BGI, 0.1) + " IOB: " + iob.iob+ " Activity: " + iob.activity + " at " + dateUtil.timeStringWithSeconds(BGTime) + " dev: " + deviation + " avgDelta: " + avgDelta + " " + type)
} }
//**************************************************************************************************************************************** //****************************************************************************************************************************************
@ -328,16 +449,20 @@ class AutotunePrep @Inject constructor(
val UAMLength = uamGlucoseData.size val UAMLength = uamGlucoseData.size
var basalLength = basalGlucoseData.size var basalLength = basalGlucoseData.size
if (sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false)) { if (sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false)) {
log("Categorizing all UAM data as basal.") if (verbose)
log("Categorizing all UAM data as basal.")
basalGlucoseData.addAll(uamGlucoseData) basalGlucoseData.addAll(uamGlucoseData)
} else if (CSFLength > 12) { } else if (CSFLength > 12) {
log("Found at least 1h of carb: assuming meals were announced, and categorizing UAM data as basal.") if (verbose)
log("Found at least 1h of carb: assuming meals were announced, and categorizing UAM data as basal.")
basalGlucoseData.addAll(uamGlucoseData) basalGlucoseData.addAll(uamGlucoseData)
} else { } else {
if (2 * basalLength < UAMLength) { if (2 * basalLength < UAMLength) {
//log.debug(basalGlucoseData, UAMGlucoseData); //log.debug(basalGlucoseData, UAMGlucoseData);
log("Warning: too many deviations categorized as UnAnnounced Meals") if (verbose) {
log("Adding $UAMLength UAM deviations to $basalLength basal ones") log("Warning: too many deviations categorized as UnAnnounced Meals")
log("Adding $UAMLength UAM deviations to $basalLength basal ones")
}
basalGlucoseData.addAll(uamGlucoseData) basalGlucoseData.addAll(uamGlucoseData)
//log.debug(basalGlucoseData); //log.debug(basalGlucoseData);
// if too much data is excluded as UAM, add in the UAM deviations, but then discard the highest 50% // if too much data is excluded as UAM, add in the UAM deviations, but then discard the highest 50%
@ -348,10 +473,12 @@ class AutotunePrep @Inject constructor(
} }
//log.debug(newBasalGlucose); //log.debug(newBasalGlucose);
basalGlucoseData = newBasalGlucose basalGlucoseData = newBasalGlucose
log("and selecting the lowest 50%, leaving " + basalGlucoseData.size + " basal+UAM ones") if (verbose)
log("and selecting the lowest 50%, leaving " + basalGlucoseData.size + " basal+UAM ones")
} }
if (2 * ISFLength < UAMLength) { if (2 * ISFLength < UAMLength) {
log("Adding $UAMLength UAM deviations to $ISFLength ISF ones") if (verbose)
log("Adding $UAMLength UAM deviations to $ISFLength ISF ones")
isfGlucoseData.addAll(uamGlucoseData) isfGlucoseData.addAll(uamGlucoseData)
// if too much data is excluded as UAM, add in the UAM deviations to ISF, but then discard the highest 50% // if too much data is excluded as UAM, add in the UAM deviations to ISF, but then discard the highest 50%
isfGlucoseData.sortWith(object: Comparator<BGDatum>{ override fun compare(o1: BGDatum, o2: BGDatum): Int = (100 * o1.deviation - 100 * o2.deviation).toInt() }) //deviation rouded to 0.01, so *100 to avoid crash during sort isfGlucoseData.sortWith(object: Comparator<BGDatum>{ override fun compare(o1: BGDatum, o2: BGDatum): Int = (100 * o1.deviation - 100 * o2.deviation).toInt() }) //deviation rouded to 0.01, so *100 to avoid crash during sort
@ -361,178 +488,29 @@ class AutotunePrep @Inject constructor(
} }
//console.error(newISFGlucose); //console.error(newISFGlucose);
isfGlucoseData = newISFGlucose isfGlucoseData = newISFGlucose
log("and selecting the lowest 50%, leaving " + isfGlucoseData.size + " ISF+UAM ones") if (verbose)
log("and selecting the lowest 50%, leaving " + isfGlucoseData.size + " ISF+UAM ones")
//log.error(ISFGlucoseData.length, UAMLength); //log.error(ISFGlucoseData.length, UAMLength);
} }
} }
basalLength = basalGlucoseData.size basalLength = basalGlucoseData.size
ISFLength = isfGlucoseData.size ISFLength = isfGlucoseData.size
if (4 * basalLength + ISFLength < CSFLength && ISFLength < 10) { if (4 * basalLength + ISFLength < CSFLength && ISFLength < 10) {
log("Warning: too many deviations categorized as meals") if (verbose) {
//log.debug("Adding",CSFLength,"CSF deviations to",basalLength,"basal ones"); log("Warning: too many deviations categorized as meals")
//var basalGlucoseData = basalGlucoseData.concat(CSFGlucoseData); //log.debug("Adding",CSFLength,"CSF deviations to",basalLength,"basal ones");
log("Adding $CSFLength CSF deviations to $ISFLength ISF ones") //var basalGlucoseData = basalGlucoseData.concat(CSFGlucoseData);
log("Adding $CSFLength CSF deviations to $ISFLength ISF ones")
}
isfGlucoseData.addAll(csfGlucoseData) isfGlucoseData.addAll(csfGlucoseData)
csfGlucoseData = ArrayList() csfGlucoseData = ArrayList()
} }
// categorize.js Lines 437-444 // categorize.js Lines 437-444
log("CRData: " + crData.size + " CSFGlucoseData: " + csfGlucoseData.size + " ISFGlucoseData: " + isfGlucoseData.size + " BasalGlucoseData: " + basalGlucoseData.size) if (verbose)
// Here is the end of categorize.js file log("CRData: " + crData.size + " CSFGlucoseData: " + csfGlucoseData.size + " ISFGlucoseData: " + isfGlucoseData.size + " BasalGlucoseData: " + basalGlucoseData.size)
/* bloc below is for --tune-insulin-curve not developed for the moment
// these lines are in index.js file (autotune-prep folder)
if (inputs.tune_insulin_curve) {
if (opts.profile.curve === 'bilinear') {
console.error('--tune-insulin-curve is set but only valid for exponential curves');
} else {
var minDeviations = 1000000;
var newDIA = 0;
var diaDeviations = [];
var peakDeviations = [];
var currentDIA = opts.profile.dia;
var currentPeak = opts.profile.insulinPeakTime;
var consoleError = console.error;
console.error = function() {};
var startDIA=currentDIA - 2;
var endDIA=currentDIA + 2;
for (var dia=startDIA; dia <= endDIA; ++dia) {
var sqrtDeviations = 0;
var deviations = 0;
var deviationsSq = 0;
opts.profile.dia = dia;
var curve_output = categorize(opts);
var basalGlucose = curve_output.basalGlucoseData;
for (var hour=0; hour < 24; ++hour) {
for (var 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 {
consoleError("Could not determine last BG time");
}
var myHour = BGTime.getHours();
if (hour === myHour) {
//console.error(basalGlucose[i].deviation);
sqrtDeviations += Math.pow(parseFloat(Math.abs(basalGlucose[i].deviation)), 0.5);
deviations += Math.abs(parseFloat(basalGlucose[i].deviation));
deviationsSq += Math.pow(parseFloat(basalGlucose[i].deviation), 2);
}
}
}
var meanDeviation = Math.round(Math.abs(deviations/basalGlucose.length)*1000)/1000;
var SMRDeviation = Math.round(Math.pow(sqrtDeviations/basalGlucose.length,2)*1000)/1000;
var RMSDeviation = Math.round(Math.pow(deviationsSq/basalGlucose.length,0.5)*1000)/1000;
consoleError('insulinEndTime', dia, 'meanDeviation:', meanDeviation, 'SMRDeviation:', SMRDeviation, 'RMSDeviation:',RMSDeviation, '(mg/dL)');
diaDeviations.push({
dia: dia,
meanDeviation: meanDeviation,
SMRDeviation: SMRDeviation,
RMSDeviation: RMSDeviation,
});
autotune_prep_output.diaDeviations = diaDeviations;
deviations = Math.round(deviations*1000)/1000;
if (deviations < minDeviations) {
minDeviations = Math.round(deviations*1000)/1000;
newDIA = dia;
}
}
// consoleError('Optimum insulinEndTime', newDIA, 'mean deviation:', Math.round(minDeviations/basalGlucose.length*1000)/1000, '(mg/dL)');
//consoleError(diaDeviations);
minDeviations = 1000000;
var newPeak = 0;
opts.profile.dia = currentDIA;
//consoleError(opts.profile.useCustomPeakTime, opts.profile.insulinPeakTime);
if ( ! opts.profile.useCustomPeakTime === true && opts.profile.curve === "ultra-rapid" ) {
opts.profile.insulinPeakTime = 55;
} else if ( ! opts.profile.useCustomPeakTime === true ) {
opts.profile.insulinPeakTime = 75;
}
opts.profile.useCustomPeakTime = true;
var startPeak=opts.profile.insulinPeakTime - 10;
var endPeak=opts.profile.insulinPeakTime + 10;
for (var peak=startPeak; peak <= endPeak; peak=(peak+5)) {
sqrtDeviations = 0;
deviations = 0;
deviationsSq = 0;
opts.profile.insulinPeakTime = peak;
curve_output = categorize(opts);
basalGlucose = curve_output.basalGlucoseData;
for (hour=0; hour < 24; ++hour) {
for (i=0; i < basalGlucose.length; ++i) {
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 {
consoleError("Could not determine last BG time");
}
myHour = BGTime.getHours();
if (hour === myHour) {
//console.error(basalGlucose[i].deviation);
sqrtDeviations += Math.pow(parseFloat(Math.abs(basalGlucose[i].deviation)), 0.5);
deviations += Math.abs(parseFloat(basalGlucose[i].deviation));
deviationsSq += Math.pow(parseFloat(basalGlucose[i].deviation), 2);
}
}
}
console.error(deviationsSq);
meanDeviation = Math.round(deviations/basalGlucose.length*1000)/1000;
SMRDeviation = Math.round(Math.pow(sqrtDeviations/basalGlucose.length,2)*1000)/1000;
RMSDeviation = Math.round(Math.pow(deviationsSq/basalGlucose.length,0.5)*1000)/1000;
consoleError('insulinPeakTime', peak, 'meanDeviation:', meanDeviation, 'SMRDeviation:', SMRDeviation, 'RMSDeviation:',RMSDeviation, '(mg/dL)');
peakDeviations.push({
peak: peak,
meanDeviation: meanDeviation,
SMRDeviation: SMRDeviation,
RMSDeviation: RMSDeviation,
});
autotune_prep_output.diaDeviations = diaDeviations;
deviations = Math.round(deviations*1000)/1000;
if (deviations < minDeviations) {
minDeviations = Math.round(deviations*1000)/1000;
newPeak = peak;
}
}
//consoleError('Optimum insulinPeakTime', newPeak, 'mean deviation:', Math.round(minDeviations/basalGlucose.length*1000)/1000, '(mg/dL)');
//consoleError(peakDeviations);
autotune_prep_output.peakDeviations = peakDeviations;
console.error = consoleError;
}
}
*/
return PreppedGlucose(autotuneIob.startBG, crData, csfGlucoseData, isfGlucoseData, basalGlucoseData, dateUtil) return PreppedGlucose(autotuneIob.startBG, crData, csfGlucoseData, isfGlucoseData, basalGlucoseData, dateUtil)
// and may be later
// return new PreppedGlucose(crData, csfGlucoseData, isfGlucoseData, basalGlucoseData, diaDeviations, peakDeviations);
} }
//dosed.js full //dosed.js full

View file

@ -3,15 +3,9 @@ package info.nightscout.androidaps.plugins.general.autotune.data
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
class DiaDatum { class DiaDeviation(var dia: Double = 0.0, var meanDeviation: Double = 0.0, var smrDeviation: Double = 0.0, var rmsDeviation: Double = 0.0) {
var dia = 0.0 constructor(json: JSONObject) : this() {
var meanDeviation = 0.0
var smrDeviation = 0.0
var rmsDeviation = 0.0
constructor() {}
constructor(json: JSONObject) {
try { try {
if (json.has("dia")) dia = json.getDouble("dia") if (json.has("dia")) dia = json.getDouble("dia")
if (json.has("meanDeviation")) meanDeviation = json.getDouble("meanDeviation") if (json.has("meanDeviation")) meanDeviation = json.getDouble("meanDeviation")
@ -32,13 +26,4 @@ class DiaDatum {
} }
return crjson return crjson
} }
fun equals(obj: DiaDatum): Boolean {
var isEqual = true
if (dia != obj.dia) isEqual = false
if (meanDeviation != obj.meanDeviation) isEqual = false
if (smrDeviation != obj.smrDeviation) isEqual = false
if (rmsDeviation != obj.rmsDeviation) isEqual = false
return isEqual
}
} }

View file

@ -3,17 +3,11 @@ package info.nightscout.androidaps.plugins.general.autotune.data
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
class PeakDatum { class PeakDeviation(var peak: Int = 0, var meanDeviation: Double = 0.0, var smrDeviation: Double = 0.0, var rmsDeviation: Double = 0.0) {
var peak = 0.0 constructor(json: JSONObject) : this() {
var meanDeviation = 0.0
var smrDeviation = 0.0
var rmsDeviation = 0.0
constructor() {}
constructor(json: JSONObject) {
try { try {
if (json.has("peak")) peak = json.getDouble("peak") if (json.has("peak")) peak = json.getInt("peak")
if (json.has("meanDeviation")) meanDeviation = json.getDouble("meanDeviation") if (json.has("meanDeviation")) meanDeviation = json.getDouble("meanDeviation")
if (json.has("SMRDeviation")) smrDeviation = json.getDouble("SMRDeviation") if (json.has("SMRDeviation")) smrDeviation = json.getDouble("SMRDeviation")
if (json.has("RMSDeviation")) rmsDeviation = json.getDouble("RMSDeviation") if (json.has("RMSDeviation")) rmsDeviation = json.getDouble("RMSDeviation")
@ -32,13 +26,4 @@ class PeakDatum {
} }
return crjson return crjson
} }
fun equals(obj: PeakDatum): Boolean {
var isEqual = true
if (peak != obj.peak) isEqual = false
if (meanDeviation != obj.meanDeviation) isEqual = false
if (smrDeviation != obj.smrDeviation) isEqual = false
if (rmsDeviation != obj.rmsDeviation) isEqual = false
return isEqual
}
} }

View file

@ -8,12 +8,12 @@ import java.util.*
class PreppedGlucose { class PreppedGlucose {
var crData: List<CRDatum>? = ArrayList() var crData: List<CRDatum> = ArrayList()
var csfGlucoseData: List<BGDatum>? = ArrayList() var csfGlucoseData: List<BGDatum> = ArrayList()
var isfGlucoseData: List<BGDatum>? = ArrayList() var isfGlucoseData: List<BGDatum> = ArrayList()
var basalGlucoseData: List<BGDatum>? = ArrayList() var basalGlucoseData: List<BGDatum> = ArrayList()
var diaDeviations: List<DiaDatum> = ArrayList() var diaDeviations: List<DiaDeviation> = ArrayList()
var peakDeviations: List<PeakDatum> = ArrayList() var peakDeviations: List<PeakDeviation> = ArrayList()
var from: Long = 0 var from: Long = 0
lateinit var dateUtil: DateUtil lateinit var dateUtil: DateUtil
@ -22,7 +22,7 @@ class PreppedGlucose {
return toString(0) return toString(0)
} }
constructor(from: Long, crData: List<CRDatum>?, csfGlucoseData: List<BGDatum>?, isfGlucoseData: List<BGDatum>?, basalGlucoseData: List<BGDatum>?, dateUtil: DateUtil) { constructor(from: Long, crData: List<CRDatum>, csfGlucoseData: List<BGDatum>, isfGlucoseData: List<BGDatum>, basalGlucoseData: List<BGDatum>, dateUtil: DateUtil) {
this.from = from this.from = from
this.crData = crData this.crData = crData
this.csfGlucoseData = csfGlucoseData this.csfGlucoseData = csfGlucoseData
@ -34,10 +34,10 @@ class PreppedGlucose {
constructor(json: JSONObject?, dateUtil: DateUtil) { constructor(json: JSONObject?, dateUtil: DateUtil) {
if (json == null) return if (json == null) return
this.dateUtil = dateUtil this.dateUtil = dateUtil
crData = null crData = ArrayList()
csfGlucoseData = null csfGlucoseData = ArrayList()
isfGlucoseData = null isfGlucoseData = ArrayList()
basalGlucoseData = null basalGlucoseData = ArrayList()
try { try {
crData = JsonCRDataToList(json.getJSONArray("CRData")) crData = JsonCRDataToList(json.getJSONArray("CRData"))
csfGlucoseData = JsonGlucoseDataToList(json.getJSONArray("CSFGlucoseData")) csfGlucoseData = JsonGlucoseDataToList(json.getJSONArray("CSFGlucoseData"))
@ -76,19 +76,19 @@ class PreppedGlucose {
val json = JSONObject() val json = JSONObject()
try { try {
val crjson = JSONArray() val crjson = JSONArray()
for (crd in crData!!) { for (crd in crData) {
crjson.put(crd.toJSON()) crjson.put(crd.toJSON())
} }
val csfjson = JSONArray() val csfjson = JSONArray()
for (bgd in csfGlucoseData!!) { for (bgd in csfGlucoseData) {
csfjson.put(bgd.toJSON(true)) csfjson.put(bgd.toJSON(true))
} }
val isfjson = JSONArray() val isfjson = JSONArray()
for (bgd in isfGlucoseData!!) { for (bgd in isfGlucoseData) {
isfjson.put(bgd.toJSON(false)) isfjson.put(bgd.toJSON(false))
} }
val basaljson = JSONArray() val basaljson = JSONArray()
for (bgd in basalGlucoseData!!) { for (bgd in basalGlucoseData) {
basaljson.put(bgd.toJSON(false)) basaljson.put(bgd.toJSON(false))
} }
val diajson = JSONArray() val diajson = JSONArray()

View file

@ -533,6 +533,7 @@
<string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string> <string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string>
<string name="insulin_oref_peak">IOB Curve Peak Time</string> <string name="insulin_oref_peak">IOB Curve Peak Time</string>
<string name="insulin_peak_time">Peak Time [min]</string> <string name="insulin_peak_time">Peak Time [min]</string>
<string name="insulin_peak">Peak</string>
<string name="free_peak_oref">Free-Peak Oref</string> <string name="free_peak_oref">Free-Peak Oref</string>
<string name="rapid_acting_oref">Rapid-Acting Oref</string> <string name="rapid_acting_oref">Rapid-Acting Oref</string>
<string name="ultrarapid_oref">Ultra-Rapid Oref</string> <string name="ultrarapid_oref">Ultra-Rapid Oref</string>

View file

@ -4,7 +4,7 @@
<PreferenceCategory <PreferenceCategory
android:key="@string/key_autotune_plugin" android:key="@string/key_autotune_plugin"
android:title="@string/autotune_settings" android:title="@string/autotune_settings"
app:initialExpandedChildrenCount="0"> app:initialExpandedChildrenCount="10">
<SwitchPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="false"
@ -18,6 +18,12 @@
android:summary="@string/autotune_categorize_uam_as_basal_summary" android:summary="@string/autotune_categorize_uam_as_basal_summary"
android:title="@string/autotune_categorize_uam_as_basal_title" /> android:title="@string/autotune_categorize_uam_as_basal_title" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_autotune_tune_insulin_curve"
android:summary="@string/autotune_tune_insulin_curve_summary"
android:title="@string/autotune_tune_insulin_curve_title" />
<EditTextPreference <EditTextPreference
android:defaultValue="5" android:defaultValue="5"
android:inputType="number" android:inputType="number"

View file

@ -68,6 +68,7 @@
<string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string> <string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string>
<string name="key_autotune_auto" translatable="false">autotune_auto</string> <string name="key_autotune_auto" translatable="false">autotune_auto</string>
<string name="key_autotune_categorize_uam_as_basal" translatable="false">categorize_uam_as_basal</string> <string name="key_autotune_categorize_uam_as_basal" translatable="false">categorize_uam_as_basal</string>
<string name="key_autotune_tune_insulin_curve" translatable="false">autotune_tune_insulin_curve</string>
<string name="key_autotune_default_tune_days" translatable="false">autotune_default_tune_days</string> <string name="key_autotune_default_tune_days" translatable="false">autotune_default_tune_days</string>
<string name="key_autotune_circadian_ic_isf" translatable="false">autotune_circadian_ic_isf</string> <string name="key_autotune_circadian_ic_isf" translatable="false">autotune_circadian_ic_isf</string>
<string name="key_autotune_additional_log" translatable="false">autotune_additional_log</string> <string name="key_autotune_additional_log" translatable="false">autotune_additional_log</string>
@ -554,6 +555,8 @@
<string name="autotune_auto_summary">If enabled, Autotune will automatically update and switch to input profile after calculation from an automation rule.</string> <string name="autotune_auto_summary">If enabled, Autotune will automatically update and switch to input profile after calculation from an automation rule.</string>
<string name="autotune_categorize_uam_as_basal_title">Categorize UAM as basal</string> <string name="autotune_categorize_uam_as_basal_title">Categorize UAM as basal</string>
<string name="autotune_categorize_uam_as_basal_summary">Enable only if you have reliably entered all carbs eaten, with this option sudden rises seen by Autotune will be used to recommend changes to the basal rate.</string> <string name="autotune_categorize_uam_as_basal_summary">Enable only if you have reliably entered all carbs eaten, with this option sudden rises seen by Autotune will be used to recommend changes to the basal rate.</string>
<string name="autotune_tune_insulin_curve_title">Tune insulin curve</string>
<string name="autotune_tune_insulin_curve_summary">Enable only if you use free peak. This option will tune peak and DIA durations</string>
<string name="autotune_default_tune_days_title">Number of days of data</string> <string name="autotune_default_tune_days_title">Number of days of data</string>
<string name="autotune_circadian_ic_isf_title">Apply average result in circadian IC/ISF</string> <string name="autotune_circadian_ic_isf_title">Apply average result in circadian IC/ISF</string>
<string name="autotune_circadian_ic_isf_summary">Autotune will not tune circadian variations, this option only apply the average tuning of IC and ISF to your circadian input profile</string> <string name="autotune_circadian_ic_isf_summary">Autotune will not tune circadian variations, this option only apply the average tuning of IC and ISF to your circadian input profile</string>
@ -589,8 +592,9 @@
<string name="autotune_profile_invalid">Profile invalid</string> <string name="autotune_profile_invalid">Profile invalid</string>
<string name="autotune_log_title" translatable="false">|Param|Profile|Tuned|%/Miss.\n</string> <string name="autotune_log_title" translatable="false">|Param|Profile|Tuned|%/Miss.\n</string>
<string name="autotune_log_separator" translatable="false">+------------------------------------------\n</string> <string name="autotune_log_separator" translatable="false">+------------------------------------------\n</string>
<string name="autotune_log_isf" translatable="false">| %1$4.4s |\t%2$3.3f |\t%3$3.3f |\n</string> <string name="autotune_log_peak" translatable="false">| %1$4.4s |\t%2$d |\t%3$d |\n</string>
<string name="autotune_log_ic" translatable="false">| %1$4.4s |\t%2$3.3f |\t%3$3.3f |\n</string> <string name="autotune_log_dia" translatable="false">| %1$4.4s |\t%2$3.1f |\t%3$3.1f |\n</string>
<string name="autotune_log_ic_isf" translatable="false">| %1$4.4s | %2$3.3f |\t%3$3.3f |\n</string>
<string name="autotune_log_basal" translatable="false">|\t%1$02.0f\t| %2$3.3f |%3$3.3f\t| %5$.0f%% / %4$d\n</string> <string name="autotune_log_basal" translatable="false">|\t%1$02.0f\t| %2$3.3f |%3$3.3f\t| %5$.0f%% / %4$d\n</string>
<string name="autotune_log_sum_basal" translatable="false">|\t∑\t|\t%1$3.1f |\t%2$3.1f |\n</string> <string name="autotune_log_sum_basal" translatable="false">|\t∑\t|\t%1$3.1f |\t%2$3.1f |\n</string>
<string name="autotune_run_without_autoswitch">Autotune runned without profile switch</string> <string name="autotune_run_without_autoswitch">Autotune runned without profile switch</string>