Changeset - d58797c8572c
[Not reviewed]
default
0 0 5
mkanning@CL-SEC241-10.cedarville.edu - 12 years ago 2012-11-14 10:21:07
mkanning@CL-SEC241-10.cedarville.edu
Added APRS library files to master/lib. Also had update conflict... not sure how
to handle merge.
5 files changed with 1120 insertions and 0 deletions:
0 comments (0 inline, 0 general)
master/master/lib/aprs_trackuino/config.h
Show inline comments
 
new file 100644
 
/* trackuino copyright (C) 2010  EA5HAV Javi
 
 *
 
 * This program is free software; you can redistribute it and/or
 
 * modify it under the terms of the GNU General Public License
 
 * as published by the Free Software Foundation; either version 2
 
 * of the License, or (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 */
 

	
 
#ifndef __CONFIG_H__
 
#define __CONFIG_H__
 

	
 

	
 
// --------------------------------------------------------------------------
 
// THIS IS THE TRACKUINO FIRMWARE CONFIGURATION FILE. YOUR CALLSIGN AND
 
// OTHER SETTINGS GO HERE.
 
//
 
// NOTE: all pins are Arduino based, not the Atmega chip. Mapping:
 
// http://www.arduino.cc/en/Hacking/PinMapping
 
// --------------------------------------------------------------------------
 

	
 

	
 
// --------------------------------------------------------------------------
 
// APRS config (aprs.c)
 
// --------------------------------------------------------------------------
 

	
 
// Set your callsign and SSID here. Common values for the SSID are
 
// (from http://zlhams.wikidot.com/aprs-ssidguide):
 
//
 
// - Balloons:  11
 
// - Cars:       9
 
// - Home:       0
 
// - IGate:      5
 
#define S_CALLSIGN      "MYCALL"
 
#define S_CALLSIGN_ID   11
 

	
 
// Destination callsign: APRS (with SSID=0) is usually okay.
 
#define D_CALLSIGN      "APRS"
 
#define D_CALLSIGN_ID   0
 

	
 
// Digipeating paths:
 
// (read more about digipeating paths here: http://wa8lmf.net/DigiPaths/ )
 
// The recommended digi path for a balloon is WIDE2-1 or pathless. The default
 
// is pathless. Uncomment the following two lines for WIDE2-1 path:
 
#define DIGI_PATH1      "WIDE2"
 
#define DIGI_PATH1_TTL  1
 

	
 
// APRS comment: this goes in the comment portion of the APRS message. You
 
// might want to keep this short. The longer the packet, the more vulnerable
 
// it is to noise. 
 
#define APRS_COMMENT    "Trackuino reminder: replace callsign with your own"
 

	
 

	
 
// --------------------------------------------------------------------------
 
// AX.25 config (ax25.cpp)
 
// --------------------------------------------------------------------------
 

	
 
// TX delay in milliseconds
 
#define TX_DELAY      300
 

	
 
// --------------------------------------------------------------------------
 
// Tracker config (trackuino.pde)
 
// --------------------------------------------------------------------------
 

	
 
// APRS packets are slotted so that multiple trackers can be used without
 
// them stepping on one another. The transmission times are governed by
 
// the formula:
 
//
 
//         APRS_SLOT (seconds) + n * APRS_PERIOD (seconds)
 
//
 
// When launching multiple balloons, use the same APRS_PERIOD in all balloons
 
// and set APRS_SLOT so that the packets are spaced equally in time.
 
// Eg. for two balloons and APRS_PERIOD = 60, set APRS_SLOT to 0 and 30, 
 
// respectively. The first balloon will transmit at 00:00:00, 00:01:00, 
 
// 00:02:00, etc. amd the second balloon will transmit at 00:00:30, 00:01:30,
 
// 00:02:30, etc.
 
#define APRS_SLOT     0     // seconds. -1 disables slotted transmissions
 
#define APRS_PERIOD   60    // seconds
 

	
 
// GPS baud rate (in bits per second). This is also the baud rate at which
 
// debug data will be printed out the serial port.
 
#define GPS_BAUDRATE  9600
 

	
 

	
 
// --------------------------------------------------------------------------
 
// Modem config (afsk.cpp)
 
// --------------------------------------------------------------------------
 

	
 
// AUDIO_PIN is the audio-out pin. The audio is generated by timer 2 using
 
// PWM, so the only two options are pins 3 and 11.
 
// Pin 11 doubles as MOSI, so I suggest using pin 3 for PWM and leave 11 free
 
// in case you ever want to interface with an SPI device.
 
#define AUDIO_PIN       3
 

	
 
// --------------------------------------------------------------------------
 
// Radio config (radio_hx1.cpp)
 
// --------------------------------------------------------------------------
 

	
 
// This is the PTT pin
 
#define PTT_PIN           4
 

	
 
// --------------------------------------------------------------------------
 
// Sensors config (sensors.cpp)
 
// --------------------------------------------------------------------------
 

	
 
// Most of the sensors.cpp functions use internal reference voltages (either
 
// AVCC or 1.1V). If you want to use an external reference, you should
 
// uncomment the following line:
 
//
 
// #define USE_AREF
 
//
 
// BEWARE! If you hook up an external voltage to the AREF pin and 
 
// accidentally set the ADC to any of the internal references, YOU WILL
 
// FRY YOUR AVR.
 
//
 
// It is always advised to connect the AREF pin through a pull-up resistor,
 
// whose value is defined here in ohms (set to 0 if no pull-up):
 
//
 
#define AREF_PULLUP           4700
 
//
 
// Since there is already a 32K resistor at the ADC pin, the actual
 
// voltage read will be VREF * 32 / (32 + AREF_PULLUP)
 
//
 
// Read more in the Arduino reference docs:
 
// http://arduino.cc/en/Reference/AnalogReference?from=Reference.AREF
 

	
 
// Pin mappings for the internal / external temperature sensors. VS refers
 
// to (arduino) digital pins, whereas VOUT refers to (arduino) analog pins.
 
#define INTERNAL_LM60_VS_PIN     6
 
#define INTERNAL_LM60_VOUT_PIN   0
 
#define EXTERNAL_LM60_VS_PIN     7
 
#define EXTERNAL_LM60_VOUT_PIN   1
 

	
 
// Units for temperature sensors (Added by: Kyle Crockett)
 
// 1 = Celsius, 2 = Kelvin, 3 = Fahrenheit
 
#define TEMP_UNIT 1
 

	
 
// Calibration value in the units selected. Use integer only.
 
#define CALIBRATION_VAL 0
 

	
 
// Resistors divider for the voltage meter (ohms)
 
#define VMETER_R1       10000
 
#define VMETER_R2       3300
 

	
 
// Voltage meter analog pin
 
#define VMETER_PIN      2
 

	
 
// --------------------------------------------------------------------------
 
// Buzzer config (buzzer.cpp)
 
// --------------------------------------------------------------------------
 

	
 
// Type of buzzer (0=active, 1=passive). An active buzzer is driven by a
 
// DC voltage. A passive buzzer needs a PWM signal.
 
#define BUZZER_TYPE             0
 

	
 
// When using a passive buzzer, specify the PWM frequency here. Choose one
 
// that maximizes the volume according to the buzzer's datasheet. Not all
 
// the frequencies are valid, check out the buzzer_*.cpp code. On Arduino,
 
// it must be between L and 65535, where L = F_CPU / 65535 and F_CPU is the
 
// clock rate in hertzs. For 16 MHz Arduinos, this gives a lower limit of 
 
// 245 Hz.
 
#define BUZZER_FREQ             895     // Hz
 

	
 
// These are the number of seconds the buzzer will stay on/off alternately
 
#define BUZZER_ON_TIME          1       // secs
 
#define BUZZER_OFF_TIME         2       // secs
 

	
 
// This option disables the buzzer above BUZZER_ALTITUDE meters. This is a
 
// float value, so make it really high (eg. 1000000.0 = 1 million meters)
 
// if you want it to never stop buzzing.
 
#define BUZZER_ALTITUDE         3000.0  // meters (1 ft = 0.3048 m)
 

	
 
// The options here are pin 9 or 10
 
#define BUZZER_PIN              9
 

	
 
// --------------------------------------------------------------------------
 
// Debug
 
// --------------------------------------------------------------------------
 

	
 
// This is the LED pin (13 on Arduinos). The LED will be on while the AVR is
 
// running and off while it's sleeping, so its brightness gives an indication
 
// of the CPU activity.
 
#define LED_PIN                 13
 

	
 
// Debug info includes printouts from different modules to aid in testing and
 
// debugging.
 
// 
 
// 1. To properly receive debug information, only connect the Arduino RX pin 
 
//    to the GPS TX pin, and leave the Arduino TX pin disconnected. 
 
//
 
// 2. On the serial monitor, set the baudrate to GPS_BAUDRATE (above),
 
//    usually 9600.
 
//
 
// 3. When flashing the firmware, disconnect the GPS from the RX pin or you
 
//    will get errors.
 

	
 
// #define DEBUG_GPS    // GPS sentence dump and checksum validation
 
// #define DEBUG_AX25   // AX.25 frame dump
 
// #define DEBUG_MODEM  // Modem ISR overrun and profiling
 
// #define DEBUG_RESET  // AVR reset
 
// #define DEBUG_SENS   // Sensors
 

	
 

	
 
#endif
 

	
master/master/lib/aprs_trackuino/gps.c
Show inline comments
 
new file 100644
 
/* trackuino copyright (C) 2010  EA5HAV Javi
 
 *
 
 * This program is free software; you can redistribute it and/or
 
 * modify it under the terms of the GNU General Public License
 
 * as published by the Free Software Foundation; either version 2
 
 * of the License, or (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 */
 

	
 
#include "config.h"
 
#include "gps.h"
 
#include <stdlib.h>
 
#include <string.h>
 

	
 
// Module declarations
 
static void parse_sentence_type(const char * token);
 
static void parse_time(const char *token);
 
static void parse_status(const char *token);
 
static void parse_lat(const char *token);
 
static void parse_lat_hemi(const char *token);
 
static void parse_lon(const char *token);
 
static void parse_lon_hemi(const char *token);
 
static void parse_speed(const char *token);
 
static void parse_course(const char *token);
 
static void parse_altitude(const char *token);
 

	
 
// Module types
 
typedef void (*t_nmea_parser)(const char *token);
 

	
 
enum t_sentence_type {
 
  SENTENCE_UNK,
 
  SENTENCE_GGA,
 
  SENTENCE_RMC
 
};
 

	
 

	
 
// Module constants
 
static const t_nmea_parser unk_parsers[] = {
 
  parse_sentence_type,    // $GPxxx
 
};
 

	
 
static const t_nmea_parser gga_parsers[] = {
 
  NULL,             // $GPGGA
 
  parse_time,       // Time
 
  NULL,             // Latitude
 
  NULL,             // N/S
 
  NULL,             // Longitude
 
  NULL,             // E/W
 
  NULL,             // Fix quality 
 
  NULL,             // Number of satellites
 
  NULL,             // Horizontal dilution of position
 
  parse_altitude,   // Altitude
 
  NULL,             // "M" (mean sea level)
 
  NULL,             // Height of GEOID (MSL) above WGS84 ellipsoid
 
  NULL,             // "M" (mean sea level)
 
  NULL,             // Time in seconds since the last DGPS update
 
  NULL              // DGPS station ID number
 
};
 

	
 
static const t_nmea_parser rmc_parsers[] = {
 
  NULL,             // $GPRMC
 
  parse_time,       // Time
 
  parse_status,     // A=active, V=void
 
  parse_lat,        // Latitude,
 
  parse_lat_hemi,   // N/S
 
  parse_lon,        // Longitude
 
  parse_lon_hemi,   // E/W
 
  parse_speed,      // Speed over ground in knots
 
  parse_course,     // Track angle in degrees (true)
 
  NULL,             // Date (DDMMYY)
 
  NULL,             // Magnetic variation
 
  NULL              // E/W
 
};
 

	
 
static const int NUM_OF_UNK_PARSERS = (sizeof(unk_parsers) / sizeof(t_nmea_parser));
 
static const int NUM_OF_GGA_PARSERS = (sizeof(gga_parsers) / sizeof(t_nmea_parser));
 
static const int NUM_OF_RMC_PARSERS = (sizeof(rmc_parsers) / sizeof(t_nmea_parser));
 

	
 
// Module variables
 
static t_sentence_type sentence_type = SENTENCE_UNK;
 
static bool at_checksum = false;
 
static unsigned char our_checksum = '$';
 
static unsigned char their_checksum = 0;
 
static char token[16];
 
static int num_tokens = 0;
 
static unsigned int offset = 0;
 
static bool active = false;
 
static char gga_time[7] = "", rmc_time[7] = "";
 
static char new_time[7];
 
static uint32_t new_seconds;
 
static float new_lat;
 
static float new_lon;
 
static char new_aprs_lat[9];
 
static char new_aprs_lon[10];
 
static float new_course;
 
static float new_speed;
 
static float new_altitude;
 

	
 
// Public (extern) variables, readable from other modules
 
char gps_time[7];       // HHMMSS
 
uint32_t gps_seconds = 0;   // seconds after midnight
 
float gps_lat = 0;
 
float gps_lon = 0;
 
char gps_aprs_lat[9];
 
char gps_aprs_lon[10];
 
float gps_course = 0;
 
float gps_speed = 0;
 
float gps_altitude = 0;
 

	
 
// Module functions
 
unsigned char from_hex(char a) 
 
{
 
  if (a >= 'A' && a <= 'F')
 
    return a - 'A' + 10;
 
  else if (a >= 'a' && a <= 'f')
 
    return a - 'a' + 10;
 
  else if (a >= '0' && a <= '9')
 
    return a - '0';
 
  else
 
    return 0;
 
}
 

	
 
void parse_sentence_type(const char *token)
 
{
 
  if (strcmp(token, "$GPGGA") == 0) {
 
    sentence_type = SENTENCE_GGA;
 
  } else if (strcmp(token, "$GPRMC") == 0) {
 
    sentence_type = SENTENCE_RMC;
 
  } else {
 
    sentence_type = SENTENCE_UNK;
 
  }
 
}
 

	
 
void parse_time(const char *token)
 
{
 
  // Time can have decimals (fractions of a second), but we only take HHMMSS
 
  strncpy(new_time, token, 6);
 
  // Terminate string
 
  new_time[6] = '\0';
 
  
 
  new_seconds = 
 
    ((new_time[0] - '0') * 10 + (new_time[1] - '0')) * 60 * 60UL +
 
    ((new_time[2] - '0') * 10 + (new_time[3] - '0')) * 60 +
 
    ((new_time[4] - '0') * 10 + (new_time[5] - '0'));
 
}
 

	
 
void parse_status(const char *token)
 
{
 
  // "A" = active, "V" = void. We shoud disregard void sentences
 
  if (strcmp(token, "A") == 0)
 
    active = true;
 
  else
 
    active = false;
 
}
 

	
 
void parse_lat(const char *token)
 
{
 
  // Parses latitude in the format "DD" + "MM" (+ ".M{...}M")
 
  char degs[3];
 
  if (strlen(token) >= 4) {
 
    degs[0] = token[0];
 
    degs[1] = token[1];
 
    degs[2] = '\0';
 
    new_lat = atof(degs) + atof(token + 2) / 60;
 
  }
 
  // APRS-ready latitude
 
  strncpy(new_aprs_lat, token, 7);
 
  new_aprs_lat[7] = '\0';
 
}
 

	
 
void parse_lat_hemi(const char *token)
 
{
 
  if (token[0] == 'S')
 
    new_lat = -new_lat;
 
  new_aprs_lat[7] = token[0];
 
  new_aprs_lon[8] = '\0';
 
}
 

	
 
void parse_lon(const char *token)
 
{
 
  // Longitude is in the format "DDD" + "MM" (+ ".M{...}M")
 
  char degs[4];
 
  if (strlen(token) >= 5) {
 
    degs[0] = token[0];
 
    degs[1] = token[1];
 
    degs[2] = token[2];
 
    degs[3] = '\0';
 
    new_lon = atof(degs) + atof(token + 3) / 60;
 
  }
 
  // APRS-ready longitude
 
  strncpy(new_aprs_lon, token, 8);
 
  new_aprs_lon[8] = '\0';
 
}
 

	
 
void parse_lon_hemi(const char *token)
 
{
 
  if (token[0] == 'W')
 
    new_lon = -new_lon;
 
  new_aprs_lon[8] = token[0];
 
  new_aprs_lon[9] = '\0';
 
}
 

	
 
void parse_speed(const char *token)
 
{
 
  new_speed = atof(token);
 
}
 

	
 
void parse_course(const char *token)
 
{
 
  new_course = atof(token);
 
}
 

	
 
void parse_altitude(const char *token)
 
{
 
  new_altitude = atof(token);
 
}
 

	
 

	
 
//
 
// Exported functions
 
//
 
void gps_setup() {
 
  strcpy(gps_time, "000000");
 
  strcpy(gps_aprs_lat, "0000.00N");
 
  strcpy(gps_aprs_lon, "00000.00E");
 
}
 

	
 
bool gps_decode(char c)
 
{
 
  int ret = false;
 

	
 
  switch(c) {
 
    case '\r':
 
    case '\n':
 
      // End of sentence
 

	
 
      if (num_tokens && our_checksum == their_checksum) {
 
#ifdef DEBUG_GPS
 
        Serial.print(" (OK!) ");
 
        Serial.print(millis());
 
#endif
 
        // Return a valid position only when we've got two rmc and gga
 
        // messages with the same timestamp.
 
        switch (sentence_type) {
 
          case SENTENCE_UNK:
 
            break;    // Keeps gcc happy
 
          case SENTENCE_GGA:
 
            strcpy(gga_time, new_time);
 
            break;
 
          case SENTENCE_RMC:
 
            strcpy(rmc_time, new_time);
 
            break;
 
        }
 

	
 
        // Valid position scenario:
 
        //
 
        // 1. The timestamps of the two previous GGA/RMC sentences must match.
 
        //
 
        // 2. We just processed a known (GGA/RMC) sentence. Suppose the
 
        //    contrary: after starting up this module, gga_time and rmc_time
 
        //    are both equal (they're both initialized to ""), so (1) holds
 
        //    and we wrongly report a valid position.
 
        //
 
        // 3. The GPS has a valid fix. For some reason, the Venus 634FLPX
 
        //    reports 24 deg N, 121 deg E (the middle of Taiwan) until a valid
 
        //    fix is acquired:
 
        //
 
        //    $GPGGA,120003.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000*69 (OK!)
 
        //    $GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0*30 (OK!)
 
        //    $GPRMC,120003.000,V,2400.0000,N,12100.0000,E,000.0,000.0,280606,,,N*78 (OK!)
 
        //    $GPVTG,000.0,T,,M,000.0,N,000.0,K,N*02 (OK!)
 

	
 
        if (sentence_type != SENTENCE_UNK &&      // Known sentence?
 
            strcmp(gga_time, rmc_time) == 0 &&    // RMC/GGA times match?
 
            active) {                             // Valid fix?
 
          // Atomically merge data from the two sentences
 
          strcpy(gps_time, new_time);
 
          gps_seconds = new_seconds;
 
          gps_lat = new_lat;
 
          gps_lon = new_lon;
 
          strcpy(gps_aprs_lat, new_aprs_lat);
 
          strcpy(gps_aprs_lon, new_aprs_lon);
 
          gps_course = new_course;
 
          gps_speed = new_speed;
 
          gps_altitude = new_altitude;
 
          ret = true;
 
        }
 
      }
 
#ifdef DEBUG_GPS
 
      if (num_tokens)
 
        Serial.println();
 
#endif
 
      at_checksum = false;        // CR/LF signals the end of the checksum
 
      our_checksum = '$';         // Reset checksums
 
      their_checksum = 0;
 
      offset = 0;                 // Prepare for the next incoming sentence
 
      num_tokens = 0;
 
      sentence_type = SENTENCE_UNK;
 
      break;
 
    
 
    case '*':
 
      // Handle as ',', but prepares to receive checksum (ie. do not break)
 
      at_checksum = true;
 
      our_checksum ^= c;
 

	
 
    case ',':
 
      // Process token
 
      token[offset] = '\0';
 
      our_checksum ^= c;  // Checksum the ',', undo the '*'
 

	
 
      // Parse token
 
      switch (sentence_type) {
 
        case SENTENCE_UNK:
 
          if (num_tokens < NUM_OF_UNK_PARSERS && unk_parsers[num_tokens])
 
            unk_parsers[num_tokens](token);
 
          break;
 
        case SENTENCE_GGA:
 
          if (num_tokens < NUM_OF_GGA_PARSERS && gga_parsers[num_tokens])
 
            gga_parsers[num_tokens](token);
 
          break;
 
        case SENTENCE_RMC:
 
          if (num_tokens < NUM_OF_RMC_PARSERS && rmc_parsers[num_tokens])
 
            rmc_parsers[num_tokens](token);
 
          break;
 
      }
 

	
 
      // Prepare for next token
 
      num_tokens++;
 
      offset = 0;
 
#ifdef DEBUG_GPS
 
      Serial.print(c);
 
#endif
 
      break;
 

	
 
    default:
 
      // Any other character
 
      if (at_checksum) {
 
        // Checksum value
 
        their_checksum = their_checksum * 16 + from_hex(c);
 
      } else {
 
        // Regular NMEA data
 
        if (offset < 15) {  // Avoid buffer overrun (tokens can't be > 15 chars)
 
          token[offset] = c;
 
          offset++;
 
          our_checksum ^= c;
 
        }
 
      }
 
#ifdef DEBUG_GPS
 
      Serial.print(c);
 
#endif
 
  }
 
  return ret;
 
}
 

	
master/master/lib/aprs_trackuino/gps.cpp
Show inline comments
 
new file 100644
 
/* trackuino copyright (C) 2010  EA5HAV Javi
 
 *
 
 * This program is free software; you can redistribute it and/or
 
 * modify it under the terms of the GNU General Public License
 
 * as published by the Free Software Foundation; either version 2
 
 * of the License, or (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 */
 

	
 
#include "config.h"
 
#include "gps.h"
 
#include <WProgram.h>
 
#include <stdlib.h>
 
#include <string.h>
 

	
 
// Module declarations
 
static void parse_sentence_type(const char * token);
 
static void parse_time(const char *token);
 
static void parse_status(const char *token);
 
static void parse_lat(const char *token);
 
static void parse_lat_hemi(const char *token);
 
static void parse_lon(const char *token);
 
static void parse_lon_hemi(const char *token);
 
static void parse_speed(const char *token);
 
static void parse_course(const char *token);
 
static void parse_altitude(const char *token);
 

	
 
// Module types
 
typedef void (*t_nmea_parser)(const char *token);
 

	
 
enum t_sentence_type {
 
  SENTENCE_UNK,
 
  SENTENCE_GGA,
 
  SENTENCE_RMC
 
};
 

	
 

	
 
// Module constants
 
static const t_nmea_parser unk_parsers[] = {
 
  parse_sentence_type,    // $GPxxx
 
};
 

	
 
static const t_nmea_parser gga_parsers[] = {
 
  NULL,             // $GPGGA
 
  parse_time,       // Time
 
  NULL,             // Latitude
 
  NULL,             // N/S
 
  NULL,             // Longitude
 
  NULL,             // E/W
 
  NULL,             // Fix quality 
 
  NULL,             // Number of satellites
 
  NULL,             // Horizontal dilution of position
 
  parse_altitude,   // Altitude
 
  NULL,             // "M" (mean sea level)
 
  NULL,             // Height of GEOID (MSL) above WGS84 ellipsoid
 
  NULL,             // "M" (mean sea level)
 
  NULL,             // Time in seconds since the last DGPS update
 
  NULL              // DGPS station ID number
 
};
 

	
 
static const t_nmea_parser rmc_parsers[] = {
 
  NULL,             // $GPRMC
 
  parse_time,       // Time
 
  parse_status,     // A=active, V=void
 
  parse_lat,        // Latitude,
 
  parse_lat_hemi,   // N/S
 
  parse_lon,        // Longitude
 
  parse_lon_hemi,   // E/W
 
  parse_speed,      // Speed over ground in knots
 
  parse_course,     // Track angle in degrees (true)
 
  NULL,             // Date (DDMMYY)
 
  NULL,             // Magnetic variation
 
  NULL              // E/W
 
};
 

	
 
static const int NUM_OF_UNK_PARSERS = (sizeof(unk_parsers) / sizeof(t_nmea_parser));
 
static const int NUM_OF_GGA_PARSERS = (sizeof(gga_parsers) / sizeof(t_nmea_parser));
 
static const int NUM_OF_RMC_PARSERS = (sizeof(rmc_parsers) / sizeof(t_nmea_parser));
 

	
 
// Module variables
 
static t_sentence_type sentence_type = SENTENCE_UNK;
 
static bool at_checksum = false;
 
static unsigned char our_checksum = '$';
 
static unsigned char their_checksum = 0;
 
static char token[16];
 
static int num_tokens = 0;
 
static unsigned int offset = 0;
 
static bool active = false;
 
static char gga_time[7] = "", rmc_time[7] = "";
 
static char new_time[7];
 
static uint32_t new_seconds;
 
static float new_lat;
 
static float new_lon;
 
static char new_aprs_lat[9];
 
static char new_aprs_lon[10];
 
static float new_course;
 
static float new_speed;
 
static float new_altitude;
 

	
 
// Public (extern) variables, readable from other modules
 
char gps_time[7];       // HHMMSS
 
uint32_t gps_seconds = 0;   // seconds after midnight
 
float gps_lat = 0;
 
float gps_lon = 0;
 
char gps_aprs_lat[9];
 
char gps_aprs_lon[10];
 
float gps_course = 0;
 
float gps_speed = 0;
 
float gps_altitude = 0;
 

	
 
// Module functions
 
unsigned char from_hex(char a) 
 
{
 
  if (a >= 'A' && a <= 'F')
 
    return a - 'A' + 10;
 
  else if (a >= 'a' && a <= 'f')
 
    return a - 'a' + 10;
 
  else if (a >= '0' && a <= '9')
 
    return a - '0';
 
  else
 
    return 0;
 
}
 

	
 
void parse_sentence_type(const char *token)
 
{
 
  if (strcmp(token, "$GPGGA") == 0) {
 
    sentence_type = SENTENCE_GGA;
 
  } else if (strcmp(token, "$GPRMC") == 0) {
 
    sentence_type = SENTENCE_RMC;
 
  } else {
 
    sentence_type = SENTENCE_UNK;
 
  }
 
}
 

	
 
void parse_time(const char *token)
 
{
 
  // Time can have decimals (fractions of a second), but we only take HHMMSS
 
  strncpy(new_time, token, 6);
 
  // Terminate string
 
  new_time[6] = '\0';
 
  
 
  new_seconds = 
 
    ((new_time[0] - '0') * 10 + (new_time[1] - '0')) * 60 * 60UL +
 
    ((new_time[2] - '0') * 10 + (new_time[3] - '0')) * 60 +
 
    ((new_time[4] - '0') * 10 + (new_time[5] - '0'));
 
}
 

	
 
void parse_status(const char *token)
 
{
 
  // "A" = active, "V" = void. We shoud disregard void sentences
 
  if (strcmp(token, "A") == 0)
 
    active = true;
 
  else
 
    active = false;
 
}
 

	
 
void parse_lat(const char *token)
 
{
 
  // Parses latitude in the format "DD" + "MM" (+ ".M{...}M")
 
  char degs[3];
 
  if (strlen(token) >= 4) {
 
    degs[0] = token[0];
 
    degs[1] = token[1];
 
    degs[2] = '\0';
 
    new_lat = atof(degs) + atof(token + 2) / 60;
 
  }
 
  // APRS-ready latitude
 
  strncpy(new_aprs_lat, token, 7);
 
  new_aprs_lat[7] = '\0';
 
}
 

	
 
void parse_lat_hemi(const char *token)
 
{
 
  if (token[0] == 'S')
 
    new_lat = -new_lat;
 
  new_aprs_lat[7] = token[0];
 
  new_aprs_lon[8] = '\0';
 
}
 

	
 
void parse_lon(const char *token)
 
{
 
  // Longitude is in the format "DDD" + "MM" (+ ".M{...}M")
 
  char degs[4];
 
  if (strlen(token) >= 5) {
 
    degs[0] = token[0];
 
    degs[1] = token[1];
 
    degs[2] = token[2];
 
    degs[3] = '\0';
 
    new_lon = atof(degs) + atof(token + 3) / 60;
 
  }
 
  // APRS-ready longitude
 
  strncpy(new_aprs_lon, token, 8);
 
  new_aprs_lon[8] = '\0';
 
}
 

	
 
void parse_lon_hemi(const char *token)
 
{
 
  if (token[0] == 'W')
 
    new_lon = -new_lon;
 
  new_aprs_lon[8] = token[0];
 
  new_aprs_lon[9] = '\0';
 
}
 

	
 
void parse_speed(const char *token)
 
{
 
  new_speed = atof(token);
 
}
 

	
 
void parse_course(const char *token)
 
{
 
  new_course = atof(token);
 
}
 

	
 
void parse_altitude(const char *token)
 
{
 
  new_altitude = atof(token);
 
}
 

	
 

	
 
//
 
// Exported functions
 
//
 
void gps_setup() {
 
  strcpy(gps_time, "000000");
 
  strcpy(gps_aprs_lat, "0000.00N");
 
  strcpy(gps_aprs_lon, "00000.00E");
 
}
 

	
 
bool gps_decode(char c)
 
{
 
  int ret = false;
 

	
 
  switch(c) {
 
    case '\r':
 
    case '\n':
 
      // End of sentence
 

	
 
      if (num_tokens && our_checksum == their_checksum) {
 
#ifdef DEBUG_GPS
 
        Serial.print(" (OK!) ");
 
        Serial.print(millis());
 
#endif
 
        // Return a valid position only when we've got two rmc and gga
 
        // messages with the same timestamp.
 
        switch (sentence_type) {
 
          case SENTENCE_UNK:
 
            break;    // Keeps gcc happy
 
          case SENTENCE_GGA:
 
            strcpy(gga_time, new_time);
 
            break;
 
          case SENTENCE_RMC:
 
            strcpy(rmc_time, new_time);
 
            break;
 
        }
 

	
 
        // Valid position scenario:
 
        //
 
        // 1. The timestamps of the two previous GGA/RMC sentences must match.
 
        //
 
        // 2. We just processed a known (GGA/RMC) sentence. Suppose the
 
        //    contrary: after starting up this module, gga_time and rmc_time
 
        //    are both equal (they're both initialized to ""), so (1) holds
 
        //    and we wrongly report a valid position.
 
        //
 
        // 3. The GPS has a valid fix. For some reason, the Venus 634FLPX
 
        //    reports 24 deg N, 121 deg E (the middle of Taiwan) until a valid
 
        //    fix is acquired:
 
        //
 
        //    $GPGGA,120003.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000*69 (OK!)
 
        //    $GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0*30 (OK!)
 
        //    $GPRMC,120003.000,V,2400.0000,N,12100.0000,E,000.0,000.0,280606,,,N*78 (OK!)
 
        //    $GPVTG,000.0,T,,M,000.0,N,000.0,K,N*02 (OK!)
 

	
 
        if (sentence_type != SENTENCE_UNK &&      // Known sentence?
 
            strcmp(gga_time, rmc_time) == 0 &&    // RMC/GGA times match?
 
            active) {                             // Valid fix?
 
          // Atomically merge data from the two sentences
 
          strcpy(gps_time, new_time);
 
          gps_seconds = new_seconds;
 
          gps_lat = new_lat;
 
          gps_lon = new_lon;
 
          strcpy(gps_aprs_lat, new_aprs_lat);
 
          strcpy(gps_aprs_lon, new_aprs_lon);
 
          gps_course = new_course;
 
          gps_speed = new_speed;
 
          gps_altitude = new_altitude;
 
          ret = true;
 
        }
 
      }
 
#ifdef DEBUG_GPS
 
      if (num_tokens)
 
        Serial.println();
 
#endif
 
      at_checksum = false;        // CR/LF signals the end of the checksum
 
      our_checksum = '$';         // Reset checksums
 
      their_checksum = 0;
 
      offset = 0;                 // Prepare for the next incoming sentence
 
      num_tokens = 0;
 
      sentence_type = SENTENCE_UNK;
 
      break;
 
    
 
    case '*':
 
      // Handle as ',', but prepares to receive checksum (ie. do not break)
 
      at_checksum = true;
 
      our_checksum ^= c;
 

	
 
    case ',':
 
      // Process token
 
      token[offset] = '\0';
 
      our_checksum ^= c;  // Checksum the ',', undo the '*'
 

	
 
      // Parse token
 
      switch (sentence_type) {
 
        case SENTENCE_UNK:
 
          if (num_tokens < NUM_OF_UNK_PARSERS && unk_parsers[num_tokens])
 
            unk_parsers[num_tokens](token);
 
          break;
 
        case SENTENCE_GGA:
 
          if (num_tokens < NUM_OF_GGA_PARSERS && gga_parsers[num_tokens])
 
            gga_parsers[num_tokens](token);
 
          break;
 
        case SENTENCE_RMC:
 
          if (num_tokens < NUM_OF_RMC_PARSERS && rmc_parsers[num_tokens])
 
            rmc_parsers[num_tokens](token);
 
          break;
 
      }
 

	
 
      // Prepare for next token
 
      num_tokens++;
 
      offset = 0;
 
#ifdef DEBUG_GPS
 
      Serial.print(c);
 
#endif
 
      break;
 

	
 
    default:
 
      // Any other character
 
      if (at_checksum) {
 
        // Checksum value
 
        their_checksum = their_checksum * 16 + from_hex(c);
 
      } else {
 
        // Regular NMEA data
 
        if (offset < 15) {  // Avoid buffer overrun (tokens can't be > 15 chars)
 
          token[offset] = c;
 
          offset++;
 
          our_checksum ^= c;
 
        }
 
      }
 
#ifdef DEBUG_GPS
 
      Serial.print(c);
 
#endif
 
  }
 
  return ret;
 
}
 

	
master/master/lib/aprs_trackuino/gps.h
Show inline comments
 
new file 100644
 
/* trackuino copyright (C) 2010  EA5HAV Javi
 
 *
 
 * This program is free software; you can redistribute it and/or
 
 * modify it under the terms of the GNU General Public License
 
 * as published by the Free Software Foundation; either version 2
 
 * of the License, or (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 */
 

	
 
#ifndef __GPS_H__
 
#define __GPS_H__
 

	
 
#include <stdint.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <stdbool.h>
 

	
 
extern char gps_time[7];       // HHMMSS
 
extern uint32_t gps_seconds;   // seconds after midnight
 
extern char gps_date[7];       // DDMMYY
 
extern float gps_lat;
 
extern float gps_lon;
 
extern char gps_aprs_lat[9];
 
extern char gps_aprs_lon[10];
 
extern float gps_course;
 
extern float gps_speed;
 
extern float gps_altitude;
 

	
 
void gps_setup();
 
bool gps_decode(char c);
 

	
 
#endif
master/master/lib/aprs_trackuino/trackuino.pde
Show inline comments
 
new file 100644
 
/* trackuino copyright (C) 2010  EA5HAV Javi
 
 *
 
 * This program is free software; you can redistribute it and/or
 
 * modify it under the terms of the GNU General Public License
 
 * as published by the Free Software Foundation; either version 2
 
 * of the License, or (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 */
 

	
 
// Mpide 22 fails to compile Arduino code because it stupidly defines ARDUINO 
 
// as an empty macro (hence the +0 hack). UNO32 builds are fine. Just use the
 
// real Arduino IDE for Arduino builds. Optionally complain to the Mpide
 
// authors to fix the broken macro.
 
#if (ARDUINO + 0) == 0
 
#error "Oops! We need the real Arduino IDE (version 22 or 23) for Arduino builds."
 
#error "See trackuino.pde for details on this"
 

	
 
// Refuse to compile on arduino version 21 or lower. 22 includes an 
 
// optimization of the USART code that is critical for real-time operation
 
// of the AVR code.
 
#elif (ARDUINO + 0) < 22
 
#error "Oops! We need Arduino 22 or 23"
 
#error "See trackuino.pde for details on this"
 

	
 
// Arduino 1.0+ introduced backwards-incompatible changes in the serial lib.
 
#elif (ARDUINO + 1) >= 100
 
#error "Ooops! We don't support Arduino 1.0+ (yet). Please use 22 or 23"
 
#error "See trackuino.pde for details on this"
 

	
 
#endif
 

	
 

	
 
// Trackuino custom libs
 
#include "config.h"
 
#include "afsk_avr.h"
 
#include "afsk_pic32.h"
 
#include "aprs.h"
 
#include "buzzer.h"
 
#include "gps.h"
 
#include "pin.h"
 
#include "power.h"
 
#include "sensors_avr.h"
 
#include "sensors_pic32.h"
 

	
 
// Arduino/AVR libs
 
#include <Wire.h>
 
#include <WProgram.h>
 

	
 
// Module constants
 
static const uint32_t VALID_POS_TIMEOUT = 2000;  // ms
 

	
 
// Module variables
 
static int32_t next_aprs = 0;
 

	
 

	
 
void setup()
 
{
 
  pinMode(LED_PIN, OUTPUT);
 
  pin_write(LED_PIN, LOW);
 

	
 
  Serial.begin(GPS_BAUDRATE);
 
#ifdef DEBUG_RESET
 
  Serial.println("RESET");
 
#endif
 

	
 
  buzzer_setup();
 
  afsk_setup();
 
  gps_setup();
 
  sensors_setup();
 

	
 
#ifdef DEBUG_SENS
 
  Serial.print("Ti=");
 
  Serial.print(sensors_int_lm60());
 
  Serial.print(", Te=");
 
  Serial.print(sensors_ext_lm60());
 
  Serial.print(", Vin=");
 
  Serial.println(sensors_vin());
 
#endif
 

	
 
  // Do not start until we get a valid time reference
 
  // for slotted transmissions.
 
  if (APRS_SLOT >= 0) {
 
    do {
 
      while (! Serial.available())
 
        power_save();
 
    } while (! gps_decode(Serial.read()));
 
    
 
    next_aprs = millis() + 1000 *
 
      (APRS_PERIOD - (gps_seconds + APRS_PERIOD - APRS_SLOT) % APRS_PERIOD);
 
  }
 
  else {
 
    next_aprs = millis();
 
  }  
 
  // TODO: beep while we get a fix, maybe indicating the number of
 
  // visible satellites by a series of short beeps?
 
}
 

	
 
void get_pos()
 
{
 
  // Get a valid position from the GPS
 
  int valid_pos = 0;
 
  uint32_t timeout = millis();
 
  do {
 
    if (Serial.available())
 
      valid_pos = gps_decode(Serial.read());
 
  } while ( (millis() - timeout < VALID_POS_TIMEOUT) && ! valid_pos) ;
 

	
 
  if (valid_pos) {
 
    if (gps_altitude > BUZZER_ALTITUDE) {
 
      buzzer_off();   // In space, no one can hear you buzz
 
    } else {
 
      buzzer_on();
 
    }
 
  }
 
}
 

	
 
void loop()
 
{
 
  // Time for another APRS frame
 
  if ((int32_t) (millis() - next_aprs) >= 0) {
 
    get_pos();
 
    aprs_send();
 
    next_aprs += APRS_PERIOD * 1000L;
 
    while (afsk_busy()) ;
 
      power_save();
 

	
 
#ifdef DEBUG_MODEM
 
    // Show modem ISR stats from the previous transmission
 
    afsk_debug();
 
#endif
 
  }
 

	
 
  power_save(); // Incoming GPS data or interrupts will wake us up
 
}
0 comments (0 inline, 0 general)