I’ve been playing with the GPS class a bit as I’m looking to try to use the Fez Cobra for some Amateur Radio uses… I made a few tweaks to suit my needs as well as added a few extra functions I figured I’d share for others in case they needed it:
//-------------------------------------------------------------------------------------
// GHI Electronics, LLC
// Copyright (c) 2010
// All rights reserved
//-------------------------------------------------------------------------------------
/*
* You can use this file if you agree to the following:
*
* 1. This header can't be changed under any condition.
*
* 2. This is a free software and therefore is provided with NO warranty.
*
* 3. Feel free to modify the code but we ask you to provide us with
* any bugs reports so we can keep the code up to date.
*
* 4. This code may ONLY be used with GHI Electronics, LLC products.
*
* THIS SOFTWARE IS PROVIDED BY GHI ELECTRONICS, LLC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* GHI ELECTRONICS, LLC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Specs are subject to change without any notice
*
-------------------------------------------------------------------------------------
*
* HISTORY:
*
* 15/8/2010 - MJL
* - Removed getValue2 and getValue3 and replaced with String.substring
* - Changed Latitude and Longitude variables to doubles from integers
* - Changed initialize function to take com port and baud rate as paramters
* - Changed resolution of the Minutes value from MM to MM.MMMM
* - Added convertLocation function to convert from DD MM.MMMM to Decimal Degrees format
* - Added getDistance to find the distance between two specified locations
* - Added getMaidenhead to get the Maidenhead grid square for a specified location
*
*
*/
using System;
using System.IO.Ports;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
namespace GHIElectronics.NETMF.FEZ
{
public static partial class FEZ_Extensions
{
static public class GPS
{
static char[] split_char = new char[] { ',' };
static string[] sentence_fields;
static char[] time_utc = new char[8];
static SerialPort _port;
static byte[] buffer = new byte[200];
static bool _data_is_valid;
private static GPSStateMachine gps_decoder_state;
static char[] pree_buffer = new char[10];
static char[] pree = { 'G', 'P', 'R', 'M', 'C' };
static int pree_index = 0;
static char[] sentence = new char[200];
static char[] GPRMC_sentence_array = new char[200];
static int GPRMC_sentence_array_length = 0;
static int sentence_index = 0;
private enum GPSStateMachine
{
FindPre,
CopyingSentence,
}
// inport - serial port such as COM1 or COM2
// inbaud - baud rate, i.e. 19200
// For the GHI GPS you want to use 19200
static public void Initialize(string inport, int inbaud)
{
_port = new SerialPort(inport, inbaud);
_port.ReadTimeout = 0;
_port.ErrorReceived += new SerialErrorReceivedEventHandler(_port_ErrorReceived);
_port.Open();// If you open the port after you set the event you will endup with problems
_port.DataReceived += new SerialDataReceivedEventHandler(_port_DataReceived);
}
static void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int buffer_index = 0;
int data_received;
//Thread.Sleep(10);
data_received = _port.Read(buffer, 0, buffer.Length);
//_port.Close();
while (buffer_index < data_received)
{
switch (gps_decoder_state)
{
case GPSStateMachine.FindPre:
if (buffer[buffer_index] == pree[pree_index])
{
pree_index++;
if (pree_index >= pree.Length)
{
gps_decoder_state = GPSStateMachine.CopyingSentence;
pree_index = 0;
}
}
else
pree_index = 0;
break;
case GPSStateMachine.CopyingSentence:
sentence[sentence_index] = (char)buffer[buffer_index];
if (sentence[sentence_index] == '*' ||
sentence[sentence_index] == '\r' ||
sentence[sentence_index] == '\n')
{
//we have a full line
if (sentence_index > 10)
{
if (sentence[12] == 'A')
{
lock (GPRMC_sentence_array)
{
Array.Copy(sentence, GPRMC_sentence_array, sentence_index);
GPRMC_sentence_array_length = sentence_index;
_data_is_valid = true;
}
//string line = new string(sentence, 0, sentence_index);
//DecodeGPSSentence(line);
//Debug.Print(line);
}
else
{
//invalid data
_data_is_valid = false;
Debug.Print("Searching for satellites...");
}
}
sentence_index = 0;
gps_decoder_state = GPSStateMachine.FindPre;
}
else
{
sentence_index++;
if (sentence_index >= sentence.Length)
{
Debug.Print("Sentence is too long!");
sentence_index = 0;
gps_decoder_state = GPSStateMachine.FindPre;
}
}
break;
}
buffer_index++;
}
//_port.Open();
}
static void _port_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
Debug.Print("COM Error: " + e.EventType.ToString());
}
public static DateTime GetUTCDateTime()
{
int year, month, day, hour, minutes, seconds;
lock (GPRMC_sentence_array)
{
if (_data_is_valid == false)
{
return DateTime.MinValue;
}
string sentence_string = new string(GPRMC_sentence_array, 0, GPRMC_sentence_array_length);
sentence_fields = sentence_string.Split(split_char);
if (sentence_fields.Length < 9)
{
return DateTime.MinValue;
}
// compute values from ASCII
hour = Convert.ToInt16(sentence_fields[1].Substring(0, 2));
minutes = Convert.ToInt16(sentence_fields[1].Substring(2, 2));
seconds = Convert.ToInt16(sentence_fields[1].Substring(4, 2));
day = Convert.ToInt16(sentence_fields[9].Substring(0, 2));
month = Convert.ToInt16(sentence_fields[9].Substring(2, 2));
year = Convert.ToInt16(sentence_fields[9].Substring(4, 2)) + 2000;
_data_is_valid = false;
return new DateTime(year, month, day, hour, minutes, seconds);
}
}
public static bool GetPosition(out double LatHour, out double LatMinute, out bool North, out double LongHour, out double LongMinute, out bool East)
{
lock (GPRMC_sentence_array)
{
if (_data_is_valid == false)
{
// nothing to return
LatHour = LatMinute = LongHour = LongMinute = 0;
North = East = false;
return false;
}
string sentence_string = new string(GPRMC_sentence_array, 0, GPRMC_sentence_array_length);
sentence_fields = sentence_string.Split(split_char);
if (sentence_fields.Length < 9)
{
// nothing to return
LatHour = LatMinute = LongHour = LongMinute = 0;
North = East = false;
return false;
}
// compute values from ASCII
LatHour = Convert.ToDouble(sentence_fields[3].Substring(0, 2));
LatMinute = Convert.ToDouble(sentence_fields[3].Substring(2, 7));
if (sentence_fields[4][0] == 'S')
North = false;
else
North = true;
LongHour = Convert.ToDouble(sentence_fields[5].Substring(0, 3));
LongMinute = Convert.ToDouble(sentence_fields[5].Substring(3, 7));
if (sentence_fields[6][0] == 'W')
East = false;
else
East = true;
_data_is_valid = false;
return true;
}
}
// Converts from the format DDD MM.MMMM to the signed Decimal Degrees location format xxx.xxxxxxx
public static double convertLocation(double inHour, double inMinute, bool inHemisphere)
{
try
{
double temploc = (inHour) + ((inMinute) / 60.0);
if (inHemisphere == false)
temploc = +-temploc;
return temploc;
}
catch (Exception se)
{
Debug.Print(se.ToString());
return 0;
}
}
// Gets the distance between two coordinates on earth using position in decimal format
// The variable units takes the following values:
// M - Miles (default)
// N - Nautical Miles
// K - Kilometers
public static double getDistance(double inlat1, double inlong1, double inlat2, double inlong2, string units)
{
try
{
Trigonometry trig = new Trigonometry();
double tempdistance = 0;
double theta = inlong1 - inlong2;
tempdistance = System.MathEx.Sin(trig.DegreeToRadian(inlat1)) *
System.MathEx.Sin(trig.DegreeToRadian(inlat2)) + System.MathEx.Cos(trig.DegreeToRadian(inlat1)) *
System.MathEx.Cos(trig.DegreeToRadian(inlat2)) * System.MathEx.Cos(trig.DegreeToRadian(theta));
tempdistance = System.MathEx.Acos(tempdistance);
tempdistance = trig.RadianToDegree(tempdistance);
// Defaults to Miles
tempdistance = tempdistance * 60 * 1.1515;
switch (units.ToUpper())
{
case "N":
tempdistance = tempdistance * 0.8684;
break;
case "K":
tempdistance = tempdistance * 1.609344;
break;
default:
break;
}
return tempdistance;
}
catch (Exception se)
{
Debug.Print(se.ToString());
return 0;
}
}
// Finds maidenhead grid square for a location given a decimal longitude/latitude
// Adapted from Delphi code written by VK4ADC - http://www.vk4adc.com/gridlocw.php
public static string getMaidenhead(double inlat, double inlong)
{
try
{
string temploc = "";
string alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string nums = "0123456789";
double ge, te;
string g1zz, g2zz, g3zz, g4zz, g5zz, g6zz;
int g1, g2, g3, g4, g5, g6;
ge = (inlong + 180) / 20;
te = (inlat + 90) / 10;
g1 = (int)ge;
g2 = (int)te;
g1zz = alphas.Substring(g1, 1);
g2zz = alphas.Substring(g2, 1); ;
ge = (ge - g1) * 10;
te = (te - g2) * 10;
g3 = (int)ge;
g4 = (int)te;
g3zz = nums.Substring(g3, 1); ;
g4zz = nums.Substring(g4, 1); ;
g5 = (int)((ge-g3)*24);
g6 = (int)((te-g4)*24);
g5zz = alphas.Substring(g5, 1);
g6zz = alphas.Substring(g6, 1);
temploc += g1zz + g2zz + g3zz + g4zz;
if (((int)inlat == inlat) && ((int)inlong == inlong))
return temploc;
else
temploc += g5zz.ToLower() + g6zz.ToLower();
return temploc;
}
catch (Exception se)
{
Debug.Print(se.ToString());
return "";
}
}
}
}
public class Trigonometry
{
public double DegreeToRadian(double angle)
{
return System.MathEx.PI * angle / 180.0;
}
public double RadianToDegree(double angle)
{
return angle * (180.0 / System.MathEx.PI);
}
}
}