This article explains how to use GPSID (GPS Intermediate Driver) interface provided in Windows Mobile 5 and 6, using FreePascal. Source code and pictures of a real device in the field is included.
STEP 1 - Configure the registryThis step is not necessary if your firmware have GPSID settings configured properly by default. I was not that lucky since I used third-party WM6 firmware package made by local enthusiasts.
Check out
this MSDN page dealing with GPSID registry settings, and, using any registry editor you like (I used
TotalCommander) make sure the registry settings is consistent (at least, CommPort and Baud are set according to what is configured in the Control Panel => External GPS on your device).
STEP 2 - Compile source codeFor simplicity reasons, this source code generates
console application, so, in order to see its output, you need to install
Console subsystem for Windows Mobile. Of course, you don't need to install console if you use the code just as reference.
Unit containing GPSID interface classunit bccGPSID;
interface
uses GPSAPI;
TYPE TGPSEvents = (evLocData = 0, evDevState = 1);
TOnPosition = procedure(Sender : TObject) of object;
TYPE
{ TGPS }
TGPS = class
constructor Create;
destructor Destroy; override;
private
Events : array [TGPSEvents] of THandle;
hGPS : THandle;
FPosition,FPrevPos : GPS_POSITION;
FPosBuffer : Pointer;
FMovementFilter : LongBool;
RealSizeOf : Cardinal;
EOnPosition : TOnPosition;
procedure DirtyHack_FindSize;
procedure CopyAndInvoke;
public
function Start : LongBool;
procedure Stop;
procedure ProcessMessages;
function CheckValidity(const Validity : Cardinal) : LongBool;
property Position : GPS_POSITION read FPosition;
property MovementFilter : LongBool read FMovementFilter write FMovementFilter;
property OnPosition : TOnPosition read EOnPosition write EOnPosition;
end;
implementation
uses Windows;
CONST DefaultAge = 60*1000; // milliseconds
constructor TGPS.Create;
begin
inherited Create;
Events[evLocData]:=CreateEvent(nil,False,False,nil);
Events[evDevState]:=CreateEvent(nil,False,False,nil);
hGPS:=0;
RealSizeOf:=SizeOf(GPS_POSITION);
DirtyHack_FindSize;
FPosBuffer:=HeapAlloc(GetProcessHeap,HEAP_ZERO_MEMORY,RealSizeOf);
FillChar(FPrevPos,SizeOf(GPS_POSITION),$00);
FMovementFilter:=True;
end;
destructor TGPS.Destroy;
begin
Stop;
CloseHandle(Events[evDevState]);
CloseHandle(Events[evLocData]);
HeapFree(GetProcessHeap,0,FPosBuffer);
inherited Destroy;
end;
procedure TGPS.DirtyHack_FindSize;
const HackRange = $100;
var i : Cardinal;
begin
FillChar(FPosition,SizeOf(GPS_POSITION),$00);
FPosition.dwVersion:=GPS_VERSION_CURRENT;
for i:=SizeOf(GPS_POSITION) to SizeOf(GPS_POSITION)+HackRange do
begin
FPosition.dwSize:=i;
if (GPSGetPosition(0,@FPosition,0,0)=ERROR_SUCCESS) then
begin
RealSizeOf:=i;
Break;
end;
end;
end;
procedure TGPS.CopyAndInvoke;
begin
memmove(@FPrevPos,@FPosition,SizeOf(GPS_POSITION));
if Assigned(EOnPosition) then EOnPosition(Self);
end;
function TGPS.Start : LongBool;
begin
Result:=False;
if (hGPS=0) then
begin
hGPS:=GPSOpenDevice(Events[evLocData],Events[evDevState],nil,0);
Result:=(hGPS<>0);
end;
end;
procedure TGPS.Stop;
begin
if (hGPS<>0) then
if (GPSCloseDevice(hGPS)=ERROR_SUCCESS) then hGPS:=0;
end;
procedure TGPS.ProcessMessages;
var WFOR : Cardinal;
begin
WFOR:=WaitForMultipleObjects(2,@Events,False,0);
case WFOR of
WAIT_OBJECT_0 : begin
// retrieve position
FillChar(FPosBuffer^,RealSizeOf,$00);
GPS_POSITION(FPosBuffer^).dwVersion:=GPS_VERSION_CURRENT;
GPS_POSITION(FPosBuffer^).dwSize:=RealSizeOf;
if (GPSGetPosition(hGPS,FPosBuffer,DefaultAge,0)=ERROR_SUCCESS) then
begin
// copy retrieved data to property field
memmove(@FPosition,FPosBuffer,SizeOf(GPS_POSITION));
// check against movement filter, and apply if necessary
if (FMovementFilter) then
begin
if not (
(FPosition.dblLongitude=FPrevPos.dblLongitude) and
(FPosition.dblLatitude=FPrevPos.dblLatitude) and
(FPosition.flAltitudeWRTEllipsoid=FPrevPos.flAltitudeWRTEllipsoid) and
(FPosition.flAltitudeWRTSeaLevel=FPrevPos.flAltitudeWRTSeaLevel)
) then CopyAndInvoke;
end
else CopyAndInvoke;
end;
end;
WAIT_OBJECT_0+1 : {I wonder what should happen?};
end;
end;
function TGPS.CheckValidity(const Validity : Cardinal) : LongBool;
begin
Result:=(FPosition.dwValidFields and Validity)=Validity;
end;
end.
Example program using unit above{$APPTYPE CONSOLE}
program GPSIDemo;
uses Windows, GPSAPI, bccGPSID;
TYPE TMyGPS = class(TGPS)
procedure Handler_OnPosition(Sender : TObject);
end;
procedure TMyGPS.Handler_OnPosition(Sender : TObject);
begin
Writeln;
with Position do
begin
if CheckValidity(GPS_VALID_LATITUDE or GPS_VALID_LONGITUDE) then Writeln('Coordinates = ',dblLatitude : 3 : 5,' : ',dblLongitude : 3 : 5);
if CheckValidity(GPS_VALID_SPEED or GPS_VALID_HEADING) then Writeln('Speed & Hdg = ',flSpeed : 3 : 1,' & ',flHeading : 3 : 1);
if CheckValidity(GPS_VALID_ALTITUDE_WRT_SEA_LEVEL) then Writeln('Elevation = ',flAltitudeWRTSeaLevel : 4 : 1);
end;
end;
var i : Cardinal;
begin
with TMyGPS.Create do
begin
OnPosition:=Handler_OnPosition();
if Start then
begin
Writeln('GPS started');
for i:=$00 to $FF do // use whatever bounds you want
begin
Write('.');
ProcessMessages;
Sleep(1000); // check every one second
end;
Stop;
end
else Writeln('GPS failed to start');
Free;
end;
end.
STEP 3 - Compile and run it for your device. Don't forget to specify target OS and CPU family.
For my testing purposes I used
ETEN Glofiish X650, which is running Windows Mobile 6 on ARM CPU. Go outside and run it, enjoy:
http://pic.ipicture.ru/uploads/090731/X45V2RRJtf.jpg