devxlogo

Take Your Apps Far and Wide with a GPS Tracking System

Take Your Apps Far and Wide with a GPS Tracking System

lobal Positioning System (GPS) is a world-wide navigational system that can tell you with pinpoint accuracy your exact current location. GPS has been around for many years and has many applications both in the commercial sector as well as in the military. The best part of GPS is that it is free to use?simply purchase a GPS receiver (prices range from $100 to $10,000+ for complex devices) and equip it with the necessary software and you will soon be using it to help you navigate unfamiliar territory.

While route navigation is one of the most popular uses of GPS, another good use is helping you track the whereabouts of your inventory, such as delivery trucks and goods. In this article, I will build a GPS tracking system that allows you to track the whereabouts of your delivery trucks, using a Windows Mobile 5.0 Pocket PC device and a Bluetooth-enabled GPS receiver. To ensure compatibility and ease of use, I will integrate a navigation data standard into the GPS tracking system for seamless communication between the Windows Mobile 5.0 Pocket PC device and the Bluetooth-enabled GPS receiver.

The tracking system comprises:

  • A Windows Mobile 5.0 Pocket PC mounted in a truck; it is also connected to the Internet via GPRS (General Packet Radio Services)
  • A Bluetooth-enabled GPS receiver that is paired with the Pocket PC
  • A Pocket PC application that displays the latitude and longitude of its current position; it also shows the speed at which the truck is moving
  • A back end server hosted on a Web server that collects the positional data
  • A server-side application that receives the data and displays the map of the location the truck is currently at, thereby allowing real-time monitoring.

Figure 1 shows what the server application will look like. As you can see, it can monitor two trucks at the same time and it uses Microsoft Virtual Earth for mapping.


Figure 1. The server application you will build in this article monitors two trucks at the same time in side-by-side maps.

Figure 2. I chose the iMate JasJar as my Pocket PC device and the Holux GPSlim 236 as my GPS receiver.

There are two main components you will build in this article:

  • A Pocket PC application that retrieves and displays GPS data from the GPS receiver
  • A server that receives the positional data sent by the Pocket PC application and then displays the map of the corresponding location

What you need
To create this application you’ll need:

  • A Windows Mobile 5.0 Pocket PC (Phone Edition) that supports Bluetooth connectivity; in addition, the device must be connected to the Internet via GPRS
  • A Bluetooth-enabled GPS receiver

Figure 2 shows the hardware I selected for my implementation: The iMate JasJar Pocket PC and the Holux GPSlim 236 GPS receiver.

You need to pair up the Windows Mobile device with the GPS receiver using Bluetooth. Once they are paired, remember to establish a serial connection between the two devices. For my device, I selected COM4 as the serial port.

Author’s Note: Due to the different setup procedures for different devices, I will not attempt to show the steps to set up the serial port. Check the documentation of your device for more information.

Creating the Pocket PC Application
Using Visual Studio 2005, create a Windows Mobile 5.0 Pocket PC application and name it C:GPSTracking (see Figure 3).

Author’s Note: In order to select the Windows Mobile 5.0 Pocket PC item in the Project types listview, you need to make sure you’ve downloaded the Windows Mobile 5.0 SDK for Pocket PC ().

Populate the default Form1 with the following controls (see Figure 4):

  • Label
  • TextBox (set the Multiline property to True and the ScrollBars property to Both)
  • MainMenu

Figure 3. Create a Windows Mobile 5.0 Pocket PC project using Visual Studio.

Figure 4. Populate the default Form1 with the various controls as shown.

Switch to the code-behind of Form1 and declare the following member variables:

  • serialPort to connect to the GPS receiver via the serial connection
  • ServerIP to store the IP address of the server
  • ID to store the ID of the user
Public Class Form1    '---serial port to connect to the GPS receiver---    Private WithEvents serialPort As New IO.Ports.SerialPort    '---IP address of the server---    Private ServerIP As String = "10.0.1.4"    '---the ID of the user---    Private ID As String = "1"

In the event handler for the “Connect GPS” menu item, configure the serial port variable with the necessary parameters and then open the connection:

    '---Connect GPS---    Private Sub MenuItem1_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles MenuItem1.Click        '---close the port if it is already open---        If serialPort.IsOpen Then            serialPort.Close()        End If        '---set the parameters and open the port---        Try            With serialPort                .PortName = "COM4"                .BaudRate = 9600                .Parity = IO.Ports.Parity.None                .DataBits = 8                .StopBits = IO.Ports.StopBits.One            End With            serialPort.Open()            '---disable the Connect GPS menu item---            MenuItem1.Enabled = False            '---enable the Disconnect menu item---            MenuItem3.Enabled = True        Catch ex As Exception            MsgBox(ex.ToString)        End Try    End Sub

To receive incoming data from the GPS receiver, you need to service the DataReceived event of the SerialPort class:

    Private Sub DataReceived( _           ByVal sender As Object, _           ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _           Handles serialPort.DataReceived        TextBox1.BeginInvoke(New _           myDelegate(AddressOf updateTextBox), _           New Object() {})    End Sub

Here, I have used a delegate (myDelegate) to update the TextBox controls using the received data from the GPS receiver:

    Public Delegate Sub myDelegate()    Public Sub updateTextBox()        TextBox1.Text += serialPort.ReadExisting()        Dim lines() As String = TextBox1.Text.Split(vbLf)        If lines.Length < 2 Then Exit Sub End If If TextBox1.Text.Length >= 500 Then            '---clear until the last $---            TextBox1.Text = _               TextBox1.Text.Substring(TextBox1.Text.LastIndexOf("$"))        End If        If lines(lines.Length - 2).StartsWith("$GPGGA") Or _           lines(lines.Length - 2).StartsWith("$GPRMC") Then            processGPSData(lines(lines.Length - 2))        End If    End Sub

One tricky issue with the GPS receiver is that data is not received in discrete blocks. For example, instead of receiving the following two lines of data in two discrete blocks:

$GPGGA,001431.092,0118.2653,N,10351.1359,E,0,00,,-19.6,M,4.1,M,,0000*5B$GPGSA,A,1,,,,,,,,,,,,,,,*1E

The data may arrive in four separate blocks (the end of each of the two lines will be appended with characters 13 and 10 (new-line characters)):

$GPGGA,001431.092,0118.2653,N,10351.1359,E,0,00,,-19.6,M,4.1,M,,0000*5B$GPGSA,A,1,,,,,,,,,,,,,,,*1E
Tip: Using the ReadLine() method of the SerialPort class seems like it will do the trick. However, in practice it does not work and often chokes on the receiving data.
Figure 5. Append all received GPS data to the TextBox control.

A good workaround is to append all incoming data to a TextBox control and manually retrieve the necessary data that you want to process. As illustrated in Figure 5, the latest data is always located at the bottom of the TextBox control. As the last line may contain incomplete GPS data, you should only examine the second-to-last line. You will do so by splitting the entire contents of the TextBox control into a string array (using the vbLf character as delimiter) and then examining the penultimate element in the array.

As the TextBox has a maximum limit on the number of characters it can store, you need to constantly clear it after a certain number of characters. To improve performance, ideally you should not leave too much data in the TextBox control as the splitting of its content into an array requires additional processing time. And hence, 500 characters is a good compromise.

Understanding GPS Data
Generally, most GPS receivers support the NMEA (National Marine Electronics Association; see http://www.gpsinformation.org/dale/nmea.htm for more information) standard. Some common NMEA data sentences (the data output of the GPS receiver) are shown in Table 1.

Table 1. Some common NMEA data.

SentenceDescription
$GPGGAGlobal positioning system fixed data
$GPGLLGeographic position – latitude / longitude
$GPGSAGNSS DOP and active satellites
$GPGSVGNSS satellites in view
$GPRMCRecommended minimum specific GNSS data
$GPVTGCourse over ground and ground speed

 

For this project, I am mainly concerned with the geographical location, that is, latitude and longitude. Therefore, I only need to look out for sentences beginning with $GPGGA.

The breakdown of a typical $GPGGA sentence is shown in Table 2.

Table 2. The fields in a $GPGGA sentence.

FieldSampleDescription
0$GPGGASentence Prefix
1001431.092UTC Time (in hhmmss.sss format)
20118.2653Latitude (in ddmm.mmmm format)
3N(N)orth or (S)outh
410351.1359Longitude (in dddmm.mmmm format)
5E(E)ast or (W)est
60Position Fix (0 is invalid,1 is valid, 2 is Valid DGPS, 3 is Valid PPS)
704Satellites used
8Horizontal dilution of precision
9-19.6Altitude (unit specified in next field)
10MM is Meter
114.1Geoid separation (unit specified in next field)
12MM is Meter
13Age of DGPS data (in seconds)
140000DGPS Station ID
15*5BChecksum
15CRLFTerminator

 

As you can see from Table 2, to obtain the latitude and longitude of a location you only need fields 2 and 4. One important point to note is that the latitude and longitude information are both represented in the “degrees, minutes, and decimal minutes” format?ddmm.mmmm. However, most mapping applications require longitude and latitude to be expressed in decimal degrees (dd.dddddd) with a corresponding sign (negative for south latitude and west longitude). Hence, you need to perform some conversions.

The following formula converts the ddmm.mmmm format to decimal degrees format:

dd.dddddd = (ddmm.mmmm  100) + _           ((ddmm.mmmm - ((ddmm.mmmm  100) * 100)) / 60)

If the direction is “South,” then the latitude must be negated. If the direction is “West,” then the longitude must be negated.

One additional requirement for this application is to display the speed in which the truck is traveling. For this, you can examine all GPS data that is prefixed with the “$GPRMC” word. Here’s an example of a sentence beginning with the “$GPRMC” word:

$GPRMC,003348.163,V,0120.0779,N,10344.8182,E,000.05,,070805,,,N*7F
Figure 6. If you test the Pocket PC application at this point, it should look similar to this.

The speed is indicated in the eighth field and is expressed in knots (this information is available in the NMEA standard). In the example above, it is 000.05 knots. Converting from knots to kilometers per hour is easy?simply multiply it by 1.85.

With this knowledge of how to interpret the GPS data, you can now define the processGPSData() subroutine and extract the relevant information:

    Private Sub processGPSData(ByVal str As String)        Dim rawLatLng As Double        Try            '---separate the GPS data into various fields---            Dim field() As String            field = str.Split(",")            Dim lat, lng As Double            Select Case field(0)                Case "$GPGGA"                    '---latitude---                    rawLatLng = Convert.ToDouble(field(2))                    lat = (rawLatLng  100) + _                         ((rawLatLng - ((rawLatLng  100) * 100)) / 60)                    '---latitude is negative if South---                    If field(3) = "S" Then                        lat *= -1.0                    End If                    '---longitude---                    rawLatLng = Convert.ToDouble(field(4))                    lng = (rawLatLng  100) + _                         ((rawLatLng - ((rawLatLng  100) * 100)) / 60)                    '---longitude is negative if West---                    If field(5) = "W" Then                        lng *= -1.0                    End If                    '---display the lat and lng---                    lblLat.Text = "Lat:" & lat                    lblLng.Text = "Lng:" & lng                Case "$GPRMC"                    '---display the speed---                    If field(7) = String.Empty Then                        lblSpeed.Text = "Speed: 0 km/h"                    Else                        lblSpeed.Text = "Speed: " & _                         (Convert.ToDouble(field(7)) * 1.85).ToString _                         & " km/h"                    End If            End Select        Catch            MsgBox("An error has occurred")        End Try    End Sub

The Disconnect menu item closes the serial port connection:

    '---Disconnect menu item---    Private Sub MenuItem3_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles MenuItem3.Click        serialPort.Close()        MenuItem1.Enabled = True  '---Connect GPS---        MenuItem3.Enabled = False '---Disconnect---    End Sub

The Server IP menu item allows you to set the IP address of the server (to be built later, in the second part of this article):

    '---Server IP menu item---    Private Sub MenuItem4_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles MenuItem4.Click        ServerIP = InputBox( _           "Please enter the IP address of server", "Server IP")    End Sub

The ID menu item sets the identity of the user. This allows the server to differentiate between different users:

    '---ID menu item---    Private Sub MenuItem5_Click( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) _       Handles MenuItem5.Click        ID = InputBox("Please enter your ID", "ID")    End Sub

Testing the Pocket PC application
With the Windows Mobile device connected to the PC via ActiveSync, you can now deploy the application onto it (simply press F5 in Visual Studio 2005). When the application is loaded, tap on the Connect GPS menu item (see Figure 6). Assuming that the GPS receiver is paired correctly and that the serial port used is COM4, you will observe GPS data are streaming in continuously. You should also be able to observe the latitude and longitude of your position. If you try this inside a moving car, you will also observe that the speed changes as the car moves.

Creating the Server Application
With the Pocket PC application completed, it is now time to build the server application. Using Visual Studio 2005, create a new Windows application and name it C:MapServer. Populate the default Form1 with the following controls (see also Figure 7):

  • GroupBox controls
  • WebBrowser controls
  • Label controls
  • TextBox controls
  • Button controls
Figure 7. Populate the default Form1 with the various controls as shown.

Using Virtual Earth to Display Maps
To display a map of locations returned by the Pocket PC clients, I will use Microsoft’s Virtual Earth, a map and search system comprising maps, aerial images, business directories, etc. Using VE, you can search for businesses, addresses, as well as ask for directions. You can access VE at http://local.live.com/.

Microsoft provides a map control that allows developers to embed VE maps into their own applications. You can use it to build a custom solution using VE mapping services. The VE Map control is made up of a JavaScript page and a cascading style sheet. The VE Map control is hosted at http://dev.virtualearth.net/mapcontrol/v3/mapcontrol.js. The CSS is located at http://local.live.com/css/MapControl.css.

The first step to using Virtual Earth is to create an HTML file to contain all the necessary JavaScript functions to interact with the VE map.

Tip: To get detailed help and the references to the various methods in the Virtual Map control, download the Virtual Earth SDK. As of this writing, Microsoft Virtual Earth is in version 3.0.

Add an HTML page to the project (right-click on project name in Solution Explorer and then select Add ?> New Item… Select HTML Page). Name the HTML page VirtualEarth.html.

Populate the HTML page with the following:

 

 

The VirtualEarth.html page contains a reference to the VE Map control (.js) as well as the CSS file (.css). It also contains two JavaScript functions:

  • goto_map_position()?Goes to a particular location on the map based on the latitude and longitude specified.
  • loadMap()?Loads the map with the various parameters such as size of the map, zoom level, latitude and longitude, etc. It also attaches event handlers to the various events so that when certain events happen, the appropriate event handler is fired.

You would also need to set the Copy to Output Directory property of VirtualEarth.html to “Copy if newer” (via the Property window) so that the HTML page is deployed during runtime.

Using Sockets for Communication
While you can use Web services to pass the positional information from the Pocket PC to the server, doing so will take up a significant amount of bandwidth. This is because Web services uses XML SOAP packets for information exchange and this XML padding consumes considerable bandwidth consider that all we want to send is latitude and longitude (usually less then 20 bytes) information. Clearly, using Web services over a GPRS connection is not a feasible option (and an expensive one at that). For this reason, I have decided to program sockets directly to pass data to the server.

In Solution Explorer, add a new Class item to the project (right-click on project name and select Add ?> New Item…. Select the Class item) and name it as WMClient.vb. Populate the file as follows:

Imports System.Net.SocketsPublic Class WMClient    Public Event LatLng( _       ByVal ID As Integer, _       ByVal lat As Double, _       ByVal lng As Double)    Private _client As TcpClient    '---used for sending/receiving data---    Private data() As Byte    Public Sub New(ByVal client As TcpClient)        _client = client        '---start reading data from the client in a separate thread---        ReDim data(_client.ReceiveBufferSize)        _client.GetStream.BeginRead(data, 0, _           CInt(_client.ReceiveBufferSize), _           AddressOf ReceiveMessage, Nothing)    End Sub    Public Sub ReceiveMessage(ByVal ar As IAsyncResult)        '---read from client---        Dim bytesRead As Integer        Try            SyncLock _client.GetStream                bytesRead = _client.GetStream.EndRead(ar)            End SyncLock            '---client has disconnected---            If bytesRead < 1 Then                Exit Sub            Else                '---get the message sent---                Dim messageReceived As String = _                    System.Text.Encoding.ASCII. _                    GetString(data, 0, bytesRead)                Dim field() As String = messageReceived.Split(":")                '---raise an event to pass back the lat and lng---                RaiseEvent LatLng( _                   field(0), _                   Convert.ToDouble(field(1)), _                   Convert.ToDouble(field(2)))            End If            '---continue reading from client---            SyncLock _client.GetStream                _client.GetStream.BeginRead(data, 0, _                   CInt(_client.ReceiveBufferSize), _                   AddressOf ReceiveMessage, Nothing)            End SyncLock        Catch ex As Exception            Console.WriteLine(ex.ToString)        End Try    End SubEnd Class

Basically, the WMClient class does the following:

  • Contains a public event named LatLng. This event is raised when it receives data sent from Windows Mobile clients.
  • Creates a constructor that begins to read incoming data from the connected client
  • Processes incoming data from the ReceiveMessage() subroutine. The LatLng event is raised so that the data can be passed to the calling function (Form1) to update the map.

Coding the Server
Back to Form1, and we are now ready to code the main logic for the server. Switching to the code-behind of Form1, first import the following namespaces:

Imports System.Net.SocketsImports System.Threading

To programmatically interact with the map displayed within the WebBrowser control, you need to mark the Form1 class as COM-visible using the ComVisibleAttribute class:

<System.Runtime.InteropServices.ComVisibleAttribute(True)> _Public Class Form1

Next, declare a member variable WMClient to store incoming connections:

<System.Runtime.InteropServices.ComVisibleAttribute(True)> _Public Class Form1    Dim WithEvents user As WMClient

Next, define the ListenForConnections() subroutine so that the server can continually listen for incoming connections:

    Private Sub ListenForConnections ()        Const portNo As Integer = 3456        Dim localAdd As System.Net.IPAddress = _           System.Net.IPAddress.Parse("10.0.1.4")        Dim listener As New TcpListener(localAdd, portNo)        listener.Start()        While True            user = New WMClient(listener.AcceptTcpClient)        End While    End Sub
Author’s Note: I am assuming the IP address of the server is 10.0.1.4. Replace this with the IP address of your server. Also, I have chosen the port 3456 to use for the communication.

In the Form1_Load event, you will read the content of the VirtualEarth.html file and load it into the two WebBrowser controls:

    Private Sub Form1_Load( _       ByVal sender As System.Object, _       ByVal e As System.EventArgs) Handles MyBase.Load        Dim fileContents As String        fileContents = My.Computer.FileSystem.ReadAllText( _            Application.StartupPath & "VirtualEarth.html")        WebBrowser1.DocumentText = fileContents        WebBrowser1.ObjectForScripting = Me        WebBrowser2.DocumentText = fileContents        WebBrowser2.ObjectForScripting = Me        Dim t1 As New Thread(AddressOf ListenForConnections)        t1.Start()    End Sub

Also, note that I am using a separate thread to invoke the ListenForConnections() subroutine. This is because the ListenForConnections() subroutine is an infinite loop and using a separate thread to invoke it will prevent the UI of the application from freezing.

To handle events raised by the WMClient class, define the LatLng subroutine as the event handler. In this event, you will receive the ID, latitude, and longitude that were sent by the client. This information will be used to update the map, which is serviced by a delegate (you cannot directly update Windows controls in a separate thread):

    Private Sub LatLng( _       ByVal ID As Integer, _       ByVal lat As Double, _       ByVal lng As Double) _       Handles user.LatLng        '---display map at specific location---        Me.BeginInvoke(New _           mydelegate(AddressOf GotoLocation), _           New Object() {ID, lat, lng})    End Sub

The GotoLocation() subroutine updates the appropriate TextBox as well as the WebBrowser controls. To update the map to display the specified location, you need to invoke the “goto_map_position” JavaScript function that you defined earlier in the HTML file:

Figure 8. This figure shows what the server will look like when it is started during testing.
    Private Delegate Sub mydelegate( _       ByVal ID As Integer, _       ByVal lat As Double, _       ByVal lng As Double)    Private Sub GotoLocation( _       ByVal ID As Integer, _       ByVal lat As Double, _       ByVal lng As Double)        Console.WriteLine(lat & ":" & lng)        Dim param() As Object = New Object() {lat, lng}        If ID = 1 Then            txtLatitude_1.Text = lat            txtLongitude_1.Text = lng            WebBrowser1.Document.InvokeScript( _               "goto_map_position", param)        ElseIf ID = 2 Then            txtLatitude_2.Text = lat            txtLongitude_2.Text = lng            WebBrowser2.Document.InvokeScript( _               "goto_map_position", param)        End If    End Sub

That’s it! You can now test your server by pressing F5. Figure 8 shows what the server will look like when it is started.

But that’s not the end of the story. You now need to modify the Pocket PC client application so that it will send the latitude and longitude information to the server.

Modifying the Pocket PC Application
Back in the Pocket PC (GPSTracking) project, add a new Class item to the project and name it as Sync.vb. Populate the class with the following:

Imports System.NetImports System.Net.SocketsPublic Class Sync    Const portNo As Integer = 3456    Dim client As Socket    Public Sub PerformSync( _       ByVal HostIP As String, _       ByVal txtData As String)        Try            Dim RemoteAdd As System.Net.IPAddress = _               System.Net.IPAddress.Parse(HostIP)            Dim endPoint As New IPEndPoint(RemoteAdd, portNo)            client = New Socket(endPoint.AddressFamily, _                     SocketType.Stream, ProtocolType.Tcp)            client.Connect(endPoint)            '---send a message to the server            Dim data As Byte() = _            System.Text.Encoding.ASCII.GetBytes(txtData)            '---send the text---            client.Send(data)            client.Close()        Catch ex As Exception            MsgBox(ex.ToString)        End Try    End SubEnd Class

The purpose of this class is to open a socket connection to the server and send the ID of the user, as well as latitude and longitude information, to it.

In Form1.vb, declare and instantiate the Sync class so that you can connect to the server:

Figure 9. Testing the entire system and tracking two different users.
Public Class Form1    '---use for synchronization---    Dim sync As New Sync

Modify the processGPSData() subroutine so that after displaying the latitude and longitude information on the screen, you will send the data to the server:

    Private Sub processGPSData(ByVal str As String)        Dim rawLatLng As Double        Try        ...        ...                    '---display the lat and lng---                    lblLat.Text = "Lat:" & lat                    lblLng.Text = "Lng:" & lng                    '---synchronize with the server---                    sync.PerformSync(ServerIP, ID & ":" & lat & ":" &         ...        ...    End Sub

That’s it! You can now test the entire system. With the server running, run the Pocket PC application and click the Connect GPS menu item. By default, the ID of the Pocket PC application is set to “1”, and hence the left map on the server (see Figure 9) will display the location for user “1”. If you set the ID of the Pocket PC application to “2”, the right map will be updated instead. Hence, the system in this article allows two Pocket PCs to be traced at the same time (of course, you can scale it up to monitor multiple users).

 

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist