首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 利用 MSCOMM 控件實現 GPS 數據采集與坐標轉換

     編輯:Delphi

前言

全球定位系統(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,負責串列通訊)

執行後介面如下圖所示:

GPS 接收程式主介面

圖 1:GPS 數據接收主視窗

二、核心程式碼實作

1. 表單變數宣告

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;

2. 表單顯示時初始化串列埠與橢球參數

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;

3. 計時器事件:解析 NMEA $GPRMC 並進行投影轉換

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;

4. 按鈕事件處理

// 開始接收
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 數據並即時顯示經緯度與投影坐標。實務應用中需注意:

  1. GPS 數據解析速度若低於接收速度,易造成緩衝區堵塞,建議引入多執行緒架構,將解析與投影運算移至背景執行緒。
  2. 單機若需同時接收多台 GPS 接收機,需動態建立多個 TMSCOMM 實例並配合執行緒池管理。
  3. 可進一步加入 NMEA 校驗和、多協議($GPGGA、$GPGSA 等)支援,提升定位可靠度。

完整專案原始碼可作為車隊管理、巡檢定位等系統的基礎模組進行擴展。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved