技術討論 | 基於基站定位APP開發實錄
父母已經上了歲數,有時給他們手機打電話總是一直沒人接聽,搞得我很擔心,怕出現意外。於是決定自己開發一款定位用APP為他們保駕護航。考慮到耗電問題,放棄GPS定位,研究了下基站定位技術,雖然定位精度不及GPS,但對於我的需求是足夠的。
PS:本文僅用於技術討論與分享,嚴禁用於任何非法用途。
一、基站定位簡介
基站定位一般應用於手機使用者,手機基站定位服務又叫做移動位置服務(LBS——Location Based Service),它是通過電信移動運營商的網路(如GSM網)獲取移動終端使用者的位置資訊(經緯度座標)。
基站定位的原理為:行動電話測量不同基站的下行導頻訊號,得到不同基站下行導頻的TOA(Time of Arrival,到達時刻)或TDOA(Time Difference of Arrival,到達時間差),根據該測量結果並結合基站的座標,一般採用三角公式估計演算法,就能夠計算出行動電話的位置。實際的位置估計演算法需要考慮多基站(3個或3個以上)定位的情況,因此演算法要複雜很多。一般而言,移動臺測量的基站數目越多,測量精度越高,定位效能改善越明顯。
二、開發思路
由於本次需求對定位精度要求不是特別高,就不採用多基點定位法了,本次採用單基點定位就可以了。單基點定位就是把與手機最新的基站認為手機的當前位置。
整個定位系統可以分為兩部分:APP部分和離線電子地圖。
下面我就詳細說明每部分的功能。
1.APP詳解。
APP以1分鐘為週期捕獲當前基站情報並存儲在檔案中,在一天結束的時候將當日的統計結果檔案傳送到指定郵箱中。
2,離線地圖詳解。
使用者手動從郵箱中下載當日的定位檔案,並手動匯入到離線地圖中,在地圖中標記出當日的行走軌跡。
三、開發過程
APP篇
APP介面如下。
APP機能中簡單的部分我就不介紹了,難點是如何獲得基站資訊。
本次需要獲得這些基站情報:網路型別、移動國家碼、行動網路碼、系統識別碼、網路識別碼、基站識別碼。
下面是獲得基站資訊的程式碼。
private String ReadCellInfo() { TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); // 返回值MCC + MNC (注意:電信的mnc 對應的是 sid) String operator = mTelephonyManager.getNetworkOperator(); if(operator!=null&&operator.length()==0){ return "手機無網路!"; } int mcc = -1; int mnc = -1; if(operator!=null&&operator.length()>3){ mcc = Integer.parseInt(operator.substring(0, 3)); mnc = Integer.parseInt(operator.substring(3)); } StringBuffer cellinfo = new StringBuffer(); //時間戳 Calendar calendar = Calendar.getInstance(); long unixTime = calendar.getTimeInMillis(); int year = calendar.get(Calendar.YEAR) ; int month = calendar.get(Calendar.MONTH)+1; int day = calendar.get(Calendar.DAY_OF_MONTH); int hour = calendar.get(Calendar.HOUR_OF_DAY); // 0-23 int minute = calendar.get(Calendar.MINUTE); int second = calendar.get(Calendar.SECOND); String mytime =""; mytime = String.format("%d%02d%02d %02d:%02d:%02d",year,month,day,hour,minute,second); cellinfo.append(mytime).append(","); //NetworkType int type = mTelephonyManager.getNetworkType(); cellinfo.append(type +","); //需要判斷網路型別,因為獲取資料的方法不一樣, if(type == TelephonyManager.NETWORK_TYPE_CDMA// 電信cdma網 || type == TelephonyManager.NETWORK_TYPE_1xRTT || type == TelephonyManager.NETWORK_TYPE_EVDO_0 || type == TelephonyManager.NETWORK_TYPE_EVDO_A || type == TelephonyManager.NETWORK_TYPE_EVDO_B || type == TelephonyManager.NETWORK_TYPE_LTE){ // TIME,TYPE,MCC,MNC,SID,NID,BID CdmaCellLocation cdma = (CdmaCellLocation) mTelephonyManager.getCellLocation(); if(cdma!=null){ //Mobile country code:移動國家碼 sb.append("MCC = " + mcc +"\n"); cellinfo.append(mcc +","); // Mobile Network Code:行動網路碼 sb.append("MNC = " + mnc +"\n" ); cellinfo.append(mnc +","); //System ID:系統識別碼 int sid = cdma.getSystemId(); sb.append("SID = " + sid +"\n"); cellinfo.append(sid +","); // Network ID:網路識別碼 int nid = cdma.getNetworkId(); sb.append("NID = " + nid +"\n"); cellinfo.append(nid +","); // Base Station ID:基站識別碼 int bid = cdma.getBaseStationId(); sb.append("BID = " + bid +"\n"); cellinfo.append(bid +","); }else{ sb.append("can not get the CDMA CellLocation"); cellinfo.append("can not get the CDMA CellLocation"); } }else if(type == TelephonyManager.NETWORK_TYPE_UNKNOWN){ cellinfo.append("電話卡不可用!"); } String result = new String(cellinfo); return result; }
每一分鐘採集一次,並將統計的資訊按【取樣時間,網路型別、移動國家碼、行動網路碼、系統識別碼、網路識別碼、基站識別碼】格式輸出到檔案中,其中一行為一條情報。
以下為某日生成的部分檔案內容。
20180806 12:31:07,13,460,11,13858,13,8691, 20180806 12:32:07,13,460,11,13858,13,8691, 20180806 12:33:10,13,460,11,13858,13,8691, 20180806 12:34:22,13,460,11,13858,13,8691, 20180806 12:35:40,13,460,11,13858,13,8691, 20180806 12:36:40,13,460,11,13858,13,8691, 20180806 12:37:40,13,460,11,13858,13,8691, 20180806 12:38:41,13,460,11,13858,13,8691, 20180806 12:39:41,13,460,11,13858,13,8691, 20180806 12:40:41,13,460,11,13858,13,8691, 20180806 12:41:41,13,460,11,13858,13,8691, 20180806 12:42:41,13,460,11,13858,13,8691, 20180806 12:43:41,13,460,11,13858,13,8691, 20180806 12:44:41,13,460,11,13858,13,8691, 20180806 12:45:41,13,460,11,13858,13,8691, 20180806 12:46:42,13,460,11,13858,13,9000, 20180806 12:47:42,13,460,11,13858,13,9000, 20180806 12:48:42,13,460,11,13858,13,9000, 20180806 12:49:42,13,460,11,13858,13,9000, 20180806 12:50:42,13,460,11,13858,13,9000, 20180806 12:51:43,13,460,11,13858,13,9000, 20180806 12:52:43,13,460,11,13858,13,9000, 20180806 12:53:43,13,460,11,13858,13,9000, 20180806 12:54:43,13,460,11,13858,13,9000, 20180806 12:55:43,13,460,11,13858,13,9000, 20180806 12:56:43,13,460,11,13858,13,9000, 20180806 12:57:43,13,460,11,13858,13,9000, 20180806 12:58:43,13,460,11,13858,13,9000, 20180806 12:59:43,13,460,11,13858,13,8488, 20180806 13:00:44,13,460,11,13858,13,8488, 20180806 13:01:44,13,460,11,13858,13,8488, 20180806 13:02:44,13,460,11,13858,13,8488,
離線地圖篇
離線地圖最核心內容將基站情報轉換為基站的經緯度經度,再通過百度地圖API進行軌跡點描畫。
有很多網站會提供API可以獲得基站的經緯度資訊,但大部分都是收費的,好不容易找到了一家穩定且免費的API。
—————介面說明如下 —————
查詢全國移動聯通電信2G/3G/4G基站位置資料,收錄資料總量1.2億條
介面地址: ofollow" rel="nofollow,noindex" target="_blank">http://api.cellocation.com:81/cell/
請求方式: GET
請求示例: http://api.cellocation.com:81/cell/?mcc=460&mnc=1&lac=4301&ci=20986&output=xml
請求引數:
名稱 | 型別 | 必填 | 說明 |
---|---|---|---|
mcc | int | 是 | mcc國家程式碼:中國程式碼 460 |
mnc | int | 是 | mnc網路型別:0移動,1聯通(電信對應sid),十進位制 |
lac | int | 是 | lac(電信對應nid),十進位制 |
ci | int | 是 | cellid(電信對應bid),十進位制 |
coord | string | 否 | 座標型別(wgs84/gcj02/bd09),預設wgs84 |
output | string | 否 | 返回格式(csv/json/xml),預設csv |
返回資料格式:CSV
errcode,緯度,經度,精度半徑,地址
errcode
0: 成功
10000: 引數錯誤
10001: 無查詢結果
示例:
0,39.999024,116.476159,222,”北京市朝陽區望京街道北京市望京實驗學校(寶星分校);阜安路與巨集泰東街路口西231米”
————— 介面說明如下 結束 —————
最後,離線地圖描畫的最終效果如下圖所示。