读取Android GPS NMEA数据

转自:http://cms.35g.tw/coding/%E8%AE%80%E5%8F%96android%E5%85%A7%E5%BB%BAgps%E7%9A%84nmea%E8%B3%87%E8%A8%8A/

如何在Android取得GPS定位資料在很多文章或是教學中都很容易了解及使用,但要如何像其他APP一樣可以取得更詳細的內容呢?這就要利用AndroidGpsStatus物件中的ListenerGpsStatus.NmeaListener,當GPS啟動時就會立即的收到GPS統一規格NMEA0831格式資料,解析這些資料就能得到:座標、衛星有效數量、速度…等一些GPS的詳細資訊,所以自已要寫一個了解GPS狀態並不是難事!

取得LocationManager服務

GPS或其他相關的定位服務由LocationManager來管理,我們必需要先取得LocationManager的物件及註冊Listener才能使用最基礎的定位服務,下面先取得定位服務後,再註冊名為locationListenerListener

1

2

3

4

5

//-----------start-----------

    LocationManagerlocationManager=(LocationManager)getSystemService(Context.LOCATION_SERVICE);

    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000, 0, locationListener);

 

//------------end------------

範例中直接指定定位更新的來源提供者為GPS,不加入其他定位來源的判斷,其中上述的locationListenerListener內容:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

//-----------start-----------

        LocationListenerlocationListener=newLocationListener(){

 

            @Override

            publicvoidonLocationChanged(Locationloc) {

                //TODO Auto-generated method stub

                //定位資料更新時會回呼

 

            }

 

            @Override

            publicvoidonProviderDisabled(Stringprovider) {

                //TODO Auto-generated method stub

                //定位提供者如果關閉時會回呼,並將關閉的提供者傳至provider字串中

            }

 

            @Override

            publicvoidonProviderEnabled(Stringprovider) {

                //TODO Auto-generated method stub

                //定位提供者如果開啟時會回呼,並將開啟的提供者傳至provider字串中

            }

 

            @Override

            publicvoidonStatusChanged(Stringprovider, int status, Bundle extras){

                //TODO Auto-generated method stub

                Log.d("GPS-NMEA",provider + "");

                //GPS狀態提供,這只有提供者為gps時才會動作

                switch(status) {

                caseLocationProvider.OUT_OF_SERVICE:

                    Log.d("GPS-NMEA","OUT_OF_SERVICE");

                    break;

                caseLocationProvider.TEMPORARILY_UNAVAILABLE:

                    Log.d("GPS-NMEA","TEMPORARILY_UNAVAILABLE");

                    break;

                caseLocationProvider.AVAILABLE:

                    Log.d("GPS-NMEA",""+ provider + "");

 

                    break;

                }

 

            }

 

        };

//------------end------------

以上程式中提供最基本的方法,如果要取得座標更新訊息其方法為onLocationChanged,例如,你可以將座標印在Log上:

1

2

3

4

5

6

//-----------start-----------

            publicvoidonLocationChanged(Locationloc) {

                //TODO Auto-generated method stub

                Log.d("GPS-NMEA",loc.getLatitude() + ","+ loc.getLongitude());

            }

//------------end------------

不過這些基本操作會在其他文章中再說明,這裡最大的重點是要註冊GpsStatus.NmeaListenerListener,前面只是做最基本的操作,接下來才能發揮作用。

註冊GpsStatus.NmeaListener

先前的註冊定位相關資訊後,再來就是要註冊GpsStatus.NmeaListener,所以先前的內容會再修改為:

1

2

3

4

5

//-----------start-----------

        locationManager=(LocationManager)getSystemService(Context.LOCATION_SERVICE);

        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000, 0, locationListener);

        locationManager.addNmeaListener(nmeaListener);

//------------end------------

其中上述的nmeaListenerListener內容:

        GpsStatus.NmeaListener nmeaListener = new GpsStatus.NmeaListener() {
            public void onNmeaReceived(long timestamp, String nmea) {
                //check nmea's checksum
                Log.d("GPS-NMEA", nmea);

            }
    };

在方法onNmeaReceived中,只要系統每次取得GPS資料時就會呼叫此方法,並將資料傳前字串nmea,此時再處理這個字串內容就行,在這我們將每次收到的訊息印出來,印出來的資訊類似下面例子:

$PGLOR,0,NEW,PERFIX,1,PER,1,QOP,50*36
$PGLOR,0,RID,BCD,2,18,2,96441*77
$GPGGA,072515.33,,,,,0,00,1000.0,,M,,M,,*53
$PGLOR,0,STA,072515.33,0.000,0.707,175,98,10000,0,P,D,L,1,C,0,S,0004,0*54
$PGLOR,1,SAT,G03,035,13,G16,036,13,G20,023,11,G27,034,13,G32,031,13*6F
$PGLOR,0,SIO,TxERR,0,RxERR,0,TxCNT,3444,RxCNT,3360,DTMS,2059,DTIN,6,DTOUT,201,HATMD,0*0A
$PGLOR,0,HLA,072515.33,L,3,Al,,A,,H,,,M,0,Ac,0,Gr,0,S,,,Sx,,,T,0,Tr,,Mn,0*01
$GPGSV,2,1,05,03,01,000,35,16,01,000,36,20,01,000,23,27,01,000,34*79
$GPGSV,2,2,05,32,01,000,31*4E
$GPGSA,A,1,,,,,,,,,,,,,140.0,99.0,99.0*35
$GNGSA,A,1,,,,,,,,,,,,,140.0,99.0,99.0*2B
$QZGSA,A,1,,,,,,,,,,,,,140.0,99.0,99.0*29
$GPRMC,072515.33,V,,,,,,,090414,,,N*71
$GPGGA,072516.33,,,,,0,00,1000.0,,M,,M,,*50
$PGLOR,0,STA,072516.33,0.000,0.707,175,98,10000,0,P,D,L,1,C,1,S,0000,0*52
$PGLOR,1,SAT,G16,037,13,G32,032,13,G27,034,13,G03,036,13*36
$PGLOR,0,SIO,TxERR,0,RxERR,0,TxCNT,1792,RxCNT,2996,DTMS,970,DTIN,7,DTOUT,152,HATMD,-27*2E
$PGLOR,0,HLA,072516.33,L,,Al,,A,,H,,,M,0,Ac,0,Gr,0,S,,,Sx,,,T,0,Tr,,Mn,0*31
$GPGSV,3,1,10,16,46,251,37,32,34,249,32,27,19,189,34,03,07,203,36*7F
$GPGSV,3,2,10,31,51,019,,14,39,138,,29,25,053,,20,17,286,*70
$GPGSV,3,3,10,23,10,320,,25,03,046,*7F
$GPGSA,A,1,,,,,,,,,,,,,140.0,99.0,99.0*35
$GNGSA,A,1,,,,,,,,,,,,,140.0,99.0,99.0*2B
$QZGSA,A,1,,,,,,,,,,,,,140.0,99.0,99.0*29

此時,你就完成讀取GPSNMEA資料的工作,但你取得此資料後還是必需要解析NMEA資料才能正確的取得資訊,後面會利用一個範例來取得GPS定位狀態、衛星有效數量、座標…等資訊。

執行範例:

執行範例之前必需要提醒一點,你要自行先將GPS開啟,範例不會對GPS是否開啟做偵測的動作,當然你也可以試著自行加入該程式內容讓範例程式更加符合實際需要。

檢查NMEA誤碼資料(checksum)

NMEA規格中規定必需要對NMEA檢查checksum是否正確,這樣才能確保資料內容正確後再去解析資料,這裡我將檢查方式寫程一個方法,需要的可以從範例擷取使用。

NMEA規格中將每串資料最後一筆從的*號後到結尾內容為checksum的值,例如:

$GPGSA,A,1,,,,,,,,,,,,,140.0,99.0,99.0*35

其取得的checksum35,而35的值為十六進制的35,再利用checksum特性將格式中的$*之間內容加總計算,與取得的比對,正確時,此資料為有效資料。

$......加總........*

所以我所寫的checksum程式如下,資料為有效值會返回true

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

//-----------start-----------

    privatebooleanisValidForNmea(StringrawNmea){

        booleanvalid = true;

        byte[]bytes = rawNmea.getBytes();

        intchecksumIndex = rawNmea.indexOf("*");

        //NMEA星號後為checksumnumber

        bytechecksumCalcValue = 0;

        intchecksumValue;

 

        //檢查開頭是否為$

        if((rawNmea.charAt(0) != '$') ||(checksumIndex==-1)){

            valid= false;

        }

        //

        if(valid){

            Stringval = rawNmea.substring(checksumIndex + 1,rawNmea.length()).trim();

            checksumValue= Integer.parseInt(val, 16);

            for(inti = 1; i< checksumIndex; i++){

                checksumCalcValue= (byte) (checksumCalcValue ^ bytes[i]);

            }

            if(checksumValue != checksumCalcValue){

                valid= false;

            }

        }

        returnvalid;

    }

//------------end------------

移除Listener

不使用時記得將Listener給移除:

1

2

3

4

//-----------start-----------

    locationManager.removeUpdates(locationListener);

    locationManager.removeNmeaListener(nmeaListener);

//------------end------------

正確來說是必需要自行移除,如果你忘記移除時,系統雖然還防止問題產生,在過程中還是會正常運作,但不增加系統上的負擔,Listener不使用時還是移除比較好。

處理NMEA資料

在每次取得NMEA資料會進行檢查,再將NMEA資料傳遞至副程式nmeaProgress

1

2

3

4

5

6

7

8

9

10

11

12

//-----------start-----------

        nmeaListener= newGpsStatus.NmeaListener() {

            publicvoidonNmeaReceived(longtimestamp, String nmea) {

                //checknmea's checksum

                if(isValidForNmea(nmea)){

                    nmeaProgress(nmea);

                    Log.d("GPS-NMEA",nmea);

                }

 

            }

    };

//------------end------------

因為讀取NMEAListener並非在UI執行緒之中,利用nmeaProgressNMEA資料傳至自建的Handler,確保在UI執行緒中能將資料傳至UI控制元件:

    private void nmeaProgress(String rawNmea){

        String[] rawNmeaSplit = rawNmea.split(",");

        if (rawNmeaSplit[0].equalsIgnoreCase("$GPGGA")){
            //send GGA nmea data to handler
            Message msg = new Message();
            msg.obj = rawNmea;
            mHandler.sendMessage(msg);
        }

    }

程式中必需要注意一段:

        if (rawNmeaSplit[0].equalsIgnoreCase("$GPGGA")){
:
:
:
        }

$GPGGANMEA0831所定義的,下圖為NMEA0831描述的規格,我也有將格式貼於範例中,

读取Android GPS NMEA数据_第1张图片

中文說明:

<1>  UTC時間,格式為hhmmss.sss
<2>  緯度,格式為ddmm.mmmm(前導位數不足則補0)。
<3>  緯度半球,NS(北緯或南緯)。
<4>  經度,格式為dddmm.mmmm(前導位數不足則補0)。
<5>  經度半球,EW(東經或西經)。
<6>  定位品質指示,0=定位無效,1=定位有效,2=差分定位(有效)
<7>  使用衛星數量,從0012(前導位數不足則補0)。
<8>  水平精確度,0.599.9
<9>  天線離海平面的高度,-9999.99999.9
<10> 高度單位,M表示單位米。
<11> 大地橢球面相對海平面的高度(-999.99999.9)。
<12> 高度單位,M表示單位米。
<13> 差分GPS數據期限(RTCM SC-104),最後設立RTCM傳送的秒數量。
<14> 差分參考基站標號,從00001023(前導位數不足則補0)。
<15> checksum校驗和。

每項資料都使用,分隔,每項資料都有其意義,範例中取用GGA中的234567的資料,並將處理過程寫在Handler內容中。

Handler內容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//-----------start-----------

    mHandler= new Handler() {

        publicvoidhandleMessage(Messagemsg) {

 

            Stringstr = (String) msg.obj;

            String[]rawNmeaSplit = str.split(",");

            txtGPS_Quality.setText(rawNmeaSplit[6]);

            txtGPS_Location.setText(rawNmeaSplit[2]+ " "+ rawNmeaSplit[3] +"," + rawNmeaSplit[4] + ""+ rawNmeaSplit[5]);

            txtGPS_Satellites.setText(rawNmeaSplit[7]);

 

        }

        };

 

}

//------------end------------

依照格式中的第6筆為GPS定位品質,第2~5為定位座標,如果未定位成功在這裡得到的資料會是空白,最後第7筆為已經接收到的衛星有效數量,再將取得的資料傳至UI控制元件的TextView,所以看到的結果如下:

读取Android GPS NMEA数据_第2张图片

以上為最基本的讀取內建GPSNMEA資料方式,如果將NMEA的資料加強利用時,提供的GPS狀態將更詳細,利用NMEA讀取的技巧與解析方式也能用在藍牙的外部GPS接收器,將從藍牙取得的NMEA資料加以利用。


你可能感兴趣的:(专项应用类)