[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