Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Torena to Somnopose data convertor for OSCAR
Notes:
https://nelsonslog.wordpress.com/2021/07/21/sleep-position-tracking-and-oscar/
http://www.apneaboard.com/forums/Thread-Torena-sleep-position-monitor-for-Android-similar-to-SomnoPose
import json, sys, csv
"""Quick and dirty convertor from Torena JSON export format to Somnapose CSV.
Written to get the data in to OSCAR.
A better solution would be to write a Torena importer for OSCAR.
Also it'd be nice if Torena exported raw orientation data like Somnapose does.
TODO: figure out some nice solution for the GotUp segments
References:
https://torena.koalative.com/
http://www.proximalbox.com/somnopose/help/export.html
https://gitlab.com/pholy/OSCAR-code/-/blob/master/oscar/SleepLib/loader_plugins/somnopose_loader.cpp
"""
# Load the JSON data from the exported file
data = json.load(open('torena.export-2021-07-21-09-44-49.json'))
data = data[1] # file stores multiple sessions? Selecting one from my sample.
# Load the JSON data from Torena and strip out timestamps and positions.
# { "position": "Right", "positionEndTimestamp": 1626873212, "positionStartTimestamp": 1626873071 }
rows = []
for p in data['positions']:
rows.append((p['positionStartTimestamp'], p['positionEndTimestamp'], p['position']))
rows.sort() # probably unnecessary
# Convert the timestamps and positions to Somnapose-style data
# Timestamp,Orientation,Inclination,Time_of_day,Date
# 329027345.46,114.47,80.18,23:29:05
position_to_orientation = { 'Back': '180', 'Left': '-90', 'Right': '90', 'Stomach': '0', 'GotUp': '0' }
last_orientation = None
last_inclination = None
csvdata = []
for r in rows:
ios_timestamp = r[0] - 978307200 # convert Unix epoch to iOS epoch
orientation = position_to_orientation[r[2]]
inclination = '90'
if r[2] == 'GotUp':
inclination = '0'
# Write an end event for the old position so the graph looks like square waves, not sawtooth.
if last_orientation is not None and last_inclination is not None:
csvdata.append((ios_timestamp, last_orientation, last_inclination))
csvdata.append((ios_timestamp, orientation, inclination))
last_orientation = orientation
last_inclination = inclination
# And print out the CSV
# Skip the redundant Time_of_day and Date columns; OSCAR ignores them anyway.
writer = csv.writer(sys.stdout, lineterminator='\n')
writer.writerow(('Timestamp','Orientation','Inclination'))
writer.writerows(csvdata)
Timestamp Orientation Inclination
648536914 0 90
648536919 0 90
648536919 0 0
648536934 0 0
648536934 0 90
648536939 0 90
648536939 0 0
648536944 0 0
648536944 0 90
648536964 0 90
648536964 0 0
648536974 0 0
648536974 -90 90
648537295 -90 90
648537295 180 90
648537330 180 90
648537330 -90 90
648537791 -90 90
648537791 180 90
648537871 180 90
648537871 -90 90
648542806 -90 90
648542806 180 90
648543112 180 90
648543112 90 90
648547850 90 90
648547850 180 90
648549264 180 90
648549264 90 90
648553545 90 90
648553545 180 90
648554939 180 90
648554939 90 90
648558495 90 90
648558495 0 0
648558686 0 0
648558686 180 90
648558706 180 90
648558706 0 0
648558711 0 0
648558711 180 90
648558726 180 90
648558726 0 0
648558731 0 0
648558731 90 90
648560489 90 90
648560489 180 90
648560527 180 90
648560527 -90 90
648563600 -90 90
648563600 180 90
648565871 180 90
648565871 90 90
648566012 90 90
648566012 0 0
648566082 0 0
648566082 180 90
[{"alarmDelay":0,"alarmEnabled":false,"alarmPosition":"Back","alarmRingtone":"None","alarmVibration":"None","degreesBackFrom":40,"degreesBackTo":125,"degreesGotUpFrom":40,"degreesLeftFrom":-40,"degreesLeftTo":40,"degreesRightFrom":125,"degreesRightTo":-125,"degreesStomachFrom":-125,"degreesStomachTo":-40,"positions":[{"position":"Stomach","positionEndTimestamp":1626814371,"positionStartTimestamp":1626814366},{"position":"Right","positionEndTimestamp":1626814381,"positionStartTimestamp":1626814371},{"position":"Back","positionEndTimestamp":1626814391,"positionStartTimestamp":1626814381},{"position":"Left","positionEndTimestamp":1626814401,"positionStartTimestamp":1626814391},{"position":"Back","positionEndTimestamp":1626814406,"positionStartTimestamp":1626814401},{"position":"Left","positionEndTimestamp":1626814662,"positionStartTimestamp":1626814406},{"position":"Right","positionEndTimestamp":1626815708,"positionStartTimestamp":1626814662},{"position":"Back","positionEndTimestamp":1626815713,"positionStartTimestamp":1626815708},{"position":"Left","positionEndTimestamp":1626815858,"positionStartTimestamp":1626815713},{"position":"Back","positionEndTimestamp":1626816008,"positionStartTimestamp":1626815858},{"position":"Right","positionEndTimestamp":1626816012,"positionStartTimestamp":1626816008}],"screenFacing":"Up","screenRotation":"LandscapeTopLeft","sessionEndTimestamp":1626816012,"sessionId":1,"sessionStartTimestamp":1626814366,"trackingDelay":0},{"alarmDelay":0,"alarmEnabled":false,"alarmPosition":"Back","alarmRingtone":"None","alarmVibration":"None","degreesBackFrom":40,"degreesBackTo":125,"degreesGotUpFrom":40,"degreesLeftFrom":-40,"degreesLeftTo":40,"degreesRightFrom":125,"degreesRightTo":-125,"degreesStomachFrom":-125,"degreesStomachTo":-40,"positions":[{"position":"Stomach","positionEndTimestamp":1626844119,"positionStartTimestamp":1626844114},{"position":"GotUp","positionEndTimestamp":1626844134,"positionStartTimestamp":1626844119},{"position":"Stomach","positionEndTimestamp":1626844139,"positionStartTimestamp":1626844134},{"position":"GotUp","positionEndTimestamp":1626844144,"positionStartTimestamp":1626844139},{"position":"Stomach","positionEndTimestamp":1626844164,"positionStartTimestamp":1626844144},{"position":"GotUp","positionEndTimestamp":1626844174,"positionStartTimestamp":1626844164},{"position":"Left","positionEndTimestamp":1626844495,"positionStartTimestamp":1626844174},{"position":"Back","positionEndTimestamp":1626844530,"positionStartTimestamp":1626844495},{"position":"Left","positionEndTimestamp":1626844991,"positionStartTimestamp":1626844530},{"position":"Back","positionEndTimestamp":1626845071,"positionStartTimestamp":1626844991},{"position":"Left","positionEndTimestamp":1626850006,"positionStartTimestamp":1626845071},{"position":"Back","positionEndTimestamp":1626850312,"positionStartTimestamp":1626850006},{"position":"Right","positionEndTimestamp":1626855050,"positionStartTimestamp":1626850312},{"position":"Back","positionEndTimestamp":1626856464,"positionStartTimestamp":1626855050},{"position":"Right","positionEndTimestamp":1626860745,"positionStartTimestamp":1626856464},{"position":"Back","positionEndTimestamp":1626862139,"positionStartTimestamp":1626860745},{"position":"Right","positionEndTimestamp":1626865695,"positionStartTimestamp":1626862139},{"position":"GotUp","positionEndTimestamp":1626865886,"positionStartTimestamp":1626865695},{"position":"Back","positionEndTimestamp":1626865906,"positionStartTimestamp":1626865886},{"position":"GotUp","positionEndTimestamp":1626865911,"positionStartTimestamp":1626865906},{"position":"Back","positionEndTimestamp":1626865926,"positionStartTimestamp":1626865911},{"position":"GotUp","positionEndTimestamp":1626865931,"positionStartTimestamp":1626865926},{"position":"Right","positionEndTimestamp":1626867689,"positionStartTimestamp":1626865931},{"position":"Back","positionEndTimestamp":1626867727,"positionStartTimestamp":1626867689},{"position":"Left","positionEndTimestamp":1626870800,"positionStartTimestamp":1626867727},{"position":"Back","positionEndTimestamp":1626873071,"positionStartTimestamp":1626870800},{"position":"Right","positionEndTimestamp":1626873212,"positionStartTimestamp":1626873071},{"position":"GotUp","positionEndTimestamp":1626873282,"positionStartTimestamp":1626873212},{"position":"Back","positionEndTimestamp":1626873288,"positionStartTimestamp":1626873282}],"screenFacing":"Up","screenRotation":"LandscapeTopLeft","sessionEndTimestamp":1626873288,"sessionId":2,"sessionStartTimestamp":1626844114,"trackingDelay":0}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment