關於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
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也將被傳輸,如果不是差分定位將為空)
$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.(檢查位).
$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>項個別衛星會重復出現,每行最多有四顆衛星。其余衛星信息會於次一行出現,若未使用,這些字段會空白。
$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這條信息,因為其中包括當前格林威治時間、經度、緯度、日期等。
$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;
/** 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
- };
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, //擴展信息
- };
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);
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 );
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) )
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
/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));
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},
- …………………………
- };
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
字符 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[]
[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
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")
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);
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);
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);
sGpsXtraInterface = (constGpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
2、接着便是android_location_GpsLocationProvider_init這個初始化函數了。
該函數主要是調用了
- if(sGpsXtraInterface &&sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
- sGpsXtraInterface = NULL;
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時間
- };
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__);
- }
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);
- }
- 創建線程。
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);
System.loadLibrary("android_servers"); init1(args);
加載庫,然后就是調用了jni層的init1函數。
- static voidandroid_server_SystemServer_init1(JNIEnv* env, jobject clazz)
- {
- system_init();
- }
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);
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();
- }
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);
- }
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);
- }
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();
- }
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();
- }
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();
- ………………
- ………………
- }
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;
- }
- }
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;
- }
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();
mThread =new GpsLocationProviderThread(); mThread.start();
然后啟動了線程,在這里會初始化,然后主要還是new了一個ProviderHandler的類。
- initialize();
- mHandler = 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;
- }
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);
- }
- }
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);
- }
- }
public void enable() { synchronized (mHandler) { sendMessage(ENABLE, 1, null); } }
這里發送了一個ENABLE消息,然后就會調用到上面ProviderHandler的函數,handleEnable();
- privatevoid handleEnable() {
- ……
- mEnabled = native_init();
- if (mEnabled) {
- mSupportsXtra =native_supports_xtra();
- ……
- }
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);
- }
- }
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);
- }
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;
- ……
- }
- }
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();
- }
- }
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);
- Location.setLongitude(longitude);
- mLocation.setTime(timestamp);
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);
- }
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;
- }
/** * 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();
- }
- }
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框架,到分成很多塊理解,再把那些模塊整合在一起,從而了解其整個機制。