如何寫GPS定位的程式碼 C# VS2005 Sample Code

 

參閱:http://www.dotblogs.com.tw/jeff-yeh/archive/2008/06/11/4266.aspx#13956

 

之前有寫了一篇"讓人知道你在那"的文章,希望能自己寫程式去運用GPS的定位功能,而這篇文章將會帶到兩個部份,第一個部份是PDA回傳資料時所用的Web Service,另一個就是PDA的GPS訊號抓取與轉換,而這部份並不是採用PAPAGO或其它的SDK,全部是採自己Coding的做法,在寫這個過程中,其實有點頭大,因為Coding的電腦是在室內,所以PDA是無法抓到GPS的訊號,所以只好從戶外拉了一條好長的GPS延長線到電腦邊,最後從抓到的經緯度丟到google map上去顯示,定出來的位置還算準確,確實定位出目前我的所在位置,另一個常在一般的衛星導航系統看到的情況,就是有時會亂跳,因為車子不可能在可行駛的道路以外,所以當經緯度偏離航道時,就會開始"亂跳",想辦法跳回最近的航道,這有好有壞,有時GPS是對的,位置真的不在道路上,但導航系統一直挑附近的道路跳,可是有時GPS訊號差,經緯度的誤差很大,明明在這條巷子,結果地圖顯示在另一條巷子裡,而導航程式本來就是用來帶領駕駛人行駛方向的,所以"車子"當然要在道路上.

 

廢話了一堆,接下來就開始進入這次的主題,首先,為了儲存PDA所回傳的資料,所以在DataBase開了一個Table來儲存,資料結構大至上如下 :

資料庫 : GPSDB

TableName : GPSTrace

Columns :

UID (int,非null) 識別自動加1

Latitude (nvarchar(10),非null) 預設值 25.048346

Longitude(nvarchar(10),非null) 預設值 121.516396

UpdTime (DateTime,非null) 預設值 getdate()

資料結構大致如上,另外安全性的帳號問題,就各位依自己的喜好去設,配合GPS及下篇MAP展示做修改即可.

 

此次的DEMO方向是PDA會回來Update資料,並不會做Insert的動作,所以只會儲存最新的所在位置,而第一次DB並沒有資料,所以Update一定會出錯,由於資料操作並非此次重點,因此先手動輸入兩筆資料進去.

SampleData

Web Service的部份也很簡單,只是很單純的Update的動作. 所以以下的Code只是為了達到Update的DEMO目地,其它安全性,例如Connection string不應直接寫在這裡面,這部份就依個人環境需求去改,如果再帶下去,文章會又臭又長,如果有需要,再額外開篇來討論.

 

[WebMethod]
public int UpdTrace( int UID, string Lat, string Long)
{
    string strconn = "Data Source=localhost;Initial Catalog=GPSDB;User Id=GPSUser;Password=gpsuser;"; //ID及PWD隨個人設定.
    SqlConnection conn = new SqlConnection(strconn);
    string strcmd = "update gpdtrace set Lat=@Lat,Long=@Long,UpdTime=getdate() where UID=@UID";
    SqlCommand cmd = new SqlCommand(strcmd, conn);
    cmd.Parameters.AddWithValue("@Lat", Lat);
    cmd.Parameters.AddWithValue("@Long", Long);
    cmd.Parameters.AddWithValue("@UID", UID);
    try
    {
        conn.Open();
        return cmd.ExecuteNonQuery();
    }

    catch (Exception ex)
    {
        throw new Exception(ex.Message);
    }

    finally
    {
        conn.Close();
        conn.Dispose();
        cmd.Dispose();
    }

}

 

 

PDA DB及Web Service好了,接下來要帶的就是PDA的GPS程式寫法(前面帶很快,因為我比較有興趣的是GPS的抓法^^),先大致看一下PDA的UI設計. 最上方的GPS Port是供使用者選擇GPS所使用的Port,因為每台Smart Device的GPS Port不見得相同,其它就都是唯讀的資訊,而資訊來源就是衛星訊號,從UI上看到的資訊只是衛星訊號的一部份,而衛星的訊號也只是抓其中的一種GPGGA出來用,其它還有GPGSV,GPGSA等等可以運用,衛星的訊號請參考GPS - NMEA sentence Information所提供的資料,在此就不再累述這些訊號一次.

 

 

 

 

 

 

 

 

 

而這次所運用的GPGGA訊號,基本上會抓到這類字串$GPGGA,095031.254,2501.9891,N,12133.8101,E,1,07,7.0,123.9,M,15.0,M,0.0,0000*74

每個逗號隔開視為一個欄位,而每個欄位值所代表的意義如下清單

 

Name

Example Data

Description

Sentence Identifier$GPGGAGlobal Positioning System Fix Data
UTC Time095031.25409:50:31
Latitude2501.9891,N北緯
Longitude12133.8101,E東經

Fix Quality:
- 0 = Invalid
- 1 = GPS fix
- 2 = DGPS fix

1Data is from a GPS fix
Number of Satellites077 Satellites are in view
Horizontal Dilution of Precision (HDOP)7.0Relative accuracy of horizontal position
Altitude123.9,M123.9 meters above mean sea level
Height of geoid above WGS84 ellipsoid15.0,M15.0,M
Time since last DGPS update0.0因為採GPS修正,不是DGPS,所以沒有資料
DGPS reference station id0000 
Checksum*74Used by program to check for transmission errors

 

瞭解了從衛星收到這字串的意義後,接下來就是怎麼去轉換經緯度的部份了,經度跟緯度的轉換方式都一樣,所以就拿其中一個來說明.

1. 2501.9891 /100 = 25.019891

2. 把小數點後的值從0.019891直接轉為19891

3. 再取小數點部份的值(19891/ 60)*10000=3315166

4. 由於第二步移了一個0,所以3315166轉小數時,要加一個0進去,所以=0.03315166

5. 所以緯度= 25+0.03315166=25.03315166

同樣的方式,算出經度=121.5635

*註此定出來的點是台北101

 

說完了GPS字串及轉換的方式,接下來就是Coding的部份.

1. 在下拉的ComboBox,供使用者選擇GPS Port的部份,先加入四個Port進去

this.cmbPort.Items.Add("COM1");
this.cmbPort.Items.Add("COM2");
this.cmbPort.Items.Add("COM3");
this.cmbPort.Items.Add("COM4");

 

2.加入一個Timer,Interval=1000

 

3.加入一個Serial Port

 

4. 在[開始]的Button加入一個click event. 如此,將GPS的Port打開,藉由Timer的Tick事件來更新資料

 

        private void btnStart_Click( object sender, EventArgs e)
        {
            serialPort1.PortName = cmbPort.SelectedItem.ToString();//依下拉選擇的com port設定Serial Port
            try
            {
                serialPort1.Open(); //開啟serial port
                txtPortStatus.Text = "";
            }

            catch (Exception ex)
            {
                txtData.Text = ex.Message;
            }

  
            if (serialPort1.IsOpen)//以下為依port的開關狀態來決定UI的動作
            {
                txtPortStatus.Text = "開啟";
                txtQuality.Text = "無";
                txtSTQty.Text = "0";
                txtSeaLevel.Text = "0";
                cmbPort.Enabled = false;
                btnStart.Enabled = false;
                btnStop.Enabled = true;
                timer1.Enabled = true;
            }

            else
            {
                timer1.Enabled = false;
                txtPortStatus.Text = "關閉";
                txtQuality.Text = "無";
                txtSTQty.Text = "0";
                txtSeaLevel.Text = "0";
                cmbPort.Enabled = true;
                btnStart.Enabled = true;
                btnStop.Enabled = false;
            }

        }

 

 

5. 在[結束]的button加入一個click event. 如此,將gps的port關閉 

div style="border-right: #cccccc 1px solid; padding-right: 4px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 10pt; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; color: #000000; word-break: break-all; line-height: 16px; padding-top: 4px; border-bottom: #cccccc 1px solid; font-family: Verdana,細明體; background-color: #eeeeee">        privatevoidbtnstop_click(objectsender, eventargs e)
        span id="CodeFunction1884_expand_text" style="display: inline">{
            try
            span id="CodeFunction7354_expand_text" style="display: inline">{
                serialport1.close();//關閉serial port br />                txtPortStatus.Text = "";
            }
            catch(Exception ex)
            span id="CodeFunction3702_expand_text" style="display: inline">{
                txtPortStatus.Text = ex.Message;
            }
  
            if(serialPort1.IsOpen)//以下為依port的開關狀態來決定UI的動作 br />            span id="CodeFunction5770_expand_text" style="display: inline">{
                txtPortStatus.Text = "開啟"
                txtQuality.Text = "無"
                txtSTQty.Text = "0"
                txtSeaLevel.Text = "0"
                cmbPort.Enabled = false
                btnStart.Enabled = false
                btnStop.Enabled = true
                timer1.Enabled = true
            }
            else
            span id="CodeFunction6204_expand_text" style="display: inline">{
                timer1.Enabled = false
                txtPortStatus.Text = "關閉"
                txtQuality.Text = "無"
                txtSTQty.Text = "0"
                txtSeaLevel.Text = "0"
                cmbPort.Enabled = true
                btnStart.Enabled = true
                btnStop.Enabled = false
                btn_upload.Enabled = false
            }
        }/div>

 

 

6. 在[回報]的Button加入一個click event. 如此回報目前所在位置 

div style="border-right: #cccccc 1px solid; padding-right: 4px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 10pt; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; color: #000000; word-break: break-all; line-height: 16px; padding-top: 4px; border-bottom: #cccccc 1px solid; font-family: Verdana,細明體; background-color: #eeeeee">        privatevoidbtn_upload_Click(objectsender, EventArgs e)
        span id="CodeFunction1323_expand_text" style="display: inline">{
            GPSWS.Service1 ws = newGPS.GPSWS.Service1(); //將之前做好的WS加入參考使用 br />            ws.Url="http://localhost/gpsws/service1.asmx";//設定Web Service的位址 br />            try
            span id="CodeFunction7656_expand_text" style="display: inline">{
                ws.UpdTrace(1, txtLatitude.Text, txtLongitude.Text);//上傳至資料庫 br />            }
            catch(Exception ex)
            span id="CodeFunction2505_expand_text" style="display: inline">{
                MessageBox.Show(ex.Message);
            }
        }
/div>

 

 

7. 在Timer的Tick事件加入判斷從Serial port的GPS訊息,並呈現於UI上 

div style="border-right: #cccccc 1px solid; padding-right: 4px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 10pt; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; color: #000000; word-break: break-all; line-height: 16px; padding-top: 4px; border-bottom: #cccccc 1px solid; font-family: Verdana,細明體; background-color: #eeeeee">        privatevoidtimer1_Tick(objectsender, EventArgs e)
        span id="CodeFunction3491_expand_text" style="display: inline">{
            if(serialPort1.IsOpen)
            span id="CodeFunction4457_expand_text" style="display: inline">{
                stringGPSData = serialPort1.ReadExisting(); //將serialPort所取得的資料存到字串內 br />                txtData.Text = GPSData;//就是顯示在UI下方的那個大TextBox br />                string] gpsArr = GPSData.Split('$');//依$拆成多個字串陣列 br />                for(inti = 0; i < gpsArr.Length; i++)
                span id="CodeFunction4543_expand_text" style="display: inline">{
                    stringstrTemp = gpsArr[i];
                    string] lineArr = strTemp.Split(',');
                    if(lineArr[0] == "GPGGA"//因為我們只用到GPGGA的訊息,所以其它的訊息不用 br />                    span id="CodeFunction7075_expand_text" style="display: inline">{
                        try
                        span id="CodeFunction5683_expand_text" style="display: inline">{
                            //Latitude 緯度 br />                            Double dLat = Convert.ToDouble(lineArr[2]);
                            dLat = dLat / 100;
                            string] lat = dLat.ToString().Split('.');
                            stringla =(((Convert.ToDouble(lat[1]) / 60)*10000)).ToString("#";
                            for(inta = 0; a < lat[1].Length; a++)
                            span id="CodeFunction9228_expand_text" style="display: inline">{
                                if(lat[1].Substring(a, 1) == "0"
                                span id="CodeFunction4374_expand_text" style="display: inline">{
                                    la = "0"+ la;
                                }
                                else
                                span id="CodeFunction2713_expand_text" style="display: inline">{
                                    break
                                }
                            }
                            Latitude = lat[0].ToString() + "."+ la.Substring(0,6);
  
                            //Longitude 經度 br />                            Double dLon = Convert.ToDouble(lineArr[4]);
                            dLon = dLon / 100;
                            string] lon = dLon.ToString().Split('.');
                            stringlo = (((Convert.ToDouble(lon[1]) / 60)*10000)).ToString("#";
                            for(intb = 0; b < lon[1].Length; b++)
                            span id="CodeFunction9303_expand_text" style="display: inline">{
                                if(lon[1].Substring(b, 1) == "0"
                                span id="CodeFunction1344_expand_text" style="display: inline">{
                                    lo = "0"+ lo;
                                }
                                else
                                span id="CodeFunction8133_expand_text" style="display: inline">{
                                    break
                                }
                            }
                            Longitude = lon[0].ToString() + "."+ lo.Substring(0,6);
  
                            //Display br />                            txtLatitude.Text = Latitude;
                            txtLongitude.Text = Longitude;
                            txtSTQty.Text = lineArr[7]; //衛星數 br />                            txtSeaLevel.Text = lineArr[9];//海平面高度 br />                            switch(lineArr[6])//訊號品質 br />                            span id="CodeFunction4900_expand_text" style="display: inline">{
                                case"0"
                                    txtQuality.Text = "品質太差"
                                    break
                                case"1"
                                    txtQuality.Text = "GPS fix(SPS)"
                                    break
                                case"2"
                                    txtQuality.Text = "DGPS fix"
                                    break
                                case"3"
                                    txtQuality.Text = "PPS fix"
                                    break
                                case"4"
                                    txtQuality.Text = "即時性動態測量"
                                    break
                                case"5"
                                    txtQuality.Text = "Float RTK"
                                    break
                                case"6"
                                    txtQuality.Text = "Estimated"
                                    break
                                case"7"
                                    txtQuality.Text = "手動輸入模式"
                                    break
                                case"8"
                                    txtQuality.Text = "Simulation mode"
                                    break
                                default
                                    txtQuality.Text = "無"
                                    break
                            }
                            btn_upload.Enabled = true
                        }
                        catch
                        span id="CodeFunction7245_expand_text" style="display: inline">{
                            txtLatitude.Text = "GPS 訊號不足"
                            txtLongitude.Text = "GPS 訊號不足"
                            txtQuality.Text = "無"
                            txtSTQty.Text = "0"
                            txtSeaLevel.Text = "0"
                            btn_upload.Enabled = false
                        }
                    }
                }
            }
            else
            span id="CodeFunction7526_expand_text" style="display: inline">{
                txtLatitude.Text = "COM Port 已關閉"
                txtLongitude.Text = "COM Port 已關閉"
                txtQuality.Text = "無"
                txtSTQty.Text = "0"
                txtSeaLevel.Text = "0"
                btn_upload.Enabled = false
            }
        }/div>

  以上的Code就完成了GPS的訊號抓取,轉換,呈現與回報資料庫的動作了,當然,一定可以寫的更好,做更多的運用,使用更多來自衛星的訊息,而這些只是用來實作出用VS2005 C#來達成這個想法,所以看起來較簡略,其它就依各自的需求與想法去延伸.當初花了一點時間在找經緯度轉換的公式,而這也是這整個的關鍵所在,所以這一整個程式碼部份,光看GPS訊號相關,其實就從Serial Port抓訊息,轉換與呈現,原本以為很複雜,結果還好

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值