-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
new sleep disturbance event #1861
base: master
Are you sure you want to change the base?
Changes from 3 commits
cf93c5b
52de3d9
182574f
c172adb
55763ca
e237896
e3fae38
71750e2
5c1dc72
c7629e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.hello.suripu.core.models.Events; | ||
|
||
import com.hello.suripu.core.models.Event; | ||
import com.hello.suripu.core.models.SleepSegment; | ||
import com.hello.suripu.core.translations.English; | ||
|
||
/** | ||
* Created by jarredheinrich on 1/19/17. | ||
*/ | ||
public class SleepDisturbanceEvent extends Event { | ||
private String description = English.SLEEP_DISTURBANCE_MESSAGE; | ||
private int sleepDepth = 0; | ||
public SleepDisturbanceEvent (final long startTimestamp, final long endTimestamp, final int offsetMillis, final int sleepDepth) { | ||
super(Type.SLEEP_DISTURBANCE, startTimestamp, endTimestamp, offsetMillis); | ||
this.sleepDepth = sleepDepth; | ||
} | ||
|
||
@Override | ||
public String getDescription(){ | ||
return this.description; | ||
} | ||
|
||
@Override | ||
public SleepSegment.SoundInfo getSoundInfo() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public int getSleepDepth() { | ||
return this.sleepDepth; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,9 +28,11 @@ public class English { | |
public final static String ALARM_NOT_SO_SMART_MESSAGE = "Your Smart Alarm rang at **%s**."; | ||
public final static String ALARM_SMART_MESSAGE = "Your Smart Alarm rang at **%s**.\nYou set it to wake you up by **%s**."; | ||
public final static String NOISE_MESSAGE = "There was a noise disturbance."; | ||
public final static String SLEEP_DISTURBANCE_MESSAGE = "Your sleep was interrupted."; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Has this been approved by product/copy/james? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checked it with copy/product. Consulting with Matt Walker following James' advices |
||
public final static String NULL_MESSAGE = ""; | ||
|
||
|
||
|
||
/* END Events Declaration */ | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
import com.hello.suripu.algorithm.sleep.scores.WaveAccumulateMotionScoreFunction; | ||
import com.hello.suripu.algorithm.sleep.scores.ZeroToMaxMotionCountDurationScoreFunction; | ||
import com.hello.suripu.algorithm.utils.MotionFeatures; | ||
import com.hello.suripu.core.algorithmintegration.OneDaysTrackerMotion; | ||
import com.hello.suripu.core.logging.LoggerWithSessionId; | ||
import com.hello.suripu.core.models.AgitatedSleep; | ||
import com.hello.suripu.core.models.AllSensorSampleList; | ||
|
@@ -32,6 +33,7 @@ | |
import com.hello.suripu.core.models.Events.NoiseEvent; | ||
import com.hello.suripu.core.models.Events.NullEvent; | ||
import com.hello.suripu.core.models.Events.OutOfBedEvent; | ||
import com.hello.suripu.core.models.Events.SleepDisturbanceEvent; | ||
import com.hello.suripu.core.models.Events.SleepingEvent; | ||
import com.hello.suripu.core.models.Events.WakeupEvent; | ||
import com.hello.suripu.core.models.Insight; | ||
|
@@ -1457,6 +1459,101 @@ public List<Event> getAlarmEvents(final List<RingTime> ringTimes, final DateTime | |
return events; | ||
} | ||
|
||
public List<Event> getSleepDisturbanceEvents(final OneDaysTrackerMotion oneDaysTrackerMotion, final Long sleepTime, final Long wakeTime, final TimeZoneOffsetMap timeZoneOffsetMap){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. describe algo briefly in comments |
||
final long sleepBuffer = 30 * DateTimeConstants.MILLIS_PER_MINUTE; | ||
final long wakeBuffer= 60 * DateTimeConstants.MILLIS_PER_MINUTE; | ||
List<TrackerMotion> trackerMotions = new ArrayList<>(); | ||
for(final TrackerMotion trackerMotion : oneDaysTrackerMotion.originalTrackerMotions){ | ||
if (trackerMotion.timestamp > sleepTime + sleepBuffer && trackerMotion.timestamp < wakeTime - wakeBuffer){ | ||
trackerMotions.add(trackerMotion); | ||
} | ||
} | ||
final HashMap<Long, Long> sleepDisturbanceTimeStamps= getSleepDisturbances(trackerMotions); | ||
final List<Event> sleepDisturbanceEvents = new ArrayList<>(); | ||
for (final long sleepDisturbanceStartTS : sleepDisturbanceTimeStamps.keySet()){ | ||
final SleepDisturbanceEvent sleepDisturbanceEvent = new SleepDisturbanceEvent(sleepDisturbanceStartTS, sleepDisturbanceTimeStamps.get(sleepDisturbanceStartTS), timeZoneOffsetMap.getOffsetWithDefaultAsZero(sleepDisturbanceStartTS), 0); | ||
sleepDisturbanceEvents.add(sleepDisturbanceEvent); | ||
} | ||
|
||
return sleepDisturbanceEvents; | ||
} | ||
|
||
//Finds intances where the pill recorded 15seconds + of motion within a 4 minute window | ||
public HashMap<Long, Long> getSleepDisturbances(final List<TrackerMotion> trackerMotions){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
|
||
// computes periods of agitated sleep using on duration. Over 16 seconds of movement within a two minute window initiates a state of agitated sleep that persists until there is a 4 minute window with no motion | ||
final int onDurationSumThreshold = 15; //secs | ||
final long noMotionThreshold = DateTimeConstants.MILLIS_PER_MINUTE * 10; | ||
final long timeWindow = DateTimeConstants.MILLIS_PER_MINUTE * 4; //4 minutes - finds consecutive minutes with some flexibility | ||
final int maxDisturbanceCount = 5; //max number of sleep disturbances to report during night | ||
long currentTS = 0L; | ||
long currentDisturbanceStartTS ; | ||
long currentDisturbanceEndTS ; | ||
long previousDisturbanceStartTS = 0L; | ||
long previousDisturbanceEndTS; | ||
long previousMotionTS = 0L; | ||
|
||
List<TrackerMotion> trackerMotionWindowCurrent = new ArrayList<>(); | ||
final HashMap<Long,Integer> sleepDisturbances = new HashMap<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
final HashMap<Long, Long> sleepDisturbanceWindows = new HashMap<>(); | ||
boolean currentlyDisturbed = false; | ||
|
||
for (TrackerMotion trackerMotionCurrent : trackerMotions) { | ||
final List<TrackerMotion> trackerMotionWindowPrevious = trackerMotionWindowCurrent; | ||
trackerMotionWindowCurrent = new ArrayList<>(); | ||
trackerMotionWindowCurrent.add(trackerMotionCurrent); | ||
previousMotionTS = currentTS; | ||
currentTS = trackerMotionCurrent.timestamp; | ||
currentDisturbanceStartTS = currentTS; | ||
|
||
currentDisturbanceEndTS = currentTS; | ||
int onDurationSum = trackerMotionCurrent.onDurationInSeconds.intValue(); | ||
if (!trackerMotionWindowPrevious.isEmpty()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that some of this would benefit from being refactored. It's a little too much of twisted logic to be easily readable. |
||
for (final TrackerMotion trackerMotionPrevious : trackerMotionWindowPrevious) { | ||
if (currentTS - trackerMotionPrevious.timestamp <= timeWindow) { | ||
trackerMotionWindowCurrent.add(trackerMotionPrevious); | ||
onDurationSum += trackerMotionPrevious.onDurationInSeconds.intValue(); | ||
currentDisturbanceEndTS = Math.max(currentDisturbanceEndTS, trackerMotionPrevious.timestamp); | ||
currentDisturbanceStartTS = Math.min(currentDisturbanceStartTS, trackerMotionPrevious.timestamp); | ||
} | ||
} | ||
} | ||
if (onDurationSum > onDurationSumThreshold){ | ||
if (!currentlyDisturbed) { | ||
currentlyDisturbed = true; | ||
if (!sleepDisturbances.containsKey(currentDisturbanceStartTS)) { | ||
sleepDisturbances.put(currentDisturbanceStartTS, onDurationSum); | ||
previousDisturbanceStartTS = currentDisturbanceStartTS; | ||
} | ||
} | ||
previousDisturbanceEndTS = currentDisturbanceEndTS; | ||
sleepDisturbanceWindows.put(previousDisturbanceStartTS, previousDisturbanceEndTS); | ||
}else if (currentTS - previousMotionTS > noMotionThreshold && currentlyDisturbed){ | ||
sleepDisturbanceWindows.put(previousDisturbanceStartTS, previousMotionTS); | ||
currentlyDisturbed = false; | ||
} | ||
|
||
} | ||
|
||
if (sleepDisturbances.size() > maxDisturbanceCount){ | ||
final int discardCount = sleepDisturbances.size() - maxDisturbanceCount; | ||
final List<Long> tsKeys = new ArrayList<>(sleepDisturbances.keySet()); | ||
final List<Integer> onDurationSums = new ArrayList<>(sleepDisturbances.values()); | ||
Collections.sort(onDurationSums); | ||
final int minOnDuration = onDurationSums.get(discardCount); | ||
int removedCount = 0; | ||
for (long tsKey : tsKeys){ | ||
if (sleepDisturbances.get(tsKey)< minOnDuration){ | ||
sleepDisturbanceWindows.remove(tsKey); | ||
removedCount +=1; | ||
} | ||
} | ||
} | ||
//possible for two sleep disturbances to have the same onDurationSum, in this case it is possible to have > 5 sleepDisturbances; | ||
return sleepDisturbanceWindows; | ||
} | ||
|
||
|
||
public List<Event> eventsFromOptionalEvents(final List<Optional<Event>> optionalEvents) { | ||
final List<Event> events = Lists.newArrayList(); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't change the enum values if you can avoid it.