Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • danc/MicroCART
  • snawerdt/MicroCART_17-18
  • bbartels/MicroCART_17-18
  • jonahu/MicroCART
4 results
Show changes
Showing
with 0 additions and 4388 deletions
// LatLonCoord.h: Definition of the CLatLonCoord class.
//
// The CLatLonCoord class, derived from the CTypedCoord class, represents a
// lat/lon coordinate. Since this class contains an internal lat/lon
// coordinate from CTypedCoord, no conversion functions are necessary.
//
// The formatted coordinate string generated by the CreateCoordString() member
// function can be in one of the three following formats:
// - Degrees (H DD.DDDDD*, H DDD.DDDDD*); this is the default.
// - Degrees, and decimal minutes (H DD*MM.MMM'. H DDD* MM.MMM')
// - Degrees, minutes, and decimal seconds (H DD*MM'SS.S", H DDD*MM'SS.S")
//
// The coordinate string format is set by calling the SetCurrentLatLonFormat()
// member function.
//
// Written by Jason Bevins in 1998. File is in the public domain.
//
#ifndef __LAT_LON_CPPOORD_HPP
#define __LAT_LON_CPPOORD_HPP
#include <string> // for string
#include "typedCoord.h" // for COORD_TYPE, etc
enum HEMISPHERE_DIRECTION
{
HEMISPHERE_NS = 0,
HEMISPHERE_EW = 1
};
enum COORD_LATLON_FORMAT
{
FORMAT_D = 0,
FORMAT_DM = 1,
FORMAT_DMS = 2
};
// These constants are used by the CreateDisplayStrings() and the
// CreateXYCoordStrings() functions to parse the string generated by the
// CreateCoordString() function. Modify these constants when you modify the
// code that generates the string. These constants do not affect the code
// that generate these strings, but you may write parsing functions that
// require the positions of the individual elements in the strings.
const int LAT_D_HPPEMISPHERE_POS = 0;
const int LAT_D_HPPEMISPHERE_LEN = 1;
const int LON_D_HPPEMISPHERE_POS = 13;
const int LON_D_HPPEMISPHERE_LEN = 1;
const int LAT_D_DEGREE_POS = 2;
const int LAT_D_DEGREE_LEN = 9;
const int LON_D_DEGREE_POS = 15;
const int LON_D_DEGREE_LEN = 10;
const int LAT_DM_HPPEMISPHERE_POS = 0;
const int LAT_DM_HPPEMISPHERE_LEN = 1;
const int LON_DM_HPPEMISPHERE_POS = 14;
const int LON_DM_HPPEMISPHERE_LEN = 1;
const int LAT_DM_DEGREE_POS = 2;
const int LAT_DM_DEGREE_LEN = 10;
const int LON_DM_DEGREE_POS = 16;
const int LON_DM_DEGREE_LEN = 11;
const int LAT_DMS_HPPEMISPHERE_POS = 0;
const int LAT_DMS_HPPEMISPHERE_LEN = 1;
const int LON_DMS_HPPEMISPHERE_POS = 15;
const int LON_DMS_HPPEMISPHERE_LEN = 1;
const int LAT_DMS_DEGREE_POS = 2;
const int LAT_DMS_DEGREE_LEN = 11;
const int LON_DMS_DEGREE_POS = 17;
const int LON_DMS_DEGREE_LEN = 12;
const char DEGREE_CHAR = '*';
class LatLonCoord: public TypedCoord
{
public:
LatLonCoord ();
LatLonCoord (double lat, double lon);
LatLonCoord (const TypedCoord& other);
LatLonCoord (const LatLonCoord& other);
LatLonCoord& operator= (const LatLonCoord& other);
LatLonCoord& operator= (const TypedCoord& other);
void copyLatLonCoord (const LatLonCoord& other);
void copyOtherCoord (const TypedCoord& other);
virtual const std::string& createCoordString (std::string& coordString) const;
virtual void createDisplayStrings (std::string& topLeftString,
std::string& topRightString, std::string& bottomLeftString,
std::string& bottomRightString) const;
virtual void createXYCoordStrings (std::string& xString, std::string& yString)
const;
virtual COORD_TYPE getCoordType () const {return COORD_LATLON;}
COORD_LATLON_FORMAT getLatLonFormat () const
{return m_latLonFormat;}
virtual void getXYCoord (double& x, double& y) const;
void setLatLonFormat (COORD_LATLON_FORMAT latLonFormat)
{m_latLonFormat = latLonFormat;}
protected:
// Conversion functions for each of the three lat/lon formats.
const std::string& degreeToD (std::string& output, double degree,
HEMISPHERE_DIRECTION direction) const;
const std::string& degreeToDM (std::string& output, double degree,
HEMISPHERE_DIRECTION direction) const;
const std::string& degreeToDMS (std::string& output, double degree,
HEMISPHERE_DIRECTION direction) const;
const std::string& degreeToString (std::string& output, double degree,
HEMISPHERE_DIRECTION formatType) const;
char getHemisphereChar (double degree, HEMISPHERE_DIRECTION direction)
const;
// Current lat/lon format in one of three format types.
COORD_LATLON_FORMAT m_latLonFormat;
};
#endif
// NmeaParser.cpp: Implementation of the NMEA-0183 2.0 parser class.
//
// Written by Jason Bevins in 1998. File is in the public domain.
//
#ifdef sgi
#include <stdlib.h>
#include <string.h>
#else
#include <cstdlib> // for atof, atoi, atol
#endif
#include "nmeaParser.h"
// This constant define the length of the date string in a sentence.
const int DATE_LEN = 6;
/////////////////////////////////////////////////////////////////////////////
// NMEAData construction/destruction
NMEAData::NMEAData ()
{
// The constructor for the NMEAData object clears all data and resets the
// times of last acquisition to 0.
reset ();
// No coordinate has ever been valid since this object was created.
hasCoordEverBeenValid = false;
}
/////////////////////////////////////////////////////////////////////////////
// NMEAData methods
void NMEAData::reset()
// Purpose:
// This function resets all the data in this object to its defaults.
{
// Reset the data.
lat = 0.0;
lon = 0.0;
altitude = 0.0;
speed = 0.0;
UTCYear = 94;
UTCMonth = 6;
UTCDay = 1;
UTCHour = 0;
UTCMinute = 0;
UTCSecond = 0;
track = 0.0;
magVariation = 0.0;
hdop = 1.0;
eStd = 1.0;
nStd = 1.0;
numSats = 0;
// All data stored by this object is currently invalid.
isValidLat = false;
isValidLon = false;
isValidAltitude = false;
isValidSpeed = false;
isValidDate = false;
isValidTime = false;
isValidTrack = false;
isValidMagVariation = false;
isValidHdop = false;
isValidNStd = false;
isValidEStd = false;
isValidZStd = false;
isValidHStd = false;
isValidSatData = false;
isValidRangeResidualData = false;
isValidFixQuality = false;
// Last fix was invalid.
lastFixQuality = FIX_AUTONOMOUS;
// Still waiting for the frame to start...
waitingForStart = true;
// Clear what messages have been seen
seenZDA = false;
seenGGA = false;
seenGLL = false;
seenRMC = false;
seenGSV = false;
seenGST = false;
seenVTG = false;
seenRRE = false;
}
/////////////////////////////////////////////////////////////////////////////
// NMEAParser construction/destruction
NMEAParser::NMEAParser () //sj: log4cpp::Category& l) : logger(l)
{
m_data = new NMEAData();
// by default, we'll look for "RMC" message to signify a new sequence of messages
strcpy(startSentence, "RMC");
// Defaults for the GSV sentence parser.
m_lastSentenceNumber = 1;
m_numSentences = 1;
m_numSatsExpected = 0;
m_numSatsLeft = 0;
m_satArrayPos = 0;
}
NMEAParser::NMEAParser (NMEAData* data)//sj: log4cpp::Category& l, NMEAData* data) : logger(l)
{
m_data = data;
// Defaults for the GSV sentence parser.
m_lastSentenceNumber = 1;
m_numSentences = 1;
m_numSatsExpected = 0;
m_numSatsLeft = 0;
m_satArrayPos = 0;
}
NMEAParser::~NMEAParser ()
{
delete m_data;
m_data = 0;
}
void NMEAParser::parseZDA(const char* sentence)
{
m_data->seenZDA=true;
char field[255];
uint_ currentPos = 0;
bool isMore = true; // More strings to parse?
// Skip past the '$xxZDA'
if ((isMore = getNextField (field, sentence, currentPos)) == false)
{
return;
}
// UTC time.
isMore = getNextField (field, sentence, currentPos);
parseAndValidateTime (field);
}
void NMEAParser::parseGGA (const char* sentence)
// Purpose:
// This function parses a GGA sentence; all data will be stored in the
// NMEAData object within this class. This function will correctly parse
// a partial GGA sentence.
// Pre:
// The string to parse must be a valid GGA sentence.
{
m_data->seenGGA=true;
char field[255];
char hemisphereField[32];
char hemisphereChar;
char unitField[32];
char unitChar;
uint_ currentPos = 0;
bool isMore = true; // More strings to parse?
// Skip past the '$xxGGA'
if ((isMore = getNextField (field, sentence, currentPos)) == false)
{
return;
}
// UTC time.
isMore = getNextField (field, sentence, currentPos);
parseAndValidateTime (field);
if (isMore == false) return;
// Latitude.
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
isMore = getNextField (hemisphereField, sentence, currentPos);
if (strlen (hemisphereField) != 0) {
hemisphereChar = hemisphereField[0];
} else {
hemisphereChar = ' ';
}
parseAndValidateLat (field, hemisphereChar);
if (isMore == false) return;
// Longitude.
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
isMore = getNextField (hemisphereField, sentence, currentPos);
if (strlen (hemisphereField) != 0) {
hemisphereChar = hemisphereField[0];
} else {
hemisphereChar = ' ';
}
parseAndValidateLon (field, hemisphereChar);
if (isMore == false) return;
// Quality of GPS fix.
isMore = getNextField (field, sentence, currentPos);
parseAndValidateFixQuality (field);
if (isMore == false) return;
// Skip number of sats tracked for now.
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Horizontal dilution of precision (HDOP).
isMore = getNextField (field, sentence, currentPos);
parseAndValidateHdop (field);
if (isMore == false) return;
// Altitude.
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
isMore = getNextField (unitField, sentence, currentPos);
if (strlen (unitField) != 0) {
unitChar = unitField[0];
} else {
unitChar = ' ';
}
parseAndValidateAltitude (field, unitChar);
if (isMore == false) return;
// Everything else (geoid height and DGPS info) is ignored for now.
return;
}
void NMEAParser::parseGLL (const char* sentence)
// Purpose:
// This function parses a GLL sentence; all data will be stored in the
// NMEAData object within this class. This function will correctly parse
// a partial GLL sentence.
// Pre:
// The string to parse must be a valid GLL sentence.
{
m_data->seenGLL=true;
char field[255];
char hemisphereField[32];
char hemisphereChar;
uint_ currentPos = 0;
bool isMore = true; // More strings to parse?
// Count commas in this sentence to see if it's valid.
if (countChars (sentence, ',', SENTENCE_GLL_COMMAS) < 0) return;
// Skip past the '$xxGLL'
if ((isMore = getNextField (field, sentence, currentPos)) == false) {
return;
}
// Latitude
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
isMore = getNextField (hemisphereField, sentence, currentPos);
if (strlen (hemisphereField) != 0) {
hemisphereChar = hemisphereField[0];
} else {
hemisphereChar = ' ';
}
parseAndValidateLat (field, hemisphereChar);
if (isMore == false) return;
// Longitude
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
isMore = getNextField (hemisphereField, sentence, currentPos);
if (strlen (hemisphereField) != 0) {
hemisphereChar = hemisphereField[0];
} else {
hemisphereChar = ' ';
}
parseAndValidateLon (field, hemisphereChar);
if (isMore == false) return;
// UTC time
isMore = getNextField (field, sentence, currentPos);
parseAndValidateTime (field);
if (isMore == false) return;
}
void NMEAParser::parseGSV (const char* sentence)
// Purpose:
// This function parses a GSV sentence; all data will be stored in the
// NMEAData object within this class. This function will correctly parse
// a partial GSV sentence.
// Pre:
// The string to parse must be a valid GSV sentence.
// Notes:
// All GSV sentences from a single packet (a collection of NMEA sentences
// sent from the GPS) must be processed before satellite information in the
// NMEAData object is updated. There is data for only four satellites
// in each GSV sentence, so multiple sentences must be processed. For
// example, if your GPS was tracking eight satellites, two GSV sentences is
// sent from your GPS in each packet; both sentences must be parsed before
// the NMEAData object is updated with the satellite information.
{
m_data->seenGSV=true;
char field[255];
uint_ numSats;
int numSentences;
int sentenceNumber;
uint_ currentPos = 0;
bool isMore = true; // More strings to parse?
// Count commas in this sentence to see if it's valid.
if (countChars (sentence, ',', SENTENCE_GSV_COMMAS) < 0) return;
// Skip past the '$xxGSV'
if ((isMore = getNextField (field, sentence, currentPos)) == false) {
return;
}
// Determine the number of sentences that will make up the satellite data.
isMore = getNextField (field, sentence, currentPos);
numSentences = atoi (field);
if (isMore == false) return;
// Which sentence is this?.
isMore = getNextField (field, sentence, currentPos);
sentenceNumber = atoi (field);
if (isMore == false) return;
// How many satellites are in total?
isMore = getNextField (field, sentence, currentPos);
numSats = atoi (field);
if (isMore == false) return;
// Is this the first sentence? If so, reset the satellite information.
if (sentenceNumber == 1) {
m_lastSentenceNumber = 1;
m_numSentences = numSentences;
m_numSatsExpected = numSats;
m_numSatsLeft = numSats;
m_satArrayPos = 0;
} else {
// Make sure the satellite strings are being sent in order. If not,
// then you're screwed.
if (sentenceNumber != m_lastSentenceNumber + 1) return;
// BUGFIX:
// Added by Clarke Brunt 20001112
m_lastSentenceNumber = sentenceNumber;
}
// parse the satellite string. There are four satellite info fields per
// sentence.
int i;
for (i = 0; i < 4; i++) {
getNextField (field, sentence, currentPos);
if (strlen (field) != 0) {
m_tempSatData[m_satArrayPos].prn = atoi (field);
getNextField (field, sentence, currentPos);
if (strlen (field) != 0) {
m_tempSatData[m_satArrayPos].elevation = atoi (field);
}
getNextField (field, sentence, currentPos);
if (strlen (field) != 0) {
m_tempSatData[m_satArrayPos].azimuth = atoi (field);
}
getNextField (field, sentence, currentPos);
if (strlen (field) != 0) {
m_tempSatData[m_satArrayPos].strength = atoi (field);
}
--m_numSatsLeft;
++m_satArrayPos;
} else {
// Jump past the next three fields.
for (int j = 0; j < 3; j++)
getNextField (field, sentence, currentPos);
}
}
// If all the satellite information has been received, then update the
// NMEAData object with the new satellite data.
if (m_numSatsLeft == 0) {
for (i = 0; i < m_numSatsExpected; i++) {
m_data->satData[i] = m_tempSatData[i];
}
m_data->numSats = m_numSatsExpected;
m_data->isValidSatData = true;
}
}
void NMEAParser::parseRRE (const char* sentence)
{
m_data->seenRRE=true;
bool isMore;
char field[255];
uint_ currentPos = 0;
// Skip past the '$xxRMC'
if ((isMore = getNextField (field, sentence, currentPos)) == false)
{
return;
}
isMore = getNextField (field, sentence, currentPos);
// I assume the RRE numSats is the same as the RRE numSats!
m_data->numSats = atoi(field);
// Skip the intermediate stuff
for (uint_ i = 0; i < m_data->numSats; i++)
{
isMore = getNextField (field, sentence, currentPos);
if (isMore == false)
{
//sj: logger.error("only read %d out of %d entries", m_data->numSats, i);
return;
}
m_data->rrData[i].prn = atoi(field);
isMore = getNextField (field, sentence, currentPos);
if (isMore == false)
{
//sj: logger.error("only read %d out of %d entries", m_data->numSats, i);
return;
}
m_data->rrData[i].residual = atof(field);
}
m_data->isValidRangeResidualData = true;
// Now read the horizontal
isMore = getNextField (field, sentence, currentPos);
if (isMore == false)
{
// sj: logger.error("no fields left to read hStd");
return;
}
m_data->hStd = atof(field);
m_data->isValidHStd = true;
// Now read the vertical
isMore = getNextField (field, sentence, currentPos);
m_data->zStd = atof(field);
m_data->isValidZStd = true;
//sj: logger.debug("isValidRangeResidualData=%d",
//sj: m_data->isValidRangeResidualData);
//sj: logger.debug("isValidHStd=%d", m_data->isValidHStd);
//sj: logger.debug("isValidZStd=%d", m_data->isValidZStd);
}
void NMEAParser::parseRMC (const char* sentence)
// Purpose:
// This function parses an RMC sentence; all data will be stored in the
// NMEAData object within this class. This function will correctly parse
// a partial RMC sentence.
// Pre:
// The string to parse must be a valid RMC sentence.
{
m_data->seenRMC=true;
char field[255];
char hemisphereField[32];
char hemisphereChar;
char directionField[32];
char directionChar;
uint_ currentPos = 0;
bool isMore = true; // More strings to parse?
// Count commas in this sentence to see if it's valid.
if (countChars (sentence, ',', SENTENCE_RMC_COMMAS) < 0) return;
// Skip past the '$xxRMC'
if ((isMore = getNextField (field, sentence, currentPos)) == false) {
return;
}
// UTC time
isMore = getNextField (field, sentence, currentPos);
parseAndValidateTime (field);
if (isMore == false) return;
// Skip past the navigation warning indicator for now.
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Latitude
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
isMore = getNextField (hemisphereField, sentence, currentPos);
if (strlen (hemisphereField) != 0) {
hemisphereChar = hemisphereField[0];
} else {
hemisphereChar = ' ';
}
parseAndValidateLat (field, hemisphereChar);
if (isMore == false) return;
// Longitude
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
isMore = getNextField (hemisphereField, sentence, currentPos);
if (strlen (hemisphereField) != 0) {
hemisphereChar = hemisphereField[0];
} else {
hemisphereChar = ' ';
}
parseAndValidateLon (field, hemisphereChar);
if (isMore == false) return;
// Current speed, in knots.
isMore = getNextField (field, sentence, currentPos);
parseAndValidateSpeed (field);
if (isMore == false) return;
// Current track, in degrees.
isMore = getNextField (field, sentence, currentPos);
parseAndValidateTrack (field);
if (isMore == false) return;
// Current date
isMore = getNextField (field, sentence, currentPos);
parseAndValidateDate (field);
if (isMore == false) return;
// Magnetic variation (degrees from true north)
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
isMore = getNextField (directionField, sentence, currentPos);
if (strlen (directionField) != 0) {
directionChar = directionField[0];
} else {
directionChar = ' ';
}
parseAndValidateMagVariation (field, directionChar);
if (isMore == false) return;
}
void NMEAParser::parseGST (const char* sentence)
{
m_data->seenGST=true;
char field[255];
uint_ currentPos = 0;
bool isMore = true; // More strings to parse?
// Count commas in this sentence to see if it's valid.
if (countChars (sentence, ',', SENTENCE_GST_COMMAS) < 0) return;
// Skip past '$GPGST'
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Get the UTC time
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
parseAndValidateTime(field);
// This field is the RMS value of the standard deviations to the range inputs
// to the navigation process
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Skip unimplemented fields
currentPos += 3;
// Standard deviation of latitude
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Parse the north standard deviation
parseAndValidateNStd(field);
// Standard deviation of longitude
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
parseAndValidateEStd(field);
// Standard deviation of altitude
isMore = getNextField (field, sentence, currentPos);
parseAndValidateZStd(field);
}
void NMEAParser::parseVTG (const char* sentence)
{
m_data->seenVTG=true;
char field[255];
char reference[256];
uint_ currentPos = 0;
bool isMore = true; // More strings to parse?
// Count commas in this sentence to see if it's valid.
if (countChars (sentence, ',', SENTENCE_VTG_COMMAS) < 0) return;
// Skip past '$GPVTG'
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Get the COG wrt to true north
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Get the reference
isMore = getNextField (reference, sentence, currentPos);
if (isMore == false) return;
// Reference should be a 'T' to denote true north
if (reference[0] != 'T')
{
// sj: logger.warn("parseVTG: the reference should be T but it's ",
// sj: reference);
return;
}
// Get the track
parseAndValidateTrack(field);
// Get the COG wrt to magnetic north
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Get the reference
isMore = getNextField (reference, sentence, currentPos);
if (isMore == false) return;
// Reference should be a 'M' to denote wrt magnetic north
if (reference[0] != 'M')
{
// sj: logger.warn("parseVTG: the reference should be M but it's ",
// sj: reference);
return;
}
// Speed, should be in miles per hour
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Get the reference
isMore = getNextField (reference, sentence, currentPos);
if (isMore == false) return;
// Reference should be a 'N' to denote knots per hour
if (reference[0] != 'N')
{
// sj: logger.warn("parseVTG: the reference should be N but it's ",
// sj: reference);
return;
}
// Speed, should be in kilometres per hour
isMore = getNextField (field, sentence, currentPos);
if (isMore == false) return;
// Get the reference
isMore = getNextField (reference, sentence, currentPos);
// Reference should be a 'K' to denote kilometers per hour
if (reference[0] != 'K')
{
// sj: logger.warn("parseVTG: the reference should be K but it's ",
// sj: reference);
return;
}
parseAndValidateSpeed(field);
}
SENTENCE_STATUS NMEAParser::parseSentence (const char* sentence)
// Purpose:
// This function parses a given NMEA sentence. All valid information will be
// stored within the NMEAData object in this class; call the GetData()
// member function to retrieve the data.
// Parameters:
// const char* sentence:
// The sentence to parse.
// Returns:
// SENTENCE_VALID if the sentence passed is a valid NMEA sentence.
// SENTENCE_INVALID if the sentence passed is not a valid NMEA sentence, or
// the sentence type is unsupported (see NMEAParser.h for a list of
// supported sentences.)
// SENTENCE_BAD_CHECKSUM if the sentence has an invalid checksum.
{
#if 0 // sj
if (logger.isDebugEnabled())
{
logger.debug("parsing sentence %s", sentence);
}
#endif
if (isCorrectChecksum (sentence) == false)
{
// sj: logger.debug("SENTENCE_BAD_CHECKSUM");
return SENTENCE_BAD_CHECKSUM;
}
if (isKnownSentenceType (sentence) == false)
{
// sj: logger.debug("SENTENCE_UNKNOWN");
return SENTENCE_UNKNOWN;
}
return parseValidSentence(sentence);
}
SENTENCE_STATUS NMEAParser::parseValidSentence (const char* sentence)
{
#if 0 // sj
if (logger.isDebugEnabled())
{
logger.debug("NMEAParser: parsing %s", sentence);
}
#endif
// Start the parsing 3 spaces past start to get past the initial
// '$xx', where xx is the device type sending the sentences (GP =
// GPS, etc.)
uint_ currentPos = 3;
char sentenceType[4];
if (getNextField (sentenceType, sentence, currentPos) == false)
{
return SENTENCE_INVALID;
}
if (strcmp (sentenceType, startSentence) == 0)
{
// this message is the first in the sequence
m_data->waitingForStart = false;
}
// If we are waiting for the start message to appear, don't bother
// processing any other message type
if (m_data->waitingForStart == true)
{
return SENTENCE_VALID;
}
// Parse the sentence. Make sure the sentence has the correct
// number of commas in it, otherwise the sentence is invalid.
if (strcmp (sentenceType, "GGA") == 0)
{
if (countChars (sentence, ',', SENTENCE_GGA_COMMAS) < 0)
{
return SENTENCE_INVALID;
}
parseGGA (sentence);
}
else if (strcmp (sentenceType, "GLL") == 0)
{
if (countChars (sentence, ',', SENTENCE_GLL_COMMAS) < 0)
{
return SENTENCE_INVALID;
}
parseGLL (sentence);
}
else if (strcmp (sentenceType, "RMC") == 0)
{
if (countChars (sentence, ',', SENTENCE_RMC_COMMAS) < 0)
{
return SENTENCE_INVALID;
}
parseRMC (sentence);
}
else if (strcmp (sentenceType, "GSV") == 0)
{
if (countChars (sentence, ',', SENTENCE_GSV_COMMAS) < 0)
{
return SENTENCE_INVALID;
}
parseGSV (sentence);
}
else if (strcmp (sentenceType, "RRE") == 0)
{
// if (countChars (sentence, ',', SENTENCE_RRE_COMMAS) < 0)
//{
// return SENTENCE_INVALID;
//}
parseRRE (sentence);
}
else if (strcmp (sentenceType, "VTG") == 0)
{
if (countChars (sentence, ',', SENTENCE_VTG_COMMAS) < 0)
{
return SENTENCE_INVALID;
}
parseVTG (sentence);
}
else if (strcmp (sentenceType, "GST") == 0)
{
if (countChars (sentence, ',', SENTENCE_GST_COMMAS) < 0)
{
return SENTENCE_INVALID;
}
parseGST (sentence);
}
else if (strcmp (sentenceType, "ZDA") == 0)
{
if (countChars (sentence, ',', SENTENCE_ZDA_COMMAS) < 0)
{
return SENTENCE_INVALID;
}
parseZDA (sentence);
}
else
{
return SENTENCE_INVALID;
}
return SENTENCE_VALID;
}
/////////////////////////////////////////////////////////////////////////////
// Member functions for individual element parsing of sentences.
bool NMEAParser::parseDate (int& year, int& month, int& day,
const char* dateString) const
// Purpose:
// This function parses a date string from an NMEA sentence in DDMMYY format
// and returns the year, month, and day values.
// Parameters:
// int& year, int& month, int& year:
// Upon exit, these variables will contain the year, month, and day
// values specified in dateString, respectively.
// const char* dateString:
// The NMEA date string to parse.
// Returns:
// true if the date string is in a valid format, false if not.
// Notes:
// - NMEA sentences are *not* "Year 2000-compliant"{tm}; the software must
// correctly determine the year's century.
// - If this function returns false, then the variables year, month, and day
// are unaffected.
{
// Date must be six characters.
if (strlen (dateString) < unsigned (DATE_LEN))
{
return false;
}
long tempDate = atol (dateString);
int tempYear, tempMonth, tempDay;
tempYear = tempDate - ((tempDate / 100) * 100);
tempMonth = (tempDate - ((tempDate / 10000) * 10000)) / 100;
tempDay = tempDate / 10000;
// Check to see if the date is valid. (This function will accept
// Feb 31 as a valid date; no check is made for how many days are in
// each month of our whacked calendar.)
if ((tempYear >= 0 && tempYear <= 99)
&& (tempMonth >= 1 && tempMonth <= 12)
&& (tempDay >= 1 && tempDay <= 31))
{
year = tempYear;
month = tempMonth;
day = tempDay;
return true;
}
return false;
}
bool NMEAParser::parseDegrees (double& degrees, const char* degString) const
// Purpose:
// This function converts a lat/lon string returned from an NMEA string into
// a numeric representation of that string, in degrees. (A lat/lon string
// must be in the format DDMM.M(...) where D = degrees and M = minutes.)
// Pre:
// The string degString must contain a number in the format DDMM.M(...).
// Parameters:
// double& degrees:
// Upon exit, degrees will contain the numeric representation of the
// string passed to this function, in decimal degrees.
// const char* degString:
// Contains the string to convert.
// Returns:
// - true if the conversion was successful. If false is returned, either
// degString was not in one of those required formats, or the string data
// itself is invalid. (For example, the string 23809.666 would not be
// valid, as the 238th degree does not exist.)
// - If this function returns false, then the parameter 'degrees' is
// unaffected.
{
if (strlen (degString) == 0)
{
return false;
}
double tempPosition = atof (degString);
double tempDegrees = (double)((int)(tempPosition / 100.0));
double tempMinutes = (tempPosition - (tempDegrees * 100.0));
tempPosition = tempDegrees + (tempMinutes / 60.0);
if (tempPosition >= 0.0 && tempPosition <= 180.0)
{
degrees = tempPosition;
return true;
}
return false;
}
bool NMEAParser::parseTime (int& hour, int& minute, int& second,
const char* timeString) const
// Purpose:
// This function parses a time string from an NMEA sentence in HHMMSS.S(...)
// format and returns the hour, minute, and second values.
// Parameters:
// int& hour, int& minute, int& second:
// Upon exit, these variables will contain the hour, minute, and second
// values specified in timeString, respectively.
// const char* timeString:
// The NMEA time string to parse.
// Returns:
// true if the time string is in a valid format, false if not.
// Notes:
// - Decimal second values are truncated.
// - If this function returns false, then the variables hour, minute, and
// second are unaffected.
{
if (strlen (timeString) == 0)
{
return false;
}
long tempTime = atol (timeString);
int tempHour = tempTime / 10000;
int tempMinute = (tempTime - ((tempTime / 10000) * 10000)) / 100;
int tempSecond = tempTime - ((tempTime / 100) * 100);
// Check to see if the time is valid.
if ((tempHour >= 0 && tempHour <= 23)
&& (tempMinute >= 0 && tempMinute <= 59)
&& (tempSecond >= 0 && tempSecond <= 61))
{ // leap seconds
hour = tempHour;
minute = tempMinute;
second = tempSecond;
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////
// parse And Validate member functions for NMEAParser.
//
// Each of these member functions parse a specified field from a sentence and
// updates the appropriate member variables in the NMEAData object. If the
// data parsed is valid, the validation member variable associated the parsed
// value is set to true, otherwise it is set to false.
void NMEAParser::parseAndValidateAltitude (const char* field, const char unit)
// Purpose:
// This function parses the altitude field of a sentence.
// Parameters:
// const char* field:
// The altitude field of the sentence to parse.
// const char unit:
// The unit of altitude. Valid values are 'f' (feet) and 'm' (meters).
// Notes:
// The resulting altitude data is specified in feet.
{
// Initially assume data is invalid.
m_data->isValidAltitude = false;
if (strlen (field) != 0) {
if (unit == 'f' || unit == 'F') {
// Altitude is in feet.
m_data->altitude = atof (field) * FEET_TO_METERS;
m_data->isValidAltitude = true;
} else if (unit == 'm' || unit == 'M') {
// Altitude is in meters. Convert to feet.
m_data->altitude = atof (field);
m_data->isValidAltitude = true;
}
}
// sj: logger.debug("isValidAltitude=%d; altitude=%f",
// sj: m_data->isValidAltitude, m_data->altitude);
}
void NMEAParser::parseAndValidateDate (const char* field)
// Purpose:
// This function parses the date field of a sentence, in the format DDMMYY.
{
// Initially assume data is invalid.
m_data->isValidDate = false;
if (strlen (field) != 0) {
int year, month, day;
if (parseDate (year, month, day, field) == true) {
m_data->UTCYear = year;
m_data->UTCMonth = month;
m_data->UTCDay = day;
m_data->isValidDate = true;
}
}
// sj: logger.debug("isValidDate=%d; year=%d; month=%d; day=%d",
// sj: m_data->isValidDate, m_data->UTCYear, m_data->UTCMonth,
// sj: m_data->UTCDay);
}
void NMEAParser::parseAndValidateFixQuality (const char* field)
// Purpose:
// This function parses the GPS fix quality field of a sentence.
{
if (strlen (field) != 0) {
m_data->isValidFixQuality = true;
int fixQuality = atoi (field);
if (fixQuality == 0) m_data->lastFixQuality = FIX_AUTONOMOUS;
else if (fixQuality == 1) m_data->lastFixQuality = FIX_RTCM;
else if (fixQuality == 2) m_data->lastFixQuality = FIX_CPD_FLOAT;
else if (fixQuality == 3) m_data->lastFixQuality = FIX_CPD_FIXED;
else if (fixQuality == 9) m_data->lastFixQuality = FIX_ALMANAC;
else
{
// sj: logger.notice("unknown fix quality %d", fixQuality);
m_data->lastFixQuality = FIX_AUTONOMOUS;
}
}
// sj: logger.debug("lastFixQuality=%d", m_data->lastFixQuality);
}
void NMEAParser::parseAndValidateHdop (const char* field)
// Purpose:
// This function parses the HDOP (horizontal dilution of precision) field of
// a sentence.
{
if (strlen (field) != 0) {
m_data->hdop = atof (field);
m_data->isValidHdop = true;
} else {
m_data->isValidHdop = false;
}
// sj: logger.debug("isValidHdop=%d; hdop=%f", m_data->isValidHdop, m_data->hdop);
}
void NMEAParser::parseAndValidateNStd (const char* field)
// Purpose:
// This function parses the NSTD (1 standard deviation of position estimate in northern
// direction)
{
if (strlen (field) != 0) {
m_data->nStd = atof (field);
m_data->isValidNStd = true;
} else {
m_data->isValidNStd = false;
}
// sj: logger.debug("isValidNStd=%d; nStd=%f", m_data->isValidNStd, m_data->nStd);
}
void NMEAParser::parseAndValidateEStd (const char* field)
// Purpose:
// This function parses the ESTD (1 standard deviation of position estimate in eastern
// direction)
{
if (strlen (field) != 0) {
m_data->eStd = atof (field);
m_data->isValidEStd = true;
} else {
m_data->isValidEStd = false;
}
}
void NMEAParser::parseAndValidateZStd (const char* field)
// Purpose:
// This function parses the ZSTD (1 standard deviation of position estimate in altitude)
{
if (strlen (field) != 0) {
m_data->zStd = atof (field);
m_data->isValidZStd = true;
} else {
m_data->isValidZStd = false;
}
// sj: logger.debug("isValidEStd=%d; eStd=%f", m_data->isValidEStd, m_data->eStd);
}
void NMEAParser::parseAndValidateLat (const char* field, const char hem)
// Purpose:
// This function parses the latitude field of a sentence in the format
// DDMM.M(...).
// Parameters:
// const char* field:
// The latitude field of the sentence to parse.
// const char hem:
// The hemisphere that contains the location. Valid values are 'N' and
// 'S'.
// Notes:
// - If the latitude is in the southern hemisphere, the latitude member
// variable will be negative. (e.g., 4000.000 S will be stored as -40.0.)
// - If the latitude field does not exist within a sentence, the fix
// quality variable, m_data->lastFixQuality, is set to FIX_AUTONOMOUS.
{
// Initially assume data is invalid.
m_data->isValidLat = false;
if (strlen (field) != 0) {
// GPS lat/lon data has been received.
// Set the fix quality to "GPS navigation." This is because some
// GPS's may not send GGA sentences; therefore the last fix quality
// would never get set.
if (m_data->lastFixQuality == FIX_AUTONOMOUS) {
m_data->lastFixQuality = FIX_RTCM;
}
m_data->hasCoordEverBeenValid = true;
double degree;
if (parseDegrees (degree, field) == true) {
if (hem == 'N') {
// Northern hemisphere.
m_data->lat = degree;
m_data->isValidLat = true;
} else if (hem == 'S') {
// Southern hemisphere, so make latitude negative.
m_data->lat = -degree;
m_data->isValidLat = true;
}
}
} else {
m_data->lastFixQuality = FIX_AUTONOMOUS;
}
// sj: logger.debug("isValidLat=%d; lat=%f", m_data->isValidLat, m_data->lat);
}
void NMEAParser::parseAndValidateLon (const char* field, const char hem)
// Purpose:
// Same as parseAndValidateLat(), but the longitude is in the format
// DDDMM.M(...).
// Notes:
// - The valid values for the hem parameter are 'E' and 'W'.
// - If the longitude is in the western hemisphere, the longitude member
// variable will be negative. (e.g., 4000.000 W will be stored as -40.0.)
// - If the latitude field does not exist within a sentence, the last fix
// quality variable, m_data->lastFixQuality, is set to FIX_AUTONOMOUS.
{
// Initially assume data is invalid.
m_data->isValidLon = false;
if (strlen (field) != 0) {
// GPS lat/lon data has been received.
// Set the fix quality to "GPS navigation." This is because some
// GPS's may not send GGA sentences; therefore the last fix quality
// would never get set.
if (m_data->lastFixQuality == FIX_AUTONOMOUS) {
m_data->lastFixQuality = FIX_RTCM;
m_data->hasCoordEverBeenValid = true;
}
double degree;
if (parseDegrees (degree, field) == true) {
if (hem == 'E') {
// Eastern hemisphere.
m_data->lon = degree;
m_data->isValidLon = true;
} else if (hem == 'W') {
// Western hemisphere, so make longitude negative.
m_data->lon = -degree;
m_data->isValidLon = true;
}
}
} else {
m_data->lastFixQuality = FIX_AUTONOMOUS;
}
// sj: logger.debug("isValidLon=%d; lon=%f", m_data->isValidLon, m_data->lon);
}
void NMEAParser::parseAndValidateMagVariation (const char* field,
const char direction)
// Purpose:
// This function parses the magnetic variation field of a sentence, in
// relation to true north.
// Parameters:
// const char* field:
// The magnetic variation field of the sentence to parse, in degrees.
// const char direction:
// The direction of the field in relation to true north. Valid values
// are 'E' and 'W'.
// Notes:
// If the magnetic variation points west of true north, the magnetic
// variation variable will be negative. (e.g., 020.3 W will be stored as
// -20.3.)
{
// Initially assume data is invalid.
m_data->isValidMagVariation = false;
if (strlen (field) != 0) {
double degree = atof (field);
if (degree >= 0.0 && degree <= 360.0) {
if (direction == 'E') {
m_data->magVariation = degree;
m_data->isValidMagVariation = true;
} else if (direction == 'W') {
m_data->magVariation = -degree;
m_data->isValidMagVariation = true;
}
}
}
// sj: logger.debug("isValidMagVariation=%d; magVariation=%f",
// sj: m_data->isValidMagVariation, m_data->magVariation);
}
void NMEAParser::parseAndValidateSpeed (const char* field)
// Purpose:
// This function parses the speed field of a sentence.
{
if (strlen (field) != 0) {
m_data->speed = atof (field);
m_data->isValidSpeed = true;
} else {
m_data->isValidSpeed = false;
}
// sj: logger.debug("isValidSpeed=%d; speed=%f",
// sj: m_data->isValidSpeed, m_data->speed);
}
void NMEAParser::parseAndValidateTime (const char* field)
// Purpose:
// This function parses the date field of a sentence, in the format
// HHMMSS.S(...), except the decimal second values are truncated.
{
// Initially assume data is invalid.
m_data->isValidTime = false;
if (strlen (field) != 0) {
int hour, minute, second;
if (parseTime (hour, minute, second, field) == true) {
m_data->UTCHour = hour;
m_data->UTCMinute = minute;
m_data->UTCSecond = second;
m_data->isValidTime = true;
}
}
// sj: logger.debug("isValidTime=%d; hour=%d; minute=%d; second=%d",
// sj: m_data->isValidTime, m_data->UTCHour, m_data->UTCMinute,
// sj: m_data->UTCSecond);
}
void NMEAParser::parseAndValidateTrack (const char* field)
// Purpose:
// This function parses the track field of a sentence.
{
// Initially assume data is invalid.
m_data->isValidTrack = false;
if (strlen (field) != 0) {
double track = atof (field);
if (track >= 0.0 && track <= 360.0) {
m_data->track = track;
m_data->isValidTrack = true;
}
}
// sj: logger.debug("isValidTrack=%d; track=%f", m_data->isValidTrack, m_data->track);
}
/////////////////////////////////////////////////////////////////////////////
// Miscellaneous member functions
bool NMEAParser::getNextField (char* data, const char* sentence,
uint_& currentPos) const
// Purpose:
// This function retrieves the next field in the NMEA sentence. A field is
// defined as the text between two delimiters (in this case of NMEA
// sentences, a delimiter is a comma character.)
// Pre:
// The specified sentence is valid. (Before calling this function, call the
// member functions IsCorrectChecksum() and ValidSentenceType(), passing the
// sentence to those functions.)
// Parameters:
// char* data:
// Upon exit, this string will contain the contents of the next field
// in the sentence.
// const char* sentence:
// The NMEA sentence to parse.
// uint_& currentPos:
// Determines the initial position within the NMEA sentence in which to
// parse. This function will grab all of the characters from
// currentPos all the way to the character before the comma delimiter.
// Upon exit, currentPosition will point to the next field in the string.
// Note that the comma is not included in the field data.
// Returns:
// true if there are more fields to parse, false if not.
// Notes:
// To grab all of the fields, you can iteratively call GetNextData()
// using the same variable that is passed as currentPosition, until
// GetNextData() returns false.
{
int srcPos = currentPos;
int dstPos = 0;
char currentChar = sentence[srcPos];
// The delimiter character is the comma.
while ((currentChar != '\0' ) && (currentChar != ',')
&& (currentChar != '\x0d') && (currentChar != '*')) {
data[dstPos++] = currentChar;
currentChar = sentence[++srcPos];
}
data[dstPos] = '\0';
if (currentChar == ',') {
// Next data field to parse will be past the comma.
currentPos = srcPos + 1;
return true;
} else {
// No more characters in the string to parse; this function has
// arrived at the end of the string.
return false;
}
}
bool NMEAParser::isCorrectChecksum (const char* sentence) const
// Purpose:
// This function calculates the sentence's checksum and compares it with the
// checksum in the sentence.
// Pre:
// The NMEA sentence is valid (ValidSentenceStructure() must be called with
// this sentence before calling this function; that function must return
// true.)
// Returns:
// true if the checksum is valid or there is no checksum in the sentence.
// (It is not necessary to have a device append a checksum to a sentence.)
// Otherwise this function returns false.
// Notes:
// The checksum in the sentence occurs after the * character.
{
// Check all characters between the initial '$' and the last "*" in the
// sentence and XOR them together.
int charPos = 1; // start past the initial '$'.
char currentChar = sentence[charPos];
uint8_ checksum = 0;
while (currentChar != '*' && currentChar != '\0') {
checksum ^= (uint8_)currentChar;
currentChar = sentence[++charPos];
}
// If no checksum exists (this function has reached the end of the string
// without finding one), the sentence is good.
if (sentence[charPos + 1] == '\0') return true;
// Convert last two hex characters (the checksum) in the sentence with
// the checksum this function has generated.
char firstDigit = sentence[charPos + 1];
char lastDigit = sentence[charPos + 2];
if ( (firstDigit <= '9' ? firstDigit - '0': (firstDigit - 'A') + 10) * 16
+ (lastDigit <= '9' ? lastDigit - '0': (lastDigit - 'A') + 10)
== checksum) {
return true;
} else {
return false;
}
return true;
}
bool NMEAParser::isValidSentenceType (const char* sentence) const
{
if (strlen (sentence) < 6)
{
return false;
}
if (sentence[0] != '$')
{
return false;
}
return isKnownSentenceType(sentence);
}
bool NMEAParser::isKnownSentenceType (const char* sentence) const
// Purpose:
// This function determines whether this is a valid NMEA sentence that this
// class can support.
// Notes:
// See the header file for a list of sentences supported by this class.
{
// Get the three letters after the '$xx'; this is the type of
// sentence. (Note the xx is the type of device which is sending
// the string. For example, GP = GPS, etc.)
char sentenceType[4];
memcpy (sentenceType, &(sentence[3]), 3);
sentenceType[3] = '\0';
return ((strcmp (sentenceType, "ZDA") == 0)
|| (strcmp (sentenceType, "GGA") == 0)
|| (strcmp (sentenceType, "GLL") == 0)
|| (strcmp (sentenceType, "RMC") == 0)
|| (strcmp (sentenceType, "GSV") == 0)
|| (strcmp (sentenceType, "GST") == 0)
|| (strcmp (sentenceType, "VTG") == 0)
|| (strcmp (sentenceType, "RRE") == 0));
}
int NMEAParser::countChars (const char* string, char charToCount, uint_ charCount) const
// Purpose:
// This function counts the number of specified occurrences of the
// specified characters and compares to the number of characters that is
// expected.
// Parameters:
// const char* string:
// The string to check.
// char charToCount:
// The character to count.
// uint_ charCount:
// The number of the characters specified by charToCount that is expected
// to be contained within that string.
// Returns:
// 0 if the number of specified characters in the sentence matches charCount.
// 1 if the number of specified characters in the sentence is less than
// charCount.
// -1 if the number of specified characters in the sentence is greater than
// charCount.
{
size_t stringSize = strlen (string);
size_t currentCharCount = 0;
const char* currentChar = string;
for (size_t i = 0; i < stringSize; i++) {
if (*currentChar++ == charToCount) ++currentCharCount;
}
if (currentCharCount > charCount) {
return 1;
} else if (currentCharCount < charCount) {
return -1;
} else {
return 0;
}
}
/*
* This class was adapted from the GPSThing CNmeaParser class.
* Originally written by Jason Bevins
*/
#ifndef __NMEAPARSER_HPP
#define __NMEAPARSER_HPP
#if 1
typedef unsigned char uint8_;
typedef unsigned short uint16_;
typedef unsigned int uint32_;
typedef unsigned int uint_;
#else
// Get standard types (uint8_, etc.)
#include <cc++/config.h>
#endif
#include <string.h>
// sj: #include <log4cpp/Category.hh>
struct SatData
{
SatData ()
{
prn = 0;
elevation = 0;
azimuth = 0;
strength = 0;
}
SatData (uint8_ prn_, uint16_ elevation_, uint16_ azimuth_,
uint16_ strength_)
{
prn = prn_;
elevation = elevation_;
azimuth = azimuth_;
strength = strength_;
}
uint16_ prn; // Satellite's ID.
uint16_ elevation; // Elevation of satellite, in degrees.
uint16_ azimuth; // Azimuth of satellite, in degrees.
uint16_ strength; // Signal strength of satellite.
};
struct RangeResidualData
{
RangeResidualData()
{
prn = 0;
residual = 0;
}
RangeResidualData(uint8_ prn_, double residual_)
{
prn = prn_;
residual = residual_;
}
uint8_ prn;
double residual;
};
const double METERS_TO_FEET = 3.280839895013;
const double FEET_TO_METERS = 1 / METERS_TO_FEET;
const double KM_TO_NM = 1.853;
const double NM_TO_KM = 1 / KM_TO_NM;
const double KM_TO_MI = FEET_TO_METERS * 5.28;
const double MI_TO_KM = 1 / KM_TO_MI;
// GPS coordinate fix quality.
enum GPS_FIX_QUALITY
{
FIX_AUTONOMOUS = 0,
FIX_RTCM = 1,
FIX_CPD_FLOAT = 2,
FIX_CPD_FIXED = 3,
FIX_ALMANAC = 9
};
// Sentence parsing status.
enum SENTENCE_STATUS
{
SENTENCE_VALID=0, // Sentence parsed is valid.
SENTENCE_INVALID, // Sentence parsed has invalid data.
SENTENCE_BAD_CHECKSUM, // Sentence parsed has a bad checksum.
SENTENCE_UNKNOWN // Sentence is of unknown type
};
// Number of commas in each sentence. In order for a sentence to be valid,
// it must have a specified number of commas.
enum SENTENCE_COMMA_SIZES
{
SENTENCE_ZDA_COMMAS = 6,
SENTENCE_GGA_COMMAS = 14,
SENTENCE_GLL_COMMAS = 6,
SENTENCE_RMC_COMMAS = 11,
SENTENCE_GSV_COMMAS = 19,
SENTENCE_VTG_COMMAS = 7,
SENTENCE_GST_COMMAS = 8,
SENTENCE_RRE_COMMAS = 4
};
// Maximum size of an NMEA sentence (plus the NULL character.)
const int MAX_SENTENCE_SIZE = 1024;
// No GPS I'm aware of can track more than 12 satellites.
const uint8_ MAX_SATS = 12;
// Data class stored with the parser. To extract the data parsed from the
// parser, pass an object of this class to the parser.
// NOTE! NMEA sentences are not "Year 2000-compliant"{tm}
struct NMEAData
{
NMEAData ();
virtual ~NMEAData()
{
}
virtual void reset ();
// Data retrieved from the NMEA sentences.
double lat; // Latitude, in degrees (positive=N, negative=S)
double lon; // Longitude, in degrees (positive=E, negative=W)
double altitude; // Altitude, in feet
double speed; // Speed, in knots
double track; // Current track, in degrees.
double magVariation; // Magnetic variation, in degrees.
double hdop; // Horizontal dilution of precision.
double nStd; // North standard deviation.
double eStd; // East standard deviation.
double zStd; // Altitude standard deviation.
double hStd; // Horizonal standard deviation.
uint_ numSats; // Number of satellites in the sky.
int UTCYear; // GPS Date (UTC), year part
int UTCMonth; // GPS Date (UTC), month part
int UTCDay; // GPS Date (UTC), day part
int UTCHour; // GPS Time (UTC), hour part.
int UTCMinute; // GPS Time (UTC), minute part
int UTCSecond; // GPS Time (UTC), second part
SatData satData[MAX_SATS];
RangeResidualData rrData[MAX_SATS];
// Quality of last fix:
// 0 = invalid, 1 = GPS fix, 2 = DGPS fix.
GPS_FIX_QUALITY lastFixQuality;
// Validity of data parsed.
bool isValidLat; // Latitude
bool isValidLon; // Longitude
bool isValidAltitude; // Altitude
bool isValidSpeed; // Speed
bool isValidDate; // Date
bool isValidTime; // Time
bool isValidTrack; // Track
bool isValidMagVariation; // Magnetic variation
bool isValidHdop; // Horizontal dilution of precision
bool isValidNStd;
bool isValidEStd;
bool isValidZStd;
bool isValidSatData; // Satellite data
bool isValidRangeResidualData; // Satellite data
bool isValidHStd;
bool isValidFixQuality;
// Has a valid coordinate ever been sent over the serial port?
bool hasCoordEverBeenValid;
// Flag indicates if we are waiting for the frame to start
bool waitingForStart;
// Whether we have seen a particular message since the data was
// reset
bool seenZDA;
bool seenGGA;
bool seenGLL;
bool seenRMC;
bool seenGSV;
bool seenGST;
bool seenVTG;
bool seenRRE;
};
class NMEAParser
{
public:
NMEAParser (/* sj: log4cpp::Category& l*/);
virtual ~NMEAParser ();
SENTENCE_STATUS parseSentence (const char* sentence);
void setStartSentence(char *sentence)
{
strcpy(startSentence, sentence);
}
void getData (NMEAData& data) const
{
data = *m_data;
}
NMEAData& getData()
{
return *m_data;
}
void reset ()
{
m_data->reset ();
}
bool isValidSentenceType (const char* sentence) const;
bool isCorrectChecksum (const char* sentence) const;
protected:
NMEAParser (/* sj: log4cpp::Category& l,*/ NMEAData* data);
bool parseDegrees (double& degrees, const char* degString) const;
bool parseDate (int& year, int& month, int& day,
const char* dateString) const;
bool parseTime (int& hour, int& minute, int& second,
const char* timeString) const;
void parseAndValidateAltitude (const char* field, const char unit);
void parseAndValidateDate (const char* field);
void parseAndValidateFixQuality (const char* field);
void parseAndValidateLat (const char* field, const char hem);
void parseAndValidateLon (const char* field, const char hem);
void parseAndValidateHdop (const char* field);
void parseAndValidateSpeed (const char* field);
void parseAndValidateNStd (const char* field);
void parseAndValidateEStd (const char* field);
void parseAndValidateZStd (const char* field);
void parseAndValidateMagVariation(const char* field,
const char direction);
void parseAndValidateTime (const char* field);
void parseAndValidateTrack (const char* field);
bool getNextField(char* data, const char* sentence,
uint_& currentPosition) const;
int countChars(const char* sentence, char charToCount,
uint_ charCount) const;
virtual bool isKnownSentenceType (const char* sentence) const;
virtual SENTENCE_STATUS parseValidSentence (const char* sentence);
NMEAData* m_data;
// the sentence that marks the beginning of the set of packets
char startSentence[16];
// Needed for parsing the GSV sentence.
int m_lastSentenceNumber;// Which sentence number was the last one?
int m_numSentences; // Number of sentences to process.
int m_numSatsExpected; // Number of satellites expected to parse.
int m_numSatsLeft; // Number of satellites left to parse.
int m_satArrayPos; // Array position of the next sat entry.
SatData m_tempSatData[MAX_SATS];
// The logging category
// sj: log4cpp::Category& logger;
private:
void parseZDA(const char* sentence);
void parseGGA (const char* sentence);
void parseGLL (const char* sentence);
void parseRMC (const char* sentence);
void parseGSV (const char* sentence);
void parseGST (const char* sentence);
void parseVTG (const char* sentence);
void parseRRE (const char* sentence);
};
#endif
// TypedCoord.C: Implementation of the CTypedCoord abstract base class.
//
// Written by Jason Bevins in 1998. File is in the public domain.
//
#include "gpsnmealib/nmeaParser.h" // for GPS_FIX_QUALITY
#include "typedCoord.h"
#ifdef sgi
#include <math.h>
#else
#include <cmath> // for sqrt, atan2, cos, sin, fabs
#endif
// Mmmmm, pi...
static const double PI = 3.141592653589793;
// Radians to degrees conversion constants.
static const double RAD_TO_DEG = 180.0 / PI;
static const double DEG_TO_RAD = PI / 180.0;
static double mod (double num, double divisor)
// Purpose:
// This function determines the remainder of num / divisor.
{
if (num >= 0) {
return num - divisor * (int)(num / divisor);
} else {
return num + divisor * (int)((-num / divisor) + 1);
}
}
/////////////////////////////////////////////////////////////////////////////
// TypedCoord operators
TypedCoord& TypedCoord::operator= (const TypedCoord& other)
{
double lat, lon;
other.getLatLonCoord (lat, lon);
setLatLonCoord (lat, lon);
return *this;
}
/////////////////////////////////////////////////////////////////////////////
// TypedCoord members
void TypedCoord::calculateDistAndBearing (const TypedCoord& coord,
double& dist, double& bearingStartToEnd, double& bearingEndToStart) const
// Purpose:
// This function calculates the distance between this coordinate and the
// specified coordinate on the Earth and determines the bearing to the
// coordinates in the forward and the reverse direction.
// Parameters:
// TypedCoord& coord:
// The specified coordinate.
// double& dist:
// Upon exit, this parameter contains the distance between the two
// coordinates, in meters.
// double& bearingStartToEnd:
// Upon exit, this parameter contains the bearing from this coordinate
// to the specified coordinate, in degrees.
// double& bearingEndToStart:
// Upon exit, this parameter contains the bearing from the specified
// coordinate to this coordinate, in degrees.
{
double startLat, startLon;
double endLat, endLon;
getLatLonCoord (startLat, startLon);
coord.getLatLonCoord (endLat, endLon);
distAndBearingWGS84 (startLat, startLon, endLat, endLon, dist,
bearingStartToEnd, bearingEndToStart);
}
void TypedCoord::distAndBearing (double a, double f, double startLat,
double startLon, double endLat, double endLon, double& dist,
double& bearingStartToEnd, double& bearingEndToStart) const
// Purpose:
// This function calculates the distance between two coordinates on the Earth
// and determines the bearing to the coordinates in the forward and the
// reverse direction.
// Pre:
// startLat and endLat does not equal 90 or -90 degrees.
// Parameters:
// double a:
// Ellipsoid semi-major axis, in meters. (For WGS84 datum, use 6378137.0)
// double f:
// Ellipsoid flattening. (For WGS84 datum, use 1 / 298.257223563)
// double startLat:
// The starting latitude.
// double startLon:
// The starting longitude.
// double endLat:
// The ending latitude.
// double startLon:
// The ending longitude.
// double& dist:
// Upon exit, this parameter contains the distance between the two
// coordinates, in meters.
// double& bearingStartToEnd:
// Upon exit, this parameter contains the bearing from the starting
// coordinate to the ending coordinate, in degrees.
// double& bearingEndToStart:
// Upon exit, this parameter contains the bearing from the ending
// coordinate to the starting coordinate, in degrees.
{
double c;
double c1;
double c2;
double c2a;
double sinX, cosX;
double cy, cz;
double d, e;
double r, s;
double s1, sa, sy;
double tan1, tan2;
double x, y;
double t, t1, t2, t3, t4, t5;
double coordStartLatRad = startLat * DEG_TO_RAD;
double coordStartLonRad = startLon * DEG_TO_RAD;
double coordEndLatRad = endLat * DEG_TO_RAD;
double coordEndLonRad = endLon * DEG_TO_RAD;
r = 1.0 - f;
tan1 = (r * sin (coordStartLatRad)) / cos (coordStartLatRad);
tan2 = (r * sin (coordEndLatRad)) / cos (coordEndLatRad);
c1 = 1.0 / sqrt ((tan1 * tan1) + 1.0);
s1 = c1 * tan1;
c2 = 1.0 / sqrt ((tan2 * tan2) + 1.0);
s = c1 * c2;
bearingEndToStart = s * tan2;
bearingStartToEnd = bearingEndToStart * tan1;
x = coordEndLonRad - coordStartLonRad;
int exitLoop = 0;
int loopCount = 0;
while (exitLoop == 0 && loopCount < 6)
{
sinX = sin (x);
cosX = cos (x);
tan1 = c2 * sinX;
tan2 = bearingEndToStart - (s1 * c2 * cosX);
sy = sqrt ((tan1 * tan1) + (tan2 * tan2));
cy = (s * cosX) + bearingStartToEnd;
y = atan2 (sy, cy);
sa = (s * sinX) / sy;
c2a = ((-sa) * sa) + 1.0;
cz = bearingStartToEnd * 2.0;
if (c2a > 0.0) {
cz = ((-cz) / c2a) + cy;
}
e = (cz * cz * 2.0) - 1.0;
c = (((((-3.0 * c2a) + 4.0 ) * f) + 4.0) * c2a * f) / 16.0;
d = x;
t = ((((e * cy * c) + cz) * sy * c) + y) * sa;
x = ((1.0 - c) * t * f) + coordEndLonRad - coordStartLonRad;
// Make sure this function does not go into an infinite loop...
if (fabs (d - x) > 0.00000000005) {
exitLoop = 0;
} else {
exitLoop = 1;
}
++loopCount;
}
bearingStartToEnd = atan2 (tan1, tan2);
bearingEndToStart = atan2 (c1 * sinX, ((bearingEndToStart * cosX)
- (s1 * c2))) + PI;
t = sqrt ((((1.0 / r / r) - 1) * c2a) + 1.0) + 1.0;
x = (t - 2.0) / t;
t = 1.0 - x;
c = (((x * x) / 4.0) + 1.0) / t;
d = ((0.375 * (x * x)) - 1.0) * x;
x *= cy;
s = (1.0 - e) - e;
t1 = (sy * sy * 4.0) - 3.0;
t2 = ((s * cz * d) / 6.0) - x;
t3 = t1 * t2;
t4 = ((t3 * d) / 4.0) + cz;
t5 = (t4 * sy * d ) + y;
dist = t5 * c * a * r;
bearingStartToEnd *= RAD_TO_DEG;
bearingEndToStart *= RAD_TO_DEG;
// Make sure the bearings are between 0 and 360, inclusive.
bearingStartToEnd = mod (bearingStartToEnd, 360.0);
bearingEndToStart = mod (bearingEndToStart, 360.0);
}
void TypedCoord::distAndBearingWGS84 (double startLat, double startLon,
double endLat, double endLon, double& dist, double& bearingStartToEnd,
double& bearingEndToStart) const
// Purpose:
// This function calculates the distance between two coordinates on the Earth
// and determines the bearing to the coordinates in the forward and the
// reverse direction.
// Pre:
// startLat and endLat does not equal 90 or -90 degrees.
// Parameters:
// See TypedCoord::DistAndBearing().
{
distAndBearing (6378137.0, 1 / 298.257223563, startLat, startLon, endLat,
endLon, dist, bearingStartToEnd, bearingEndToStart);
}
void TypedCoord::getLatLonCoord (double& lat, double& lon) const
// Purpose:
// This function returns the latitude and longitude contained within this
// object.
{
lat = m_lat;
lon = m_lon;
}
void TypedCoord::setFixQuality (GPS_FIX_QUALITY fixQuality)
// Purpose:
// This function sets the fix quality of the coordinate. Use this function
// to store the quality of the fix from a GPS with the coordinate.
{
m_fixQuality = fixQuality;
}
// TypedCoord.h: Definition of the CTypedCoord abstract base class.
//
// The CTypedCoord class is an abstract base class whose derived classes
// represents a WGS84 coordinate with a specific type. For example, a UTM
// coordinate class can be derived from the CTypedCoord class. These classes
// store a coordinate in the class' coordinate type.
//
// Since a GPS uses lat/lon format for its coordinates, and distance/bearing
// formulas require lat/lon coordinates, CTypedCoord contains a latitude and
// a longitude member variable; member functions exist for converting between
// the lat/lon coordinate and the class' coordinate type.
//
// Conversion of coordinates from lat/lon to another format can be very
// computationally intensive, especially on machines without FPUs. Because
// of this, it is encouraged that derived classes use lazy evaluation when
// returning coordinates, so the conversions take place only when necessary.
// Because of the lazy evaluation, derived classes should not access the
// coordinate member variables directly; use the GetLatLonCoord() and the
// SetLatLonCoord() member functions to access and/or modify these variables.
//
// Since this is an abstract base class, you *must* override the following
// member functions in your derived classes:
// - GetCoordType(): Override this member function to return the type of this
// object. Each derived class from CTypedCoord has a unique type ID that
// specified the type of this object. Add a value to the enumerated type
// COORD_TYPE when deriving a new class, and return this value from this
// function. The COORD_TYPE enumerated type is found elsewhere in this
// project.
// - CreateCoordString(): Override this member function to create a formatted
// coordinate std::string using the coordinate within the class.
// - CreateXYCoordStrings(): Override this member function to create two
// formatted std::strings containing the x and y coordinate values;
// - GetXYCoord(): Override this member function to return the (x, y)
// coordinate values. This function is useful when the other data (e.g.,
// zones, etc.) are not needed.
// - CreateDisplayStrings: Override this function to create four std::strings that
// can be used for display purposes. The four std::strings contain coordinate
// data; these std::strings are displayed at the top left, top right, bottom
// left, and bottom right of a display. For example:
// A lat/lon coordinate class may create std::strings as follows:
// - top left: North/south hemisphere character
// - top right: Latitude
// - bottom left: East/west hemisphere character
// - bottom right: Longitude
// This would create a display as follows:
// N 48*25'33.3"
// W 123*20'09.8"
// A UTM coordinate class may create std::strings as follows:
// - top left: UTM zone (e.g., "10U")
// - top right: UTM easting
// - bottom left A std::string describing the coordinate type ("UTM")
// - bottom right: UTM northing
// This would create a display as follows:
// 10U 475139
// UTM 5363695
// It is up to the calling function to write the std::strings to a display.
//
// Member functions that can (and should) be overridden include:
// - SetLatLonCoord(): Override this member function to set the current lat/
// lon coordinate within this class and convert the lat/lon coordinate to
// the appropriate coordinate type so it can be stored within this class.
// - GetLatLonCoord(): Override this member function so that the lat/lon
// coordinate can be returned by converting the class' coordinate from the
// class' coordinate type.
// - operator=(): Override this operator to implement assignment operations.
// Two operator=() functions should be created as follows:
// - An operator=() function that takes a parameter of type CTypedCoord&.
// Use the GetLatLonCoord() function to convert the right hand side
// coordinate from its current type to a pair of lat/lon values, then
// call this class' SetLatLonCoord() function to convert the lat/lon pair
// to a coordinate of this class' type.
// - An operator=() function that takes a parameter of a reference to an
// object as the same type as the object. Simply copy all member
// variables from the right hand side object to this object.
//
// Using coordinate objects derived from this base class, it is simple to
// perform such operations as:
// - Finding the distance between two coordinate objects with different
// coordinate types.
// - Assigning the coordinates of an object of one coordinate type to another
// object with a different coordinate type.
//
// [TO DO: Allow for other map datums. For now, these coordinates are using
// the WGS84 datum.]
//
// Written by Jason Bevins in 1998. File is in the public domain.
//
#ifndef __TYPED_COORD_HPP
#define __TYPED_COORD_HPP
#include <string>
// Because of the way I hacked up the GPSThing code
// Needed for GPS_FIX_QUALITY
#include "nmeaParser.h"
enum COORD_TYPE
{
COORD_LATLON = 0,
COORD_UTM = 1
};
class TypedCoord
{
public:
TypedCoord () {m_lat = 0; m_lon = 0;}
virtual ~TypedCoord () {}
TypedCoord& operator= (const TypedCoord& other);
void calculateDistAndBearing (const TypedCoord& coord, double& dist,
double& dirStartToEnd, double& bearingEndToStart) const;
virtual const std::string& createCoordString (std::string& coordString) const
= 0;
virtual void createDisplayStrings (std::string& topLeftString,
std::string& topRightString, std::string& bottomLeftString,
std::string& bottomRightString) const = 0;
virtual void createXYCoordStrings (std::string& xString,
std::string& yString) const = 0;
virtual COORD_TYPE getCoordType () const = 0;
GPS_FIX_QUALITY getFixQuality () const
{return m_fixQuality;}
virtual void getLatLonCoord (double& lat, double& lon) const;
virtual void getXYCoord (double& x, double& y) const = 0;
void setFixQuality (GPS_FIX_QUALITY fixQuality);
virtual void setLatLonCoord (double lat, double lon)
{m_lat = lat; m_lon = lon;}
protected:
void distAndBearing (double a, double f, double startLat,
double startLon, double endLat, double endLon,
double& dist,
double& bearingStartToEnd,
double& bearingEndToStart) const;
void distAndBearingWGS84 (double startLat, double startLon,
double endLat, double endLon, double& dist,
double& bearingStartToEnd,
double& bearingEndToStart) const;
// Current lat/lon coordinate.
double m_lat;
double m_lon;
// Fix quality for the coordinate that originated from a GPS.
GPS_FIX_QUALITY m_fixQuality;
};
#endif // __TYPED_COORD_HPP
// UtmCoord.C: Implementaion of the CUtmCoord class.
//
// Written by Jason Bevins in 1998. File is in the public domain.
//
#include "gpsnmealib/typedCoord.h" // for TypedCoord
#include "utmCoord.h"
#ifdef sgi
#include <assert.h>
#include <math.h>
#else
#include <cassert> // for assert
#include <cmath> // for sin, cos, sqrt, M_PI
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// Radians to degrees conversion constants.
static const double RAD_TO_DEG = 180.0 / M_PI;
static const double DEG_TO_RAD = M_PI / 180.0;
// Some constants used by these functions.
static const double fe = 500000.0;
static const double ok = 0.9996;
// An array containing each vertical UTM zone.
static char cArray[] = "CDEFGHJKLMNPQRSTUVWX";
/////////////////////////////////////////////////////////////////////////////
// Miscellaneous functions for these UTM conversion formulas.
static double calculateESquared (double a, double b)
{
return ((a * a) - (b * b)) / (a * a);
}
static double calculateE2Squared (double a, double b)
{
return ((a * a) - (b * b)) / (b * b);
}
static double denom (double es, double sphi)
{
double sinSphi = sin (sphi);
return sqrt (1.0 - es * (sinSphi * sinSphi));
}
static double sphsr (double a, double es, double sphi)
{
double dn = denom (es, sphi);
return a * (1.0 - es) / (dn * dn * dn);
}
static double sphsn (double a, double es, double sphi)
{
double sinSphi = sin (sphi);
return a / sqrt (1.0 - es * (sinSphi * sinSphi));
}
static double sphtmd (double ap, double bp, double cp, double dp, double ep,
double sphi)
{
return (ap * sphi) - (bp * sin (2.0 * sphi)) + (cp * sin (4.0 * sphi))
- (dp * sin (6.0 * sphi)) + (ep * sin (8.0 * sphi));
}
/////////////////////////////////////////////////////////////////////////////
// UTMCoord construction/destruction
UTMCoord::UTMCoord ()
{
m_requireLatLonConvert = false;
m_requireUTMConvert = false;
setLatLonCoord (0.0, 0.0);
}
UTMCoord::UTMCoord (int utmXZone, char utmYZone, double easting,
double northing)
{
m_requireLatLonConvert = false;
m_requireUTMConvert = false;
setUTMCoord (utmXZone, utmYZone, easting, northing);
}
UTMCoord::UTMCoord (const UTMCoord& other)
{
copyUTMCoord (other);
}
UTMCoord::UTMCoord (const TypedCoord& other)
{
copyOtherCoord (other);
}
/////////////////////////////////////////////////////////////////////////////
// UTMCoord operators
UTMCoord& UTMCoord::operator= (const UTMCoord& other)
{
copyUTMCoord (other);
return *this;
}
UTMCoord& UTMCoord::operator= (const TypedCoord& other)
{
copyOtherCoord (other);
return *this;
}
/////////////////////////////////////////////////////////////////////////////
// UTMCoord members
void UTMCoord::copyUTMCoord (const UTMCoord& other)
// Purpose:
// This function copies the UTM coordinate of the specified object and
// assigns that coordinate to this object.
// Notes:
// This function copies every member function from that object, including
// its lat/lon value and its lazy evaluation flags.
{
m_lat = other.m_lat;
m_lon = other.m_lon;
m_utmXZone = other.m_utmXZone;
m_utmYZone = other.m_utmYZone;
m_easting = other.m_easting;
m_northing = other.m_northing;
m_requireLatLonConvert = other.m_requireLatLonConvert;
m_requireUTMConvert = other.m_requireUTMConvert;
}
void UTMCoord::copyOtherCoord (const TypedCoord& other)
// Purpose:
// This function copies the coordinate of the specified object, converts
// the coordinate to the coordinate type of this object, then assigns the
// converted coordinate to this object.
{
// Convert the right hand side coordinate to lat/lon; then convert this
// lat/lon coordinate to UTM.
double lat, lon;
other.getLatLonCoord (lat, lon);
setLatLonCoord (lat, lon);
}
const std::string& UTMCoord::createCoordString (std::string& coordString) const
// Purpose:
// This function creates a formatted coordinate string containing this
// object's coordinate.
// Parameters:
// CString& coordString:
// Upon exit, this parameter will contain the coordinate string.
// Returns:
// The coordinate string.
// Notes:
// If the coordinate is outside of the UTM grid boundary (>= 84 N or <= 80 S)
// the string will contain the resource string specified by the constant
// IDS_OUTSIDE_UTM_BOUNDARY.
{
double easting;
double northing;
int utmXZone;
char utmYZone;
getUTMCoord (utmXZone, utmYZone, easting, northing);
#if 0
if (utmYZone != '*') {
coordString.Format ("%02d%c %06d %07d", utmXZone, utmYZone,
(int)easting, (int)northing);
} else {
// UTM vertical zone is out of range.
coordString.LoadString (IDS_OUTSIDE_UTM_BOUNDARY);
}
#endif
return coordString;
}
void UTMCoord::createDisplayStrings (std::string& topLeftString,
std::string& topRightString, std::string& bottomLeftString,
std::string& bottomRightString) const
// Purpose:
// This function creates four strings that can be used for display; these
// strings contain coordinate data. The four strings are for the top left,
// top right, bottom left, and bottom right text of a display; each string
// is specified as follows:
// - top left: UTM zone
// - top right: Easting
// - bottom left: "UTM"
// - bottom right: Northing
// Notes:
// It is up to the calling function to write these strings to the display.
{
#if 0
bottomLeftString.LoadString (IDS_UTM);
if (IsOutsideUTMGrid () == false) {
// Extract the elements of the coordinate string.
CString coordString;
CreateCoordString (coordString);
topLeftString = coordString.Mid (UTM_ZONE_POS, UTM_ZONE_LEN);
topRightString = coordString.Mid (UTM_EASTING_POS, UTM_EASTING_LEN);
bottomRightString = coordString.Mid (UTM_NORTHING_POS,
UTM_NORTHING_LEN);
} else {
// The coordinate is outside of the UTM grid boundary, so the
// coordiante strings are empty.
topLeftString = "";
topRightString = "";
bottomRightString = "";
}
#endif
}
void UTMCoord::createXYCoordStrings (std::string& xString, std::string& yString)
const
// Purpose:
// This function generates two strings: a string containing the easting
// coordinate (stored in the parameter xString), and a string containing the
// northing coordinate (stored in the parameter yString.)
{
double easting;
double northing;
int utmXZone;
char utmYZone;
getUTMCoord (utmXZone, utmYZone, easting, northing);
#if 0
xString.Format ("%06d", (int)easting);
yString.Format ("%07d", (int)northing);
#endif
}
void UTMCoord::getLatLonCoord (double& lat, double& lon) const
// Purpose:
// This function converts the object's UTM coordinate to a lat/lon coordinate
// and returns the latitude and longitude.
{
if (m_requireLatLonConvert == true) {
// The conversion between UTM and lat/lon has not occurred yet, so do
// the conversion now. A non-const pointer to this object must be
// created so that this function can convert the UTM coordinate stored
// within this object to a lat/lon coordinate if the conversion is
// necessary. This lat/lon coordinate is then copied to the m_lat
// and m_lon members of this object, requiring the object to be
// non-const.
UTMCoord* myThis = const_cast<UTMCoord*>(this);
// Convert to lat/lon.
utmToLatLonWGS84 (myThis->m_utmXZone, myThis->m_utmYZone,
myThis->m_easting, myThis->m_northing, myThis->m_lat,
myThis->m_lon);
// Do not make this conversion again (unless the coordinates have been
// changed and another conversion is required.)
myThis->m_requireLatLonConvert = false;
}
lat = m_lat;
lon = m_lon;
}
void UTMCoord::getUTMCoord (int& utmXZone, char& utmYZone, double& easting,
double& northing) const
// Purpose:
// This function returns the current UTM coordinate.
{
if (m_requireUTMConvert == true) {
// The conversion between lat/lon and UTM has not occurred yet, so do
// the conversion now. A non-const pointer to this object must be
// created so that this function can convert the lat/lon coordinate
// stored within this object to a UTM coordinate if the conversion
// is necessary. This UTM coordinate is then copied to the m_utmXZone
// m_utmYZone, m_easting, and m_northing members of this object,
// requiring the object to be non-const.
UTMCoord* myThis = const_cast<UTMCoord*>(this);
// Convert to UTM.
latLonToUTMWGS84 (myThis->m_utmXZone, myThis->m_utmYZone,
myThis->m_easting, myThis->m_northing, myThis->m_lat,
myThis->m_lon);
// Do not make this conversion again (unless the coordinates have been
// changed and another conversion is required.)
myThis->m_requireUTMConvert = false;
}
easting = m_easting;
northing = m_northing;
utmXZone = m_utmXZone;
utmYZone = m_utmYZone;
}
void UTMCoord::getUTMZone (int& utmXZone, char& utmYZone) const
// Purpose:
// This function returns the current UTM zone.
// Parameters:
// int& utmXZone:
// Upon exit, this parameter contains the UTM horizontal zone. This
// zone will be between 1 and 60, inclusive.
// char& utmYZone:
// Upon exit, this parameter contains the UTM vertical zone. This
// zone will be one of: CDEFGHJKLMNPQRSTUVWX.
{
double easting;
double northing;
getUTMCoord (utmXZone, utmYZone, easting, northing);
}
void UTMCoord::getXYCoord (double& x, double& y) const
// Purpose:
// This function returns the coordinate's (x, y) coordinate. For this class,
// the x coordinate is the easting, and the y coordinate is the northing.
{
int utmXZone;
char utmYZone;
getUTMCoord (utmXZone, utmYZone, x, y);
}
bool UTMCoord::isOutsideUTMGrid () const
// Purpose:
// This function determines whether the current coordinate is outside of the
// UTM grid boundary (i.e., >= 84 N or <= 80 s)
{
// If a UTM coordinate has been converted to a lat/lon coordinate by lazy
// evaluation, simply determine whether the coordinate is north of 84, or
// south of 80.
if (m_requireLatLonConvert == false) {
if (m_lat >= 84.0 || m_lat <= -80.0) {
return true;
} else {
return false;
}
} else if (m_requireUTMConvert == false) {
if (m_utmYZone == '*') {
return true;
} else {
return false;
}
} else {
// This should not happen
assert(false);
return false;
}
}
void UTMCoord::latLonToUTM (double a, double f, int& utmXZone, char& utmYZone,
double& easting, double& northing, double lat, double lon) const
// Purpose:
// This function converts the specified lat/lon coordinate to a UTM
// coordinate.
// Parameters:
// double a:
// Ellipsoid semi-major axis, in meters. (For WGS84 datum, use 6378137.0)
// double f:
// Ellipsoid flattening. (For WGS84 datum, use 1 / 298.257223563)
// int& utmXZone:
// Upon exit, this parameter will contain the hotizontal zone number of
// the UTM coordinate. The returned value for this parameter is a number
// within the range 1 to 60, inclusive.
// char& utmYZone:
// Upon exit, this parameter will contain the zone letter of the UTM
// coordinate. The returned value for this parameter will be one of:
// CDEFGHJKLMNPQRSTUVWX.
// double& easting:
// Upon exit, this parameter will contain the UTM easting, in meters.
// double& northing:
// Upon exit, this parameter will contain the UTM northing, in meters.
// double lat, double lon:
// The lat/lon coordinate to convert.
// Notes:
// - The code in this function is a C conversion of some of the source code
// from the Mapping Datum Transformation Software (MADTRAN) program,
// written in PowerBasic. To get the source code for MADTRAN, go to:
//
// http://164.214.2.59/publications/guides/MADTRAN/index.html
//
// and download MADTRAN.ZIP
// - If the UTM zone is out of range, the y-zone character is set to the
// asterisk character ('*').
{
double recf;
double b;
double eSquared;
double e2Squared;
double tn;
double ap;
double bp;
double cp;
double dp;
double ep;
double olam;
double dlam;
double s;
double c;
double t;
double eta;
double sn;
double tmd;
double t1, t2, t3, t6, t7;
double nfn;
if (lon <= 0.0) {
utmXZone = 30 + (int)(lon / 6.0);
} else {
utmXZone = 31 + (int)(lon / 6.0);
}
if (lat < 84.0 && lat >= 72.0) {
// Special case: zone X is 12 degrees from north to south, not 8.
utmYZone = cArray[19];
} else {
utmYZone = cArray[(int)((lat + 80.0) / 8.0)];
}
if (lat >= 84.0 || lat < -80.0) {
// Invalid coordinate; the vertical zone is set to the invalid
// character.
utmYZone = '*';
}
double latRad = lat * DEG_TO_RAD;
double lonRad = lon * DEG_TO_RAD;
recf = 1.0 / f;
b = a * (recf - 1.0) / recf;
eSquared = calculateESquared (a, b);
e2Squared = calculateE2Squared (a, b);
tn = (a - b) / (a + b);
ap = a * (1.0 - tn + 5.0 * ((tn * tn) - (tn * tn * tn)) / 4.0 + 81.0 *
((tn * tn * tn * tn) - (tn * tn * tn * tn * tn)) / 64.0);
bp = 3.0 * a * (tn - (tn * tn) + 7.0 * ((tn * tn * tn)
- (tn * tn * tn * tn)) / 8.0 + 55.0 * (tn * tn * tn * tn * tn) / 64.0)
/ 2.0;
cp = 15.0 * a * ((tn * tn) - (tn * tn * tn) + 3.0 * ((tn * tn * tn * tn)
- (tn * tn * tn * tn * tn)) / 4.0) / 16.0;
dp = 35.0 * a * ((tn * tn * tn) - (tn * tn * tn * tn) + 11.0
* (tn * tn * tn * tn * tn) / 16.0) / 48.0;
ep = 315.0 * a * ((tn * tn * tn * tn) - (tn * tn * tn * tn * tn)) / 512.0;
olam = (utmXZone * 6 - 183) * DEG_TO_RAD;
dlam = lonRad - olam;
s = sin (latRad);
c = cos (latRad);
t = s / c;
eta = e2Squared * (c * c);
sn = sphsn (a, eSquared, latRad);
tmd = sphtmd (ap, bp, cp, dp, ep, latRad);
t1 = tmd * ok;
t2 = sn * s * c * ok / 2.0;
t3 = sn * s * (c * c * c) * ok * (5.0 - (t * t) + 9.0 * eta + 4.0
* (eta * eta)) / 24.0;
if (latRad < 0.0) nfn = 10000000.0; else nfn = 0;
northing = nfn + t1 + (dlam * dlam) * t2 + (dlam * dlam * dlam
* dlam) * t3 + (dlam * dlam * dlam * dlam * dlam * dlam) + 0.5;
t6 = sn * c * ok;
t7 = sn * (c * c * c) * (1.0 - (t * t) + eta) / 6.0;
easting = fe + dlam * t6 + (dlam * dlam * dlam) * t7 + 0.5;
if (northing >= 9999999.0) northing = 9999999.0;
}
void UTMCoord::latLonToUTMWGS84 (int& utmXZone, char& utmYZone,
double& easting, double& northing, double lat, double lon) const
// Purpose:
// This function converts the specified lat/lon coordinate to a UTM
// coordinate in the WGS84 datum. (See the comment block for the
// LatLonToUTM() member function.)
{
latLonToUTM (6378137.0, 1 / 298.257223563, utmXZone, utmYZone,
easting, northing, lat, lon);
}
void UTMCoord::setLatLonCoord (double lat, double lon)
// Purpose:
// This function sets the UTM coordinate given a latitude and a longitude.
{
m_lat = lat;
m_lon = lon;
// No conversion between UTM and lat/lon is necessary, since this function
// has set a new lat/lon coordinate.
m_requireLatLonConvert = false;
// Do not perform the conversion between lat/lon and UTM now; wait until
// the user requests the UTM coordinate.
m_requireUTMConvert = true;
}
void UTMCoord::setUTMCoord (int utmXZone, char utmYZone, double easting,
double northing)
{
m_easting = easting;
m_northing = northing;
m_utmXZone = utmXZone;
m_utmYZone = utmYZone;
// Do not perform the conversion between UTM and lat/lon now; wait until
// the user requests the lat/lon coordinate.
m_requireLatLonConvert = true;
// No conversion between lat/lon and UTM is necessary, since this function
// has set a new UTM coordinate.
m_requireUTMConvert = false;
}
void UTMCoord::utmToLatLon (double a, double f, int utmXZone, char utmYZone,
double easting, double northing, double& lat, double& lon) const
// Purpose:
// This function converts the specified UTM coordinate to a lat/lon
// coordinate.
// Pre:
// - utmXZone must be between 1 and 60, inclusive.
// - utmYZone must be one of: CDEFGHJKLMNPQRSTUVWX
// Parameters:
// double a:
// Ellipsoid semi-major axis, in meters. (For WGS84 datum, use 6378137.0)
// double f:
// Ellipsoid flattening. (For WGS84 datum, use 1 / 298.257223563)
// int utmXZone:
// The horizontal zone number of the UTM coordinate.
// char utmYZone:
// The vertical zone letter of the UTM coordinate.
// double easting, double northing:
// The UTM coordinate to convert.
// double& lat:
// Upon exit, lat contains the latitude.
// double& lon:
// Upon exit, lon contains the longitude.
// Notes:
// The code in this function is a C conversion of some of the source code
// from the Mapping Datum Transformation Software (MADTRAN) program, written
// in PowerBasic. To get the source code for MADTRAN, go to:
//
// http://164.214.2.59/publications/guides/MADTRAN/index.html
//
// and download MADTRAN.ZIP
{
double recf;
double b;
double eSquared;
double e2Squared;
double tn;
double ap;
double bp;
double cp;
double dp;
double ep;
double nfn;
double tmd;
double sr;
double sn;
double ftphi;
double s;
double c;
double t;
double eta;
double de;
double dlam;
double olam;
recf = 1.0 / f;
b = a * (recf - 1) / recf;
eSquared = calculateESquared (a, b);
e2Squared = calculateE2Squared (a, b);
tn = (a - b) / (a + b);
ap = a * (1.0 - tn + 5.0 * ((tn * tn) - (tn * tn * tn)) / 4.0 + 81.0 *
((tn * tn * tn * tn) - (tn * tn * tn * tn * tn)) / 64.0);
bp = 3.0 * a * (tn - (tn * tn) + 7.0 * ((tn * tn * tn)
- (tn * tn * tn * tn)) / 8.0 + 55.0 * (tn * tn * tn * tn * tn) / 64.0)
/ 2.0;
cp = 15.0 * a * ((tn * tn) - (tn * tn * tn) + 3.0 * ((tn * tn * tn * tn)
- (tn * tn * tn * tn * tn)) / 4.0) / 16.0;
dp = 35.0 * a * ((tn * tn * tn) - (tn * tn * tn * tn) + 11.0
* (tn * tn * tn * tn * tn) / 16.0) / 48.0;
ep = 315.0 * a * ((tn * tn * tn * tn) - (tn * tn * tn * tn * tn)) / 512.0;
if ((utmYZone <= 'M' && utmYZone >= 'C')
|| (utmYZone <= 'm' && utmYZone >= 'c')) {
nfn = 10000000.0;
} else {
nfn = 0;
}
tmd = (northing - nfn) / ok;
sr = sphsr (a, eSquared, 0.0);
ftphi = tmd / sr;
double t10, t11, t14, t15;
for (int i = 0; i < 5; i++) {
t10 = sphtmd (ap, bp, cp, dp, ep, ftphi);
sr = sphsr (a, eSquared, ftphi);
ftphi = ftphi + (tmd - t10) / sr;
}
sr = sphsr (a, eSquared, ftphi);
sn = sphsn (a, eSquared, ftphi);
s = sin (ftphi);
c = cos (ftphi);
t = s / c;
eta = e2Squared * (c * c);
de = easting - fe;
t10 = t / (2.0 * sr * sn * (ok * ok));
t11 = t * (5.0 + 3.0 * (t * t) + eta - 4.0 * (eta * eta) - 9.0 * (t * t)
* eta) / (24.0 * sr * (sn * sn * sn) * (ok * ok * ok * ok));
lat = ftphi - (de * de) * t10 + (de * de * de * de) * t11;
t14 = 1.0 / (sn * c * ok);
t15 = (1.0 + 2.0 * (t * t) + eta) / (6 * (sn * sn * sn) * c
* (ok * ok * ok));
dlam = de * t14 - (de * de * de) * t15;
olam = (utmXZone * 6 - 183.0) * DEG_TO_RAD;
lon = olam + dlam;
lon *= RAD_TO_DEG;
lat *= RAD_TO_DEG;
}
void UTMCoord::utmToLatLonWGS84 (int utmXZone, char utmYZone, double easting,
double northing, double& lat, double& lon) const
// Purpose:
// This function converts the specified UTM coordinate to a lat/lon
// coordinate in the WGS84 datum. (See the comment block for the
// UTMToLatLon() member function.
{
utmToLatLon (6378137.0, 1 / 298.257223563, utmXZone, utmYZone,
easting, northing, lat, lon);
}
// UtmCoord.h: Definition of the CUtmCoord class.
//
// This class represents a coordinate on the UTM grid.
//
// Like all other typed coordinate classes, this class contains an internal
// lat/lon coordinate. This lat/lon coordinate is created by converting the
// lat/lon coordinate from a UTM coordinate. Since either lat/lon or UTM can
// be set and read, a conversion may be necessary from one type to another.
// This conversion is quite slow on machines without FPUs, so these conver-
// sions are done internally only when the converted coordinates are required
// by code using a UTM object (this is known as 'lazy evaluation.') In order
// to implement this lazy evaluation code, this class uses member functions
// to access the coordinates (since those member functions modify the lazy
// evaluation flags), rather than manipulating the coordinates directly.
//
// If the lat/lon coordinate contained within an object of this class is
// outside of the UTM grid boundary (>= 84 N or <= 80 S), the vertical UTM
// zone character is set to an asterisk ('*'). You can check the vertical
// UTM zone contained within this class to determine if the coordinate is
// outside of the UTM grid boundary.
//
// Written by Jason Bevins in 1998. File is in the public domain.
//
#ifndef __UTM_COORD_HPP
#define __UTM_COORD_HPP
#include <string> // for string
#include "typedCoord.h" // for COORD_TYPE, etc
// These constants are used by the CreateDisplayStrings() and the
// CreateXYCoordStrings() functions to parse the string generated by the
// CreateCoordString() function. Modify these constants when you modify the
// code that generates the string. These constants do not affect the code
// that generate these strings, but you may write parsing functions that
// require the positions of the individual elements in the strings.
const int UTM_ZONE_POS = 0;
const int UTM_ZONE_LEN = 3;
const int UTM_EASTING_POS = 4;
const int UTM_EASTING_LEN = 6;
const int UTM_NORTHING_POS = 11;
const int UTM_NORTHING_LEN = 7;
class UTMCoord: public TypedCoord
{
public:
UTMCoord ();
UTMCoord (int utmXZone, char utmYZone, double easting,
double northing);
UTMCoord (const TypedCoord& other);
UTMCoord (const UTMCoord& other);
void copyUTMCoord (const UTMCoord& other);
void copyOtherCoord (const TypedCoord& other);
UTMCoord& operator= (const UTMCoord& other);
UTMCoord& operator= (const TypedCoord& other);
virtual const std::string& createCoordString (std::string& coordString) const;
virtual void createDisplayStrings (std::string& topLeftString,
std::string& topRightString, std::string& bottomLeftString,
std::string& bottomRightString) const;
virtual void createXYCoordStrings (std::string& xString, std::string& yString)
const;
virtual COORD_TYPE getCoordType () const {return COORD_UTM;}
virtual void getLatLonCoord (double& lat, double& lon) const;
virtual void getUTMCoord (int& utmXZone, char& utmYZone,
double& easting, double& northing) const;
void getUTMZone (int& utmXZone, char& utmYZone) const;
virtual void getXYCoord (double& x, double& y) const;
bool isOutsideUTMGrid () const;
virtual void setLatLonCoord (double lat, double lon);
virtual void setUTMCoord (int utmXZone, char utmYZone, double easting,
double northing);
protected:
// Lazy evaluation flags.
mutable bool m_requireLatLonConvert;
mutable bool m_requireUTMConvert;
// UTM conversion functions.
void latLonToUTM (double a, double f, int& utmXZone, char& utmYZone,
double& easting, double& northing, double lat, double lon) const;
void latLonToUTMWGS84 (int& utmXZone, char& utmYZone, double& easting,
double& northing, double lat, double lon) const;
void utmToLatLon (double a, double f, int utmXZone, char utmYZone,
double easting, double northing, double& lat, double& lon) const;
void utmToLatLonWGS84 (int utmXZone, char utmYZone, double easting,
double northing, double& lat, double& lon) const;
// UTM values.
mutable int m_utmXZone;
mutable char m_utmYZone;
mutable double m_easting;
mutable double m_northing;
};
#endif // _UTM_COORD_HPP
if(NOT VRPN_BUILD_SERVER_LIBRARY OR NOT VRPN_USE_HID)
message(FATAL_ERROR
"Cannot compile HID gui without the full (server) library and HID support. Enable it and try again.")
endif()
set(VRPN_SERVER_LIBRARY vrpnserver)
if(NOT WIN32)
set(VRPN_ATMEL_LIBRARY vrpn_atmel)
endif()
set(SOURCES
HIDDevice.cpp
Inspector.cpp
main.cpp
MainWindow.cpp
QuickChart.cpp)
set(HEADERS)
set(MOCS
HIDDevice.h
Inspector.h
MainWindow.h
QuickChart.h)
set(UIS
mainwindow.ui
plot.ui)
find_package(Qt4 4.7)
if(QT_FOUND)
include(${QT_USE_FILE})
qt4_wrap_ui(GENERATED_SOURCES ${UIS})
qt4_wrap_cpp(GENERATED_SOURCES ${MOCS})
source_group("Generated Sources - Do Not Edit"
FILES
${GENERATED_SOURCES})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Build and link the app!
add_executable(vrpn_hid_gui
MACOSX_BUNDLE
${SOURCES}
${GENERATED_SOURCES}
${HEADERS}
${UIS}
${MOCS}
${RESOURCES})
target_link_libraries(vrpn_hid_gui ${QT_LIBRARIES} vrpnserver)
endif()
/**
@file
@brief Implementation
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Internal Includes
#include "HIDDevice.h"
#include "vrpn_HumanInterface.h"
// Library/third-party includes
#include <QDateTime>
// Standard includes
// - none
class HIDDevice::VRPNDevice : public vrpn_HidInterface {
HIDDevice * _container;
public:
VRPNDevice(vrpn_HidAcceptor * acceptor, HIDDevice * container)
: vrpn_HidInterface(acceptor)
, _container(container)
{}
protected:
void on_data_received(size_t bytes, vrpn_uint8 *buffer) {
_container->send_data_signal(bytes, reinterpret_cast<char *>(buffer));
}
};
HIDDevice::HIDDevice(vrpn_HidAcceptor * acceptor, QObject * parent)
: QObject(parent)
, _connected(false)
, _device(new VRPNDevice(acceptor, this))
, _startingTimestamp(-1) {}
HIDDevice::~HIDDevice() {
delete _device;
_device = NULL;
}
void HIDDevice::do_update() {
bool wasConnected = _connected;
_connected = _device->connected();
if (_connected && !wasConnected) {
emit message("Connected to device!");
} else if (!_connected && wasConnected) {
emit message("Lost connection to device!");
}
_device->update();
}
void HIDDevice::send_data_signal(size_t bytes, const char * buffer) {
qint64 current = QDateTime::currentMSecsSinceEpoch();
if (_startingTimestamp < 0) {
_startingTimestamp = current;
}
emit inputReport(QByteArray(buffer, bytes), current - _startingTimestamp);
}
void HIDDevice::send_message_signal(QString const& msg) {
emit message(msg);
}
/** @file
@brief Header
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
// Internal Includes
#include "vrpn_Shared.h"
// Library/third-party includes
#include <QObject>
#include <QByteArray>
#include <QString>
// Standard includes
// - none
class vrpn_HidAcceptor;
/// Qt wrapper for a debugging HidInterface.
class HIDDevice: public QObject {
Q_OBJECT
public:
explicit HIDDevice(vrpn_HidAcceptor * acceptor, QObject * parent = NULL);
~HIDDevice();
signals:
void inputReport(QByteArray buffer, qint64 timestamp);
void message(QString const& msg);
public slots:
void do_update();
protected:
bool _connected;
class VRPNDevice;
friend class HIDDevice::VRPNDevice;
void send_data_signal(size_t bytes, const char * buffer);
void send_message_signal(QString const& msg);
VRPNDevice * _device;
qint64 _startingTimestamp;
};
/**
@file
@brief Implementation
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Internal Includes
#include "Inspector.h"
#include "vrpn_Shared.h"
// Library/third-party includes
// - none
// Standard includes
#include <stdexcept>
#include <iostream>
//#include <stdint.h>
template<typename T>
T getFromByteArray(QByteArray const& input) {
union {
char bytes[sizeof(T)];
T value;
};
for (int i = 0; i < sizeof(T); ++i) {
bytes[i] = input.at(i);
}
return value;
}
Inspector::Inspector(std::size_t first_index, std::size_t length, bool signedVal, bool bigEndian, QObject * parent)
: QObject(parent)
, _first(first_index)
, _length(length)
, _signed(signedVal)
, _bigEndian(bigEndian) {
switch (_length) {
case 1:
case 2:
case 4:
break;
default:
throw std::logic_error("Can't get an integer with that many bytes!");
}
}
void Inspector::updatedData(QByteArray buf, qint64 timestamp) {
QByteArray myPortion;
myPortion.reserve(_length);
if (!_bigEndian) {
myPortion = buf.mid(_first, _length);
} else {
unsigned i;
for (i = 0; i < _length; ++i) {
myPortion.prepend(buf.at(_first + i));
}
}
switch (_length) {
case 1:
_sendNewValue(timestamp, _signed ?
myPortion.at(0)
: getFromByteArray<vrpn_uint8>(myPortion));
break;
case 2:
_sendNewValue(timestamp, _signed ?
getFromByteArray<vrpn_int16>(myPortion) :
getFromByteArray<vrpn_uint16>(myPortion));
break;
case 4:
_sendNewValue(timestamp, _signed ?
getFromByteArray<vrpn_int32>(myPortion) :
getFromByteArray<vrpn_uint32>(myPortion));
break;
}
}
void Inspector::_sendNewValue(qint64 timestamp, float val) {
emit newValue(val);
emit newValue(float(timestamp) / 1000.0f, val);
}
/** @file
@brief Header
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
// Internal Includes
// - none
// Library/third-party includes
#include <QObject>
#include <QByteArray>
// Standard includes
// - none
/// Class to extract bytes from an array and turn into some kind of value
class Inspector : public QObject {
Q_OBJECT
public:
explicit Inspector(std::size_t first_index, std::size_t length, bool signedVal, bool bigEndian = false, QObject * parent = NULL);
~Inspector() {}
signals:
void newValue(float val);
void newValue(float elapsed, float val);
public slots:
void updatedData(QByteArray buf, qint64 timestamp);
private:
void _sendNewValue(qint64 timestamp, float val);
std::size_t _first;
std::size_t _length;
bool _signed;
bool _bigEndian;
};
/**
@file
@brief Implementation
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Internal Includes
#include "MainWindow.h"
#include "ui_mainwindow.h"
#include "HIDDevice.h"
#include "Inspector.h"
#include "QuickChart.h"
// Library/third-party includes
#include <QTimer>
#include <QInputDialog>
// Standard includes
#include <cmath>
MainWindow::MainWindow(vrpn_HidAcceptor * acceptor, QWidget * parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, _device(new HIDDevice(acceptor))
, _timer(new QTimer)
, _singlebyteIntMenu(new QMenu)
, _multibyteIntMenu(new QMenu)
, _selectionLabel(new QLabel) {
ui->setupUi(this);
/// When the device has a message, add it to the log.
connect(_device.data(), SIGNAL(message(QString const&)), ui->textLog, SLOT(append(QString const&)));
/// When the device gets an input report, update our controls
connect(_device.data(), SIGNAL(inputReport(QByteArray, qint64)), this, SLOT(gotReport(QByteArray)));
/// Update the HID device every 20 ms
connect(_timer.data(), SIGNAL(timeout()), _device.data(), SLOT(do_update()));
_timer->start(20);
/// Widget for showing what offset and length are selected
statusBar()->addPermanentWidget(_selectionLabel);
_selectionLabel->setText(QString("No selected bytes"));
/// Create context menu for when a single byte is selected
_singlebyteIntMenu->addAction(QString("Inspect as signed integer"), this, SLOT(addSignedLEInspector()));
_singlebyteIntMenu->addAction(QString("Inspect as unsigned integer"), this, SLOT(addUnsignedLEInspector()));
/// Create context menu for when 2, 4 bytes are selected
QMenu * submenu = _multibyteIntMenu->addMenu(QString("Inspect as signed integer"));
submenu->addAction(QString("Big endian"), this, SLOT(addSignedBEInspector()));
submenu->addAction(QString("Little endian"), this, SLOT(addSignedLEInspector()));
submenu = _multibyteIntMenu->addMenu(QString("Inspect as unsigned integer"));
submenu->addAction(QString("Big endian"), this, SLOT(addUnsignedBEInspector()));
submenu->addAction(QString("Little endian"), this, SLOT(addUnsignedLEInspector()));
}
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::gotReport(QByteArray buf) {
/// Update display of report size
ui->reportSizeLabel->setText(QString("%1 bytes").arg(buf.size()));
/// Save selection range, if applicable
int initialStart = -1;
int initialLength = -1;
if (ui->reportContents->hasSelectedText()) {
initialStart = ui->reportContents->selectionStart();
initialLength = ui->reportContents->selectedText().length();
}
/// Update text
ui->reportContents->setText(buf.toHex());
/// Restore selection range
if (initialStart >= 0) {
ui->reportContents->setSelection(initialStart, initialLength);
}
}
void MainWindow::on_actionRemove_all_inspectors_triggered() {
/// remove all inspectors
_inspectors.clear();
/// remove all charts
QLayoutItem * child;
while ((child = ui->chartBox->takeAt(0)) != NULL) {
delete child->widget();
delete child;
}
}
void MainWindow::on_reportContents_selectionChanged() {
if (ui->reportContents->hasSelectedText()) {
const QPair<int, int> offsetLength = _getSelectionByteOffsetLength();
const int byteOffset = offsetLength.first;
const int byteLength = offsetLength.second;
const int initialStart = ui->reportContents->selectionStart();
const int initialLength = ui->reportContents->selectedText().length();
/// Normalize selection if needed
if (initialStart != byteOffset * 2 || initialLength != byteLength * 2) {
ui->reportContents->setSelection(byteOffset * 2, byteLength * 2);
}
_selectionLabel->setText(QString("Offset %1, length %2").arg(byteOffset).arg(byteLength));
} else {
_selectionLabel->setText(QString("No selected bytes"));
}
}
void MainWindow::on_reportContents_customContextMenuRequested(const QPoint & pos) {
QPoint globalPos = ui->reportContents->mapToGlobal(pos);
const QPair<int, int> offsetLength = _getSelectionByteOffsetLength();
const int byteLength = offsetLength.second;
switch (byteLength) {
case -1:
/// no selection, no menu.
statusBar()->showMessage("No bytes selected to inspect.");
break;
case 1:
/// Single byte
_singlebyteIntMenu->popup(globalPos);
break;
case 2:
case 4:
/// Valid multibyte
_multibyteIntMenu->popup(globalPos);
break;
default:
/// Some other number of bytes - not handled
statusBar()->showMessage("Can only inspect integers with powers of 2 lengths.");
break;
}
}
QPair<int, int> MainWindow::_getSelectionByteOffsetLength() const {
if (ui->reportContents->hasSelectedText()) {
int initialStart = ui->reportContents->selectionStart();
int initialLength = ui->reportContents->selectedText().length();
int endingCharacter = initialStart + initialLength;
/// get the initial byte
int startingByte = initialStart / 2;
int endingByte = (endingCharacter + 1) / 2;
int byteLength = endingByte - startingByte;
return QPair<int, int>(startingByte, byteLength);
}
/// Default case for when there's no selection
return QPair<int, int>(-1, -1);
}
void MainWindow::_addInspector(bool signedVal, bool bigEndian) {
const QPair<int, int> offsetLength = _getSelectionByteOffsetLength();
const int byteOffset = offsetLength.first;
const int byteLength = offsetLength.second;
_addInspector(byteOffset, byteLength, signedVal, bigEndian);
}
void MainWindow::_addInspector(std::size_t size, bool signedVal, bool bigEndian) {
bool ok;
std::size_t offset = QInputDialog::getInt(this,
QString("Starting index"),
QString("What (0-based) index should we start at? Current report length is %1.")
.arg(ui->reportSizeLabel->text()),
0, 0, 255, 1, &ok);
if (ok) {
_addInspector(offset, size, signedVal, bigEndian);
}
}
void MainWindow::_addInspector(std::size_t offset, std::size_t size, bool signedVal, bool bigEndian) {
QuickChart * chart = new QuickChart(this);
int range = std::pow(2.0, 8.0 * size);
chart->setMin(signedVal ? - range / 2 : 0);
chart->setMax(signedVal ? range / 2 : range);
QString label = QString("Offset %1 as %2-endian %3 int %4")
.arg(offset)
.arg(bigEndian ? "big" : "little")
.arg(signedVal ? "signed" : "unsigned")
.arg(size * 8);
chart->setLabel(label);
ui->chartBox->addWidget(chart);
InspectorPtr inspect(new Inspector(offset, size, signedVal, bigEndian));
connect(_device.data(), SIGNAL(inputReport(QByteArray, qint64)), inspect.data(), SLOT(updatedData(QByteArray, qint64)));
connect(inspect.data(), SIGNAL(newValue(float, float)), chart, SLOT(addSample(float, float)));
_inspectors.push_back(inspect);
}
/** @file
@brief Header
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
// Internal Includes
// - none
// Library/third-party includes
#include <QMainWindow>
#include <QSharedPointer>
#include <QPair>
// Standard includes
// - none
namespace Ui {
class MainWindow;
}
class QTimer;
class QLabel;
class QMenu;
class HIDDevice;
class vrpn_HidAcceptor;
class Inspector;
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(vrpn_HidAcceptor * acceptor, QWidget *parent = 0);
~MainWindow();
public slots:
void gotReport(QByteArray buf);
void addSignedLEInspector() {
_addInspector(true, false);
}
void addUnsignedLEInspector() {
_addInspector(false, false);
}
void addSignedBEInspector() {
_addInspector(true, true);
}
void addUnsignedBEInspector() {
_addInspector(false, true);
}
void on_actionInt8_2_triggered() {
_addInspector(1, true, false);
}
void on_actionUint8_2_triggered() {
_addInspector(1, false, false);
}
void on_actionInt16_LE_triggered() {
_addInspector(2, true, false);
}
void on_actionInt16_BE_triggered() {
_addInspector(2, true, true);
}
void on_actionUint16_LE_triggered() {
_addInspector(2, false, false);
}
void on_actionUint16_BE_triggered() {
_addInspector(2, false, true);
}
void on_actionRemove_all_inspectors_triggered();
void on_reportContents_selectionChanged();
void on_reportContents_customContextMenuRequested(const QPoint & pos);
private:
/// @brief return the offset and the length, in bytes, of the selection,
/// or -1, -1 if no selection.
QPair<int, int> _getSelectionByteOffsetLength() const;
/// @brief Helper function to add an inspector from the selection
void _addInspector(bool signedVal, bool bigEndian);
/// @brief Helper function to add an inspector from the menu, prompting for offset
void _addInspector(std::size_t size, bool signedVal, bool bigEndian);
/// @brief Helper function called by the other overloads adding an inspector once
/// we know all the parameters
void _addInspector(std::size_t offset, std::size_t size, bool signedVal, bool bigEndian);
typedef QSharedPointer<Inspector> InspectorPtr;
Ui::MainWindow *ui;
QSharedPointer<HIDDevice> _device;
QSharedPointer<QTimer> _timer;
std::vector<InspectorPtr> _inspectors;
QSharedPointer<QMenu> _singlebyteIntMenu;
QSharedPointer<QMenu> _multibyteIntMenu;
/// ownership transferred to status bar
QLabel * _selectionLabel;
};
/**
@file
@brief Implementation
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Internal Includes
#include "QuickChart.h"
#include "ui_plot.h"
// Library/third-party includes
#include <QGraphicsScene>
// Standard includes
// - none
QuickChart::QuickChart(QWidget * parent)
: QFrame(parent)
, ui(new Ui::Plot)
, _x(0)
, _last(0)
, _min(0)
, _max(255)
, _sampleWidth(10)
, _gotOne(false)
, _scene(new QGraphicsScene) {
ui->setupUi(this);
ui->graphicsView->setScene(_scene.data());
updateViewFit();
}
QuickChart::~QuickChart() {
delete ui;
}
void QuickChart::addSample(float sample) {
addSample(_x + 1, sample);
}
void QuickChart::addSample(float x, float sample) {
if (_gotOne) {
_scene->addLine(_x, _last, x, sample);
}
_gotOne = true;
_last = sample;
_x = x;
setSceneRect();
ui->lastValue->setText(QString("(%1, %2)").arg(x).arg(sample));
}
void QuickChart::updateViewFit() {
const QSize s = ui->graphicsView->size();
const float h(s.height());
const float w(s.width());
const float xScale = w / _sampleWidth;
const float yScale = h / (_max - _min);
ui->graphicsView->setTransform(QTransform::fromScale(xScale, yScale).translate(-_min, 0));
setSceneRect();
}
void QuickChart::setSceneRect() {
float xmin = 0;
float width = _x;
if (_x < _sampleWidth) {
xmin = _x - _sampleWidth;
width = _sampleWidth;
}
_scene->setSceneRect(xmin, _min, width, _max - _min);
}
void QuickChart::setSampleWidth(float w) {
_sampleWidth = w;
updateViewFit();
}
void QuickChart::setLabel(QString const& l) {
ui->label->setText(l);
}
/** @file
@brief Header
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
// Internal Includes
// - none
// Library/third-party includes
#include <QFrame>
#include <QSharedPointer>
// Standard includes
// - none
namespace Ui {
class Plot;
}
class QGraphicsScene;
class QuickChart : public QFrame {
Q_OBJECT
public:
explicit QuickChart(QWidget *parent = 0);
~QuickChart();
/// set minimum y value
void setMin(float v) {
_min = v;
updateViewFit();
}
/// get minimum y value
float getMin(void) const {
return _min;
}
/// set maximum y value
void setMax(float v) {
_max = v;
updateViewFit();
}
/// get maximum y value
float getMax(void) const {
return _max;
}
/// set width of x values we should fit in the control at once
void setSampleWidth(float w);
/// Set the text of the label
void setLabel(QString const& l);
public slots:
/// Add a sample, with x defaulted to 1 + previous x
void addSample(float sample);
/// Add a sample specifying both x and the sample (y)
void addSample(float x, float sample);
void updateViewFit();
void setSceneRect();
private:
Ui::Plot *ui;
float _x;
float _last;
float _min;
float _max;
float _sampleWidth;
bool _gotOne;
QSharedPointer<QGraphicsScene> _scene;
};
/**
@file
@brief Implementation
@date 2011
@author
Ryan Pavlik
<rpavlik@iastate.edu> and <abiryan@ryand.net>
http://academic.cleardefinition.com/
Iowa State University Virtual Reality Applications Center
Human-Computer Interaction Graduate Program
*/
// Copyright Iowa State University 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Internal Includes
#include "MainWindow.h"
#include "vrpn_HumanInterface.h"
// Library/third-party includes
#include <QtGui/QApplication>
// Standard includes
#include <sstream>
#include <stdio.h>
int usage(char * argv0) {
printf("Usage:\n\n"
"%s -h|--help\n"
" Display this help text.\n\n"
"%s [N]\n"
" Open HID device number N (default to 0)\n\n"
"%s VEND PROD [N]\n"
" Open HID device number N (default to 0) that matches\n"
" vendor VEND and product PROD, in _decimal_\n\n"
,
argv0, argv0, argv0);
return 1;
}
int failedOnArgument(int argNum, const char * expected, char * argv[]) {
fprintf(stderr, "Failed to interpret argument %d: expected %s, got '%s' - usage help follows.\n\n", argNum, expected, argv[argNum]);
return usage(argv[0]);
}
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
if (argc > 1 && (std::string("-h") == argv[1] || std::string("--help") == argv[1])) {
return usage(argv[0]);
}
vrpn_HidAcceptor * acceptor = NULL;
unsigned N = 0; // Which device to open?
if (argc >= 3) {
vrpn_uint16 vend;
std::istringstream vendS(argv[1]);
if (!(vendS >> vend)) {
return failedOnArgument(1, "a decimal vendor ID", argv);
}
vrpn_uint16 prod;
std::istringstream prodS(argv[2]);
if (!(prodS >> prod)) {
return failedOnArgument(2, "a decimal product ID", argv);
}
if (argc >= 4) {
std::istringstream nS(argv[3]);
if (!(nS >> N)) {
return failedOnArgument(3, "a number indicating which matching device to pick, or nothing for the default '0'", argv);
}
}
printf("Will accept HID device number %u that has vendor:product %04x:%04x\n", N, vend, prod);
acceptor = new vrpn_HidProductAcceptor(vend, prod);
} else {
if (argc == 2) {
std::istringstream nS(argv[1]);
if (!(nS >> N)) {
return failedOnArgument(1, "a number indicating which device to pick, or nothing for the default '0'", argv);
}
}
printf("Will accept HID device number %u\n", N);
acceptor = new vrpn_HidAlwaysAcceptor;
}
MainWindow w(new vrpn_HidNthMatchAcceptor(N, acceptor));
w.show();
return a.exec();
}
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>768</height>
</rect>
</property>
<property name="windowTitle">
<string>VRPN HID Tool</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>780</width>
<height>445</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="chartBox"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Last Report:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="reportSizeLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLineEdit" name="reportContents">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Courier New</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="textLog">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
<property name="title">
<string>&amp;File</string>
</property>
<addaction name="action_Quit"/>
</widget>
<widget class="QMenu" name="menu_Inspect">
<property name="title">
<string>&amp;Inspect</string>
</property>
<addaction name="actionInt8_2"/>
<addaction name="actionUint8_2"/>
<addaction name="actionInt16_LE"/>
<addaction name="actionInt16_BE"/>
<addaction name="actionUint16_LE"/>
<addaction name="actionUint16_BE"/>
<addaction name="separator"/>
<addaction name="actionRemove_all_inspectors"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Inspect"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="action_Quit">
<property name="text">
<string>&amp;Quit</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="actionUint8">
<property name="text">
<string>uint8</string>
</property>
</action>
<action name="actionInt8">
<property name="text">
<string>int8</string>
</property>
</action>
<action name="actionBig_Endian">
<property name="text">
<string>Big Endian</string>
</property>
</action>
<action name="actionLittle_Endian">
<property name="text">
<string>Little Endian</string>
</property>
</action>
<action name="actionInt8_LE">
<property name="text">
<string>int8 (LE)</string>
</property>
</action>
<action name="actionInt8_BE">
<property name="text">
<string>int8 (BE)</string>
</property>
</action>
<action name="actionInt8_2">
<property name="text">
<string>int8</string>
</property>
</action>
<action name="actionUint8_2">
<property name="text">
<string>uint8</string>
</property>
</action>
<action name="actionInt16_LE">
<property name="text">
<string>int16 (LE)</string>
</property>
</action>
<action name="actionInt16_BE">
<property name="text">
<string>int16 (BE)</string>
</property>
</action>
<action name="actionUint16_LE">
<property name="text">
<string>uint16 (LE)</string>
</property>
</action>
<action name="actionUint16_BE">
<property name="text">
<string>uint16 (BE)</string>
</property>
</action>
<action name="actionRemove_all_inspectors">
<property name="text">
<string>Remove all inspectors</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>action_Quit</sender>
<signal>activated()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
</ui>
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Plot</class>
<widget class="QFrame" name="Plot">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>633</width>
<height>116</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Frame</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lastValue">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGraphicsView" name="graphicsView">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="renderHints">
<set>QPainter::Antialiasing|QPainter::TextAntialiasing</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
[General]
FileVersion=1.1
FileType=QSetupIniFile
ComposerVersion=5.0.0.0
OperatingSystem=WinXP 5.1 2600 (Service Pack 2)
[Project]
ProjectName=Nikon_focus_control
ProjectDirectory=F:\taylorr\STM\src\vrpn\installers\Nikon_focus_control
NonSFXDirectory=F:\taylorr\STM\src\vrpn\installers\Nikon_focus_control\NonSFX
MediaFileName=nikon_focus_control_v01.00
CopyMediaToFName=
ProgramDescriptiveName=Nikon Focus Control v01.00
ProgramVersion=01.00
CompanyName=UNC Nanoscale Science Research Group
ProgVerLiveUpdate=0
RegisterAsApp=1
LanguageFlag=1
LanguageNames=|
UseNonSFXDir=0
ClrNonSFXDir=0
CompressNonSFXFiles=0
ForceCopyNonSFXFiles=0
CompressionLevel=0
[Display]
AddSetupBackground=1
SetupBackground3D=0
TopLabel=1
TopLabelVer=0
TopLabel2=0
BottomLabel=0
TopLabelText="Nikon Focus Control v01.00"
TopLabel2Text=""
BottomLabelText="Copyright 2004"
BackgroundDesign=Windmill (Right)
TopBackgroundColor=16758613
BottomBackgroundColor=11613445
AddBottomColor=1
DlgBmp3DFrame=1
DialogStyle=Modern
AddDialogBitmap=0
DialogBitmapFileName=
AddBackgroundBitmap=0
BackgroundBitmapFileName=
BackgroundBitmapLocation=|Top/Left|0|0|
AddBackgroundWave=0
BgBmpTransparent=0
BackgroundWaveFileName=
BackgroundWaveLocation=|Center|300|0|
BackgroundWaveLoop=1
BackgroundWaveDelay=0
AddBackgroundWaveSlider=1
BackgroundWaveSliderType=Fixed
TopLabelSide=Left
TopLabel2Side=Left
BottomLabelSide=Left
DialogLocation=|Center|0|0|
TopLabelFont=|Tahoma|32|16777215|1|0|1|0|
TopLabel2Font=|Tahoma|16|16777215|1|0|1|0|
BottomLabelFont=|Tahoma|10|16777215|1|0|1|0|
CompanyUrl=
[TargetExe]
TargetDirectory=<AbsoluteDir>C:\NSRG\bin\Nikon focus control 01.00
CommonDirectory=<CommonFilesDir>UNC Nanoscale Science Research Group
AuxDirectory=<FontDir>
TargetExeName=<Application Folder>\vrpn_server.exe
RunTimeDir_1=|||||||1||||1||1|<ProgramFilesDir>|||||
RunTimeDir_2=|||||||1||||1||1|<ProgramFilesDir>|||||
RunTimeDir_3=|||||||1||||1||1|<ProgramFilesDir>|||||
[Dialogs]
SelectedDialogs=*Welcome,License,Readme,User Information,Setup Type,Custom,*Destination,Associate,*Shortcuts,*Confirm Setup,*Copy Files,*Complete
LicenseTextFile=
ReadmeTextFile=
ReadmeTextFile2=
UseNotepad=0
SpaceReqOnDriveCheckBox=0
SpaceReqOnDrive=
RequestUserName=1
RequestCompanyName=1
RequestSerialNumberAlso=0
MandatoryUserName=1
MandatoryCompanyName=1
MandatorySerialNumber=1
RequestSerialCheckDLL=0
SerialCheckDll=
RequestPredefinedSerial=0
CaseSensitiveSerial=0
RequestTokenizedSerial=0
CDSetup=0
ShowCompactSetupAlso=1
ForcePartialSetup=0
ShowProgBar2=0
SilentSetup=0
ProposeReadme=0
ProposeLaunch=1
ProposeRestart=0
ProposeReadmeChecked=0
ProposeLaunchChecked=0
ShowCloseRecommendation=0
ShowAdwareDisclaimer=0
ShowCopyrightWarning=1
[Shortcuts]
AddMainShortCut=1
ForceRemoveShortcutFolder=0
ProgramLinkFolder=NSRG\Nikon Focus Control v01.00
ProgramLinkName=VRPN Nikon Focus Control Server Artemis
ProgramLinkParam=-f vrpn_artemis.cfg
ProgramLinkWorkDir=<Application Folder>
ProgramLinkRun=Normal Window
ProgramLinkIconFile=
ProgramLinkIconNum=0
StartMenuLinkName=Nikon Focus Control v01.00
DesktopLinkName=Nikon Focus Control Artemis v01.00
SendToLinkName=Nikon Focus Control v01.00
StartUpLinkName=Nikon Focus Control v01.00
QuickLaunchLinkName=Nikon Focus Control v01.00
StartMenuCheckBox=0
DesktopCheckBox=1
SendToCheckBox=0
StartUpCheckBox=0
QuickLaunchCheckBox=0
StartMenuChecked=1
DesktopChecked=1
SendToChecked=1
StartUpChecked=1
QuickLaunchChecked=1
SetAllUsers=1
AddUninstallShortcut=1
Item-000=*|VRPN Nikon Focus Control Server Hercules|<Application Folder>\vrpn_server.exe|NSRG\Nikon Focus Control v01.00|-f vrpn_hercules.cfg|<Application Folder>|Normal Window||0|1||0|
[Billboard]
AddBillboard=1
Item-000=|1|0|0|1|0|Center|0|0|8388608|16711680|Verdana|14|16777215|1|0|360|360|0|1|
[SplitSetup]
SplitSetup=0
ForceDownload=1
DownloadURL_SPLIT=http://
ConfirmSplitDownload=0
[AutoUpdate]
AutoUpdate=0
RunAutoUpdate=1
AddAutoUpdateShortcut=1
ReqConfirmDownload=0
ReqConfirmInstall=0
ShutDownMethod=Don't Shut Down
BackgroundUpdate=0
RestartProcess=0
InformFinish=0
UpdateProjectFName=
DownloadURL_UPDATE=http:/
NameOfUpdateFile=
RunningProcess=
VersionOriginal=1
ValidVersions=
VersionNew=1
ReAskDays=0
AutoUpdateProbeSpin=1
AutoUpdateProbeText=Only at Agent Start
[Switches]
OperatingSystems=All,95,98,ME,NT,2000,XP,2003
OverwriteFlag=1
CreateUnInstall=1
ScrambleUnInstallData=0
HideErrMsgUnInstall=0
HideLogBtnUnInstall=0
AddRemoveProgramsUnInstall=1
AddRemoveButtonButtonInstall=1
UnInstallExeStampSwitch=0
UnInstallExeStamp=
CreateLogFile=0
ConfirmExtract=0
TestAdminRights=0
TestAdminRightsUnInstall=0
SetAllowFullAccess=0
TestDotNetFramework=0
TestExeBeforeInstall=0
TestExeBeforeUnInstall=0
AutorunCreateInfFile=0
AutorunTest=0
AutorunStartApp=0
[Registry]
[IniFile]
[Environment]
[Association]
OpenWith=0
OpenWithName=
OpenWithMenuText=Open with
OpenWithExtensions=
[VerInfo]
CopyFromExe=0
DefineManually=0
Item-001=|Comments||
Item-002=|CompanyName||
Item-003=|FileDescription||
Item-004=|FileVersion||
Item-005=|InternalName||
Item-006=|LegalCopyright||
Item-007=|LegalTrademarks||
Item-008=|OriginalFilename||
Item-009=|ProductName||
Item-010=|ProductVersion||
CopyIconFromTargetExe=0
CopyIconFromOtherApp=0
OtherIconAppFileName=
Spin1=0
Spin2=0
[Execute]
ExecuteDllCheckBox=0
ExecutionDllFileName=
[GroupNames]
Group-000=Main Group
[Group-000]
TypicalCompactData=15
Description=This is the main group of files
Item-000=Fol*<Application Folder>[0]
Item-001=Fil*F:\taylorr\STM\src\vrpn\installers\Nikon_focus_control\vrpn_artemis.cfg
Item-002=Fil*F:\taylorr\STM\src\vrpn\installers\Nikon_focus_control\vrpn_hercules.cfg
Item-003=Fil*F:\taylorr\STM\src\vrpn\pc_win32\server_src\vrpn_server\Release\vrpn_server.exe