和菜鳥一起學android4.0.3原始碼之硬體gps簡單移植

東月之神發表於2013-02-02

關於android定位方式

android 定位一般有四種方法,這四種方式分別是GPS定位、WIFI定位、基站定位、AGPS定位。

1、 Android GPS

需要GPS硬體支援直接和衛星互動來獲取當前經緯度,這種方式需要手機支援GPS模組現在大部分的智慧機應該都有了。通過GPS方式準確度是最高的但是它的缺點也非常明顯。

1、 比較耗電

2、 絕大部分使用者預設不開啟GPS模組

3、 從GPS模組啟動到獲取第一次定位資料可能需要比較長的時間

4、 室內幾乎無法使用。

這其中缺點2、3都是比較致命的。需要指出的是GPS走的是衛星通訊的通道在沒有網路連線的情況下也能用。

 

2、 Android基站定位

Android基站定位只要明白了基站/WIFI定位的原理自己實現基站/WIFI定位其實不難。基站定位一般有幾種:第一種是利用手機附近的三個基站進行三角定位,由於每個基站的位置是固定的,利用電磁波在這三個基站間中轉所需要時間來算出手機所在的座標;第二種則是利用獲取最近的基站的資訊,其中包括基站 id、location area code、mobile country code、mobile network code和訊號強度將這些資料傳送到google的定位web服務裡,就能拿到當前所在的位置資訊,誤差一般在幾十米到幾百米之內。其中訊號強度這個資料很重要。

 

3、 Android Wifi定位

根據一個固定的Wifi MAC地址通過收集到的該Wifi熱點的位置然後訪問網路上的定位服務以獲得經緯度座標。因為它和基站定位其實都需要使用網路所以在Android也統稱為Network方式。

 

4、 AGPS定位

AGPS(AssistedGPS)輔助全球衛星定位系統是結合GSM或GPRS與傳統衛星定位利用基地臺代送輔助衛星資訊以縮減GPS晶片獲取衛星訊號的延遲時間受遮蓋的室內也能借基地臺訊號彌補減輕GPS晶片對衛星的依賴度。和純GPS、基地臺三角定位比較,AGPS能提供範圍更廣、更省電、速度更快的定位服務。理想誤差範圍在10公尺以內,日本和美國都已經成熟運用AGPS於LBS服務(Location Based Service)基於位置的服務。AGPS技術是一種結合了網路基站資訊和GPS資訊對移動臺進行定位的技術,可以在GSM/GPRS、WCDMA和CDMA2000網路中進行使用。該技術需要在手機內增加GPS接收機模組並改造手機的天線,同時要在行動網路上加建位置伺服器、差分GPS基準站等裝置。AGPS解決方案的優勢主要體現在其定位精度上在室外等空曠地區其精度在正常的GPS工作環境下可以達到10米左右,堪稱目前定位精度最高的一種定位技術。該技術的另一優點為首次捕獲GPS訊號的時間一般僅需幾秒,不像GPS的首次捕獲時間可能要2-3分鐘。

 

 

 

 

 

 

 

 

 

 

 

 

關於android上的gps定位

關於gps定位,從衛星訊號到android終端地圖顯示的整體流程圖如下:


以下簡單介紹下GPS定位的相關知識

一、GPS簡介

GPS(Global Positioning System), 即全球定位系統,它是一個由覆蓋全球的24顆衛星組成的衛星系統。其目的是在全球範圍內對地面和空中目標進行準確定位和監測。隨著全球性空間定位資訊應用的日益廣泛,GPS提供的全時域、全天候、高精度定位服務將給空間技術、地球物理、大地測繪、遙感技術、交通排程、軍事作戰以及人們的日常生活帶來巨大的變化和深遠的影響。

目前的民用GPS裝置包括測量型和導航型。其中測量型產品的精度可達到米級甚至毫米級,但至少需要兩臺(套)才能達到設計精度要求,而且其內部結構複雜,單機成本一般在幾萬到幾十萬,適合專業高精度測量環境使用;導航型產品,由於其使用者對精度要求不高,一般為幾十米,因此機器內部硬體相對簡單,只須一臺就可以完成導航工作,加之其價格相對較低,因而更有普及和推廣價值。

GPS系統一般由地面控制站、導航衛星和使用者接收機(GPS的移動使用者端)三大部分組成。導航衛星至少24顆,均勻分佈在6個極地軌道上,軌道的夾角為60度,距地平均高度為20200公里,每12恆星時繞地球一週。

 

 

 

二、GPS衛星訊號結構

GPS衛星發射的訊號包含有三種成分,即50Hz導航電文(D碼)偽隨機碼(C/A碼或P碼)載波(Ll,L2波段)。這3種訊號分量都是在同一基準頻率F0=10.23MHZ的控制下產生的。

GPS衛星訊號結構

 

 


GPS衛星所採用的兩種測距碼,即C/A碼和P碼(或Y碼),均屬於偽隨機碼。

 1)C/A碼:是由兩個10級反饋移位暫存器組合而產生。碼長Nu=1024-1=1023位元,碼元寬為tu=1/f1=0.97752s,(f1為基準頻率f0的10分之1,1.023 MHz),相應的距離為293.1m。週期為Tu= Nutu=1ms,數位元速率為1.023Mbit/s。

    C/A碼的碼長短,共1023個碼元,若以每秒50碼元的速度搜尋,只需20.5s,易於捕獲,稱捕獲碼。

    碼元寬度大,假設兩序列的碼元對齊誤差為為碼元寬度的1/100,則相應的測距誤差為2.9m。由於精度低,又稱粗碼。

 2)P碼

    P碼產生的原理與C/A碼相似,但更復雜。發生電路採用的是兩組各由12級反饋移位暫存器構成。碼長Nu=2.35*10^14位元,碼元寬為tu=1/f0=0.097752s,相應的距離為29.3m。週期為Tu= Nutu=267d,數位元速率為10.23Mbit/s。

     P碼的週期長,267天重複一次,實際應用時P碼的週期被分成38部分,(每一部分為7天,碼長約6.19 ,1012位元),其中1部分閒置,5部分給地面監控站使用,32部分分配給不同衛星,每顆衛星使用P碼的不同部分,都具有相同的碼長和週期,但結構不同。P碼的捕獲一般是先捕獲C/A碼,再根據導航電文資訊,捕獲P碼。由於P碼的碼元寬度為C/A碼的1/10,若取碼元對齊精度仍為碼元寬度的1/100,則相應的距離誤差為0.29m,故P碼稱為精碼。

 

導航電文是包含有關衛星的參考星曆、衛星工作狀態、時間改正引數、衛星鐘執行狀態、軌道攝動改正、大氣折射改正和由C/A碼捕獲P碼等導航資訊的資料碼(或D碼)。

    導航電文也是二進位制碼,依規定格式組成,按幀向外播送。每幀電文含有1500位元,播送速度50bit/s,每幀播送時間30s。

    每幀導航電文含5個子幀,每個子幀分別含有10個字,每個字30位元,故每個子幀共300位元,播發時間6s。為記載多達25顆衛星,子幀4、5各含有25頁。子幀1、2、3和子幀4、5的每一頁構成一個主幀。主幀中1、2、3的內容每小時更新一次,4、5的內容僅當給衛星注入新的導航電文後才得以更新。

       導航電文的格式


 

一幀導航電文的內容


1、 遙測字(TLM-Telemetry WORD)

    位於每個子幀的開頭,作為捕獲導航電文的前導。

2、轉換碼(交接字)(HOW-Hand Over Word)    

緊接各子幀的遙測字,主要向使用者提供用於捕獲P碼的Z記數。所謂Z記數是從每個星期六/星期日子夜零時起算的時間記數(1.5s),表明下一子幀開始瞬間的GPS時。

3、資料塊1:含有衛星鐘改正引數及資料齡期、星期的週數編號、電離層改正引數、和衛星工作狀態等資訊。 衛星鐘改正引數a0、a1、a2分別表示該衛星的鐘差、鐘速和鐘速變化率。任意時刻t的鐘改正數為: 

t=a0+a1(t-t0c)+a2(t-t0c)^2。

參考曆元t0e為資料塊1的基準時間,從GPS時星期六/星期日子夜零時起算,變化於0-604800s之間。

資料齡期AODC表示衛星鐘改正引數的參考時刻t0c與最近一次更新鍾改正引數的時間TL之差,主要用於評價鍾改正數的可信程度。

現時星期編號WN:表示從1980年1月6日協調時零點起算的GPS時星期數。

4、資料塊2:包含在2、3兩個子幀裡,主要向使用者提供有關計算該衛星執行位置的資訊。該資料一般稱為衛星星曆,每30s重複1次,每小時更新一次。

5、資料塊3:包含在4、5兩個子幀中,主要向使用者提供其他GPS衛星的概略星曆及其工作狀態資訊,稱為衛星的歷書。第3資料塊的內容每12.5分鐘重複一次,每天更新一次。

 

 

 

三、GPS接收機

 

天線單元

GPS訊號接收機的天線單元為接收裝置的前置部分。天線單元包含接收天線和前置放大器兩部分。

其中天線部分可能是全向振子天線或小型螺旋天線或微帶天線,但從發展趨勢來看,以微帶天線用的最廣、最有前途。

為了提高訊號強度,一般在天線後端設定前置放大器,前置放大器的作用是將由極微弱的GPS訊號的電磁波能量轉換成為弱電流放大。前置放大器分外差式和高放式兩種。由於外差式前置放大器不僅具有放大功能,還具有變頻功能,即將高頻的GPS訊號變換成中頻訊號,這有利於獲得穩定的定位精度,所以絕大多數GPS接收機採用外差式天線單元。

 

訊號通道

訊號通道是一種軟體和硬體相結合的複雜電子裝置,是GPS接收機中的核心部分。其主要功能是捕獲、跟蹤、處理和量測衛星訊號,以獲得導航定位所需要的資料和資訊。通道數目有1到24個不等,由接收機的型別而定。總的來講,訊號通道目前有相關型、平方型和相位型等三種。新一代GPS訊號接收機廣泛採用相關型通道,主要由訊號捕獲電路、偽噪聲跟蹤環路和載波跟蹤環路組成。

 

儲存器

這是GPS訊號中接收機將定位現場採集的偽距、載波相位測量、人工量測的資料及解譯的衛星星曆儲存起來的一種裝置,以供差分導航和作相對定位的測後資料。

 

微處理機

接收機的計算部分由微處理機和機內軟體組成。機內軟體是由接收機生產廠家提供的,是實現資料採集、通道自校自動化的重要組成部分,主要用於訊號捕獲、跟蹤和定位計算。微處理機結合機內軟體作下列計算和處理:

 (1)開機後指令各通道自檢,並測定、校正和儲存各通道的時延值;

 (2)解譯衛星星曆,計算測站的三維座標;

 (3)由測站定位座標和衛星星曆計算所有衛星的升降時間、方位和高度角,提供可視衛星資料及衛星的工作狀況,以便獲得最佳定位星位,提高定位精度。

 

定位

靜態定位時,GPS接收機在捕獲和跟蹤GPS衛星的過程中固定不變,接收機通過高精度測量GPS訊號的傳播時間,並利用GPS衛星在軌的已知位置解算出接收機天線所在位置的三維座標。而動態定位則是用GPS接收機測定一個運動物體的執行軌跡。GPS訊號接收機所在的運動物體叫做載體(如航行中的船艦,空中的飛機,行走的車輛等)。由於載體上的GPS接收機天線在跟蹤GPS衛星的過程中將相對地球而運動,這樣,接收機用GPS訊號就可實時地測量運動載體的狀態引數(瞬間三維位置和三維速度)。

GPS定位還受GPS網的限制,應用GPS衛星定位技術建立的控制網叫GPS網。歸納起來大致可分為兩大類:一類是全球或全國性的高精度GPS網,這類GPS網中相鄰點的距離在數千公里至上萬公里, 其主要任務是作為全球高精度座標框架或全國高精度座標框架,以為全球性地球動力學和空間科學方面的科學研究工作服務。另一類是區域性的 GPS網,包括城市或礦區GPS網,GPS工程網等,這類網中的相鄰點間的距離為幾公里至幾十公里,其主要任務是直接為國民經濟建設服務。

 

二維位置的確定

 

由衛星產生的測距訊號確定三維位置

 

GPS接收機常識:

1. 座標 (coordinate)

  有2維、3維兩種座標表示,當GPS能夠收到4顆及以上衛星的訊號時,它能計算出本地的3維座標:經度、緯度、高度,若只能收到3顆衛星的訊號,它只能計算出2維座標:經度和緯度,這時它可能還會顯示高度資料,但這資料是無效的。大部分GPS不僅能以經/緯度(Lat/Long) 的方式,顯示座標,而且還可以用 UTM(Universal TransverseMercator) 等座標系統顯示座標但我們一般還是使用 LAT/LONG 系統,這主要是由你所使用的地圖的座標系統決定的。

2. 航點 (Landmark or Waypoint)

  GPS記憶體中儲存的一個點的座標值。在有GPS訊號時,你可以儲存成一個易認的名字,還可以給它選定一個圖示。航點是GPS資料核心,它是構成“航線”的基礎。標記航點是GPS主要功能之一,但是你也可以從地圖上讀出一個地點的座標,手工或通過計算機介面輸入GPS,成為一個航點。一個航點可以將來用於GOTO功能的目標,也可以選進一條航線 Route,作為一個支點。一般 GPS 能記錄500個或以上的航點。

      3. 航線 (ROUTE)

  航線是GPS記憶體中儲存的一組資料,包括一個起點和一個終點的座標,還可以包括若干中間點的座標,每兩個座標點之間的線段叫一條"腿"(leg) 。常見 GPS 能儲存20條線路,每條線路30條"腿"。各座標點可以從現有航點中選擇,或是手工/計算機輸入數值,輸入的路點同時做為一個航點 (Waypoint/Landmark) 儲存。

4. 前進方向 (Heading)

  GPS沒有指北針的功能,靜止不動時它是不知道方向的。但是一旦動了起來,它就能知道自己的運動方向。GPS每隔一秒更新一次當前地點資訊,每一點的座標和上一點的座標一比較,就可以知道前進的方向 。

      5. 導向 (Bearing)

  導向功能在以下條件下起作用:

  1.) 以設定"走向"(GOTO) 目標。"走向"目標的設定可以按"GOTO"鍵,然後從列表中選擇一個航點。以後"導向"功能將導向此航點

   2.) 目前有活躍航線 (Activity route)。活躍航線一般在設定 -> 航線選單下設定。如果目前有活動航線,那麼"導向"的點是航線中第一個路點,每到達一個路點後,自動指到下一個路點。

6. 日出日落時間 (Sun set/raise time)

  大多數GPS能夠顯示當地的日出、日落時間,這在計劃出發 / 宿營時間時是有用的。這個時間是 GPS 根據當地經度和日期計算得到的,是指平原地區的日出、日落時間,在山區因為有山脊遮擋,日照時間根據情況要早晚各少半個小時以上。GPS的時間是從衛星訊號得到的格林尼制時間,在設定 (setup) 選單裡可以設定本地的時間偏移,對中國來說,應設+8小時,此值只與時間的顯示有關。

7. 航跡 (Plot trail)

  GPS每秒更新一次座標資訊,所以可以記載自己的運動軌跡。一般GPS能記錄1024個以上足跡點,在一個專用頁面上,以可調比例尺顯示移動軌跡。足跡點的取樣有自動和定時兩種方式自動取樣由 GPS 自動決定足跡點的取樣方式,一般是隻記錄方向轉折點,長距離直線行走時不記點;定時取樣可以規定取樣時間間隔,比如30秒、一分鐘、 5 分鐘或其他時間,每隔這麼長時間記一個足跡點。

 

 

 

四、主流GPS方案供應商

一臺GPS裝置關鍵的元件有天線、低噪音放大器(LNA)、射頻接收轉換(RF Section)、數字部分(也稱數字基帶,Digital Baseband)、微處理器(Microprocessor)、微處理器周邊外設(Processor Peripherals)、輸入輸出和驅動(I/OandDriver)等幾個部分。晶片提供商也強手如雲,包括 SiRF、u-blox、Ti、Analog Devices、NXP、高通、英飛凌、索尼、意法半導體、Trimble(天寶)、Atmel、SiGe、u-Nav 等等。

1、SiRF公司

SiRF是GPS晶片的龍頭供應商,產品線完整,能夠提供完整的解決方案。

代表產品:基於SiRFstarIII 架構的晶片GSC3e/LP與GSC3f/LP、GSC3LT與 GSC3LTf、GSC3LTi and GSC3Ltif,基於 SiRF Instant 架構的 GSCi-5000。現最新的模組為Fastrax iT430,它是基於SiRFstar IV晶片和SiRFaware軟體技術的GPS模組。


尺寸:9.6 x 9.6 x 1.85 mm

2、Nemerix 

NemeriX 提供的產品包括模擬射頻接收器和數字基帶處理器。

Nemerix NB1042GPS 接收器模組,世界上功耗最低的GPS晶片組。


3、TI

TI的輔助GPS (A-GPS)解決方案在非同步和同步蜂窩式網路內提供快速而精確的定位服務。這些解決方案在優化之後,適用於所有當前和發展中的無線標準(如 GSM、GPRS、EDGE、CDMA、UMTS 和 WCDMA)。 TI 在 2005 年推出的 90nm 工藝技術的單晶片 A-GPS 解決方案,GPS5300NaviLink 4.0單晶片採用 TI DRP技術,可實現離散的GPS解決方案。

4、Atmel

Atmel 的低功耗 GPS 模組晶片組高度整合並能極大節省製版空間。

Atmel與Magellan推出新的GPS晶片組ATR0663,包括一個先進的 GPS 基帶和一個具備整合 2D 圖形加速器的 LCD 控制器(以實現 2048 x 2048畫素的虛擬螢幕支援)、一個 AC97 音訊控制器,以及一個影像感測器介面。多種輸入/輸出 (I/O) 選項,包括乙太網 (Ethernet)、USB 2.0 Full Speed Hostand Device、SD/MMC、TWI 和 USART,為PND應用提供了一個高針對性的片上系統 (SoC) 解決方案。

5、意法半導體

ST 也能夠提供面向 GPS 應用的全系列解決方案,適用於車載與行動式導航系統。最新一代的 ST 導航/資訊娛樂平臺名為 NaviFlex,其整合度更高:融合 GPS 接收器和 Nomadik 應用處理器,保證了汽車多媒體應用無與倫比的音訊、視訊和成像質量。

6、Maxim

       Maxim 公司能夠提供低噪聲、低功耗的GPS前端接收器和獨特的GPS方案。

7、NXP

恩智浦半導體 (NXP Semiconductors,原飛利浦半導體),恩智浦的解決方案成功地將高質量導航功能與豐富的多媒體處理結合在一起,包括 MP3 播放、標準的以及高清晰的視訊播放及錄製、調頻收音機、影像儲存和遊戲等。

8、英飛凌 

Infineon和Global Locate合作推出的Hammerhead是全球首款單晶片CMOS GPS 接收器。該晶片支援移動站輔助式(MS-A)、移動站基於式(MS-B)、自主式和增強式跟蹤模式。一流的室內訊號跟蹤效果,完全支援輔助式和自主式跟蹤模式,即使在最微弱的訊號環境中也可以進行高度精確的導航。Hammerhead 晶片的基於主機的軟體架構,不僅將器件尺寸和成本減至最小,還允許將協議訊息直接嵌入到 GPS 導航軟體中。

9、U-Blox

來自瑞士的GPS技術公司u-blox AG公司以往主要提供命名為 TIM的GPS 模組,其中採用的SiRF公司GPS晶片。現在u-blox 也開始注重核心晶片的開發。新推出的 u-blox 5 系列全球定位系統以及隨時可用的伽利略系統單晶片和晶片組擁有不到一秒的接收效能。這種新的晶片還擁有 SuperSense-160 dBm 探測和跟蹤靈敏度、小於 50mW 的功率需求以及一個小於 100 平方毫米的覆蓋區,適用於掌上電腦(PDA)、個人導航裝置、照相機、手機、媒體播放器和其它電池操作行動式裝置。

10、高通

目前,全球已有總計超過兩億部手機裝備了高通公司的gpsOne輔助型GPS 技術。gpsOne 技術支援一系列極具吸引力的位置服務,其中包括各種各樣針對消費者、商務和個人安全的應用。

 

 

 

五、GPS標準格式資料

 

模組輸出資訊主要包括4個部分:

1、GPS定位資訊GPGGA(Global Positioning SystemFix Data)

$GPGGA,063740.998,2234.2551,N,11408.0339,E,1,08,00.9,00053.A,M,-2.1,M,,*7B
$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh<CR><LF>
<1> UTC時間,hhmmss(時分秒)格式
<2> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸)
<3> 緯度半球N(北半球)或S(南半球)
<4> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸)
<5> 經度半球E(東經)或W(西經)
<6> GPS狀態:0=未定位,1=非差分定位,2=差分定位,6=正在估算
<7> 正在使用解算位置的衛星數量(00~12)(前面的0也將被傳輸)
<8> HDOP水平精度因子(0.5~99.9)
<9> 海拔高度(-9999.9~99999.9)
<10> 地球橢球面相對大地水準面的高度
<11> 差分時間(從最近一次接收到差分訊號開始的秒數,如果不是差分定位將為空)
<12> 差分站ID號0000~1023(前面的0也將被傳輸,如果不是差分定位將為空)

 

2、當前衛星資訊GPGSA(GPS DOP and ActiveSatellites)

   

$GPGSA,A,3,06,16,14,22,25,01,30,20,,,,,01.6,00.9,01.3*0D
$GPGSA,<1>,<2>,<3>,<3>,,,,,<3>,<3>,<3>,<4>,<5>,<6>,<7><CR><LF>
<1>模式 :M = 手動, A = 自動。
<2>定位型式 1 = 未定位, 2 = 二維定位, 3 = 三維定位。
<3>PRN 數字:01 至 32 表天空使用中的衛星編號,最多可接收12顆衛星資訊。
<4> PDOP位置精度因子(0.5~99.9)
<5> HDOP水平精度因子(0.5~99.9)
<6> VDOP垂直精度因子(0.5~99.9)
<7> Checksum.(檢查位).

 

3、可見衛星資訊GPGSV(GPS Satellites in View)     

$GPGSV,2,1,08,06,26,075,44,16,50,227,47,14,57,097,44,22,17,169,41*70
$GPGSV,2,2,08,25,49,352,45,01,64,006,45,30,13,039,39,20,15,312,34*7A
$GPGSV,<1>,<2>,<3>,<4>,<5>,<6>,<7>,?<4>,<5>,<6>,<7>,<8><CR><LF>
<1> GSV語句的總數
<2> 本句GSV的編號
<3> 可見衛星的總數,00 至 12。
<4> 衛星編號, 01 至 32。
<5>衛星仰角, 00 至 90 度。
<6>衛星方位角, 000 至 359 度。實際值。
<7>訊號噪聲比(C/No), 00 至 99 dB;無表未接收到訊號。
<8>Checksum.(檢查位).
第<4>,<5>,<6>,<7>項個別衛星會重複出現,每行最多有四顆衛星。其餘衛星資訊會於次一行出現,若未使用,這些欄位會空白。

 

4、推薦最小定位資訊GPRMC(Recommended MinimumSpecific GPS/TRANSIT Data)

$GPRMC,012724.000,A,2234.3157,N,11408.0921,E,0.00,,290108,,,A*71
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh<CR><LF>
<1> UTC時間,hhmmss(時分秒)格式
<2> 定位狀態,A=有效定位,V=無效定位
<3> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸)
<4> 緯度半球N(北半球)或S(南半球)
<5> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸)
<6> 經度半球E(東經)或W(西經)
<7> 地面速率(000.0~999.9節,前面的0也將被傳輸)
<8> 地面航向(000.0~359.9度,以真北為參考基準,前面的0也將被傳輸)
<9> UTC日期,ddmmyy(日月年)格式
<10> 磁偏角(000.0~180.0度,前面的0也將被傳輸)
<11> 磁偏角方向,E(東)或W(西)
<12> 模式指示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)
我們所關心的是GPRMC這條資訊,因為其中包括當前格林威治時間、經度、緯度、日期等。


六、linux driver層

       UART介面,與gps模組通訊,即讀取gps的資訊。

 

七、android GPS hal層

       因為linux底層驅動只需要有uart介面就可以接收到gps資料了,而android的hal層會呼叫linux核心層的uart驅動,所以,這裡uart驅動就不再分析了,只要hal層開啟串列埠,然後read就可以了,這裡我參考的是模擬器的gps。

自己新建的資料夾,用來適配gps:

hardware/libhardware_legacy/gps

gps標頭檔案:

hardware/libhardware_legacy/include/hardware_legacy/gps.h

首先看一下GpsLocation這個結構體。

/** Represents a location. */
typedef struct {
    /**set to sizeof(GpsLocation) */
   size_t          size;
    /**Contains GpsLocationFlags bits. */
   uint16_t        flags;
    /**Represents latitude in degrees. */
   double          latitude;
    /**Represents longitude in degrees. */
   double          longitude;
    /**Represents altitude in meters above the WGS 84 reference
     *ellipsoid. */
   double          altitude;
    /**Represents speed in meters per second. */
   float           speed;
    /**Represents heading in degrees. */
   float           bearing;
    /**Represents expected accuracy in meters. */
   float           accuracy;
    /**Timestamp for the location fix. */
   GpsUtcTime      timestamp;
} GpsLocation;

所有我們要知道的資料都在這裡了:經度、緯度、海拔、速度、精確度、世界標準時間等。而上層也只是需要這個資料結構,所以只要把這個資料結構給android上層,那麼就OK了。

下面還是主要分析下android模擬器是如何實現的。先看下簡單的流程圖:


首先看下hw主要結構體,gps是註冊為hw模組的。編譯後是生成gps.*.so的,而他的方法是呼叫gps_module_methods。

const struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag =HARDWARE_MODULE_TAG,
   .version_major = 1,
   .version_minor = 0,
    .id =GPS_HARDWARE_MODULE_ID,
    .name ="Goldfish GPS Module",
    .author ="The Android Open Source Project",
    .methods =&gps_module_methods,
};
而gps_module_methods又呼叫了open_gps
static struct hw_module_methods_t gps_module_methods ={
    .open = open_gps
};

再看看open_gps做了什麼,初始化了很多函式指標,具體含義可以看

static constGpsInterface  qemuGpsInterface = {
    sizeof(GpsInterface),
    qemu_gps_init,   //開啟介面,提供callback函式
    qemu_gps_start,  //開始導航
    qemu_gps_stop,   //停止導航
    qemu_gps_cleanup, //關閉介面
    qemu_gps_inject_time,//注入當前時間
    qemu_gps_inject_location, //注入從其他定位方式得的當前位置。
    qemu_gps_delete_aiding_data, //
    qemu_gps_set_position_mode, //設定座標模式
    qemu_gps_get_extension, //擴充套件資訊
};

然後再看看各個函式,這些函式是jni層會呼叫到的,這裡簡單介紹初始化、資料上報等操作。

首先是初始化函式qemu_gps_init,這裡呼叫了gps_state_init函式,然後在這裡開啟我們要的串列埠驅動裝置,開啟讀取gps資料的執行緒。

state->fd = open(GPS_Serial_Name, O_RDONLY );
state->thread = callbacks->eate_thread_cb("gps_state_thread”,gps_state_thread,  tate);

當執行緒啟動以後,那麼就會在這個執行緒中處理一些事情了,接著看下gps_state_thread函式。

這裡會把callback函式給設定好,用以傳資料給jni層。

nmea_reader_set_callback( reader,state->callbacks.location_cb );

串列埠讀取gps的資料ret = read( fd, buff, sizeof(buff) );

解析資料 nmea_reader_addc(reader, buff[nn] );

       然後他會呼叫nmea_reader_parse(r );函式

這個函式會根據不同的gps協議格式來分別處理。

if ( !memcmp(tok.p, "GGA", 3) )

這裡會根據GGA的格式來處理,比如:

更新時間nmea_reader_update_time(r, tok_time);

       更新經緯度nmea_reader_update_latlong

       更新海拔nmea_reader_update_altitude

else if ( !memcmp(tok.p, "GLL", 3) )
else if ( !memcmp(tok.p, "GSA", 3) )
else if ( !memcmp(tok.p, "GSV", 3) )

       其他的依次都差不多。具體可以看原始碼。

最後處理完了以後,把便要把資料給jni層了。通過r->callback( &r->fix );這個callback函式,就是資料傳輸的主要函式了。

 

 

 

八、android GPS的JNI層

先還是看下jni層的主要流程吧:分為1、java到jni到hal和2、hal到jni到java。


 

GPS的jni的程式碼是在下面這個目錄下的:

/framework/base/services/jni/com_android_server_location_GpsLocationProvider.cpp

在com_android_server_location_GpsLocationProvider.cpp檔案中,實現JNI方法。注意檔案的命令方法,com_android_server_location字首表示的是包名,表示硬體服務GpsLocationProvider是放在frameworks/base/services/java目錄的com/android/server/location目錄下的,即存在一個命令為

com.android.server.location.GpsLocationProvider的類。

在同一個目錄下有一個onload.cpp,這個會註冊這個jni並且呼叫register_android_server_location_GpsLocationProvider來實現java層到hal層的介面的呼叫。

在android_location_GpsLocationProvider_init函式中,通過Android硬體抽象層提供的hw_get_module方法來載入模組ID為GPS_HARDWARE_MODULE_ID的硬體抽象層模組,其中,GPS_HARDWARE_MODULE_ID是在<hardware/gps.h>中定義的。Android硬體抽象層會根據GPS_HARDWARE_MODULE_ID的值在Android系統的/system/lib/hw目錄中找到相應的模組,然後載入起來,並且返回hw_module_t介面給呼叫者使用。在jniRegisterNativeMethods函式中,第二個引數的值必須對應GpsLocationProvider所在的包的路徑,即com/android/server/location/GpsLocationProvider。

       下面分步來看下,jni是如何工作的。

首先是onload.cpp這裡會呼叫到

com_android_server_location_GpsLocationProvider.cpp的註冊函式

register_android_server_location_GpsLocationProvider,而這個註冊函式就註冊了應用層的GpsLocationProvider的jni。他會呼叫下面這個註冊函式。具體各個引數含義,第一個是env,是jni技術的主要結構體JNIEnv,第二個引數就是所要註冊的java服務程式的包。最後一個是native方法的回撥函式了。

jniRegisterNativeMethods(env,"com/android/server/location/GpsLocationProvider", sMethods,NELEM(sMethods));

下面的是native方法的一些函式。

staticJNINativeMethod sMethods[] = {
{"class_init_native","()V", (void*)android_location_GpsLocationProvider_class_init_native},
    {"native_is_supported","()Z",(void*)android_location_GpsLocationProvider_is_supported},
    {"native_init", "()Z",(void*)android_location_GpsLocationProvider_init},
    {"native_cleanup","()V", (void*)android_location_GpsLocationProvider_cleanup},
{"native_set_position_mode","(IIIII)Z",(void*)android_location_GpsLocationProvider_set_position_mode},
    {"native_start", "()Z",(void*)android_location_GpsLocationProvider_start},
    {"native_stop", "()Z",(void*)android_location_GpsLocationProvider_stop},
…………………………
};

對於這個method做一些簡單的解釋吧:。

第一個引數name是在java服務層定義的native函式。具體可以看

frameworks/base/location/java/com/android/server/location/GpsLocationProvide.java下的程式碼。

這個稍後分析。

第二個引數signature是java服務層呼叫jni層的引數,具體的可以先看下面這個表:

字元   Java型別    C型別
V      void         void
Z       jboolean     boolean
I        jint         int
J       jlong        long
D      jdouble       double
F      jfloat         float
B      jbyte         byte
C      jchar         char
S      jshort         short

 

陣列則以"["開始,用兩個字元表示

 
[I       jintArray      int[]
[F     jfloatArray      float[]
[B     jbyteArray      byte[]
[C    jcharArray       char[]
[S    jshortArray       short[]
[D    jdoubleArray     double[]
[J     jlongArray       long[]
[Z    jbooleanArray     boolean[]
 

實際上這些字元是與函式的引數型別一一對應的。

"()"中的字元表示引數,後面的則代表返回值。例如"()V" 就表示void Func();

"(II)V"表示 void Func(int, int);

 

上面的都是基本型別。如果Java函式的引數是class,則以"L"開頭,以";"結尾中間是用"/" 隔開的包及類名。而其對應的C函式名的引數則為jobject. 一個例外是String類,其對應的類為jstring

Ljava/lang/String;String jstring
Ljava/net/Socket;Socket jobject

 

如果JAVA函式位於一個嵌入類,則用$作為類名間的分隔符。

例如"(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"

由此,上面寫函式的引數也知道了。

 

第三個變數fnPtr是函式指標,指向C函式,也就是在jni的cpp程式碼中的。

 

而jni和java程式碼的互動可以看下圖所示:

其實,java層和jni層都是可以互相呼叫的,而具體的java層呼叫jni的方法就是native宣告的方法;jni呼叫java的話,要先獲取java層的方法id,然後通過CallVoidMethod()等回撥函式實現。


有了以上的知識點作為基礎的話,那麼現在來分析下GPS的jni程式碼了。

1、首先是android_location_GpsLocationProvider_class_init_native這個函式了。

method_reportLocation =env->GetMethodID(clazz, "reportLocation","(IDDDFFFJ)V");
    ……………………………………
   method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V")

他先是獲取所有的方法id。然後呼叫hw模組,也就是上面hal層編譯的gps.*.so連結庫

err =hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);

然後呼叫了hal層的gps的open函式了。

err =module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);

獲取介面sGpsInterface =gps_device->get_gps_interface(gps_device);

因為是gps不是agps所以

sGpsXtraInterface =
          (constGpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);

2、接著便是android_location_GpsLocationProvider_init這個初始化函式了。

       該函式主要是呼叫了

if(sGpsXtraInterface &&sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
        sGpsXtraInterface = NULL;

這個函式的函式指標init就呼叫到hal層的init函式。也就是qemu_gps_init函式了。

這裡傳進來了callback函式了。

具體如下:

GpsCallbackssGpsCallbacks = {
    sizeof(GpsCallbacks),
    location_callback,  //回撥位置資訊
    status_callback,    //回撥狀態資訊
    sv_status_callback,  //回撥sv狀態資訊
    nmea_callback,    //上報nema資訊
    set_capabilities_callback, //回撥告知框架層GPS的效能
    acquire_wakelock_callback, //獲取GPS鎖,不進行休眠
    release_wakelock_callback, //釋放GPS鎖
    create_thread_callback, //建立執行緒,可以呼叫java framework的程式碼
    request_utc_time_callback, //獲取utc時間
};

具體函式可以看下面:

staticvoid location_callback(GpsLocation* location)
{
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->CallVoidMethod(mCallbacksObj, method_reportLocation,location->flags,
            (jdouble)location->latitude,(jdouble)location->longitude,
            (jdouble)location->altitude,
            (jfloat)location->speed,(jfloat)location->bearing,
            (jfloat)location->accuracy,(jlong)location->timestamp);
    checkAndClearExceptionFromCallback(env,__FUNCTION__);
}

這裡會呼叫method_reportLocation這個方法,這個方法是在java服務層呼叫的。這裡通過CallVoidMethod函式呼叫了java framework的函式。

staticvoid status_callback(GpsStatus* status)
{
    JNIEnv* env = AndroidRuntime::getJNIEnv();
   env->CallVoidMethod(mCallbacksObj, method_reportStatus,status->status);
    checkAndClearExceptionFromCallback(env,__FUNCTION__);
}
 
staticvoid sv_status_callback(GpsSvStatus* sv_status)
{
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    memcpy(&sGpsSvStatus, sv_status,sizeof(sGpsSvStatus));
    env->CallVoidMethod(mCallbacksObj,method_reportSvStatus);
    checkAndClearExceptionFromCallback(env,__FUNCTION__);
}
 
staticvoid nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
{
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    // The Java code will call back to readthese values
    // We do this to avoid creating unnecessaryString objects
    sNmeaString = nmea;
    sNmeaStringLength = length;
    env->CallVoidMethod(mCallbacksObj,method_reportNmea, timestamp);
    checkAndClearExceptionFromCallback(env,__FUNCTION__);
}
 
staticvoid set_capabilities_callback(uint32_t capabilities)
{
    LOGD("set_capabilities_callback:%ld\n", capabilities);
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->CallVoidMethod(mCallbacksObj,method_setEngineCapabilities, capabilities);
    checkAndClearExceptionFromCallback(env,__FUNCTION__);
}
 
staticvoid acquire_wakelock_callback()
{
    acquire_wake_lock(PARTIAL_WAKE_LOCK,WAKE_LOCK_NAME);
}
獲取GPS的鎖
staticvoid release_wakelock_callback()
{
    release_wake_lock(WAKE_LOCK_NAME);
}
釋放了GPS的鎖
staticvoid request_utc_time_callback()
{
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->CallVoidMethod(mCallbacksObj,method_requestUtcTime);
    checkAndClearExceptionFromCallback(env,__FUNCTION__);
}
更新時間
staticpthread_t create_thread_callback(const char* name, void (*start)(void *), void*arg)
{
    return(pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
}
建立執行緒。

 

3、其他函式,開啟導航,關閉導航等的就不一一介紹了,和上面的類似。可以看原始碼。

好了,jni層就大概分析好了,那麼接著就是framework層的程式碼了。

 

 

 

九、android GPS的framework層

       首先還是主要流程圖吧:

 

Framework層主要就是為上層應用呼叫提供介面。而GPS的framework層,根據jni的程式碼我們也可以知道就在下面這個目錄下了。

frameworks/base/services/java/com/android/server/location/GpsLocationProvide.java

那麼android framework是怎麼開始往下呼叫jni的呢?還是看程式碼吧。

首先是在frameworks/base/services/java/com/android/server/SystemServer.Java

對於這個系統服務,還是要從android啟動流程講起,首先linux核心啟動完了之後,會呼叫android的init程式,然後掛載檔案最小系統,解析init.rc和init.*.rc等指令碼,接著zygote,啟動虛擬機器,然後就是執行到了systemserver了,也就是上面那個SystemServer.Java函式,在這個函式中,先看下SystemServer 這個類,發現了有一個native的init1函式。

native public static void init1(String[]args);

這個函式是呼叫了jni層的函式的,可以看

frameworks/base/services/jni/com_android_server_SystemServer.cpp

首先是進入其main函式,

System.loadLibrary("android_servers");
      init1(args);

載入庫,然後就是呼叫了jni層的init1函式。

static voidandroid_server_SystemServer_init1(JNIEnv* env, jobject clazz)
{
   system_init();
}

其呼叫了system_init();函式,而在system_init函式中

jmethodID methodId =env->GetStaticMethodID(clazz, "init2", "()V");
env->CallStaticVoidMethod(clazz,methodId);

他又回撥了SystemServer.Java中的init2函式。

在init2函式中

public static final void init2() {
       Slog.i(TAG, "Entered the Android system server!");
       Thread thr = new ServerThread();
       thr.setName("android.server.ServerThread");
       thr.start();
    }

建立了一個執行緒,最後他會呼叫到ServerThread的run方法。

在其run()方法中會啟動很多服務,比如Account Manager、ContentManager、System Content Providers、Lights Service、BatteryService、Vibrator Service、Alarm Manager、InitWatchdog、Window Manager等等,我們這裡最主要的就是關注LocationManagerService。

try {
                Slog.i(TAG, "LocationManager");
                location = new LocationManagerService(context);
               ServiceManager.addService(Context.LOCATION_SERVICE, location);
            } catch (Throwable e) {
                reportWtf("startingLocation Manager", e);
           }

這裡new了一個LocationManagerService物件,並且新增了這個service。

然後finalLocationManagerService locationF = location;

try {
                    if (locationF != null)locationF.systemReady();
                } catch (Throwable e) {
                    reportWtf("makingLocation Service ready", e);
                }

當activity manager好了之後就呼叫了locationF.systemReady();函式了。

下面進LocationManagerService.java中的system函式。

voidsystemReady() {
        // we defer starting up the service untilthe system is ready
        Thread thread = new Thread(null, this,"LocationManagerService");
        thread.start();
    }

這裡建立了一個執行緒,然後執行緒啟動了,接著他會呼叫run方法。也就是

publicvoid run()
    {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Looper.prepare();
        mLocationHandler = newLocationWorkerHandler();
        initialize();
        Looper.loop();
    }

在run方法中,他會初始化一些東東,也就是initialize();函式了。

privatevoid initialize() {
        // Create a wake lock, needs to be donebefore calling loadProviders() below
        PowerManager powerManager =(PowerManager)
mContext.getSystemService(Context.POWER_SERVICE);
        mWakeLock =
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,WAKELOCK_KEY);
 
        // Load providers
        loadProviders();
………………
………………
}

這裡主要看loadProviders();函式。

privatevoid loadProviders() {
        synchronized (mLock) {
            if (sProvidersLoaded) {
                return;
            }
 
            // Load providers
            loadProvidersLocked();
            sProvidersLoaded = true;
        }
    }

這裡又會呼叫loadProvidersLocked(),然後是_loadProvidersLocked()函式。其中就會呼叫到我們需要的gpsprovider了。

if(GpsLocationProvider.isSupported()) {
            // Create a gps location provider
            GpsLocationProvider gpsProvider =new GpsLocationProvider(mContext, this);
            mGpsStatusProvider =gpsProvider.getGpsStatusProvider();
            mNetInitiatedListener =gpsProvider.getNetInitiatedListener();
            addProvider(gpsProvider);
            mGpsLocationProvider = gpsProvider;
        }

在new一個GpsLocationProvider的時候會開一個GpsLocationProviderThread的執行緒

mThread =new GpsLocationProviderThread();
mThread.start();

然後啟動了執行緒,在這裡會初始化,然後主要還是new了一個ProviderHandler的類。

         

   initialize();
            mHandler = new ProviderHandler();

這個類中

publicvoid handleMessage(Message msg) {
            int message = msg.what;
            switch (message) {
                case ENABLE:
                    if (msg.arg1 == 1) {
                        handleEnable();
                    } else {
                        handleDisable();
                    }
                    break;
                case ENABLE_TRACKING:
                   handleEnableLocationTracking(msg.arg1 == 1);
                    break;
                case REQUEST_SINGLE_SHOT:
                    handleRequestSingleShot();
                    break;
                case UPDATE_NETWORK_STATE:
                   handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
                    break;
                case INJECT_NTP_TIME:
                    handleInjectNtpTime();
                    break;
                case DOWNLOAD_XTRA_DATA:
                    if (mSupportsXtra) {
                        handleDownloadXtraData();
                    }
                    break;
                case UPDATE_LOCATION:
                   handleUpdateLocation((Location)msg.obj);
                    break;
                case ADD_LISTENER:
                    handleAddListener(msg.arg1);
                    break;
                case REMOVE_LISTENER:
                   handleRemoveListener(msg.arg1);
                    break;
            }

就會處理很多事情了。具體可以看上面的程式碼。

接著他呼叫了updateProvidersLocked();這個函式。

updateProviderListenersLocked(name,true);

然後就啟動了gps服務了

if (enabled) {
            p.enable();
            if (listeners > 0) {
               p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
                p.enableLocationTracking(true);
            }
        }

接著我們開始往GpsLocationProvide.java這裡看

public void enable() {
        synchronized (mHandler) {
            sendMessage(ENABLE, 1, null);
        }
    }

這裡傳送了一個ENABLE訊息,然後就會呼叫到上面ProviderHandler的函式,handleEnable();

privatevoid handleEnable() {
       ……
        mEnabled = native_init();
 
        if (mEnabled) {
            mSupportsXtra =native_supports_xtra();
       ……
    }

這裡就會呼叫到了jni的native函式了。

而native的所有函式,在jni中我們都已經分析過了。

接著便是

 

enable函式結束後,那便是

publicvoid enableLocationTracking(boolean enable) {
        // FIXME - should set a flag here to avoidrace conditions with single shot request
        synchronized (mHandler) {
            sendMessage(ENABLE_TRACKING,(enable ? 1 : 0), null);
        }
    }

同樣,他也傳送了ENABLE_TRACKING的訊息,接著呼叫

handleEnableLocationTracking(msg.arg1== 1);
if (enable) {
            mTTFF = 0;
            mLastFixTime = 0;
            startNavigating(false);
        }

接著呼叫了startNavigating函式,

privatevoid startNavigating(boolean singleShot) {
       ……
 if (!native_set_position_mode(mPositionMode,GPS_POSITION_RECURRENCE_PERIODIC,
    interval, 0, 0)) {
                mStarted = false;
                Log.e(TAG,"set_position_mode failed in startNavigating()");
                return;
   }
    if(!native_start()) {
         mStarted = false;
         Log.e(TAG, "native_start failedin startNavigating()");
        return;
    ……
   }
}

這裡主要還是設定position mode,然後啟動了。

接下來,我們看看,jni呼叫的一些callback函式在這裡做了些什麼。

首先就是reportLocation函式了。

privatevoid reportLocation(int flags, double latitude, double longitude, doublealtitude,
            float speed, float bearing, floataccuracy, long timestamp) {
        …………
        synchronized (mLocation) {
            mLocationFlags = flags;
            if ((flags &LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
               mLocation.setLatitude(latitude);
               mLocation.setLongitude(longitude);
                mLocation.setTime(timestamp);
            }
            if ((flags &LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
                mLocation.setAltitude(altitude);
            } else {
                mLocation.removeAltitude();
            }
            if ((flags &LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
                mLocation.setSpeed(speed);
            } else {
                mLocation.removeSpeed();
            }
            if ((flags &LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
                mLocation.setBearing(bearing);
            } else {
                mLocation.removeBearing();
            }
            if ((flags & LOCATION_HAS_ACCURACY) ==LOCATION_HAS_ACCURACY) {
               mLocation.setAccuracy(accuracy);
            } else {
                mLocation.removeAccuracy();
            }
 
            try {
                mLocationManager.reportLocation(mLocation,false);
            } catch (RemoteException e) {
                Log.e(TAG,"RemoteException calling reportLocation");
            }
        }
 
        mLastFixTime =System.currentTimeMillis();
        // report time to first fix
        if (mTTFF == 0 && (flags &LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
            mTTFF = (int)(mLastFixTime -mFixRequestTime);
            if (DEBUG) Log.d(TAG, "TTFF:" + mTTFF);
 
            // notify status listeners
            synchronized(mListeners) {
                int size = mListeners.size();
                for (int i = 0; i < size;i++) {
                    Listener listener =mListeners.get(i);
                    try {
                       listener.mListener.onFirstFix(mTTFF);
                    } catch (RemoteException e){
                        Log.w(TAG,"RemoteException in stopNavigating");
                       mListeners.remove(listener);
                        // adjust for size oflist changing
                        size--;
                    }
                }
            }
        }
 
        if (mSingleShot) {
            stopNavigating();
        }
        if (mStarted && mStatus !=LocationProvider.AVAILABLE) {
            // we want to time out if we do notreceive a fix
            // within the time out and we arerequesting infrequent fixes
            if(!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval <NO_FIX_TIMEOUT) {
               mAlarmManager.cancel(mTimeoutIntent);
            }
 
            // send an intent to notify that the GPSis receiving fixes.
            Intent intent = newIntent(LocationManager.GPS_FIX_CHANGE_ACTION);
           intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
            mContext.sendBroadcast(intent);
           updateStatus(LocationProvider.AVAILABLE, mSvCount);
        }
 
       if(!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&mFixInterval > 1000) {
            if (DEBUG) Log.d(TAG, "gotfix, hibernating");
            hibernate();
        }
   }

這裡對於jni層獲取的資料,做了處理後,更新了location資訊。

其中下面這些都是給應用層的介面。

                           mLocation.setLatitude(latitude);
               mLocation.setLongitude(longitude);
                mLocation.setTime(timestamp);

Ok,那麼framwork層就介紹到這裡了。

 

 

 

十、android GPS的APP層

首先看流程:


接上面的framework層,這裡呼叫了mLocation是new了一個Location的類,這個類主要是給應用層介面的。

if((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
               mLocation.setLatitude(latitude);
               mLocation.setLongitude(longitude);
                mLocation.setTime(timestamp);
            }

其程式碼在frameworks/base/location/java/android/location/Location.java

取其中一部分的函式做下簡單的介紹。

 

    /**
     * Sets the UTC time of this fix, inmilliseconds since January 1,
     * 1970.
     */
    public void setTime(long time) {
        mTime = time;
    }
 
    /**
     * Returns the latitude of this fix.
     */
    public double getLatitude(){
        return mLatitude;
    }
 
    /**
     * Sets the latitude of this fix.
     */
    public void setLatitude(double latitude) {
        mLatitude = latitude;
    }
 
    /**
     * Returns the longitude of this fix.
     */
    public double getLongitude() {
        return mLongitude;
    }
 
    /**
     * Sets the longitude of this fix.
     */
    public void setLongitude(double longitude){
        mLongitude = longitude;
    }
 

mLocation.setLatitude(latitude);函式就會把

mLatitude= latitude;

然後,應用層只要getLatitude()就知道了緯度了。

其他的函式介面類似。

簡單看下下面的一段應用層的程式碼

功能:展示GPS資訊

 

 

private void updateToNewLocation(Locationlocation)
   {     
 
     if (location != null)
     {         
      bear = location.getBearing(); //偏離正北方的度數
      double latitude =location.getLatitude();      //維度
      double longitude=location.getLongitude();     //經度
      double GpsSpeed = location.getSpeed(); //速度
      long GpsTime = location.getTime(); //時間
      Date date = new Date(GpsTime);
      DateFormat df = newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     
      float GpsAlt =(float)location.getAltitude();  //海拔
      latitudeview.setText("" +latitude);
      longitudeview.setText("" +longitude);
      speedview.setText(""+GpsSpeed);
     timeview.setText(""+df.format(date));
     altitudeview.setText(""+GpsAlt);
      bearingview.setText(""+bear);
      }
      else
      {   
       Toast.makeText(this, "無法獲取地理資訊", Toast.LENGTH_SHORT).show();
      }
 
}

終於,從衛星訊號到android APP層獲取到資料那個簡單的資料流程分析好了。要想更加細緻的瞭解其機制,還是得需要花更多的時間繼續深入下去的。其中還有很多的細節,很多概念都不是很明白。只知道個大概。不過通過這次分析,收穫不少。凡事都需要從整體到部分再到整理,從整理的GPS框架,到分成很多塊理解,再把那些模組整合在一起,從而瞭解其整個機制。

 

 

 

相關文章