现在的位置: 首页 > 综合 > 正文

android基础知识34:android手机定位技术概述

2012年02月22日 ⁄ 综合 ⁄ 共 14288字 ⁄ 字号 评论关闭

目前主流定位技术分为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 {
            /** 构造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的信号。(在美国,每个点收到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信息转换成一个字符串包
            // 其中把包括: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();
    }
}

参考资料:

手机定位技术概述

抱歉!评论已关闭.