diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index f8c7e844f7..ef77072a40 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -215,6 +215,10 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain return activePump; } + public static SensitivityInterface getActiveSensitivity() { + return activeSensitivity; + } + void logPluginStatus() { for (PluginBase p : pluginList) { log.debug(p.getName() + ":" + diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java index 8f15653135..9b0aa3749a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -7,11 +7,11 @@ import java.util.Date; */ public class AutosensData { - long time = 0L; + public long time = 0L; public String pastSensitivity = ""; public double deviation = 0d; boolean nonCarbsDeviation = false; - boolean nonEqualDeviation = false; + public boolean nonEqualDeviation = false; double absorbed = 0d; public double carbsFromBolus = 0d; public double cob = 0; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index c6a58edf30..12bdad5674 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -29,6 +29,7 @@ import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventNewBasalProfile; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.BasalData; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; @@ -55,9 +56,9 @@ public class IobCobCalculatorPlugin implements PluginBase { private static Handler sHandler = null; private static HandlerThread sHandlerThread = null; - private static Object dataLock = new Object(); + private static final Object dataLock = new Object(); - static IobCobCalculatorPlugin plugin = null; + private static IobCobCalculatorPlugin plugin = null; public static IobCobCalculatorPlugin getPlugin() { if (plugin == null) @@ -65,6 +66,14 @@ public class IobCobCalculatorPlugin implements PluginBase { return plugin; } + public static LongSparseArray getAutosensDataTable() { + return autosensDataTable; + } + + public static List getBucketedData() { + return bucketed_data; + } + @Override public int getType() { return GENERAL; @@ -120,7 +129,7 @@ public class IobCobCalculatorPlugin implements PluginBase { } - public IobCobCalculatorPlugin() { + IobCobCalculatorPlugin() { MainApp.bus().register(this); if (sHandlerThread == null) { sHandlerThread = new HandlerThread(IobCobCalculatorPlugin.class.getSimpleName()); @@ -198,14 +207,14 @@ public class IobCobCalculatorPlugin implements PluginBase { } } - public void createBucketedData() { + private void createBucketedData() { if (isAbout5minData()) createBucketedData5min(); else createBucketedDataRecalculated(); } - public void createBucketedDataRecalculated() { + private void createBucketedDataRecalculated() { synchronized (dataLock) { if (bgReadings == null || bgReadings.size() < 3) { bucketed_data = null; @@ -310,7 +319,7 @@ public class IobCobCalculatorPlugin implements PluginBase { //log.debug("Releasing createBucketedData"); } - public void calculateSensitivityData() { + private void calculateSensitivityData() { if (MainApp.getConfigBuilder() == null) return; // app still initializing //log.debug("Locking calculateSensitivityData"); @@ -523,106 +532,10 @@ public class IobCobCalculatorPlugin implements PluginBase { } } - public static AutosensResult detectSensitivity(long fromTime, long toTime) { - String age = SP.getString(R.string.key_age, ""); - int defaultHours = 24; - if (age.equals(MainApp.sResources.getString(R.string.key_adult))) defaultHours = 24; - if (age.equals(MainApp.sResources.getString(R.string.key_teenage))) defaultHours = 4; - if (age.equals(MainApp.sResources.getString(R.string.key_child))) defaultHours = 4; - int hoursForDetection = SP.getInt(R.string.key_openapsama_autosens_period, defaultHours); - - long now = System.currentTimeMillis(); - - if (autosensDataTable == null || autosensDataTable.size() < 4) { - log.debug("No autosens data available"); - return new AutosensResult(); - } - - AutosensData current = getAutosensData(toTime); - if (current == null) { - log.debug("No autosens data available"); - return new AutosensResult(); - } - - - List deviationsArray = new ArrayList<>(); - String pastSensitivity = ""; - int index = 0; - while (index < autosensDataTable.size()) { - AutosensData autosensData = autosensDataTable.valueAt(index); - - if (autosensData.time < fromTime) { - index++; - continue; - } - - if (autosensData.time > toTime) { - index++; - continue; - } - - if (autosensData.time > now - hoursForDetection * 60 * 60 * 1000L) - deviationsArray.add(autosensData.nonEqualDeviation ? autosensData.deviation : 0d); - if (deviationsArray.size() > hoursForDetection * 60 / 5) - deviationsArray.remove(0); - - - pastSensitivity += autosensData.pastSensitivity; - int secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time); - if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) { - pastSensitivity += "(" + Math.round(secondsFromMidnight / 3600d) + ")"; - } - index++; - } - - Double[] deviations = new Double[deviationsArray.size()]; - deviations = deviationsArray.toArray(deviations); - - Profile profile = MainApp.getConfigBuilder().getProfile(); - - double sens = profile.getIsf(); - - String ratioLimit = ""; - String sensResult = ""; - - log.debug("Records: " + index + " " + pastSensitivity); - Arrays.sort(deviations); - - double percentile = percentile(deviations, 0.50); - double basalOff = percentile * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); - double ratio = 1 + (basalOff / profile.getMaxDailyBasal()); - - if (percentile < 0) { // sensitive - sensResult = "Excess insulin sensitivity detected"; - } else if (percentile > 0) { // resistant - sensResult = "Excess insulin resistance detected"; - } else { - sensResult = "Sensitivity normal"; - } - - log.debug(sensResult); - - double rawRatio = ratio; - ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString("openapsama_autosens_min", "0.7"))); - ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString("openapsama_autosens_max", "1.2"))); - - if (ratio != rawRatio) { - ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; - log.debug(ratioLimit); - } - - log.error("Sensitivity to: " + new Date(toTime).toLocaleString() + " percentile: " + percentile); - - AutosensResult output = new AutosensResult(); - output.ratio = Round.roundTo(ratio, 0.01); - output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); - output.pastSensitivity = pastSensitivity; - output.ratioLimit = ratioLimit; - output.sensResult = sensResult; - return output; + private static AutosensResult detectSensitivity(long fromTime, long toTime) { + return ConfigBuilderPlugin.getActiveSensitivity().detectSensitivity(fromTime, toTime); } - public static JSONArray convertToJSONArray(IobTotal[] iobArray) { JSONArray array = new JSONArray(); for (int i = 0; i < iobArray.length; i++) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityMK/SensitivityMKPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityMK/SensitivityMKPlugin.java index 210614714f..ac7f8ccb86 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityMK/SensitivityMKPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityMK/SensitivityMKPlugin.java @@ -1,16 +1,34 @@ package info.nightscout.androidaps.plugins.SensitivityMK; +import android.support.v4.util.LongSparseArray; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.SensitivityInterface; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; +import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.utils.Round; +import info.nightscout.utils.SP; +import info.nightscout.utils.SafeParse; /** * Created by mike on 24.06.2017. */ public class SensitivityMKPlugin implements PluginBase, SensitivityInterface{ + private static Logger log = LoggerFactory.getLogger(SensitivityMKPlugin.class); + private static boolean fragmentEnabled = true; private static boolean fragmentVisible = false; @@ -80,6 +98,103 @@ public class SensitivityMKPlugin implements PluginBase, SensitivityInterface{ @Override public AutosensResult detectSensitivity(long fromTime, long toTime) { - return null; + LongSparseArray autosensDataTable = IobCobCalculatorPlugin.getAutosensDataTable(); + + String age = SP.getString(R.string.key_age, ""); + int defaultHours = 24; + if (age.equals(MainApp.sResources.getString(R.string.key_adult))) defaultHours = 24; + if (age.equals(MainApp.sResources.getString(R.string.key_teenage))) defaultHours = 4; + if (age.equals(MainApp.sResources.getString(R.string.key_child))) defaultHours = 4; + int hoursForDetection = SP.getInt(R.string.key_openapsama_autosens_period, defaultHours); + + long now = System.currentTimeMillis(); + + if (autosensDataTable == null || autosensDataTable.size() < 4) { + log.debug("No autosens data available"); + return new AutosensResult(); + } + + AutosensData current = IobCobCalculatorPlugin.getAutosensData(toTime); + if (current == null) { + log.debug("No autosens data available"); + return new AutosensResult(); + } + + + List deviationsArray = new ArrayList<>(); + String pastSensitivity = ""; + int index = 0; + while (index < autosensDataTable.size()) { + AutosensData autosensData = autosensDataTable.valueAt(index); + + if (autosensData.time < fromTime) { + index++; + continue; + } + + if (autosensData.time > toTime) { + index++; + continue; + } + + if (autosensData.time > now - hoursForDetection * 60 * 60 * 1000L) + deviationsArray.add(autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + if (deviationsArray.size() > hoursForDetection * 60 / 5) + deviationsArray.remove(0); + + + pastSensitivity += autosensData.pastSensitivity; + int secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time); + if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) { + pastSensitivity += "(" + Math.round(secondsFromMidnight / 3600d) + ")"; + } + index++; + } + + Double[] deviations = new Double[deviationsArray.size()]; + deviations = deviationsArray.toArray(deviations); + + Profile profile = MainApp.getConfigBuilder().getProfile(); + + double sens = profile.getIsf(); + + String ratioLimit = ""; + String sensResult = ""; + + log.debug("Records: " + index + " " + pastSensitivity); + Arrays.sort(deviations); + + double percentile = IobCobCalculatorPlugin.percentile(deviations, 0.50); + double basalOff = percentile * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + double ratio = 1 + (basalOff / profile.getMaxDailyBasal()); + + if (percentile < 0) { // sensitive + sensResult = "Excess insulin sensitivity detected"; + } else if (percentile > 0) { // resistant + sensResult = "Excess insulin resistance detected"; + } else { + sensResult = "Sensitivity normal"; + } + + log.debug(sensResult); + + double rawRatio = ratio; + ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString("openapsama_autosens_min", "0.7"))); + ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString("openapsama_autosens_max", "1.2"))); + + if (ratio != rawRatio) { + ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; + log.debug(ratioLimit); + } + + log.error("Sensitivity to: " + new Date(toTime).toLocaleString() + " percentile: " + percentile); + + AutosensResult output = new AutosensResult(); + output.ratio = Round.roundTo(ratio, 0.01); + output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); + output.pastSensitivity = pastSensitivity; + output.ratioLimit = ratioLimit; + output.sensResult = sensResult; + return output; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java index 672e878acb..5cf74be25d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java @@ -1,16 +1,33 @@ package info.nightscout.androidaps.plugins.SensitivityOref0; +import android.support.v4.util.LongSparseArray; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.SensitivityInterface; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; +import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.utils.Round; +import info.nightscout.utils.SP; +import info.nightscout.utils.SafeParse; /** * Created by mike on 24.06.2017. */ -public class SensitivityOref0Plugin implements PluginBase, SensitivityInterface{ +public class SensitivityOref0Plugin implements PluginBase, SensitivityInterface { + private static Logger log = LoggerFactory.getLogger(IobCobCalculatorPlugin.class); + private static boolean fragmentEnabled = true; private static boolean fragmentVisible = false; @@ -80,6 +97,114 @@ public class SensitivityOref0Plugin implements PluginBase, SensitivityInterface{ @Override public AutosensResult detectSensitivity(long fromTime, long toTime) { - return null; + LongSparseArray autosensDataTable = IobCobCalculatorPlugin.getAutosensDataTable(); + + String age = SP.getString(R.string.key_age, ""); + int defaultHours = 24; + if (age.equals(MainApp.sResources.getString(R.string.key_adult))) defaultHours = 24; + if (age.equals(MainApp.sResources.getString(R.string.key_teenage))) defaultHours = 24; + if (age.equals(MainApp.sResources.getString(R.string.key_child))) defaultHours = 24; + int hoursForDetection = SP.getInt(R.string.key_openapsama_autosens_period, defaultHours); + + long now = System.currentTimeMillis(); + + if (autosensDataTable == null || autosensDataTable.size() < 4) { + log.debug("No autosens data available"); + return new AutosensResult(); + } + + AutosensData current = IobCobCalculatorPlugin.getLastAutosensData(); + if (current == null) { + log.debug("No current autosens data available"); + return new AutosensResult(); + } + + + List deviationsArray = new ArrayList<>(); + String pastSensitivity = ""; + int index = 0; + while (index < autosensDataTable.size()) { + AutosensData autosensData = autosensDataTable.valueAt(index); + + if (autosensData.time < fromTime) { + index++; + continue; + } + + if (autosensData.time > toTime) { + index++; + continue; + } + + if (autosensData.time > now - hoursForDetection * 60 * 60 * 1000L) + deviationsArray.add(autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + if (deviationsArray.size() > hoursForDetection * 60 / 5) + deviationsArray.remove(0); + + pastSensitivity += autosensData.pastSensitivity; + int secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time); + if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) { + pastSensitivity += "(" + Math.round(secondsFromMidnight / 3600d) + ")"; + } + index++; + } + + Double[] deviations = new Double[deviationsArray.size()]; + deviations = deviationsArray.toArray(deviations); + + Profile profile = MainApp.getConfigBuilder().getProfile(); + + double sens = profile.getIsf(); + + double ratio = 1; + String ratioLimit = ""; + String sensResult = ""; + + log.debug("Records: " + index + " " + pastSensitivity); + Arrays.sort(deviations); + + for (double i = 0.9; i > 0.1; i = i - 0.02) { + if (IobCobCalculatorPlugin.percentile(deviations, (i + 0.02)) >= 0 && IobCobCalculatorPlugin.percentile(deviations, i) < 0) { + log.debug(Math.round(100 * i) + "% of non-meal deviations negative (target 45%-50%)"); + } + } + double pSensitive = IobCobCalculatorPlugin.percentile(deviations, 0.50); + double pResistant = IobCobCalculatorPlugin.percentile(deviations, 0.45); + + double basalOff = 0; + + if (pSensitive < 0) { // sensitive + basalOff = pSensitive * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + sensResult = "Excess insulin sensitivity detected"; + } else if (pResistant > 0) { // resistant + basalOff = pResistant * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + sensResult = "Excess insulin resistance detected"; + } else { + sensResult = "Sensitivity normal"; + } + log.debug(sensResult); + ratio = 1 + (basalOff / profile.getMaxDailyBasal()); + + double rawRatio = ratio; + ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString("openapsama_autosens_min", "0.7"))); + ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString("openapsama_autosens_max", "1.2"))); + + if (ratio != rawRatio) { + ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; + log.debug(ratioLimit); + } + + double newisf = Math.round(Profile.toMgdl(sens, profile.getUnits()) / ratio); + if (ratio != 1) { + log.debug("ISF adjusted from " + Profile.toMgdl(sens, profile.getUnits()) + " to " + newisf); + } + + AutosensResult output = new AutosensResult(); + output.ratio = Round.roundTo(ratio, 0.01); + output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); + output.pastSensitivity = pastSensitivity; + output.ratioLimit = ratioLimit; + output.sensResult = sensResult; + return output; } }