Recording real-time data into .CSV files...

English

I have created a program that captures all of the information coming from the Zeo and stores it into .csv files in real time.  I have already used this on 3 of my 20 minute naps and on one of my 3 hour core sleeps.  It seems to be exactly what I want!

The data from those sessions can be found here and here.

Note that the files for the raw samples can be quite big and might choke spreadsheeting programs.  I am tempted to output these in a binary format instead, since you'd probably want to use a dedicated program to view them anyways.  I considered using an audio format (like .WAV), but the format would need to support some means of timestamping and would also need to not grow in size when many zeros are appended to it.  Using an audio file for this output would allow people a cheap way to view/edit the raw samples by using existing audio editing software.  Besides, the idea of listening to my own sleep patterns sounds just plain cool.

Here is a hypnogram from one of my naps:

Hypnogram

Here is a spectrogram from the same nap:

Spectrogram

Now, correct me if I'm wrong, but it is impossible to obtain this spectrogram from the Data Decoder Library alone.  As far as I can tell, the DDL doesn't offer frequency data.  Otherwise, the raw samples would be needed as input to a fourier transform to get all of the frequency information.

...

Yo polyphasers: I heard you like REM sleep, so I put some naps in your naps so you can dream while you dream!

I have written another program that will wake you up after 2 consecutive minutes of light sleep.  
When I was looking at my data from the .csv files, I noticed that I was actually going into REM for a short period of time during the naps that seemed like they were only light sleep.  It made me wonder: what if I interrupted the light sleep by waking up, then went back to sleep immediately?  Would I get more REM?  Coincidentally I had a tickle in my throat partway through my next nap.  It woke me up during light sleep.  Lo and behold, I ended up with more REM sleep than I would have if I just stayed in light sleep.  The data that shows this is the hypnogram for nap 2 on 2010-11-14.  Now I intend to see if this can be leveraged in a mechanical way by using the Zeo and RDL to force these kinds of interruptions.  I will try to put more naps into my naps!

Unfortunately this forum software doesn't allow me to create attachments, so I'll just put the code in the message (sorry) :

#!/usr/bin/python -d
# -*- coding: utf-8 -*-

# DataRecorder.pyw
#
# Streams the real-time data coming from a Zeo unit into .csv files.

import sys
import time
import csv
import os
from serial import *
from glob import glob
from ZeoRawData import BaseLink, Parser

def scanPorts():
    portList = []
    
    # Helps find USB>Serial converters on linux
    for p in glob('/dev/ttyUSB*'):
        portList.append(p)
    #Linux and Windows
    for i in range(256):
        try:
            ser = Serial(i)
            if ser.isOpen():
                #Check that the port is actually valid
                #Otherwise invalid /dev/ttyS* ports may be added
                portList.append(ser.portstr)
            ser.close()
        except SerialException:
            pass

    return portList

class ZeoToCSV:
    def __init__(self, parent=None):
        samplesFileName = 'raw_samples.csv'
        sgramFileName = 'spectrogram.csv'
        hgramFileName = 'hypnogram.csv'
        eventsFileName = 'events.csv'
        
        self.hypToHeight = {'Undefined' : 0,
                            'Deep'      : 1,
                            'Light'     : 2,
                            'REM'       : 3,
                            'Awake'     : 4}
        
        # Only create headers when the files are being created for the first time.
        # After that, all new data should be appended to the existing files.
        samplesNeedHeader = False
        sgramNeedHeader = False
        hgramNeedHeader = False
        eventsNeedHeader = False
        
        if not os.path.isfile(samplesFileName):
            samplesNeedHeader = True
        
        if not os.path.isfile(sgramFileName):
            sgramNeedHeader = True
        
        if not os.path.isfile(hgramFileName):
            hgramNeedHeader = True
    
        if not os.path.isfile(eventsFileName):
            eventsNeedHeader = True
        
        self.rawSamples = csv.writer(open(samplesFileName, 'a+b'), delimiter=',',
                                quotechar='"', quoting=csv.QUOTE_MINIMAL)
        self.spectrogram = csv.writer(open(sgramFileName, 'a+b'), delimiter=',',
                                quotechar='"', quoting=csv.QUOTE_MINIMAL)
        self.hypnogram = csv.writer(open(hgramFileName, 'a+b'), delimiter=',',
                                quotechar='"', quoting=csv.QUOTE_MINIMAL)
        self.eventsOut = csv.writer(open(eventsFileName, 'a+b'), delimiter=',',
                                quotechar='"', quoting=csv.QUOTE_MINIMAL)
        
        if samplesNeedHeader:
            self.rawSamples.writerow(["Time Stamp","Version","SQI","Impedance","Bad Signal (Y/N)","Voltage (uV)"])
        
        if sgramNeedHeader:
            self.spectrogram.writerow(["Time Stamp","Version","SQI","Impedance","Bad Signal (Y/N)",
                                    "2-4 Hz","4-8 Hz","8-13 Hz","11-14 Hz","13-18 Hz","18-21 Hz","30-50 Hz"])
        
        if hgramNeedHeader:
            self.hypnogram.writerow(["Time Stamp","Version","SQI","Impedance","Bad Signal (Y/N)","State (0-4)","State (named)"])
        
        if eventsNeedHeader:
            self.eventsOut.writerow(["Time Stamp","Version","Event"])
    
    def updateSlice(self, slice):
        
        timestamp = slice['ZeoTimestamp']
        ver = slice['Version']
                                        
        if not slice['SQI'] == None:
            sqi = str(slice['SQI'])
        else:
            sqi = '--'
                                        
        if not slice['Impedance'] == None:
            imp = str(int(slice['Impedance']))
        else:
            imp = '--'

        if slice['BadSignal']:
            badSignal = 'Y'
        else:
            badSignal = 'N'

        if not slice['Waveform'] == []:
            self.rawSamples.writerow([timestamp,ver,sqi,imp,badSignal] + slice['Waveform'])

        if len(slice['FrequencyBins'].values()) == 7:
            f = slice['FrequencyBins']
            bins = [f['2-4'],f['4-8'],f['8-13'],f['11-14'],f['13-18'],f['18-21'],f['30-50']]
            self.spectrogram.writerow([timestamp,ver,sqi,imp,badSignal] + bins)

        if not slice['SleepStage'] == None:
            stage = slice['SleepStage']
            self.hypnogram.writerow([timestamp,ver,sqi,imp,badSignal] +
                                     [self.hypToHeight[stage],str(stage)])
    
    def updateEvent(self, timestamp, version, event):
        self.eventsOut.writerow([timestamp,version,event])

if __name__ == '__main__':
    
    # Find ports.
    # TODO: offer a command line option for selecting ports.
    ports = scanPorts()
    if len(ports) > 0 :
        print "Found the following ports:"
        for port in ports:
            print port
        print ""
        print "Using port "+ports[0]
        print ""
        portStr = ports[0]
    else:
        sys.exit("No serial ports found.")
    
    # Initialize
    output = ZeoToCSV()
    link = BaseLink.BaseLink(portStr)
    parser = Parser.Parser()
    # Add callbacks
    link.addCallback(parser.update)
    parser.addEventCallback(output.updateEvent)
    parser.addSliceCallback(output.updateSlice)
    # Start Link
    link.start()
    
    # TODO: perhaps use a more forgiving key?  This would require polling the keyboard without blocking.
    print "Hit ctrl-C at any time to stop."
    while True:
        time.sleep(5)
    
    sys.exit()

--------------------------------------------------

#!/usr/bin/python -d
# -*- coding: utf-8 -*-

# NapTrainer.pyw
# This program will attempt to wake the sleeper whenever they are in light sleep
#   for too long.  This could potentially be used to acquire better polyphasic
#   naps very early in an adaptation and possibly train the user to nap
#   efficiently, though this is all guesswork so far.  This is definitely not
#   recommended for normal sleep!
# You will need to provide a program for this script to run as a way to wake
#   you up.  The default setup calls mplayer and tells it to play the
#   "alarm.wav" file that should be found in the same directory as this script.

import sys
import time
import subprocess
import os
from serial import *
from glob import glob
from ZeoRawData import BaseLink, Parser

def scanPorts():
    portList = []
    
    # Helps find USB>Serial converters on linux
    for p in glob('/dev/ttyUSB*'):
        portList.append(p)
    #Linux and Windows
    for i in range(256):
        try:
            ser = Serial(i)
            if ser.isOpen():
                #Check that the port is actually valid
                #Otherwise invalid /dev/ttyS* ports may be added
                portList.append(ser.portstr)
            ser.close()
        except SerialException:
            pass

    return portList

class NapTrainer:
    def __init__(self, parent=None):
        
        self.lightCount = 0
        self.hypToHeight = {'Undefined' : 0,
                            'Deep'      : 1,
                            'Light'     : 2,
                            'REM'       : 3,
                            'Awake'     : 4}
        
    
    def updateSlice(self, slice):
        
        timestamp = slice['ZeoTimestamp']

        if not slice['BadSignal'] and not slice['SleepStage'] == None:
            stage = slice['SleepStage']
            print "Sleep stage: "+ stage
            #if stage == 'Awake':
            if stage == 'Light':
                self.lightCount = self.lightCount + 1
            else:
                self.lightCount = 0
        
        # This is about 2 minutes of light sleep.
        #if self.lightCount >= 1:
        if self.lightCount >= 4:
            subprocess.call(["mplayer","alarm.wav"])
            self.lightCount = 0
    
    def updateEvent(self, timestamp, version, event):
        """Stub: not needed."""

if __name__ == '__main__':
    # Find ports.
    # TODO: offer a command line option for selecting ports.
    ports = scanPorts()
    if len(ports) > 0 :
        print "Found the following ports:"
        for port in ports:
            print port
        print ""
        print "Using port "+ports[0]
        print ""
        portStr = ports[0]
    else:
        sys.exit("No serial ports found.")
    
    # Initialize
    trainer = NapTrainer()
    link = BaseLink.BaseLink(portStr)
    parser = Parser.Parser()
    # Add callbacks
    link.addCallback(parser.update)
    parser.addEventCallback(trainer.updateEvent)
    parser.addSliceCallback(trainer.updateSlice)
    # Start Link
    link.start()
    
    # TODO: perhaps use a more forgiving key?  This would require polling the keyboard without blocking.
    print "Hit ctrl-C at any time to stop."
    while True:
        time.sleep(5)
    
    sys.exit()

I almost forgot to mention:

When I tried to run both scripts at the same time it seemed to create a collision in the serial code:

Capture error: Mismatched lengths.
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/mnt/sharedhd/downloads/zeo/ZeoRawDataLib/Distribute/NapTrainer/ZeoRawData/BaseLink.py", line 81, in run
    c = self.ser.read(1)
  File "/usr/lib64/python2.6/site-packages/serial/serialposix.py", line 449, in read
    buf = os.read(self.fd, size-len(read))
OSError: [Errno 11] Resource temporarily unavailable

I'm probably just going to make one script that combines the functionality of both of the scripts I just made.  Nonetheless, this makes me wonder if it's worthwhile to have a system-wide deamon that proxies all of the serial communications for all instances of BaseLink on a given port.  It looks easy enough to just register more event handlers within a script, but that requires the scripts to know about each other and share BaseLink instances.

Wow great programs, I'll definitely be investigating this week.

Thanks!

Also I definitely have to agree with the idea of listening to my sleep patterns is an AWESOME one.

Here's the program that both records data and wakes someone in light stage sleep:

 

#!/usr/bin/python -d
# -*- coding: utf-8 -*-

# NapTrainer.pyw
# This program will attempt to wake the sleeper whenever they are in light sleep
#   for too long.  This could potentially be used to acquire better polyphasic
#   naps very early in an adaptation and possibly train the user to nap
#   efficiently, though this is all guesswork so far.  This is definitely not
#   recommended for normal sleep!
# You will need to provide a program for this script to run as a way to wake
#   you up.  The default setup calls mplayer and tells it to play the
#   "alarm.wav" file that should be found in the same directory as this script.

import sys
import time
import csv
import subprocess
import os
from serial import *
from glob import glob
from ZeoRawData import BaseLink, Parser

def scanPorts():
    portList = []
    
    # Helps find USB>Serial converters on linux
    for p in glob('/dev/ttyUSB*'):
        portList.append(p)
    #Linux and Windows
    for i in range(256):
        try:
            ser = Serial(i)
            if ser.isOpen():
                #Check that the port is actually valid
                #Otherwise invalid /dev/ttyS* ports may be added
                portList.append(ser.portstr)
            ser.close()
        except SerialException:
            pass

    return portList
    

class ZeoToCSV:
    def __init__(self, parent=None):
        samplesFileName = 'raw_samples.csv'
        sgramFileName = 'spectrogram.csv'
        hgramFileName = 'hypnogram.csv'
        eventsFileName = 'events.csv'
        
        self.hypToHeight = {'Undefined' : 0,
                            'Deep'      : 1,
                            'Light'     : 2,
                            'REM'       : 3,
                            'Awake'     : 4}
        
        # Only create headers when the files are being created for the first time.
        # After that, all new data should be appended to the existing files.
        samplesNeedHeader = False
        sgramNeedHeader = False
        hgramNeedHeader = False
        eventsNeedHeader = False
        
        if not os.path.isfile(samplesFileName):
            samplesNeedHeader = True
        
        if not os.path.isfile(sgramFileName):
            sgramNeedHeader = True
        
        if not os.path.isfile(hgramFileName):
            hgramNeedHeader = True
    
        if not os.path.isfile(eventsFileName):
            eventsNeedHeader = True
        
        self.rawSamples = csv.writer(open(samplesFileName, 'a+b'), delimiter=',',
                                quotechar='"', quoting=csv.QUOTE_MINIMAL)
        self.spectrogram = csv.writer(open(sgramFileName, 'a+b'), delimiter=',',
                                quotechar='"', quoting=csv.QUOTE_MINIMAL)
        self.hypnogram = csv.writer(open(hgramFileName, 'a+b'), delimiter=',',
                                quotechar='"', quoting=csv.QUOTE_MINIMAL)
        self.eventsOut = csv.writer(open(eventsFileName, 'a+b'), delimiter=',',
                                quotechar='"', quoting=csv.QUOTE_MINIMAL)
        
        if samplesNeedHeader:
            self.rawSamples.writerow(["Time Stamp","Version","SQI","Impedance","Bad Signal (Y/N)","Voltage (uV)"])
        
        if sgramNeedHeader:
            self.spectrogram.writerow(["Time Stamp","Version","SQI","Impedance","Bad Signal (Y/N)",
                                    "2-4 Hz","4-8 Hz","8-13 Hz","11-14 Hz","13-18 Hz","18-21 Hz","30-50 Hz"])
        
        if hgramNeedHeader:
            self.hypnogram.writerow(["Time Stamp","Version","SQI","Impedance","Bad Signal (Y/N)","State (0-4)","State (named)"])
        
        if eventsNeedHeader:
            self.eventsOut.writerow(["Time Stamp","Version","Event"])
    
    def updateSlice(self, slice):
        
        timestamp = slice['ZeoTimestamp']
        ver = slice['Version']
                                        
        if not slice['SQI'] == None:
            sqi = str(slice['SQI'])
        else:
            sqi = '--'
                                        
        if not slice['Impedance'] == None:
            imp = str(int(slice['Impedance']))
        else:
            imp = '--'

        if slice['BadSignal']:
            badSignal = 'Y'
        else:
            badSignal = 'N'

        if not slice['Waveform'] == []:
            self.rawSamples.writerow([timestamp,ver,sqi,imp,badSignal] + slice['Waveform'])

        if len(slice['FrequencyBins'].values()) == 7:
            f = slice['FrequencyBins']
            bins = [f['2-4'],f['4-8'],f['8-13'],f['11-14'],f['13-18'],f['18-21'],f['30-50']]
            self.spectrogram.writerow([timestamp,ver,sqi,imp,badSignal] + bins)

        if not slice['SleepStage'] == None:
            stage = slice['SleepStage']
            self.hypnogram.writerow([timestamp,ver,sqi,imp,badSignal] +
                                     [self.hypToHeight[stage],str(stage)])
    
    def updateEvent(self, timestamp, version, event):
        self.eventsOut.writerow([timestamp,version,event])

class NapTrainer:
    def __init__(self, parent=None):
        
        self.lightCount = 0
        self.hypToHeight = {'Undefined' : 0,
                            'Deep'      : 1,
                            'Light'     : 2,
                            'REM'       : 3,
                            'Awake'     : 4}
        
    
    def updateSlice(self, slice):
        
        timestamp = slice['ZeoTimestamp']

        if not slice['BadSignal'] and not slice['SleepStage'] == None:
            stage = slice['SleepStage']
            print "Sleep stage: "+ stage
            #if stage == 'Awake':
            if stage == 'Light':
                self.lightCount = self.lightCount + 1
            else:
                self.lightCount = 0
        
        # This is about 2 minutes of light sleep.
        #if self.lightCount >= 1:
        if self.lightCount >= 4:
            subprocess.call(["mplayer","alarm.wav"])
            self.lightCount = 0
    
    def updateEvent(self, timestamp, version, event):
        """Stub: not needed."""

if __name__ == '__main__':
    # Find ports.
    # TODO: offer a command line option for selecting ports.
    ports = scanPorts()
    if len(ports) > 0 :
        print "Found the following ports:"
        for port in ports:
            print port
        print ""
        print "Using port "+ports[0]
        print ""
        portStr = ports[0]
    else:
        sys.exit("No serial ports found.")
    
    # Initialize
    trainer = NapTrainer()
    output = ZeoToCSV()
    link = BaseLink.BaseLink(portStr)
    parser1 = Parser.Parser()
    parser2 = Parser.Parser()
    # Add callbacks
    link.addCallback(parser1.update)
    parser1.addEventCallback(trainer.updateEvent)
    parser1.addSliceCallback(trainer.updateSlice)
    link.addCallback(parser2.update)
    parser2.addEventCallback(output.updateEvent)
    parser2.addSliceCallback(output.updateSlice)
    # Start Link
    link.start()
    
    # TODO: perhaps use a more forgiving key?  This would require polling the keyboard without blocking.
    print "Hit ctrl-C at any time to stop."
    while True:
        time.sleep(5)
    
    sys.exit()

Great work Chad!

I particularly like the time-domain graph you have of the frequency bins.

Unfortunately, you are right that currently in order to run multiple scripts they must share a BaseLink instance. I'm unsure of how to change this and would appreciate any thoughts you have on it.

I can't see wanting to run more than a few scripts at a time though. A simple work-around would be simply having a master script that creates the BaseLink instance and then have any scripts you want to run accept a BaseLink instance as an argument.

Chad,

Thanks for sharing your code! It is exactly what I've been looking for!

 

John

Here's an idea: a program that causes the alarm to go off after twenty total minutes of non-wake. Why? Because some people take longer to fall asleep during naps while attempting to adapt to polyphasic. It would be nice if this equipment could compensate for how long it takes to fall asleep automatically when determining alarm time, rather than requiring us to get up and manually set it after failing to fall asleep for ten minutes. That would really help, not only for adapting, but for nappers in general. I know that I would use it.

 

Daniel

Daniel,

I like your idea - I'm looking for a similar thing - basically do wake based upon intelligent pattern recognition rather than simply time.

 

For instance when I take a nap I want to wake up after my deep sleep is over, not during it. (I'm not polyphasic, but many of the tools being developed for this type of sleep will adapt to many situations).

 

John

Doesn't Zeo's "SmartWake" feature do this already? Make sure to wake you before a certain time, but not during your deep sleep to avoid "sleep inertia"?

 

Daniel

Ben@Zeo's picture

It does - but the suggestion here is a wakeup after 20 minutes of any type of sleep - not when/if you are entering certain sleep phases.  I've often anted this for non-polyphasic reasons - how do I set the alarm for a '20 minute nap' if I want 20 minutes of sleep?  If I set for 20 minutes I may be awake for 10, and if I set for 30 minutes I may be asleep for 30!

Separately - it will be very cool in the future to tune up SmartWake for optimal power-naps.

This was in reference to JxA's post:

 

"For instance when I take a nap I want to wake up after my deep sleep is over, not during it. (I'm not polyphasic, but many of the tools being developed for this type of sleep will adapt to many situations)."

 

Daniel

Daniel,

Smart wake wakes me up at 7AM with a window that will wake me up as early as 6:20AM - this is nice but is schedule driven rather than data driven.

 

I'm looking for data driven alarms rather than time based alarms - I'll explain more when I figure out how to program in python and prototype it.

John

Daniel said:

Here's an idea: a program that causes the alarm to go off after twenty total minutes of non-wake. Why? Because some people take longer to fall asleep during naps while attempting to adapt to polyphasic. It would be nice if this equipment could compensate for how long it takes to fall asleep automatically when determining alarm time, rather than requiring us to get up and manually set it after failing to fall asleep for ten minutes. That would really help, not only for adapting, but for nappers in general. I know that I would use it.

 

Daniel


I agree that this would be neat.  The reason I didn't write it is because I (personally) haven't needed it.  I actually thought I would, but didn't.  At some level I'm not just being rigid about when I take naps, but about when I wake up from them as well.  So far I've managed it quite well, which is even a bit surprising to me.  Note that in preparation for going into polyphasic sleep I trained myself to wake up at exactly the same time every morning, regardless of how much sleep I had obtained.  I suspect this is why it's very easy for me to wake up from my core 3 hours, which end at exactly that time.  Of course, it hasn't helped at all with the nasty task of staying awake after the core; that still sucks ;) 

Sorry, I'm rambling.  I can tell how this would be useful to others.

chadjoan said:

Daniel said:

Here's an idea: a program that causes the alarm to go off after twenty total minutes of non-wake. Why? Because some people take longer to fall asleep during naps while attempting to adapt to polyphasic. It would be nice if this equipment could compensate for how long it takes to fall asleep automatically when determining alarm time, rather than requiring us to get up and manually set it after failing to fall asleep for ten minutes. That would really help, not only for adapting, but for nappers in general. I know that I would use it.

 

Daniel


I agree that this would be neat.  The reason I didn't write it is because I (personally) haven't needed it.  I actually thought I would, but didn't.  At some level I'm not just being rigid about when I take naps, but about when I wake up from them as well.  So far I've managed it quite well, which is even a bit surprising to me.  Note that in preparation for going into polyphasic sleep I trained myself to wake up at exactly the same time every morning, regardless of how much sleep I had obtained.  I suspect this is why it's very easy for me to wake up from my core 3 hours, which end at exactly that time.  Of course, it hasn't helped at all with the nasty task of staying awake after the core; that still sucks ;) 

Sorry, I'm rambling.  I can tell how this would be useful to others.


Here it is.  This is only lightly tested (I ran it to tease out any obvious crashes, but have not napped with it).  This doesn't have any intelligence in it besides the obvious "wake me up 20 after the onset", so JxA you'll be looking for something else. This is just an easy change to the light-sleep inhibitor. 

 

#!/usr/bin/python -d
# -*- coding: utf-8 -*-

# NapAlarm.pyw
# This program will attempt to wake the sleeper when they have been asleep for
#   a fixed amount of time.  By default this is set to the typical polyphasic
#   nap length of 20 minutes.
# You will need to provide a program for this script to run as a way to wake
#   you up.  The default setup calls mplayer and tells it to play the
#   "alarm.wav" file that should be found in the same directory as this script.

import sys
import time
import subprocess
import os
from serial import *
from glob import glob
from ZeoRawData import BaseLink, Parser

def scanPorts():
    portList = []
    
    # Helps find USB>Serial converters on linux
    for p in glob('/dev/ttyUSB*'):
        portList.append(p)
    #Linux and Windows
    for i in range(256):
        try:
            ser = Serial(i)
            if ser.isOpen():
                #Check that the port is actually valid
                #Otherwise invalid /dev/ttyS* ports may be added
                portList.append(ser.portstr)
            ser.close()
        except SerialException:
            pass

    return portList

class NapAlarm:
    def __init__(self, parent=None):
        
        # The time, in minutes, to wait until waking the sleeper.
        # This starts to count down after sleep onset.
        #self.napLength = 20.0
        self.napLength = 0.25  #Testing
        
        #
        self.sleepStarted = False
        self.sleepCount = 0
        self.hypToHeight = {'Undefined' : 0,
                            'Deep'      : 1,
                            'Light'     : 2,
                            'REM'       : 3,
                            'Awake'     : 4}
        
    
    def updateSlice(self, slice):
        
        timestamp = slice['ZeoTimestamp']

        if not slice['BadSignal'] and not slice['SleepStage'] == None:
            stage = slice['SleepStage']
            print "Sleep stage: "+ stage
            if stage == 'REM' or stage == 'Light' or stage == 'Deep':
                self.sleepStarted = True
            if self.sleepStarted:
                self.sleepCount = self.sleepCount + 1
            ## I originally considered doing the below, but decided against it
            ##   because the sleeper could wake up in the middle of a 20 second
            ##   nap and reset the timer, thus allowing themselves to oversleep.
            #if stage == 'Awake':
            #    self.sleepCount = 0
            #elif stage == 'REM' or stage == 'Light' or stage == 'Deep':
            #    self.sleepCount = self.sleepCount + 1
            # 'Undefined' sleep doesn't count either way.
        
        # Multiply napLength by 2 to get the number of 30-second sleep epochs
        #   that are recorded by the Zeo.
        if self.sleepCount >= self.napLength * 2:
            # Run a separate program to wake up the sleeper.
            subprocess.call(["mplayer","alarm.wav"])
            
            # TODO: there are probably more intelligent things that could be
            #   done after sounding the alarm.
            self.sleepStarted = False
            self.sleepCount = 0
    
    def updateEvent(self, timestamp, version, event):
        """Stub: not needed."""

if __name__ == '__main__':
    # Find ports.
    # TODO: offer a command line option for selecting ports.
    ports = scanPorts()
    if len(ports) > 0 :
        print "Found the following ports:"
        for port in ports:
            print port
        print ""
        print "Using port "+ports[0]
        print ""
        portStr = ports[0]
    else:
        sys.exit("No serial ports found.")
    
    # Initialize
    trainer = NapAlarm()
    link = BaseLink.BaseLink(portStr)
    parser = Parser.Parser()
    # Add callbacks
    link.addCallback(parser.update)
    parser.addEventCallback(trainer.updateEvent)
    parser.addSliceCallback(trainer.updateSlice)
    # Start Link
    link.start()
    
    # TODO: perhaps use a more forgiving key?  This would require polling the keyboard without blocking.
    print "Hit ctrl-C at any time to stop."
    while True:
        time.sleep(5)
    
    sys.exit()

That's great! So let me get this straight: this runs on the Zeo itself? How would I get this to run once it's on the SD card?

I assume there's a link somewhere that explains this? Just pointing it out would be fine...

 

Daniel

No, this is a script that runs on a computer connected to Zeo by utilizing the Raw data Library. You can find out more about it at:

http://blog.myzeo.com/forum/ze.....a-library/

&

http://zeorawdata.sourceforge.net/

 

-Brian

Chad,

After one successful night I've had a couple problems getting this running the last couple nights.

The error I'm getting is: "Capture Error: Read timeout in sync."

Is anyone else seeing this?

 

I can't yet determine the root cause of the error - python, windows7, Zeo, USB-Serial Cable/driver. (I suspect it is windows7).

I think I'll make a USB Ubuntu build and boot off of there to rule out my current OS.

 

Thanks,

 

John

Sounds like a serial error between Zeo and your computer. Are you getting this all the time or sporadically? Just to check, you do have the raw data firmware on your Zeo, correct?

Brian said:

Sounds like a serial error between Zeo and your computer. Are you getting this all the time or sporadically? Just to check, you do have the raw data firmware on your Zeo, correct?


It worked this evening when I tried it out - probably just a PEBKAC issue

 Chad,

I was just working with my friend who has been doing opensource for years and he asked me to ask for your permission to use your code, modify it and put it on github.

He'll give you attribution and post the code as open source for all to build upon.

 

Thanks,

John

JxA said:

 Chad,

I was just working with my friend who has been doing opensource for years and he asked me to ask for your permission to use your code, modify it and put it on github.

He'll give you attribution and post the code as open source for all to build upon.

 

Thanks,

John


Oh jeeze, I thought I responded to this like three days ago.  Sorry for the delay!

I'm entirely fine with sharing!

At some level it is my wish that these things be boost licensed.  I didn't do that before because I was being lazy and I didn't reread the RDL's terms and conditions to see if that would even make sense.  These are very simple scripts, so I wasn't too worried about licensing and such.

JxA said:

Chad,

After one successful night I've had a couple problems getting this running the last couple nights.

The error I'm getting is: "Capture Error: Read timeout in sync."

Is anyone else seeing this?

 

I can't yet determine the root cause of the error - python, windows7, Zeo, USB-Serial Cable/driver. (I suspect it is windows7).

I think I'll make a USB Ubuntu build and boot off of there to rule out my current OS.

 

Thanks,

 

John


Note that this is the error message I get when my Zeo is off or the serial cable is not seated in the Zeo correctly. 

 

The serial cable has some circuitry in the USB side of the cable that converts the serial transmissions into USB protocol.  That USB conversion cirtcuitry is, in and of itself, a USB device.  What this means is that you can probably plug the cable into the computer with nothing connected on the serial end and you will still get a USB device registered.  This is how it works on my Linux system at least.  Of course, even though the serial port is there, the scripts will still be unable to use it until something starts sending actual meaningful data on it.

I've hit this one a lot because I take my Zeo and laptop to work with me and use them to take naps in the car.  I have to check my serial connector every time because it will very easily sit in the Zeo's serial socket yet be slightly twisted away such that a pin or two don't make contact.  It doesn't like that ;)

Can someone compile this to a mac app (or windows, if it is more convenient)? I'm just starting my cs course and I don't know exactly how to run what you guys are making. Thanks!

How to get it going:

Make sure you have python 2.7+ installed.

Make sure you have pyserial installed.

Place the code in a .pyw file.  I call it "DataRecorder.pyw".  Put that file in a directory next to the "ZeoRawData" folder.

Run "python DataRecorder.pyw".  Receive bacon- err, data.

 

The "ZeoRawData" folder is found in the following place in the raw data library:

Distribute/ZeoRawData-2.0/ZeoRawData

where Distribute is (or is in) the root folder of the zip file you download.

 

I usually take the optional step of closing it down between runs and moving the *.csv files to separate directories so that I can keep track of stuff better.  What I end up with looks like the more recent directories found here.  (Yes that's my data, and I'll update it later)

 

I'm actually working heavily on a much improved version of this script that brings it all together under a nicer interface.  I'm trying to avoid distractions for the time being, but I'll look into releasing the new code with executables on the big 3 platforms. 

Mac might be a bit glitchy though because I have no machine to test it on.  Actually Windows too because I was never able to run PySerial on my 64-bit WinXP.  Maybe it'll run under Wine.

 

- Chad