定位技術概述

定位技術概述
目前主流定位技術分爲3種:GPS定位、基站定位和Wifi定位。
GPS篇
GPS是英文Global Positioning System(全球定位系統)的簡稱,而其中文簡稱爲“球位系”。GPS是20世紀70年代由美國陸海空三軍聯合研製的新一代空間衛星導航定位系統 。其主要目的是爲陸、海、空三大領域提供實時、 全天候和全球性的導航服務,並用於情報收集、核爆監測和應急通訊等一些軍事目的,經過20餘年的研究實驗,耗資300億美元,到1994年3月,全球覆蓋率高達98%的24顆GPS衛星星座己佈設完成。GPS功能必須具備GPS終端、傳輸網絡和監控平臺三個要素;這三個要素缺一不可;通過這三個要素,可以提供車輛防盜、反劫、行駛路線監控及呼叫指揮等功能。最初的GPS計劃在聯合計劃局的領導下誕生了,該方案將24顆衛星放置在互成120度的三個軌道上。每個軌道上有8顆衛星,地球上任何一點均能觀測到6至9顆衛星。這樣,粗碼精度可達100m,精碼精度爲10m。由於預算壓縮,GPS計劃不得不減少衛星發射數量,改爲將18顆衛星分佈在互成60度的6個軌道上。然而這一方案使得衛星可靠性得不到保障。1988年又進行了最後一次修改:21顆工作星和所3顆備用星工作在互成30度的6條軌道上。這也是現在GPS衛星使用的工作方式。
Android定位功能
在Android系統下面,對GPS的支持還是很好的。廢話不多說,直接看看與實現Android定位有關的API吧。這些API都在android.location包下,一共有三個接口和八個類。它們配合使用即可實現定位功能。
  三個接口:
  GpsStatus.Listener: 這是一個當GPS狀態發生改變時,用來接收通知的接口。
  GpsStatus.NmeaListener:這是一個用來從GPS裏接收Nmea-0183(爲海用電子設備制定的標準格式)信息的接口。
  LocationListener:位置監聽器,用於接收當位置信息發生改變時從LocationManager接收通知的接口。
  八個類:
  Address:描述地址的類,比如:北京
  Criteria:用於描述Location Provider標準的類,標準包括位置精度水平,電量消耗水平,是否獲取海拔、方位信息,是否允許接收付費服務。
  GeoCoder:用於處理地理位置的編碼。
  GpsSatellite:和GpsStatus聯合使用,用於描述當前GPS衛星的狀態。
  GpsStatus:和GpsStatus.Listener聯合使用,用於描述當前GPS衛星的狀態。
  Location:用於描述位置信息。
  LocationManager:通過此類獲取和調用系統位置服務
  LocationProvider:用於描述Location Provider的抽象超類,一個LocationProvider應該能夠週期性的報告當前設備的位置信息。
  這裏通過一個代碼示例,演示一下如何實現定位。
  首先,在AndroidManifest.xml清單文件裏需要加入ACCESS_FINE_LOCATION權限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
  其次,實現代碼如下:
package com.veer;

import android.app.Activity;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class LocationGPSActivity extends Activity {

private String tag = "LocationGPSActivity";


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LocationManager loctionManager;
String contextService = Context.LOCATION_SERVICE;
// 通過系統服務,取得LocationManager對象
loctionManager = (LocationManager) getSystemService(contextService);

// 得到位置提供器,通過位置提供器,得到位置信息,可以指定具體的位置提供器,也可以提供一個標準集合,讓系統根據
// 標準匹配最適合的位置提供器,位置信息是由位置提供其提供的。
// a. 通過GPS位置提供器獲得位置(指定具體的位置提供器)
String provider = LocationManager.GPS_PROVIDER;
Location location = loctionManager.getLastKnownLocation(provider);
// b. 使用標準集合,讓系統自動選擇可用的最佳位置提供器,提供位置
// Criteria criteria = new Criteria();
// criteria.setAccuracy(Criteria.ACCURACY_FINE);// 高精度
// criteria.setAltitudeRequired(false);// 不要求海拔
// criteria.setBearingRequired(false);// 不要求方位
// criteria.setCostAllowed(true);// 允許有花費
// criteria.setPowerRequirement(Criteria.POWER_HIGH);//高功耗
// // 從可用的位置提供器中,匹配以上標準的最佳提供器
// String provider = loctionManager.getBestProvider(criteria, true);
// // 獲得最後一次變化的位置
// Location location = loctionManager.getLastKnownLocation(provider);
Log.v(tag, "location===" + location);
// 使用新的location更新TextView顯示
updateWithNewLocation(location);
// 監聽位置變化,2秒一次,距離1000米以上
loctionManager.requestLocationUpdates(provider, 2 * 1000, 1000,
locationListener);
}

// 位置監聽器
private final LocationListener locationListener = new LocationListener() {
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}

@Override
public void onProviderEnabled(String provider) {
}

@Override
public void onProviderDisabled(String provider) {
}

// 當位置變化時觸發

@Override
public void onLocationChanged(Location location) {
// 使用新的location更新TextView顯示

updateWithNewLocation(location);
}
};

// 通過改變位置經緯度,程序會自動更新TextView顯示的位置信息
private void updateWithNewLocation(Location location) {
String latLongString;
TextView myLoctionText;
myLoctionText = (TextView) findViewById(R.id.myLoctionText);
if (location != null) {
double lat = location.getLatitude();
double lng = location.getLongitude();
latLongString = "Lat(緯度): " + lat + "\nLong(經度): " + lng;
} else {
latLongString = "沒有獲取到經緯度,悲劇啊。";
}
myLoctionText.setText("我當前的位置是:\n" + latLongString);
}
}


基站篇
基站定位一般應用於手機用戶,手機基站定位服務又叫做移動位置服務(LBS——Location Based Service),它是通過電信移動運營商的網絡(如GSM網)獲取移動終端用戶的位置信息(經緯度座標),在電子地圖平臺的支持下,爲用戶提供相應服務的一種增值業務,例如目前中國移動動感地帶提供的動感位置查詢服務等。其大致原理爲:移動電話測量不同基站的下行導頻信號,得到不同基站下行導頻的TOA(Time of Arrival,到達時刻)或TDOA(Time Difference of Arrivalm,到達時間差),根據該測量結果並結合基站的座標,一般採用三角公式估計算法,就能夠計算出移動電話的位置。實際的位置估計算法需要考慮多基站(3個或3個以上)定位的情況,因此算法要複雜很多。一般而言,移動臺測量的基站數目越多,測量精度越高,定位性能改善越明顯。
實現代碼如下:
package com.veer;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import android.widget.TextView;

public class LocationCellActivity extends Activity {

private SCell cell = null;
private SItude itude = null;
private String userLongitude = "";
private String userLatitude = "";
private String tag = "LocationCellActivity";

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

try {
cell = getCellInfo();
} catch (Exception e1) {
Log.v(tag, "獲取基站數據 失敗 **************************************");
}

try {
itude = getItude(cell);
Log.v(tag, "itude===" + itude);
} catch (Exception e1) {
Log.v(tag, "根據基站數據獲取經緯度 失敗 **************************************");
}
/* 獲取用戶當前位置信息 */
if (itude != null) {
userLongitude = itude.longitude;
userLatitude = itude.latitude;
} else {
userLongitude = "";
userLatitude = "";
}

Log.v(tag, "賦值後的經度====" + userLongitude);
Log.v(tag, "賦值後到的緯度====" + userLatitude);

TextView tv = (TextView) findViewById(R.id.info);
String info = "經度爲:" + userLongitude + "\n" + "緯度爲:" + userLatitude;
tv.setText(info);
}

/** 基站信息結構體 */
public class SCell {
public int MCC;
public int MNC;
public int LAC;
public int CID;
}

/** 經緯度信息結構體 */
public class SItude {
public String latitude;
public String longitude;
}

/**
* 獲取基站信息
*
* @throws Exception
*/
private SCell getCellInfo() throws Exception {
SCell cell = new SCell();

/** 調用API獲取基站信息 */
TelephonyManager mTelNet = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
GsmCellLocation location = (GsmCellLocation) mTelNet.getCellLocation();
if (location == null)
throw new Exception("獲取基站信息失敗");

String operator = mTelNet.getNetworkOperator();
int mcc = Integer.parseInt(operator.substring(0, 3));
int mnc = Integer.parseInt(operator.substring(3));
int cid = location.getCid();
int lac = location.getLac();

/** 將獲得的數據放到結構體中 */
cell.MCC = mcc;
cell.MNC = mnc;
cell.LAC = lac;
cell.CID = cid;

return cell;
}

/**
* 獲取經緯度
*
* @throws Exception
*/
private SItude getItude(SCell cell) throws Exception {
SItude itude = new SItude();

/** 採用Android默認的HttpClient */
HttpClient client = new DefaultHttpClient();
/** 採用POST方法 */
HttpPost post = new HttpPost("http://www.google.com/loc/json");
try {
/** 構造POST的JSON數據 */
JSONObject holder = new JSONObject();
holder.put("version", "1.1.0");
holder.put("host", "maps.google.com");
holder.put("address_language", "zh_CN");
holder.put("request_address", true);
holder.put("radio_type", "gsm");
holder.put("carrier", "HTC");

JSONObject tower = new JSONObject();
tower.put("mobile_country_code", cell.MCC);
tower.put("mobile_network_code", cell.MNC);
tower.put("cell_id", cell.CID);
tower.put("location_area_code", cell.LAC);

JSONArray towerarray = new JSONArray();
towerarray.put(tower);
holder.put("cell_towers", towerarray);

StringEntity query = new StringEntity(holder.toString());
post.setEntity(query);

/** 發出POST數據並獲取返回數據 */
HttpResponse response = client.execute(post);
HttpEntity entity = response.getEntity();
BufferedReader buffReader = new BufferedReader(
new InputStreamReader(entity.getContent()));
StringBuffer strBuff = new StringBuffer();
String result = null;
while ((result = buffReader.readLine()) != null) {
strBuff.append(result);
}

/** 解析返回的JSON數據獲得經緯度 */
JSONObject json = new JSONObject(strBuff.toString());
JSONObject subjosn = new JSONObject(json.getString("location"));

itude.latitude = subjosn.getString("latitude");
itude.longitude = subjosn.getString("longitude");
Log.v(tag, "剛剛獲取到的經度====" + itude.longitude);
Log.v(tag, "剛剛獲取到的緯度====" + itude.latitude);
} catch (Exception e) {
throw new Exception("獲取經緯度出現錯誤:" + e.getMessage());
} finally {
post.abort();
client = null;
}
Log.v(tag, "方法返回的經度====" + itude.longitude);
Log.v(tag, "方法返回的緯度====" + itude.latitude);
return itude;
}
}


Wifi篇
與手機基站定位方式類似,都需要採集wifi接入點的位置信息。
最早開發這個技術的是Skyhook公司。這個技術的原理是利用下面三條事實:wifi熱點(也就是AP,或者無線路由器)越來越多,在城市中更趨向於空間任何一點都能接收到至少一個AP的信號。(在美國,每個點收到3、5個AP信號的情況相當多見。中國也會越來越多的) 熱點只要通電,不管它怎麼加密的,都一定會向周圍發射信號。信號中包含此熱點的唯一全球ID。即使距離此熱點比較遠,無法建立連接,但還是可以偵聽到它的存在。 熱點一般都是很少變位置的,比較固定。這樣,定位端只要偵聽一下附近都有哪些熱點,檢測一下每個熱點的信號強弱,然後把這些信息發送給Skyhook的服務器。服務器根據這些信息,查詢每個熱點在數據庫裏記錄的座標,進行運算,就能知道客戶端的具體位置了,再把座標告訴客戶端。可以想想,只要收到的AP信號越多,定位就會越準。原理就是這麼簡單。
不過,一次成功的定位還需要兩個先決條件:第二,客戶端能上網。偵聽到的熱點的座標在Skyhook的數據庫裏有第一條不用說了,不管是wifi還是edge,只要能連上Skyhook的服務器就行。第三條是Skyhook的金礦所在。它怎麼知道每個AP的座標信息的呢?有一種說法是靠網友自己蒐集,然後發給Skyhook,Skyhook會付錢。不過官方網站上的說法是開着車滿大街轉悠,邊走邊採集AP信號,並用GPS定位,從而就有了座標信息。
package com.veer;

import java.io.IOException;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.TextView;

public class LocationWifiActivity extends Activity {
/** Called when the activity is first created. */
WifiManager mainWifi;

WifiReceiver receiverWifi;

List<ScanResult> wifiList;
TextView textview;
StringBuilder sb = new StringBuilder();

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

textview = (TextView) findViewById(R.id.textView1);
mainWifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
receiverWifi = new WifiReceiver();
registerReceiver(receiverWifi, new IntentFilter(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
mainWifi.startScan();
}

public boolean onKeyUp(int KeyCode, KeyEvent envent) {
if (KeyCode == KeyEvent.KEYCODE_0)
onDestroy();
else
super.onKeyUp(KeyCode, envent);
return true;
}

public void onDestroy() {
Log.e("wifi", "onDestroy");
super.onDestroy();

}

class WifiReceiver extends BroadcastReceiver {
public void onReceive(Context c, Intent intent) {
wifiList = mainWifi.getScanResults();
for (int i = 0; i < wifiList.size(); i++) {
Log.e("wifi", wifiList.get(i).toString());
}
HttpPost httpRequest = new HttpPost(
"http://www.google.com/loc/json");
JSONObject holder = new JSONObject();
JSONArray array = new JSONArray();
try {
holder.put("version", "1.1.0");
holder.put("host", "maps.google.com");
holder.put("address_language", "zh_CN");
holder.put("request_address", true);
for (int i = 0; i < wifiList.size(); i++) {
JSONObject current_data = new JSONObject();
current_data.put("mac_address", wifiList.get(i).BSSID);
current_data.put("ssid", wifiList.get(i).SSID);
current_data.put("signal_strength", wifiList.get(i).level);
array.put(current_data);
}
holder.put("wifi_towers", array);
Log.e("wifi", holder.toString());
StringEntity se = new StringEntity(holder.toString());
httpRequest.setEntity(se);
HttpResponse resp = new DefaultHttpClient()
.execute(httpRequest);
if (resp.getStatusLine().getStatusCode() == 200) {
/* 取出響應字符串 */
String strResult = EntityUtils.toString(resp.getEntity());
textview.setText(strResult);
}
} catch (JSONException e) {
textview.setText(e.getMessage().toString());
e.printStackTrace();
} catch (ClientProtocolException e) {
textview.setText(e.getMessage().toString());
e.printStackTrace();
} catch (IOException e) {
textview.setText(e.getMessage().toString());
e.printStackTrace();
} catch (Exception e) {
textview.setText(e.getMessage().toString());
e.printStackTrace();
}

}
}
}

另一個封裝類:
package com.veer;

import java.util.List;
import android.content.Context;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;

public class WifiAdmin {

// 定義WifiManager對象
private WifiManager mWifiManager;

// 定義WifiInfo對象
private WifiInfo mWifiInfo;

// 掃描出的網絡連接列表
private List<ScanResult> mWifiList;

// 網絡連接列表
private List<WifiConfiguration> mWifiConfiguration;

// 定義一個WifiLock
WifiLock mWifiLock;

// 構造器
public WifiAdmin(Context context) {

// 取得WifiManager對象
mWifiManager = (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);

// 取得WifiInfo對象
mWifiInfo = mWifiManager.getConnectionInfo();

}

// 打開WIFI
public void OpenWifi() {

if (!mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(true);
}

}

// 關閉WIFI
public void CloseWifi() {

if (!mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(false);
}

}

// 鎖定WifiLock
public void AcquireWifiLock() {
mWifiLock.acquire();
}

// 解鎖WifiLock
public void ReleaseWifiLock() {

// 判斷時候鎖定
if (mWifiLock.isHeld()) {
mWifiLock.acquire();
}

}

// 創建一個WifiLock
public void CreatWifiLock() {
mWifiLock = mWifiManager.createWifiLock("Test");
}

// 得到配置好的網絡
public List<WifiConfiguration> GetConfiguration() {
return mWifiConfiguration;
}

// 指定配置好的網絡進行連接
public void ConnectConfiguration(int index) {

// 索引大於配置好的網絡索引返回
if (index > mWifiConfiguration.size()) {
return;
}

// 連接配置好的指定ID的網絡
mWifiManager.enableNetwork(mWifiConfiguration.get(index).networkId,
true);
}

public void StartScan() {
mWifiManager.startScan();
// 得到掃描結果
mWifiList = mWifiManager.getScanResults();
// 得到配置好的網絡連接
mWifiConfiguration = mWifiManager.getConfiguredNetworks();
}

// 得到網絡列表
public List<ScanResult> GetWifiList() {
return mWifiList;
}

// 查看掃描結果
public StringBuilder LookUpScan() {
StringBuilder stringBuilder = new StringBuilder();

for (int i = 0; i < mWifiList.size(); i++) {
stringBuilder
.append("Index_" + new Integer(i + 1).toString() + ":");

// 將ScanResult信息轉換成一個字符串包
// 其中把包括:BSSID、SSID、capabilities、frequency、level
stringBuilder.append((mWifiList.get(i)).toString());

stringBuilder.append("\n");
}

return stringBuilder;
}

// 得到MAC地址
public String GetMacAddress() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.getMacAddress();
}

// 得到接入點的BSSID
public String GetBSSID() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.getBSSID();
}

// 得到IP地址
public int GetIPAddress() {
return (mWifiInfo == null) ? 0 : mWifiInfo.getIpAddress();
}

// 得到連接的ID
public int GetNetworkId() {
return (mWifiInfo == null) ? 0 : mWifiInfo.getNetworkId();
}

// 得到WifiInfo的所有信息包
public String GetWifiInfo() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.toString();
}

// 添加一個網絡並連接
public void AddNetwork(WifiConfiguration wcg) {
int wcgID = mWifiManager.addNetwork(wcg);
mWifiManager.enableNetwork(wcgID, true);
}

// 斷開指定ID的網絡
public void DisconnectWifi(int netId) {
mWifiManager.disableNetwork(netId);
mWifiManager.disconnect();
}

}

當然這些都只是一些概述,在具體實現的是後還需要注意很多問題,尤其是各種異常的處理,請大家自己注意。
發佈了18 篇原創文章 · 獲贊 12 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章