Android系列之Wifi定位

Broncho A1还不支持基站和WIFI定位,Android的老版本里是有NetworkLocationProvider的,它实现了基站和WIFI定位,但从 android 1.5之后就被移除了。本来想在broncho A1里自己实现NetworkLocationProvider的,但一直没有时间去研究。我知道 gears(http://code.google.com/p/gears/)是有提供类似的功能,昨天研究了一下Gears的代码,看能不能移植到 android中来
1.下载源代码  
[url]svn checkout http://gears.googlecode.com/svn/trunk/ gears-read-only[/url]  
定位相关的源代码在gears/geolocation目录中。
2.关注android平台中的基站位置变化
JAVA类AndroidRadioDataProvider是 PhoneStateListener的子类,用来监听Android电话的状态变化。当服务状态、信号强度和基站变化时,
就会用下面代码获取小区信息:
1 RadioData radioData = new RadioData();          
2             GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;          
3              
4             // Extract the cell id, LAC, and signal strength.          
5             radioData.cellId = gsmCellLocation.getCid();          
6             radioData.locationAreaCode = gsmCellLocation.getLac();          
7             radioData.signalStrength = signalStrength;          
8              
9             // Extract the home MCC and home MNC.        
10             String operator = telephonyManager.getSimOperator();        
11             radioData.setMobileCodes(operator, true);        
12            
13             if (serviceState != null) {        
14                 // Extract the carrier name.        
15                 radioData.carrierName = serviceState.getOperatorAlphaLong();        
16            
17                 // Extract the MCC and MNC.        
18                 operator = serviceState.getOperatorNumeric();        
19                 radioData.setMobileCodes(operator, false);        
20             }        
21            
22             // Finally get the radio type.        
23             int type = telephonyManager.getNetworkType();        
24             if (type == TelephonyManager.NETWORK_TYPE_UMTS) {        
25                 radioData.radioType = RADIO_TYPE_WCDMA;        
26             } else if (type == TelephonyManager.NETWORK_TYPE_GPRS        
27                                    || type == TelephonyManager.NETWORK_TYPE_EDGE) {        
28                 radioData.radioType = RADIO_TYPE_GSM;        
29             }      
30
然后再调用用C代码实现的onUpdateAvailable函数。  
2.Native函数onUpdateAvailable是在 radio_data_provider_android.cc里实现的。  
声明Native函数
1 JNINativeMethod AndroidRadioDataProvider::native_methods_[] = {        
2     { "onUpdateAvailable",        
3         "(L"  GEARS_JAVA_PACKAGE   "/AndroidRadioDataProvider$RadioData;J)V",        
4        reinterpret_cast< void*>(AndroidRadioDataProvider::OnUpdateAvailable)        
5     },        
6 };      
7
 
JNI调用好像只能调用静态成员函数,把对象本身用一个参数传进来,然后再调用对象的成员函数。
 
 
void  AndroidRadioDataProvider::OnUpdateAvailable(JNIEnv* env,                                                                                                        jclass cls,                                                                                                        jobject radio_data,                                                                                                        jlong self) {            
assert(radio_data);            
assert(self);         AndroidRadioDataProvider *self_ptr =                 reinterpret_cast<AndroidRadioDataProvider*>(self);            
RadioData new_radio_data;            
if  (InitFromJavaRadioData(env, radio_data, &new_radio_data)) {             self_ptr->NewRadioDataAvailable(&new_radio_data);            
}        
}    
 
先判断基站信息有没有变化,如果有变化则通知相关的监听者。
 
1   void  AndroidRadioDataProvider::NewRadioDataAvailable(          
2         RadioData* new_radio_data) {          
3       bool  is_update_available =   false;          
4     data_mutex_.Lock();          
5       if  (new_radio_data && !radio_data_.Matches(*new_radio_data)) {          
6         radio_data_ = *new_radio_data;          
7         is_update_available =   true;          
8     }          
9       // Avoid holding the mutex locked while notifying observers.        
10     data_mutex_.Unlock();        
11            
12       if  (is_update_available) {        
13         NotifyListeners();        
14     }        
15 }    
 
接下来的过程,在基站定位和WIFI定位是一样的,后面我们再来介绍。下面我们先看 WIFI定位
3.关注android平台中的WIFI变化。  

JAVA类AndroidWifiDataProvider扩展了 BroadcastReceiver类,它关注WIFI扫描结果:
1 IntentFilter filter =   new  IntentFilter();        
2         filter.addAction(mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION);        
3         mContext.registerReceiver( this, filter,   null, handler);    
当收到WIFI扫描结果后,调用Native函数 onUpdateAvailable,并把WIFI的扫描结果传递过去。
1   public   void  onReceive(Context context, Intent intent) {        
2         if  (intent.getAction().equals(        
3                        mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {        
4             if  (Config.LOGV) {        
5                Log.v(TAG,   "Wifi scan resulst available");        
6            }        
7            onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);        
8        }        
9    }    
Native函数onUpdateAvailable是在 wifi_data_provider_android.cc里实现的。
1 JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {    
2 { "onUpdateAvailable",    
3   "(Ljava/util/List;J)V",    
4 reinterpret_cast< void*>(AndroidWifiDataProvider::OnUpdateAvailable)    
5 },    
6 };    
7    
8   void  AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv*   /* env */,    
9 jclass   /* cls */,    
10 jobject wifi_data,    
11 jlong self) {    
12 assert(self);    
13 AndroidWifiDataProvider *self_ptr =    
14 reinterpret_cast<AndroidWifiDataProvider*>(self);    
15 WifiData new_wifi_data;    
16   if  (wifi_data) {    
17 InitFromJava(wifi_data, &new_wifi_data);    
18 }    
19   // We notify regardless of whether new_wifi_data is empty    
20   // or not. The arbitrator will decide what to do with an empty    
21   // WifiData object.    
22 self_ptr->NewWifiDataAvailable(&new_wifi_data);    
23 }    
24    
25   void  AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {    
26 assert(supported_);    
27 assert(new_wifi_data);    
28   bool  is_update_available =   false;    
29 data_mutex_.Lock();    
30 is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);    
31 wifi_data_ = *new_wifi_data;    
32   // Avoid holding the mutex locked while notifying observers.    
33 data_mutex_.Unlock();    
34    
35   if  (is_update_available) {    
36 is_first_scan_complete_ =   true;    
37 NotifyListeners();    
38 }    
39    
40 # if  USING_CCTESTS    
41   // This is needed for running the WiFi test on the emulator.    
42   // See wifi_data_provider_android.h for details.    
43   if  (!first_callback_made_ && wifi_data_.access_point_data.empty()) {    
44 first_callback_made_ =   true;    
45 NotifyListeners();    
46 }    
47 #endif    
48 }    
49    
50 JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {  
51 { "onUpdateAvailable",  
52   "(Ljava/util/List;J)V",  
53 reinterpret_cast< void*>(AndroidWifiDataProvider::OnUpdateAvailable)  
54 },  
55 };  
56    
57   void  AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv*   /* env */,  
58 jclass   /* cls */,  
59 jobject wifi_data,  
60 jlong self) {  
61 assert(self);  
62 AndroidWifiDataProvider *self_ptr =  
63 reinterpret_cast<AndroidWifiDataProvider*>(self);  
64 WifiData new_wifi_data;  
65   if  (wifi_data) {  
66 InitFromJava(wifi_data, &new_wifi_data);  
67 }  
68   // We notify regardless of whether new_wifi_data is empty 
69   // or not. The arbitrator will decide what to do with an empty 
70   // WifiData object. 
71 self_ptr->NewWifiDataAvailable(&new_wifi_data);  
72 }  
73    
74   void  AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {  
75 assert(supported_);  
76 assert(new_wifi_data);  
77   bool  is_update_available =   false;  
78 data_mutex_.Lock();  
79 is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);  
80 wifi_data_ = *new_wifi_data;  
81   // Avoid holding the mutex locked while notifying observers. 
82 data_mutex_.Unlock();  
83    
84   if  (is_update_available) {  
85 is_first_scan_complete_ =   true;  
86 NotifyListeners();  
87 }  
88    
89 # if  USING_CCTESTS  
90   // This is needed for running the WiFi test on the emulator. 
91   // See wifi_data_provider_android.h for details. 
92   if  (!first_callback_made_ && wifi_data_.access_point_data.empty()) {  
93 first_callback_made_ =   true;  
94 NotifyListeners();  
95 }  
96 #endif  
97 }  
98
从以上代码可以看出,WIFI定位和基站定位的逻辑差不多,只是前者获取的WIFI的扫描结果,而后者获取的基站信息。
后面代码的基本上就统一起来了,接下来我们继续看。  

5.把变化(WIFI/基站)通知给相应的监听者。
1 AndroidWifiDataProvider和AndroidRadioDataProvider都是继承了DeviceDataProviderImplBase,DeviceDataProviderImplBase的主要功能就是管理所有Listeners。          
2              
3       static  DeviceDataProvider *Register(ListenerInterface *listener) {          
4         MutexLock mutex(&instance_mutex_);          
5           if  (!instance_) {          
6             instance_ =   new  DeviceDataProvider();          
7         }          
8         assert(instance_);          
9         instance_->Ref();        
10         instance_->AddListener(listener);        
11           return  instance_;        
12     }        
13            
14       static   bool  Unregister(ListenerInterface *listener) {        
15         MutexLock mutex(&instance_mutex_);        
16           if  (!instance_->RemoveListener(listener)) {        
17               return   false;        
18         }        
19           if  (instance_->Unref()) {        
20             delete instance_;        
21             instance_ = NULL;        
22         }        
23           return   true;        
24     }      
25
6.谁在监听变化(WIFI/基站)  

NetworkLocationProvider在监听变化(WIFI/基站): 
1 radio_data_provider_ = RadioDataProvider::Register( this);        
2     wifi_data_provider_ = WifiDataProvider::Register( this);    
 
当有变化时,会调用函数DeviceDataUpdateAvailable:
 
 

 无论是WIFI还是基站变化,最后都会调用 DeviceDataUpdateAvailableImpl:
1   void  NetworkLocationProvider::DeviceDataUpdateAvailableImpl() {  
2     timestamp_ = GetCurrentTimeMillis();  
3      
4       // Signal to the worker thread that new data is available. 
5     is_new_data_available_ =   true;  
6     thread_notification_event_.Signal();  
7 }
 
 
这里面只是发了一个signal,通知另外一个线程去处理。  

7.谁在等待thread_notification_event_  

线程函数NetworkLocationProvider::Run在一个循环中等待 thread_notification_event,当有变化(WIFI/基站)时,就准备请求服务器查询位置。  

先等待:
1   if  (remaining_time > 0) {  
2             thread_notification_event_.WaitWithTimeout(  
3                     static_cast< int>(remaining_time));  
4         }   else  {  
5             thread_notification_event_.Wait();  
6         }
准备请求:
1   if  (make_request) {        
2     MakeRequest();        
3     remaining_time = 1;        
4 }    
 
再来看MakeRequest的实现:  

先从cache中查找位置:
1     const  Position *cached_position =    
2             position_cache_->FindPosition(radio_data_, wifi_data_);    
3     data_mutex_.Unlock();    
4       if  (cached_position) {    
5         assert(cached_position->IsGoodFix());    
6           // Record the position and update its timestamp.    
7         position_mutex_.Lock();    
8         position_ = *cached_position;    
9         position_.timestamp = timestamp_;  
10         position_mutex_.Unlock();  
11      
12           // Let listeners know that we now have a position available. 
13         UpdateListeners();  
14           return   true;  
15     }
 
如果找不到,再做实际的请求
1       return  request_->MakeRequest(access_token,  
2                                                                radio_data_,  
3                                                                wifi_data_,  
4                                                                request_address_,  
5                                                                address_language_,  
6                                                                kBadLatLng,     // We don't have a position to pass 
7                                                                kBadLatLng,     // to the server. 
8                                                                timestamp_);
 
 
7.客户端协议包装  

前面的request_是NetworkLocationRequest实例,先看 MakeRequest的实现:  

先对参数进行打包:
1       if  (!FormRequestBody(host_name_, access_token, radio_data, wifi_data,  
2                                                request_address, address_language, latitude, longitude,  
3                                                is_reverse_geocode_, &post_body_)) {  
4           return   false;  
5     }
通知负责收发的线程 
1 thread_event_.Signal();
8.负责收发的线程
1   void  NetworkLocationRequest::Run() {    
2       while  ( true) {    
3         thread_event_.Wait();    
4           if  (is_shutting_down_) {    
5               break;    
6         }    
7         MakeRequestImpl();    
8     }    
9 }  
10      
11   void  NetworkLocationRequest::MakeRequestImpl() {  
12     WebCacheDB::PayloadInfo payload;
 

把打包好的数据通过HTTP请求,发送给服务器
 
1    scoped_refptr<BlobInterface> payload_data;    
2       bool  result = HttpPost(url_.c_str(),    
3                                                     false,                         // Not capturing, so follow redirects    
4                                                    NULL,                           // reason_header_value    
5                                                    HttpConstants::kMimeApplicationJson,     // Content-Type    
6                                                    NULL,                           // mod_since_date    
7                                                    NULL,                           // required_cookie    
8                                                     true,                           // disable_browser_cookies    
9                                                    post_body_.get(),  
10                                                    &payload,  
11                                                    &payload_data,  
12                                                    NULL,                           // was_redirected 
13                                                    NULL,                           // full_redirect_url 
14                                                    NULL);                         // error_message 
15      
16     MutexLock   lock(&is_processing_response_mutex_);  
17       // is_aborted_ may be true even if HttpPost succeeded. 
18       if  (is_aborted_) {  
19         LOG(( "NetworkLocationRequest::Run() : HttpPost request was cancelled./n"));  
20           return;  
21     }  
22       if  (listener_) {  
23         Position position;  
24         std:: string  response_body;  
25           if  (result) {  
26               // If HttpPost succeeded, payload_data is guaranteed to be non-NULL. 
27             assert(payload_data.get());  
28               if  (!payload_data->Length() ||  
29                     !BlobToString(payload_data.get(), &response_body)) {  
30                 LOG(( "NetworkLocationRequest::Run() : Failed to get response body./n"));  
31             }  
32         }
 
解析出位置信息
1 std::string16 access_token;  
2         GetLocationFromResponse(result, payload.status_code, response_body,  
3                                                         timestamp_, url_, is_reverse_geocode_,  
4                                                         &position, &access_token);
通知位置信息的监听者
1     bool  server_error =  
2                 !result || (payload.status_code >= 500 && payload.status_code < 600);  
3         listener_->LocationResponseAvailable(position, server_error, access_token);  
4     }  
5 }
有人会问,请求是发哪个服务器的?当然是google了,缺省的URL是:
1   static   const  char16 *kDefaultLocationProviderUrl =  
2         STRING16(L "https://www.google.com/loc/json");
回过头来,我们再总结一下:
1.WIFI和基站定位过程如下:
2.NetworkLocationProvider和 NetworkLocationRequest各有一个线程来异步处理请求。  

3.这里的NetworkLocationProvider与android中的 NetworkLocationProvider并不是同一个东西,这里是给gears用的,要在android的google map中使用,还得包装成android中的NetworkLocationProvider的接口。  

4.WIFI和基站定位与平台无关,只要你能拿到WIFI扫描结果或基站信息,而且能访问google的定位服务器,不管你是Android平台,Windows Mobile平台还是传统的feature phone,你都可以实现WIFI和基站定位。
附: WIFI和基站定位原理  

无论是WIFI的接入点,还是移动网络的基站设备,它们的位置基本上都是固定的。设备端(如手机)可以找到它们的ID,现在的问题就是如何通过这些ID找到对应的位置。网上的流行的说法是开车把所有每个位置都跑一遍,把这些设备的位置与 GPS测试的位置关联起来。  

本文出自 “失落的程序” 博客,请务必保留此出处http://350526.blog.51cto.com/340526/367139

随着人们对基于位置的服务(Location Based Service,LBS)需求日益增大,以及无线通信技术的快速发展,无线定位技术成为了一个研究热点。人们在室外广泛使用目前较成熟的GPS,A-GPS等定位系统进行定位,但是在复杂的室内环境中,这些技术的定位精度不高,不能满足室内定位的需求。WIFI网络具有通信快速、部署方便的特点,它在室内场所广受欢迎.Android系统从几年前发布以来在智能手机操作系统市场占有率不断升高,成为目前使用最为广泛的智能手机操作系统,同时Android移动终端自身具备WIFI无线连接功能。指纹定位算法以其独特的优势减小了对室内难以精确定义的信号传播模型的依赖性,成为定位技术中的一个研究热点。基于此,本课题重点研究并改进指纹定位算法,设计实现基于AndroidWIFI室内定位系统。 首先,通过阅读大量相关的文献资料,对比分析了当前国内外WIFI室内指纹定位技术的研究现状对其中涉及到的相关技术的原理和特点进行介绍分析,包括WIF1无线通信技术,室内无线定位技术以及位置指纹定位技术,并根据室内WIFI指纹定位技术的特征对定位过程中的影响因素进行分析。 其次,根据前面提到的定位过程中的关键影响因素,介绍了对应的解决方案。分析与研究了几种典型的指纹定位算法,包括最近邻法(NN).K近邻法(KNN)、K加权近邻法(WKNN),并提出算法的改进方案,使用MATLAB软件进行算法的仿真分析,寻求其中的最佳参数值以及定位性能差异。通过分析几种算法的性能仿真结果,拟定了基于最强AP法的改进算法作为定位系统采纳的算法。 然后,通过对基于AndroidWIFI室内定位系统的需求分析,提出了一种基于Android 的WIF1室内定位系统设计方案。接着介绍了定位系统软件开发环境,并设计了定位系统总体架构,以及定位系统的各个功能模块。在各项设计确定以后,采用JAVA语言编程实现定位系统的各项功能。 最后,搭建了WIFI室内定位实验环境,使用完成的室内定位系统结合硬件资源,在实验环境下,进行离线阶段创建数据库以及在线阶段的定位测试,并记录呈现在定位客户端上定位结果,分析对应的定位性能.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值