[Tinyos-contrib-commits]
CVS: tinyos-1.x/contrib/handhelds/apps/ThreeAxisRecorder
Makefile, NONE, 1.1 README, NONE, 1.1 ThreeAxisRecorder.nc,
NONE, 1.1 ThreeAxisRecorderM.nc, NONE, 1.1 download_data.py,
NONE, 1.1 shimmerUtil.py, NONE, 1.1 start_recording.py, NONE,
1.1 test_kinematics.py, NONE, 1.1
steve ayer
ayer1 at users.sourceforge.net
Wed Sep 12 11:35:26 PDT 2007
Update of /cvsroot/tinyos/tinyos-1.x/contrib/handhelds/apps/ThreeAxisRecorder
In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv6831
Added Files:
Makefile README ThreeAxisRecorder.nc ThreeAxisRecorderM.nc
download_data.py shimmerUtil.py start_recording.py
test_kinematics.py
Log Message:
first checkin of stable, tested three-axis a/d conversion recorded on
sd card, with card serial-download utilities and error-checking.
--- NEW FILE: Makefile ---
# $Id: Makefile,v 1.1 2007/09/12 18:35:24 ayer1 Exp $
#
# Copyright (c) 2007, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# Neither the name of the Intel Corporation nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: Jason Waterman
# July, 2007
COMPONENT=ThreeAxisRecorder
PFLAGS=-DDEFAULT_BAUDRATE=115200
include ../Makerules
--- NEW FILE: README ---
The ThreeAxisRecorder tinyos program acts as an accelerometer data logger
program. To start the datalogging, place the SHIMMER in the dock and
run the python script start_recording.py
python start_recording.py
The script will autodetect the USB port of the dock. If you have more
than one dock connected, it will pick the one of them, which may not
be the one you are using, so only have one dock connected when you are
using this program.
This script syncronizes the time on the SHIMMER with the time on the
PC and starts the SHIMMER data collection. After runing this script,
the green LED on SHIMMER will blink, letting you know it is collecting
data. You then can take SHIMMER out of the dock and use it.
When you are done collecting data, place SHIMMMER back on the dock and
press the square user button on the dock. This will stop SHIMMER from
recording data. The green LED will shut off and the orange LED will
come on, letting you know there is data to be downloaded.
To get the data off of SHIMMER, run the download_data.py script with
SHIMMER still in the dock. Run the program like this:
python download_data.py > output.csv
This will store the data in the file output.csv
The output is lines of 5 comma seperated values. The first field is
the timestamp (in localtime) in a format that SPSS handles. The
second field is the timestamp (in localtime) if Excel format. The
next 3 fields are the x, y, and z axis of the accelerometer. The last
field is the timestamp (in UTC) in standard Unix time format. Not
every sample tuplet (x,y,z) has a timestamp. Only every 83 sample
tuplets have an actual timestamp.
After the download is complete, the orange LED will shut off and the
green LED will be on, letting you know that SHIMMER is ready to
collect more data.
SD Card File Format:
--------------------
There is no filesystem on the SD card. The accelerometer data is
written to the SD card is sectors of 512 bytes. The first written
sector, which starts at SD card sector 2000, is the header for the
file. The first 4 bytes of the header contains 0s. This is normally
the timestamp of the sector. The next 8 bytes the timstamp offset.
This is the difference between the clock on the host PC and the clock
running on the shimmer. The next four bytes are the number of sectors
in the file. The rest of the 512 bytes are randomly generated.
Data sectors:
-------------
After the header, the rest of the sectors are data sectors. Samples
are taken as an x,y,z group every 20/1024 milliseconds, or 51.2 Hz.
83 sample groups fit on one 512 byte sector. Each sector has a 4 byte
timestamp. This number is the value of the SHIMMER clock after the
first 3 samples have been taken for this sector. The last two bytes
of the sector is a crc16 checksum.
--- NEW FILE: ThreeAxisRecorder.nc ---
// $Id: ThreeAxisRecorder.nc,v 1.1 2007/09/12 18:35:24 ayer1 Exp $
/*
* Copyright (c) 2007, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Jason Waterman
* July, 2007
*/
configuration ThreeAxisRecorder {
}
implementation {
components Main,
HPLUARTC,
ThreeAxisRecorderM,
UserButtonC,
TimerC,
LedsC,
DMA_M,
MMA7260_AccelM,
SDC;
Main.StdControl -> TimerC;
Main.StdControl -> ThreeAxisRecorderM;
Main.StdControl -> UserButtonC;
ThreeAxisRecorderM.HPLUART -> HPLUARTC;
ThreeAxisRecorderM.Button -> UserButtonC.UserButton;
ThreeAxisRecorderM.sampleTimer -> TimerC.Timer[unique("Timer")];
ThreeAxisRecorderM.recordTimer -> TimerC.Timer[unique("Timer")];
ThreeAxisRecorderM.Leds -> LedsC;
ThreeAxisRecorderM.LocalTime -> TimerC;
ThreeAxisRecorderM.SD -> SDC;
ThreeAxisRecorderM.DMA0 -> DMA_M.DMA[0];
ThreeAxisRecorderM.AccelStdControl -> MMA7260_AccelM;
ThreeAxisRecorderM.Accel -> MMA7260_AccelM;
}
--- NEW FILE: ThreeAxisRecorderM.nc ---
// $Id: ThreeAxisRecorderM.nc,v 1.1 2007/09/12 18:35:24 ayer1 Exp $
/*
* Copyright (c) 2007, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Jason Waterman
* July, 2007
*/
includes DMA;
includes crc;
module ThreeAxisRecorderM {
provides {
interface StdControl;
}
uses {
interface Leds;
interface Timer as sampleTimer;
interface Timer as recordTimer;
interface HPLUART;
interface LocalTime;
interface MSP430Event as Button;
interface SD;
interface StdControl as AccelStdControl;
interface MMA7260_Accel as Accel;
interface DMA as DMA0;
}
}
implementation {
// Our Output Buffers
#define BUFFER_SIZE 600
#define NEXT_BUFFER(ent) (((ent) >= ((BUFFER_SIZE) - 1)) ? 0 : ((ent) + 1))
// Where to start writing on the SD card
#define START_SECTOR 2000
// Number of ADC channels used
#define NUM_ADC_CHAN 3
// Command state
enum {
S_IDLE = 0, // Not processing any commands
S_OFFSET_RECV = 1, // Currently receiving time offset from host
S_MARKER_RECV = 2, // Currently receiving file marker from host
S_SECTOR_RECV = 3, // Currently receiving a sector number from host
};
uint8_t recording; // are we currently recording
uint8_t state; // current command when in command state
uint16_t Head; // Next entry in the buffer to fill
uint16_t Tail; // Oldest entry in the buffer
uint8_t Buffer[BUFFER_SIZE]; // Circular buffer of bytes
uint32_t localtime0, localtime1; // SHIMMER Clock time
bool BytePending; // TRUE if we have put without a putDone
uint8_t file_marker[512]; // used to mark the start and end of files on SD card
uint8_t sd_send_buffer[512]; // stores data read from the DS card
uint8_t current_buffer; // toggles between the sd_data banks
uint32_t sectors_written; // number of sectors written to the SD card
uint32_t send_sector_num; // sector to send to the host
uint8_t dma_blocks; // how many blocks (one group sample of NUM_ADC_CHANs) gotten
uint16_t sbuf0[256], sbuf1[256]; // storage for DMA'd accelerometer data
// Send one byte across the serial line
result_t SendNextByte() {
bool busy;
atomic {
busy = (BytePending == TRUE);
}
if (busy) {
return FAIL;
}
if (Head == Tail) {
return SUCCESS; // buffer is empty
}
atomic {
BytePending = TRUE;
}
call HPLUART.put(Buffer[Tail]);
return SUCCESS;
}
// Send data across the serial line
result_t SendBuffer(uint8_t* data, uint16_t length) {
uint16_t i;
uint16_t size;
bool not_busy;
// see if there's enough room for this packet
size = (Head < Tail) ? Head + BUFFER_SIZE - Tail : Head - Tail;
if (size + length >= BUFFER_SIZE) {
return FAIL; // not enough room
}
// copy incoming bytes to the buffer
for (i = 0; i < length; i++) {
Buffer[Head] = data[i];
Head = NEXT_BUFFER(Head);
}
atomic {
not_busy = (BytePending == FALSE);
}
if (not_busy) SendNextByte();
return SUCCESS;
}
// Sets up the ADC correctly
void startADC() {
call DMA0.ADCinit();
atomic{
SET_FLAG(ADC12CTL1, ADC12DIV_7);
// sample and hold time 4 adc12clk cycles
SET_FLAG(ADC12CTL0, SHT0_0);
// set reference voltage to 2.5v
SET_FLAG(ADC12CTL0, REF2_5V);
// conversion start address
SET_FLAG(ADC12CTL1, CSTARTADD_0); // really a zero, for clarity
}
SET_FLAG(ADC12MCTL0, INCH_5); // accel x
SET_FLAG(ADC12MCTL1, INCH_4); // accel y
SET_FLAG(ADC12MCTL2, INCH_3); // accel z
SET_FLAG(ADC12MCTL2, EOS); // sez "this is the last reg"
SET_FLAG(ADC12MCTL0, SREF_1); // Vref = Vref+ and Vr-
SET_FLAG(ADC12MCTL1, SREF_1); // Vref = Vref+ and Vr-
SET_FLAG(ADC12MCTL2, SREF_1); // Vref = Vref+ and Vr-
/* set up for three adc channels -> three adcmem regs -> three dma
channels in round-robin */
/* clear init defaults first */
CLR_FLAG(ADC12CTL1, CONSEQ_2); // clear default repeat single channel
SET_FLAG(ADC12CTL1, CONSEQ_1); // single sequence of channels
call DMA0.init();
call DMA0.setSourceAddress((uint16_t)ADC12MEM0_);
call DMA0.setBlockSize(NUM_ADC_CHAN);
// we want block transfer, repeated
DMA0CTL = DMADT_5 + DMADSTINCR_3 + DMASRCINCR_3;
call DMA0.beginTransfer();
}
task void start_recording() {
sectors_written = 0;
recording = 1;
// write start marker
call SD.init();
call SD.writeSector(START_SECTOR, file_marker);
sectors_written++;
// Set up memory
atomic {
current_buffer = 0;
dma_blocks = 0;
// DMA Destination address
// First 4 bytes reserved for a timestamp
DMA0DA = (uint16_t)&sbuf0[2];
}
// Set up accelrometer
call AccelStdControl.start();
call Accel.setSensitivity(RANGE_4_0G);
// Set up ADC
startADC();
// Ready to go
call sampleTimer.start(TIMER_REPEAT, 20); // 51.2 Hz
call recordTimer.start(TIMER_REPEAT, 1024); // 1 Hz
call Leds.orangeOff();
}
task void stop_recording() {
uint32_t *ptr;
uint16_t crc_fsc, i;
crc_fsc = 0;
if (recording == 1) {
call sampleTimer.stop();
call recordTimer.stop();
call Leds.greenOff();
call Leds.yellowOff();
call Leds.orangeOn();
// update file marker with sectors written
ptr = (uint32_t *)&file_marker[10];
*ptr = sectors_written;
// calc CRC16 of data
for (i = 0; i < 510; i++) {
crc_fsc = crcByte(crc_fsc, *(file_marker + i));
}
*(uint16_t *)(file_marker + 510) = crc_fsc;
call SD.writeSector(START_SECTOR, file_marker);
call HPLUART.init();
CLR_FLAG(ADC12CTL0, REF2_5V); // turn off voltage ref
recording = 0;
}
}
task void write_sector() {
uint16_t *write_buffer;
uint8_t *time_ptr; // points to localtime
uint8_t *ptr; // points to data buffer
uint8_t my_buffer; // which is our buffer
uint16_t i; // counter
uint16_t crc_fsc; // crc checksum
crc_fsc = 0;
atomic {
if (current_buffer == 0) {
my_buffer = 1;
}
else {
my_buffer = 0;
}
} // end atomic
if (my_buffer == 0) {
write_buffer = sbuf0;
time_ptr = (uint8_t *) &localtime0;
}
else {
write_buffer = sbuf1;
time_ptr = (uint8_t *) &localtime1;
}
// Insert the time stamp we saved earlier
ptr = (uint8_t *)write_buffer;
*ptr = time_ptr[0]; ptr++;
*ptr = time_ptr[1]; ptr++;
*ptr = time_ptr[2]; ptr++;
*ptr = time_ptr[3]; ptr++;
// Insert padding for sector
write_buffer[254] = 0xaaaa;
// Calc CRC16 of data
ptr = (uint8_t *)write_buffer;
for (i = 0; i < 510; i++) {
crc_fsc = crcByte(crc_fsc, *(ptr + i));
}
write_buffer[255] = crc_fsc;
// call SD.init();
call SD.writeSector(START_SECTOR + sectors_written, ptr);
sectors_written++;
}
task void send_sector() {
uint8_t err;
uint32_t sector;
uint32_t marker = 0xaaaaaaaa;
atomic {
sector = send_sector_num;
}
// Sends a sector to the host
call Leds.orangeToggle();
call SD.init();
err = call SD.readSector((START_SECTOR + sector), sd_send_buffer);
call HPLUART.init();
atomic {
SendBuffer((uint8_t *)&marker, 4);
SendBuffer(sd_send_buffer, 512);
}
}
command result_t StdControl.init() {
call Leds.init();
call AccelStdControl.init();
call HPLUART.init();
atomic {
Head = 0;
Tail = 0;
BytePending = FALSE;
state = S_IDLE;
sectors_written = 0;
send_sector_num = 0;
recording = 0;
}
return SUCCESS;
}
command result_t StdControl.start() {
call Leds.greenOn();
return SUCCESS;
}
command result_t StdControl.stop() {
return SUCCESS;
}
/* Gets called when we have input from the host PC. To keep things
simple we're just allowing one byte commands to be received.
Also note that the the offset is stored at a double (8 bytes) and
on TinyOS a double is 4 bytes, so we keep it around as an array
of bytes. */
event async result_t HPLUART.get(uint8_t data) {
static uint16_t Index = 0; // how many bytes of data received
static uint8_t offset[8]; // holds our constructed offset
static uint8_t sector[4]; // holds our constructed sector number
if (state == S_MARKER_RECV) {
// receive offset mode
file_marker[Index] = data;
if (Index == 511) {
// got all our data
Index = 0;
state = S_IDLE;
}
else {
Index++;
}
}
else if (state == S_SECTOR_RECV) {
// receive sector mode
sector[Index] = data;
if (Index == 3) {
// got all our data
Index = 0;
state = S_IDLE;
atomic {
send_sector_num = *((uint32_t *)sector);
}
post send_sector();
}
else {
Index++;
}
}
else if (state == S_OFFSET_RECV) {
// receive offset mode
offset[Index] = data;
if (Index == 7) {
// got all our data
Index = 0;
state = S_IDLE;
}
else {
Index++;
}
}
else {
// command mode
if (data == 'a') {
// start recording
post start_recording();
}
else if (data == 'd') {
// toggle on the red led
call Leds.redToggle();
}
else if (data == 'e') {
// send current file marker
SendBuffer(file_marker, 512);
}
else if (data == 'f') {
// Send a sector (512 bytes) of data. The sector to send is
// sent as next 4 bytes and is 1 based (i.e. 1 is the first
// block that was written to the SD card.
state = S_SECTOR_RECV;
}
else if (data == 'g') {
// turns off the orange led
call Leds.orangeOff();
}
else if (data == 'h') {
// turns on the green led
call Leds.greenOn();
}
else if (data == 'm') {
// receive marker
state = S_MARKER_RECV;
}
else if (data == 'o') {
// send offset (used to determine offset was received correctly)
SendBuffer(offset, 8);
}
if (data == 'r') {
// receive offset
state = S_OFFSET_RECV;
}
else if (data == 's') {
// send localtime (used for timing calculations)
localtime0 = call LocalTime.read();
SendBuffer((uint8_t *)&localtime0, 4);
}
}
return SUCCESS;
}
// Called when the UART is ready to send another byte
event async result_t HPLUART.putDone() {
atomic {
BytePending = FALSE;
}
if (Head == Tail) {
return SUCCESS; // buffer is empty
}
Tail = NEXT_BUFFER(Tail);
SendNextByte();
return SUCCESS;
}
// This gets called when the button is pressed.
// Stop recording
async event void Button.fired()
{
post stop_recording();
}
// This is for our blinking record led
event result_t recordTimer.fired() {
call Leds.greenToggle();
return SUCCESS;
}
// time to take another sample...
event result_t sampleTimer.fired() {
call DMA0.ADCbeginConversion();
return SUCCESS;
}
// Called when the data from the ADC has been written to memory.
async event void DMA0.transferComplete() {
dma_blocks++;
//call Leds.yellowToggle();
// check for start of a new sector
if (dma_blocks == 1) {
if (current_buffer == 0) {
atomic localtime0 = call LocalTime.read();
}
else {
atomic localtime1 = call LocalTime.read();
}
}
// check for end of sector
else if (dma_blocks == 84) {
// Magic number warning:
// 84 (x,y,x) accelerometer samples fit in one 512 sector.
// You'll need to change this if NUM_ADC_CHAN is not 3.
// What were doing here is changing buffers. We have two
// buffers so we can write out one buffer to the SD card while
// the ADC is writing data to the other buffer. First we pad
// out our data so we get exactly 512 bytes to write to the SD
// card. Then we move our DMA address ot the other buffer,
// skipping the first four bytes for the time stamp (which gets
// filled in later).
atomic {
if (current_buffer == 0) {
DMA0DA = (uint16_t)&sbuf1[2];
current_buffer = 1;
}
else {
DMA0DA = (uint16_t)&sbuf0[2];
current_buffer = 0;
}
dma_blocks = 0;
} // end atomic
post write_sector();
return;
}
// Move ADC memory pointer to the next location
atomic DMA0DA += (NUM_ADC_CHAN * 2);
}
// We should *not* see this, as the adc interrupts are eaten by
// the dma controller!
async event void DMA0.ADCInterrupt(uint8_t regnum) {
call Leds.redOn();
}
}
--- NEW FILE: download_data.py ---
#! /usr/bin/env python
#
# $Id: download_data.py,v 1.1 2007/09/12 18:35:24 ayer1 Exp $
#
# Copyright (c) 2007, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# Neither the name of the Intel Corporation nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: Jason Waterman
# July, 2007
import serial
import struct
import time
import sys
import glob
import re
import os.path
import string
import platform
import getopt
import shimmerUtil
def get_data(sector):
"""
Reads a sector off of the SHIMMER sd card.
This function returns the 512 bytes read from sd card on the
sector specified. Sector 0 is the file header, and the data is on
the following sectors. If there was a problem reading from the sd
card, the empty string is returned.
"""
data = ''
marker_not_found = 1
# Command to read sector
ser.write('f' + struct.pack('I', sector))
# Look for start marker
markers_seen = 0
marker_found = 0
while not marker_found:
char = ser.read(1)
# check for timeout
if char == '':
return data
if char == '\xaa':
markers_seen += 1
if markers_seen == 4:
marker_found = 1
else:
markers_seen = 0
# Found start of sector
data = ser.read(512)
if len(data) != 512:
data = ''
return data
read_crc = struct.unpack('H', data[-2:])[0]
calc_crc = shimmerUtil.crc_compute(data[0:-2])
if read_crc != calc_crc:
data = ''
return data
# Print out revision number
revision_id = "$Revision: 1.1 $"
revision = revision_id.split()[1]
print >> sys.stderr, 'SHIMMER Data Downloader v%s' % (revision)
# Find the data serial port
port = shimmerUtil.find_data_port()
if port == '':
print >> sys.stderr, 'Could not find SHIMMER data port port. Exiting.'
sys.exit()
speed = 115200
print >> sys.stderr, 'Found SHIMMER data port on %s' % (port)
ser = serial.Serial(port, speed, timeout = 1)
ser.flushInput()
# Uncomment one of these depending on the senstivity of the SHIMMER
# 1.5g senstivity setting
#sensitivity = 0.8 * (3.0 / 3.3)
# 2g senstivity setting
#sensitivity = 0.6 * (3.0 / 3.3)
# 4g senstivity setting
sensitivity = 0.3 * (3.0 / 3.3)
# 6g senstivity setting
#sensitivity = 0.2 * (3.0 / 3.3)
# get file header
data = get_data(0)
if len(data) != 512:
print >> sys.stderr, 'Bad file header read. Only read %s bytes. Exiting' \
% (len(data))
sys.exit()
offset = struct.unpack('d',data[2:10])[0]
sectors_to_read = struct.unpack('I',data[10:14])[0]
# Keeps track of the overflow of the shimmer timestamp
overflow = 0
old_timestamp = 0
output_filename = ''
if platform.system() == 'Windows':
output_filename = 'output.csv'
opts, args = getopt.getopt(sys.argv[1:], 'o:')
for o, a in opts:
if o == '-o':
output_filename = a
if output_filename:
try:
outfile = open(output_filename, 'w')
print >> sys.stderr, "Writing output to %s" % output_filename
except IOError:
print >> sys.stderr, "Unable to open file %s for writing. Exiting." % output_filename
sys.exit()
else:
outfile = sys.stdout
print >> sys.stderr, "Percent of download completed: 00",
for sector in xrange(1,sectors_to_read):
data = get_data(sector)
if data == '': # Sector was bad
print >> sys.stderr, "\nSector %i has errors." % sector
print >> sys.stderr, "Percent of download completed: %02d" % \
((sector*100)/sectors_to_read),
continue
# timestamp is in UTC
shimmer_timestamp = struct.unpack('I',data[0:4])[0]
# Check for timestamp overflow
if old_timestamp > shimmer_timestamp:
overflow += 2**32
old_timestamp = shimmer_timestamp
shimmer_time = offset + (shimmer_timestamp + overflow)/32768.0
shimmer_excel_time = ((shimmer_time - time.altzone)/86400.0) + 25569
print >> sys.stderr, "\b\b\b%02d" % ((sector*100)/sectors_to_read),
# Human readable time
#time_str = time.strftime('%m/%d/%Y %H:%M:%S',
# time.localtime(shimmer_time))
for sample in range(4,508,6):
(x,y,z) = struct.unpack('HHH', data[sample:sample+6])
# Convert to g's. A reading of 1638 = 1.0v with a 2.5v
# reference. First we divide by 1638 to convert sample back
# to volts. Then subtract the DC offset of the accelerometer.
# Then divide by the senstivity to get g's.
x = ((x/1638.0) - 1.5) / sensitivity
y = ((y/1638.0) - 1.5) / sensitivity
z = ((z/1638.0) - 1.5) / sensitivity
# gat a time format usable in SPSS
second_fraction = '%f' % shimmer_time
second_fraction = second_fraction.split('.')[1]
spss_time = string.upper(time.strftime('%d-%b-%Y %H:%M:%S',
time.localtime(shimmer_time))
+ '.' + second_fraction)
print >> outfile, "%s,%r,%r,%r,%r,%r" % (spss_time,shimmer_excel_time,x,y,z,
shimmer_time)
# Measured time between samples. It should be 0.01953125 but
# the value below matches the data better
shimmer_time += (0.019763785861017256)
shimmer_excel_time = ((shimmer_time - time.altzone)/86400.0) + 25569
print >> sys.stderr, "\b\b\b100"
# Turn on the green led and turn off the orange
ser.write('gh')
time.sleep(1) # give time for the write
ser.close()
--- NEW FILE: shimmerUtil.py ---
#! /usr/bin/env python
#
# $Id: shimmerUtil.py,v 1.1 2007/09/12 18:35:24 ayer1 Exp $
#
# Copyright (c) 2007, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# Neither the name of the Intel Corporation nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: Jason Waterman
# August, 2007
# This file contains a collection of useful utilities for working with SHIMMER.
import platform
import glob
import re
import os.path
""" CRC16-CCITT lookup table """
crc_table = (
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 )
def crc_compute(data):
"""
Calculate CCITT CRC16.
Calculates the CCIT CRC16 of a string. It returns the two byte
CRC of the data.
"""
crc = 0
for byte in (ord(part) for part in data):
ushort = (crc << 8) & 0xff00
crc = ((ushort) ^ crc_table[((crc >> 8) ^ (0xff & byte))])
return crc
def find_data_port():
"""
Autodetect the SHIMMER data serial port. Works under Linux and
Windows, however the Linux code is more robust.
Returns the string of the port found or the empty string if no
port was found.
"""
speed = 115200
if platform.system() == 'Windows':
for port in ['com5', 'com7', 'com9', 'com11', 'com13']:
try:
ser = serial.Serial(port, speed)
ser.close()
return port
except serial.serialutil.SerialException:
pass
else:
for f in glob.iglob('/sys/bus/usb/drivers/usb/*/product'):
try:
file_to_search = open(f, 'r')
data = file_to_search.read()
shimmer_match = re.compile('^SHIMMER DUAL UART')
if shimmer_match.search(data):
dirpath = os.path.dirname(f)
busnum = os.path.basename(dirpath)
myglob = dirpath + '/' + busnum + ':1.1/tty*'
portpath = glob.glob(myglob)[0]
port = '/dev/' + os.path.basename(portpath)
return port
except IOError:
pass
# Found nothing
return ''
--- NEW FILE: start_recording.py ---
#! /usr/bin/env python
#
# $Id: start_recording.py,v 1.1 2007/09/12 18:35:24 ayer1 Exp $
#
# Copyright (c) 2007, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# Neither the name of the Intel Corporation nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: Jason Waterman
# July, 2007
import serial
import struct
import time
import random
import sys
import shimmerUtil
# Find the data serial port
port = shimmerUtil.find_data_port()
if port == '':
print 'Could not find SHIMMER data port port. Exiting.'
sys.exit()
speed = 115200
print 'Found SHIMMER data port on %s' % (port)
ser = serial.Serial(port, speed, timeout = 1)
ser.flushInput()
offset = 0.0
print "Syncronizing clocks..."
loop = 5
# t1 = host time at start of packet send
# t2 = shimmer time
# t3 = host time at received reply
#
# round trip delay = t3 - t1
for n in range(0,loop):
t1 = time.time()
ser.write('s')
d = ser.read(4)
t3 = time.time()
t2 = struct.unpack('I', d)[0]
# convert SHIMMER time into seconds
t2 = t2/32768.0
# Calculate delay, round trip time divided by 2
delay = (t3-t1)/2.0
# Calculate offset
current_offset = t1 - t2 - delay
#print t1, t2, current_offset
offset += current_offset
time.sleep(random.random())
offset = offset/float(loop)
print "Offset is: %r" % offset
# We have the offset. Now store it on the SHIMMER
ser.write('r')
offset_str = struct.pack('d', offset)
ser.write(offset_str)
# Now read it back to make sure the offset was received correctly
ser.write('o')
offset_str = ser.read(8)
new_offset = struct.unpack('d',offset_str)[0]
if new_offset != offset:
print "Error writing offset to SHIMMER. Please try again."
ser.close()
sys.exit(1)
# We are now ready to generate our marker block. The marker starts
# with the 0x0000 (normally the SHIMMER timestamp goes here) and then
# the 8 byte offset offset followed by 500 bytes of random characters
# and a 2 byte checksum.
data = '\x00\x00' + offset_str
for i in range(500):
data += chr(random.randint(0,255))
data += struct.pack('H',shimmerUtil.crc_compute(data))
# Now send this marker to SHIMMER
# We have the offset. Now store it on the SHIMMER
ser.write('m')
ser.write(data)
ser.write('e')
new_data = ser.read(512)
if new_data != data:
print "File marker mismatch. Please try again."
ser.close()
sys.exit(1)
now = time.strftime('%m/%d/%Y %H:%M:%S',
time.localtime(time.time()))
print "Starting recording at %s" % now
ser.write('a')
# All done, close up shop
time.sleep(1) # give time to do the write
ser.close()
--- NEW FILE: test_kinematics.py ---
# Use this to test sending commands to SHIMMER interactively at the
# python command prompt:
#
# from test_kinematics import *
#
# will set it up for you.
import serial
import struct
ser = serial.Serial('/dev/ttyUSB2', 115200)
ser.flushInput()
More information about the Tinyos-contrib-commits
mailing list