全球定位系統(GPS)憑藉 24 顆衛星的測距與測時能力,已廣泛應用於機場導航、車隊調度、水利災害預測等領域。本文示範如何在 Delphi 5.0 中結合 MSCOMM 控件,以非同步串列方式接收 GARMIN 12C 輸出的 NMEA-0183 協議數據,並將經緯度轉換為高斯-克吕格投影平面直角坐標,為資訊管理與指揮調度提供即時定位支援。
在 Delphi 5.0 IDE 中建立專案 GPSReceiver,主視窗放置以下元件:
TEdit:分別顯示緯度(EditLatitude)、經度(EditLongitude)、投影 X 座標(EditXCoord)、投影 Y 座標(EditYCoord)BtnStart(接收)、BtnStop(斷開)、BtnExit(返回)TTimer(Timer1,Interval = 1000 ms,每秒輪詢一次)TMSCOMM(MSComm1,負責串列通訊)執行後介面如下圖所示:

圖 1:GPS 數據接收主視窗
var
nn, x, y, sm, n, weidud, jinchad, firstpxl, secondpxl, a, b,
longitude, longitudemargin, latitude: Double;
weidustr, weidustrcpy, longitudestr1cpy, longitudestrccpy,
weidustr1, weidustr2, jinchastr, jinchastr1, jinchastr2,
longitudestr1, longitudestr11, longitudestr12, longitudestrc,
longitudestrc1, longitudestrc2: string;
gpsstrlist: TStringList;
gpsstr, gpsstrcpy: string;
gpsstrlen: Integer;
procedure TMainFrm.FormShow(Sender: TObject);
begin
MSComm1.CommPort := 1; // 預設 COM1
MSComm1.InBufferSize := 1024;
MSComm1.Settings := '600,n,8,1'; // 600 bps, 無奇偶, 8 資料位, 1 停止位
if not MSComm1.PortOpen then
MSComm1.PortOpen := True; // 開啟串列埠
MSComm1.InBufferCount := 0;
MSComm1.RThreshold := 512;
MSComm1.InputLen := 0;
Timer1.Enabled := False; // 先關閉計時器
// 克拉索夫斯基橢球長短半軸
a := 6378245.0;
b := 6356863.0;
firstpxl := (a*a - b*b) / (a*a); // 第一偏心率平方
secondpxl := (a*a - b*b) / (b*b); // 第二偏心率平方
end;
procedure TMainFrm.Timer1Timer(Sender: TObject);
var
i: Integer;
tmpstr, strq, strq1, strq2, latitudestr, longitudestr: string;
begin
gpsstr := MSComm1.Input;
gpsstrcpy := gpsstr;
while Length(gpsstr) > 0 do
begin
// 以 LF 為行分隔,CR 為結尾
tmpstr := Copy(gpsstr, Pos(#10, gpsstr), Pos(#13, gpsstr) - Pos(#10, gpsstr));
Delete(gpsstr, 1, Pos(#10, gpsstr));
if Copy(tmpstr, 1, Pos(',', tmpstr)-1) = '$GPRMC' then
begin
Delete(tmpstr, 1, Pos(',', tmpstr)); // 跳過 $GPRMC
Delete(tmpstr, 1, Pos(',', tmpstr)); // 跳過 UTC 時間
if Copy(tmpstr, 1, Pos(',', tmpstr)-1) = 'A' then // 定位有效
begin
Delete(tmpstr, 1, Pos(',', tmpstr)); // 跳過狀態碼
// --- 緯度解析 ---
latitudestr := Copy(tmpstr, 1, Pos(',', tmpstr)-1);
strq1 := Copy(latitudestr, 1, 2); // 度
strq2 := Copy(latitudestr, 3, 6); // 分.秒
EditLatitude.Text := strq1 + '°' + strq2 + '′';
weidud := StrToFloat(strq1) + StrToFloat(strq2) / 60;
weidud := weidud * Pi / 180; // 轉弧度
Delete(tmpstr, 1, Pos(',', tmpstr)); // 跳過 N/S
Delete(tmpstr, 1, Pos(',', tmpstr)); // 跳過經度欄位前的逗號
// --- 經度解析 ---
longitudestr := Copy(tmpstr, 1, Pos(',', tmpstr)-1);
longitude := StrToFloat(Copy(longitudestr, 1, 3)) +
StrToFloat(Copy(longitudestr, 4, 6)) / 60;
strq1 := Copy(longitudestr, 1, 3);
strq2 := Copy(longitudestr, 4, 6);
EditLongitude.Text := strq1 + '°' + strq2 + '′';
// 經差(相對中央經線)
jinchad := Abs(longitude - StrToInt(CenterLongitudeStr));
jinchad := jinchad * Pi / 180;
Break; // 只處理第一條有效 $GPRMC
end;
end;
end;
// --- 高斯-克吕格投影正算 (3 度帶) ---
n := a / Sqrt(1 - firstpxl * Sin(weidud) * Sin(weidud));
sm := 6367558.496 * weidud
- 16036.48 * Sin(2 * weidud)
+ 16.828 * Sin(4 * weidud)
- 0.022 * Sin(6 * weidud)
+ 0.00003 * Sin(8 * weidud);
nn := Sqrt(firstpxl) * Cos(weidud);
x := sm
+ n * Sin(weidud) * Cos(weidud) * jinchad * jinchad / 2
+ n * jinchad * jinchad * jinchad * Sin(weidud) * Cos(weidud) * Cos(weidud) * Cos(weidud)
* (5 - Tan(weidud)*Tan(weidud) + 9*nn*nn + 4*nn*nn*nn*nn) / 24
+ n * Power(jinchad, 6) * Sin(weidud) * Power(Cos(weidud), 5)
* (61 - 58*Tan(weidud)*Tan(weidud) + Power(Tan(weidud),4)
+ 270*nn*nn*nn*nn - 330*nn*nn*Tan(weidud)*Tan(weidud)) / 270;
y := n * jinchad * Cos(weidud)
+ n * Power(jinchad, 3) * Cos(weidud)*Cos(weidud)*Cos(weidud)
* (1 - Tan(weidud)*Tan(weidud) + nn*nn) / 6
+ n * Power(jinchad, 5) * Power(Cos(weidud), 5)
* (5 - 18*Tan(weidud)*Tan(weidud) + Power(Tan(weidud),4)
+ 14*nn*nn - 58*nn*nn*Tan(weidud)*Tan(weidud)) / 120;
EditXCoord.Text := FormatFloat('00000.00', x);
EditYCoord.Text := FormatFloat('00000.00', y);
end;
// 開始接收
procedure TMainFrm.BtnStartClick(Sender: TObject);
begin
MessageBeep(1);
EditLatitude.Clear; EditLongitude.Clear;
EditXCoord.Clear; EditYCoord.Clear;
Timer1.Enabled := True;
end;
// 停止接收
procedure TMainFrm.BtnStopClick(Sender: TObject);
begin
MessageBeep(1);
EditLatitude.Clear; EditLongitude.Clear;
EditXCoord.Clear; EditYCoord.Clear;
Timer1.Enabled := False;
end;
// 退出程式
procedure TMainFrm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Application.MessageBox('確定要離開程式嗎?', 'GPS 輸入', MB_OKCANCEL) = IDOK then
begin
Timer1.Enabled := False;
if MSComm1.PortOpen then MSComm1.PortOpen := False;
gpsstrlist.Free;
Action := caFree;
end
else
Action := caNone;
end;
程式在 Windows 2000 + Delphi 5.0 環境下測試通過,能穩定接收 GPS 數據並即時顯示經緯度與投影坐標。實務應用中需注意:
TMSCOMM 實例並配合執行緒池管理。完整專案原始碼可作為車隊管理、巡檢定位等系統的基礎模組進行擴展。