Library tutorials & articles
Writing GPS Applications in .NET: Part 1
Real-Time Satellite Tracking
Knowing the location of satellites is important when determining how precise readings are and how stable a GPS fix is. Since GPS precision will be covered in detail in part two of this series, so this section will focus on interpreting satellite location and signal strength.
There are twenty-four operational satellites in orbit. Satellites are spaced in orbit so that at any time a minimum of six satellites will be in view to users anywhere in the world. Satellites are constantly in motion, which is good because it prevents the existence of “blind spots” in the world with little or no satellite visibility. Just like finding stars in the sky, satellite locations are described as the combination of an azimuth and an elevation. As mentioned above, azimuth measures a direction around the horizon. Elevation measures a degree value up from the horizon between 0° and 90°, where 0° represents the horizon and 90° represents “zenith,” directly overhead. So, if the device says a satellite’s azimuth is 45° and its elevation is 45°, the satellite is located halfway up from the horizon towards the northeast. In addition to location, devices report each satellite’s “Pseudo-Random Code” (or PRC) which is a number used to uniquely identify one satellite from another.
Here’s an example of a $GPGSV sentence:
$GPGSV,3,1,10,24,82,023,40,05,62,285,32,01,62,123,00,17,59,229,28*70
Each sentence contains up to four blocks of satellite information, comprised of four words. For example, the first block is “24,82,023,40” and the second block is “05,62,285,32” and so on. The first word of each block gives the satellite’s PRC. The second word gives each satellite’s elevation, followed by azimuth and signal strength. If this satellite information were to be shown graphically, it would look like figure 1-1.
(Figure 1-1: Graphical representation of a $GPGSV sentence, where the center of the circle marks the current position and the edge of the circle marks the horizon.)
Perhaps the most important number in this sentence is the “signal-to-noise ratio” (or SNR for short). This number indicates how strongly a satellite’s radio signal is being received. Remember, satellites transmit signals at the same strength, but things like trees and walls can obscure a signal beyond recognition. Typical SNR values are between zero and fifty, where fifty means an excellent signal. (SNR can be as high as ninety-nine, but I’ve never seen readings above fifty even in wide open sky.) In Figure 1-1, the green satellites indicate a strong signal, whereas the yellow satellite signifies a moderate signal (in part two, I will provide a way to classify signal strengths). Satellite #1’s signal is completely obscured. Listing 1-7 shows the interpreter after it is expanded to read satellite info.
Listing 1-7: The interpreter is improved to interpret the location of GPS satellites currently in view.
'** Listing 1-7. Extracting satellite information
'*******************************************************
Public Class NmeaInterpreter
' Raised when the current location has changed
Public Event PositionReceived(ByVal latitude As String, _
ByVal longitude As String)
Public Event DateTimeChanged(ByVal dateTime As DateTime)
Public Event BearingReceived(ByVal bearing As Double)
Public Event SpeedReceived(ByVal speed As Double)
Public Event SpeedLimitReached()
Public Event FixObtained()
Public Event FixLost()
Public Event SatelliteReceived(ByVal pseudoRandomCode As Integer, _
ByVal azimuth As Integer, _
ByVal elevation As Integer, _
ByVal signalToNoiseRatio As Integer)
' Processes information from the GPS receiver
Public Function Parse(ByVal sentence As String) As Boolean
' Discard the sentence if its checksum does not match our calculated ' checksum
If Not IsValid(sentence) Then Return False
' Look at the first word to decide where to go next
Select Case GetWords(sentence)(0)
Case "$GPRMC" ' A "Recommended Minimum" sentence was found!
Return ParseGPRMC(sentence)
Case "$GPGSV" ' A "Satellites in View" message was found
Return ParseGPGSV(sentence)
Case Else
' Indicate that the sentence was not recognized
Return False
End Select
End Function
' Divides a sentence into individual words
Public Function GetWords(ByVal sentence As String) As String()
Return sentence.Split(","c)
End Function
' Interprets a $GPRMC message
Public Function ParseGPRMC(ByVal sentence As String) As Boolean
' Divide the sentence into words
Dim Words() As String = GetWords(sentence)
' Do we have enough values to describe our location?
If Words(3) <> "" And Words(4) <> "" And Words(5) <> "" And _
Words(6) <> "" Then
' Yes. Extract latitude and longitude
Dim Latitude As String = Words(3).Substring(0, 2) & "°" ' Append hours
Latitude = Latitude & Words(3).Substring(2) & """" ' Append minutes
Latitude = Latitude & Words(4) ' Append the hemisphere
Dim Longitude As String = Words(5).Substring(0, 3) & "°" ' Append hours
Longitude = Longitude & Words(5).Substring(3) & """" ' Append minutes
Longitude = Longitude & Words(6) ' Append the hemisphere
' Notify the calling application of the change
RaiseEvent PositionReceived(Latitude, Longitude)
End If
' Do we have enough values to parse satellite-derived time?
If Words(1) <> "" Then
' Yes. Extract hours, minutes, seconds and milliseconds
Dim UtcHours As Integer = CType(Words(1).Substring(0, 2), Integer)
Dim UtcMinutes As Integer = CType(Words(1).Substring(2, 2), Integer)
Dim UtcSeconds As Integer = CType(Words(1).Substring(4, 2), Integer)
Dim UtcMilliseconds As Integer
' Extract milliseconds if it is available
If Words(1).Length > 7 Then UtcMilliseconds = _
CType(Words(1).Substring(7), Integer)
' Now build a DateTime object with all values
Dim Today As DateTime = System.DateTime.Now.ToUniversalTime
Dim SatelliteTime As New System.DateTime(Today.Year, Today.Month, _
Today.Day, UtcHours, UtcMinutes, UtcSeconds, UtcMilliseconds)
' Notify of the new time, adjusted to the local time zone
RaiseEvent DateTimeChanged(SatelliteTime.ToLocalTime)
End If
' Do we have enough information to extract the current speed?
If Words(7) <> "" Then
' Yes. Convert it into MPH
Dim Speed As Double = CType(Words(7), Double) * 1.150779
' If we're over 55MPH then trigger a speed alarm!
If Speed > 55 Then RaiseEvent SpeedLimitReached()
' Notify of the new speed
RaiseEvent SpeedReceived(Speed)
End If
' Do we have enough information to extract bearing?
If Words(8) <> "" Then
' Indicate that the sentence was recognized
Dim Bearing As Double = CType(Words(8), Double)
RaiseEvent BearingReceived(Bearing)
End If
' Does the device currently have a satellite fix?
If Words(2) <> "" Then
Select Case Words(2)
Case "A"
RaiseEvent FixObtained()
Case "V"
RaiseEvent FixLost()
End Select
End If
' Indicate that the sentence was recognized
Return True
End Function
' Interprets a "Satellites in View" NMEA sentence
Public Function ParseGPGSV(ByVal sentence As String) As Boolean
Dim PseudoRandomCode As Integer
Dim Azimuth As Integer
Dim Elevation As Integer
Dim SignalToNoiseRatio As Integer
' Divide the sentence into words
Dim Words() As String = GetWords(sentence)
' Each sentence contains four blocks of satellite information. ' Read each block and report each satellite's information
Dim Count As Integer
For Count = 1 To 4
' Does the sentence have enough words to analyze?
If (Words.Length - 1) >= (Count * 4 + 3) Then
' Yes. Proceed with analyzing the block. Does it contain any ' information?
If Words(Count * 4) <> "" And Words(Count * 4 + 1) <> "" _
And Words(Count * 4 + 2) <> "" And Words(Count * 4 + 3) <> "" Then
' Yes. Extract satellite information and report it
PseudoRandomCode = CType(Words(Count * 4), Integer)
Elevation = CType(Words(Count * 4 + 1), Integer)
Azimuth = CType(Words(Count * 4 + 2), Integer)
SignalToNoiseRatio = CType(Words(Count * 4 + 2), Integer)
' Notify of this satellite's information
RaiseEvent SatelliteReceived(PseudoRandomCode, Azimuth, Elevation, _
SignalToNoiseRatio)
End If
End If
Next
' Indicate that the sentence was recognized
Return True
End Function
' Returns True if a sentence's checksum matches the calculated checksum
Public Function IsValid(ByVal sentence As String) As Boolean
' Compare the characters after the asterisk to the calculation
Return sentence.Substring(sentence.IndexOf("*") + 1) = GetChecksum(sentence)
End Function
' Calculates the checksum for a sentence
Public Function GetChecksum(ByVal sentence As String) As String
' Loop through all chars to get a checksum
Dim Character As Char
Dim Checksum As Integer
For Each Character In sentence
Select Case Character
Case "$"c
' Ignore the dollar sign
Case "*"c
' Stop processing before the asterisk
Exit For
Case Else
' Is this the first value for the checksum?
If Checksum = 0 Then
' Yes. Set the checksum to the value
Checksum = Convert.ToByte(Character)
Else
' No. XOR the checksum with this character's value
Checksum = Checksum Xor Convert.ToByte(Character)
End If
End Select
Next
' Return the checksum formatted as a two-character hexadecimal
Return Checksum.ToString("X2")
End Function
End Class
Related articles
Related discussion
-
Creating a Windows Service in VB.NET
by Templario55 (107 replies)
-
About User Control in .Net
by andypat (4 replies)
-
High-Performance .NET Application Development & Architecture
by Manjot Bawa (0 replies)
-
How can i send data from VB.Net to Dymo Labels
by himanshu.tomar (0 replies)
-
popup window in vb.net... how it's work?..
by yf2009 (6 replies)
Related podcasts
-
More jQuery in ASP.NET
In this episode Chris Brandsma, Rick Strahl, Dave Ward, Bertrand Le Roy, and Scott Koon conclude their discussion of Microsoft's jQuery in ASP.NET announcement1.This episode of the Alt.NET Podcast is brought to you by LLBLGen Pro, the most mature O/R mapper and code generator out there.Are ...
Events coming up
-
Nov
18
15 Minutes of Fame
Dresher, United States
This is a yearly tradition. We select 10 of the favorite speakers from monthly meetings, code camps, and hands on labs. Each one does a 15 minute talk on their favorite .NET technology. This is our 10th anniversary so we plan a gala event with special prizes and refreshments.
Hi ,
I need the same program as well.Can you find any information.Please contact me.
tunc@te-mob.com
The checksum routine has been modified slightly to clarify the Select statement.
The "$" case is used to initialise Checksum and the If statement in the Else case has been removed. This works because Checksum = value gives the same result as Checksum = 0 Xor value when processing the first byte.
' Calculates the checksum for a sentence
Public Function GetChecksum(ByVal sentence As String) As String
' Loop through all chars to get a checksum
Dim Character As Char
Dim Checksum As Integer = 0
For Each Character In sentence
Select Case Character
Case "$"c
' Ignore the dollar sign
Checksum = 0
Case "*"c
' Stop processing before the asterisk
Exit For
Case Else
' XOR the checksum with this character's value
Checksum = Checksum Xor Convert.ToByte(Character)
End Select
Next
' Return the checksum formatted as a two-character hexadecimal
Return Checksum.ToString("X2")
End Function
The NMEA protocol specifies that the last parameter is immediately followed by "*" and a 2-cfharacter checksum. For example, 3.45,"W"*1A is valid as is 200804,,*1A. In the fist case there is data in the last parameter and in the second case, because the last parameter has been omitted. we effectively have "200804,," & "" & "*" & "1A". That is the data string upto the second last parameter and it's following comma, the omitted final parameter, the asterisc to denote both end of data and the start of the checksum, and finally the 2-character checksum.
Jon's excellent articles give us the start for writing our code but does say that additional error trapping is required.
My change to the GetWords function is:
Public Function GetWords(ByVal sentence As String) As String()
Dim temp As String
temp = sentence.Substring(0, sentence.IndexOf("*")) & ",*"
Return temp.Split(","c)
End Function
I need somebody to test my mobile APP. You can download the cab file from http://gps.gpsxml.com/viewtopic.php?t=4
Please feel free to give any comments, suggestions or ideas
Thanks
Imtiyaz Momin
http://gps.gpsxml.com/
imtu80@hotmail.com
Also, added new feature where I can upload picture from the phone using my application and the picture shows up on the trail where the picture was taken.
Check it out at http://gps.gpsxml.com/tracker.cfm?userID=1
Please feel free to give any comments, suggestions or ideas
Thanks
Imtiyaz Momin
http://gps.gpsxml.com/
imtu80@hotmail.com
I am a novice programmer and need to get my hands on some code for phone tracking which will plot a phones movement every (n) minutes then plot the position on a map.
Any help at all will be greatly appreciated.
Can anyone please please help me.
Thanks
Nev
Above link shows an example which I created for my brother for his carpc.
It can covertly run on mobile device and send location information via internet.
let me know what you think and anything can be done with my project.
Imtiyaz Momin
imtu80@hotmail.com
http://www.imomin.com
Regarding with GPRMC message
$GPRMC,040302.663,A,3939.7,N,10506.6,W,0.27,358.86,200804,,*1A
Some antennas (like Leadtek) See quotes, transmitt the last parameter before * char like this
$GPRMC,040302.663,A,3939.7,N,10506.6,W,0.27,358.86,200804,3.45,"W"*1A
Other antennas (like Garmin), transmit the last parameter like this
$GPRMC,040302.663,A,3939.7,N,10506.6,W,0.27,358.86,200804,3.45,"W",*1A
Note that has a colon "," before * char.
Please someone explain me this question.
Thanks.
Jean
Can I get the c# code for this nice article?
Regards,
Ravi
Well...as soon as you write a post you figure soething out.
It seems that the error is only thrown when there isn't enough data in the nmea sentence because if I use this sentence
$GPGSV,2,2,08,14,28,047,21,28,20,259,,19,16,156,26,23,07,254,41*71
it seems to work fine. I think the problem is with the SNR for the first satellite in view as the previous NMEA didn't have a value for this.
Hope this helps someone.
Nice article, very useful except I've come across a problem with the GPGSV sentences. If you try and run this code
MyInterpreter.Parse("$GPGSV,2,1,08,11,78,132,,20,63,226,,01,46,102,,17,33,308,*71")
It will throw an error (Conversion from string "*71" to type 'Integer' is not valid.)
This is because of this line of code
SignalToNoiseRatio =
CType(Words(Count * 4 + 3), Integer)Does anyone know how to solve this problem as it's kind of a thorn in my side and I can't get rid of it.
Cheers
Jon
I like to write GPS navigating system pleas tell me what kind of hardware and signals I need, I am living in Sri Lanka, how I get that connection
If you require Ordnance Survey Grid References, I have written an article on how to do so using NMEA data derived from Jon Person's excellent NMEAinterpreter class.
GPS- Deriving British Ordnance Survey Grid Referece from NMEA data
AlexE
However this led to a new error as the final word in the string contained the checksum.
ie in the example sentence
$GPGSV, 3, 1, 10, 24, 82, 023, 40, 05, 62, 285, 32, 01, 62, 123, 00, 17, 59, 229, 28*70
the final word would be 28*70
this caused an error when trying to convert this to an int32 in the line
SignalToNoiseRatio = Convert.ToInt32(Words[Count * 4 + 3]);
My solution was to remove the checksum part of the sentence in GetWords, before splitting the sentence
public string[] GetWords(string sentence)
{
//remove the final * + checksum
sentence = sentence.Substring(0, sentence.IndexOf("*"));
//now split it up
return sentence.Split(',');
}
Assuming that I'm not talking out of my rear, I hope this proves useful
Alex
Original Article: http://www.gpsdotnet.com/kb/article.aspx?id=10350
Example Sentence: $GPGSV, 3, 1, 10, 24, 82, 023, 40, 05, 62, 285, 32, 01, 62, 123, 00, 17, 59, 229, 28*70
Each Block consist of 4 words.. "24, 82, 023, 40" == "PseudoRandomCode, Elevation, Azimuth, SignalToNoiseRatio"
According to your Article... SNR values range from 0-50...where 50 means "Excellent Signal"...though SNR can go as high as 99, like you've stated.
' Yes. "Extract satellite information and report it" -- section.
' ERROR suspect
Azimuth = CType ( Words ( Count * 4 + 2 ), Integer)
SignalToNoiseRatio = CType( Words ( Count * 4 + 2 ), Integer)
'// ^--- Logical Bug, I believe it should be ....
' CORRECTION
SignalToNoiseRatio = CType ( Words ( Count * 4 + 3), Integer)
'// ^--- This would be correct.
Otherwise I believe it would return the same value from the Azimuth extraction. So you wouldn't get any SNR information to be able to base precision correctly. For the future of dependability and reliability of code production, I post this correction. As far as the Signal Strength of the satellites, within the Notification, of the Event call to,...
RaiseEvent SatelliteReceived ( PseudoRandomCode, Azimuth, Elevation, SignalToNoiseRatio )
I believe it would generate incorrect results...to what ever is going to be done with the SNR variable.
Please feel free to e-mail me at deciphered_scripturez@yahoo.com for additional details...I really Thank you, Jon Person, for your hard work and time put into this and I really would LOVE to help as much as possible...I believe in your product.
Thank You,
-- c_programming_guru
As I understand it, that should be fine... so long as you add a GPS receiver?
Just curious, what kind of hardware and signal service will be required to make it happen?
I have a ViewSonic V37 pocket PC. Will that be a good instrument for this? Please advice.
Thanks
Pankaj
Thank you, Jon. I look forward to your next article.
Steve
This thread is for discussions of How to Write a GPS Application.