I recently came down with a very serious case of pneumonia which has left me pretty much stuck in bed for several weeks while I recover. Of course being a fully signed up member of the geek squad I can’t help but monitor my condition so I’ve bought a data logging pulse oximeter. This page details my fiddling with the device.
I admit I got a little carried away with my purchase. All I really needed was a simple little pulse oximeter to tell me when to take it a bit easier but instead I splashed out and bought one of these: CMS-50EW. The W on the end presumably means wireless as it apparently has the ability to connect via Bluetooth. My phone can certainly detect the device but I don’t know what the passcode is so that feature is as good as useless to me (yes, I’ve checked the box and manual without luck).
Over all I’m really chuffed with the device. It’s fast to pick up a reading, robust enough that I can walk around with it on and seemingly as accurate as the machines I was on while in hospital. One thing to note is that you must use the USB cable supplied with the device if you want to record data (charging can be done with any mini-USB cable). It would appear that the supplied USB cable is a USB to UART bridge device, I’m guessing that internally the oximeter is using older serial based hardware.
The software that comes with the CMS-50EW is surprisingly good. Normally I find the software supplied with this type of device is of such low quality it’s not worth the bother but this makes a pleasant change. Fair enough it’s not without it’s quirks. For some reason while live monitoring you can’t just request that the data be saved you have to close the monitoring and let it pop up it’s save box. It then automatically saves into the data folder in the install directory e.g. C:\Program Files (x86)\SpO2 Assistant\Data.
This is totally nuts as any programmer worth his salt should know application and data files should be kept separate. The result of this is that on Windows 7 a normal user can’t even see the data files and all interaction with them has to be through the SpO2 Assistant application. A good search through the registry doesn’t reveal any obvious keys that can be changed to fix this nutty behavior either, my guess is that it’s using the key that tells the application where it’s installed.
Understanding the Data File
Getting hold of the data files is, therefore, a matter of opening them in SpO2 Assistant so that it generates a report and then saving them again to a friendly location such as the desktop. For most people there will never be any reason to do this since the file format is specific to the device and it’s binary but I’m the curious type so I thought I’d have a crack at figuring out the file format.
I recorded two short data files (one of a few seconds another a couple of minutes) in order to try and determine how the data was being stored. I analyzed the files using Notepad++ and the excellent Hex Editor plugin. In both cases data appeared to start at 0x43c since this was where there was some crazy output, it also makes sense that the first chunk of the file is meta-data and then the data just gets appended on to the end. I opened the smaller file in the SpO2 Assistant application and checked the first two readings SpO2 of 91% and pulse of 107. Position 0x43c has a value of 0x5b which is 91 in decimal and position 0x43d has a value of 0x6b which is 107 in decimal. I checked a few more readings and it seems to be a simple SpO2 reading followed by pulse rate. There are 15 readings in the data file which corresponds perfectly with the 15 seconds of recording that the application (first recording at 13:27:15, last at 13:27:29) indicating that the device is logging once a second.
Now that I have this information I can write my first rough and ready application to process the binary file into something that can be read by other applications such as Excel. What I want to produce is a CSV or XML file that is easier for other applications to consume. Once I’ve got something that strips out the data I’ll work on reading some of the meta-data which can include name, height, weight, age and the time the recording started.
So a few minutes tinkering with NetBeans and I had myself an application that read the data out of the file. The next piece of information I was interested in was the time. This looked a little harder to extract since there were few likely looking candidates in the file. The most likely looking bytes were from 0x420 to 0x43b which were as follows with a decimal translation below:
0 1 2 3 4 5 6 7 8 9 a b c d e f 0x420 db 07 00 00 09 00 00 00 0b 00 00 00 0d 00 00 00 219 7 0 0 9 0 0 0 11 0 0 0 13 0 0 0 0x430 1b 00 00 00 0f 00 00 00 0f 00 00 00 27 0 0 0 15 0 0 0 15 0 0 0
I think I probably struck lucky with this guess, the SpO2 Assistant application tells me that data recording started at 13:27:15 which matches up rather well with the bytes at 0x42c, 0x430 and 0x434. My guess is that 0x438 and 0x439 comprise a two byte millisecond reading since in my second test file 0x439 is non-zero. It would appear that I also have the date of the recording here as well. The recording took place on 2011/09/11 (Y/M/D). Therefore I think it’s safe to guess that 0x420 and 0x421 encode the year, 0x424 gives the month and 0x428 gives the day. I’m going to push the boat out and say that starting at 0x420 we have seven four byte little endian int values making up the date and time.
A little play with a MappedByteBuffer put into little endian mode and I now have an application that reads out the time and data from the binary file. I think I’ll leave it at that for now as that is the most interesting data from a graphing point of view. The question now is how to make this little application available to the world. I don’t fancy setting up a whole web application for it but likewise I don’t want to have to create a desktop application – why is delivery always the most difficult bit of an application like this?
I was contacted recently by someone who wanted to use this utility to extract the data from their device. They had a slightly later model than mine which also recorded perfusion index values. The data file was slightly different which caused the original application to output the data in a less than useful format. The only obvious difference in the file was the way the data was formatted. It appeared to be four bytes rather than the two my device was providing (this is what threw out the output, every other line was nonsense).
Looking at the data section it was clear that the first two bytes of each block were new and the second two were spO2 and pulse in that order. Looking at the graph I had been sent it was clear that perfusion was a being measured to at least one decimal place but I didn’t know of any two byte floating point numbers. Turns out there is such beast called a half-precision float but I was pretty sure that wasn’t what I was seeing. I decided to just read the data as a short and deal with it later. When I graphed the data in Excel it was blindingly obvious what the device was doing. Rather than use a floating point number it simply scales it by a factor of 100 and stores it as a two byte int. A quick scaling in the application and a storage as a double and I’ve got the perfusion data as well.
The other update needed was to add a model type and command line interface.
Turns out the application was reporting the wrong start time for some sample data sets (thanks to my only other user for spotting this). Looking at the data showed something interesting. The last field contained 0xd363 which after suitable conversion gave a decimal value of 25555. Because of the way the Java calendar works when I put that value into the millisecond field it added 25 seeconds onto the time. I’m currently at a bit of a loss as to what a number that large is doing in that field. I still think it’s probably something to do with the time but as I can’t see anyone being interested in millisecond accuracy for their recording start time I’m now skipping those four bytes.
I have finally found the PIN for the bluetooth connectivity. It would seem that Clinical Guard only want you to connect your device to their software so the PIN could change at any time. Currently for my device, and it would seem quite a lot of others, the PIN code is: 7762.
I’m planning on doing some more work on the software in the near future and as this page is getting quite long I’ll be starting a new one for the new software.
I’ve come to the conclusion that I’ll probably never get around to writing a user interface to this application but I have managed to put a command line interface on it. I’m releasing this under Apache license so you are free to distribute the application as long as you give me credit for it.
To use the application open a command line and, from the directory containing the jar, run a command something like this:
java -jar spo2.jar -i example_1.spo2 -o example_1.csv -m CMS50I
Use the -h option to get help.