手機定位技術概述

目前主流定位技術分爲3種:GPS定位、基站定位和Wifi定位。

GPS

GPS是英文Global Positioning System全球定位系統)的簡稱,而其中文簡稱爲“球位系”。GPS20世紀70年代由美國陸海空三軍聯合研製的新一代空間衛星導航定位系統 。其主要目的是爲陸、海、空三大領域提供實時、 全天候和全球性的導航服務,並用於情報收集、核爆監測和應急通訊等一些軍事目的,經過20餘年的研究實驗,耗資300億美元,到19943月,全球覆蓋率高達98%24GPS衛星星座己佈設完成。GPS功能必須具備GPS終端、傳輸網絡和監控平臺三個要素;這三個要素缺一不可;通過這三個要素,可以提供車輛防盜、反劫、行駛路線監控及呼叫指揮等功能。最初的GPS計劃在聯合計劃局的領導下誕生了,該方案將24顆衛星放置在互成120度的三個軌道上。每個軌道上有8顆衛星,地球上任何一點均能觀測到69顆衛星。這樣,粗碼精度可達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網)獲取移動終端用戶的位置信息(經緯度座標),在電子地圖平臺的支持下,爲用戶提供相應服務的一種增值業務,例如目前中國移動動感地帶提供的動感位置查詢服務等。其大致原理爲:移動電話測量不同基站的下行導頻信號,得到不同基站下行導頻的TOATime 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 {

            /** 構造POSTJSON數據 */

            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的信號。(在美國,每個點收到35AP信號的情況相當多見。中國也會越來越多的) 熱點只要通電,不管它怎麼加密的,都一定會向周圍發射信號。信號中包含此熱點的唯一全球ID。即使距離此熱點比較遠,無法建立連接,但還是可以偵聽到它的存在。 熱點一般都是很少變位置的,比較固定。這樣,定位端只要偵聽一下附近都有哪些熱點,檢測一下每個熱點的信號強弱,然後把這些信息發送給Skyhook的服務器。服務器根據這些信息,查詢每個熱點在數據庫裏記錄的座標,進行運算,就能知道客戶端的具體位置了,再把座標告訴客戶端。可以想想,只要收到的AP信號越多,定位就會越準。原理就是這麼簡單。

不過,一次成功的定位還需要兩個先決條件:第二,客戶端能上網。偵聽到的熱點的座標在Skyhook的數據庫裏有第一條不用說了,不管是wifi還是edge,只要能連上Skyhook的服務器就行。第三條是Skyhook的金礦所在。它怎麼知道每個AP的座標信息的呢?有一種說法是靠網友自己蒐集,然後發給SkyhookSkyhook會付錢。不過官方網站上的說法是開着車滿大街轉悠,邊走邊採集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信息轉換成一個字符串包

            // 其中把包括:BSSIDSSIDcapabilitiesfrequencylevel

            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();

    }

}

當然這些都只是一些概述,在具體實現的是後還需要注意很多問題,尤其是各種異常的處理,請大家自己注意。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章