Mad*_*Dim 6 gps android locationmanager fusedlocationproviderapi android-fusedlocation
我正在开发一个项目,我们正在尝试跟踪设备的位置并保留数据供以后使用.在我谈论这个问题之前,我想提供一些背景知识.
通过搜索StackExchange和Google以及其他任何地方,我得出的结论是,几乎不可能使用Fused Location API获取有关卫星的信息(Google的工作很好).
大多数人使用的方法是在Fused位置旁边实际使用LocationManager来获取GPS状态.我的第一个问题是:我们怎么能100%确定LocationManager提供的数字与Fused Location给我们的数据同步?Fused Location是否在内部使用Manager?
而现在问题.该应用程序正在使用"永远在线"粘性服务来获取位置,无论如何.当没有卫星时,一切都按预期工作.将设备放置在可以看到卫星的位置,它似乎没有锁定.使用调试器,GpsStatus.getSatellites()带来一个空列表.现在,在没有移动设备的情况下,我启动了具有GPS型罗盘方案的应用程序Compass(由Catch.com提供,因为有很多).那个锁定卫星的速度非常快,从那时起我的应用程序也会报告卫星.如果指南针关闭,那么应用程序会卡在指南针提供的最后一个数字上!我个人用于测试的设备是带有最新官方更新的Nexus 7 2013(Android 6.0.1).
这是一些代码:
public class BackgroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
GpsStatus.Listener,
LocationListener {
// Constants here....
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private LocationManager locationManager;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private NotificationManagement myNotificationManager;
private Boolean servicesAvailable = false;
//And other variables here...
@Override
public void onCreate()
{
super.onCreate();
myNotificationManager = new NotificationManagement(getApplicationContext());
myNotificationManager.displayMainNotification();
mInProgress = false;
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval
mLocationRequest.setInterval(PREFERRED_INTERVAL);
// Set the fastest update interval
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
servicesAvailable = servicesConnected();
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.addGpsStatusListener(this);
setUpLocationClientIfNeeded();
}
/**
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
protected synchronized void buildGoogleApiClient()
{
this.mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private boolean servicesConnected()
{
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode)
{
return true;
}
else
{
return false;
}
}
public int onStartCommand(Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress)
{
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
private void setUpLocationClientIfNeeded()
{
if (mGoogleApiClient == null)
buildGoogleApiClient();
}
public void onGpsStatusChanged(int event)
{
}
// Define the callback method that receives location updates
@Override
public void onLocationChanged(Location location)
{
simpleGPSFilter(location);
}
// Other fancy and needed stuff here...
/**
* "Stupid" filter that utilizes experience data to filter out location noise.
* @param location Location object carrying all the needed information
*/
private void simpleGPSFilter(Location location)
{
//Loading all the required variables
int signalPower = 0;
satellites = 0;
// Getting the satellites
mGpsStatus = locationManager.getGpsStatus(mGpsStatus);
Iterable<GpsSatellite> sats = mGpsStatus.getSatellites();
if (sats != null)
{
for (GpsSatellite sat : sats)
{
if (sat.usedInFix())
{
satellites++;
signalPower += sat.getSnr();
}
}
}
if (satellites != 0)
signalPower = signalPower/satellites;
mySpeed = (location.getSpeed() * 3600) / 1000;
myAccuracy = location.getAccuracy();
myBearing = location.getBearing();
latitude = location.getLatitude();
longitude = location.getLongitude();
Log.i("START OF CYCLE", "START OF CYCLE");
Log.i("Sat Strength", Integer.toString(signalPower));
Log.i("Locked Sats", Integer.toString(satellites));
// Do the math for the coordinates distance
/*
* Earth's radius at given Latitude.
* Formula: Radius = sqrt( ((equatorR^2 * cos(latitude))^2 + (poleR^2 * sin(latitude))^2 ) / ((equatorR * cos(latitude))^2 + (poleR * sin(latitude))^2)
* IMPORTANT: Math lib uses radians for the trigonometry equations so do not forget to use toRadians()
*/
Log.i("Lat for Radius", Double.toString(latitude));
double earthRadius = Math.sqrt((Math.pow((EARTH_RADIUS_EQUATOR * EARTH_RADIUS_EQUATOR * Math.cos(Math.toRadians(latitude))), 2)
+ Math.pow((EARTH_RADIUS_POLES * EARTH_RADIUS_POLES * Math.cos(Math.toRadians(latitude))), 2))
/ (Math.pow((EARTH_RADIUS_EQUATOR * Math.cos(Math.toRadians(latitude))), 2)
+ Math.pow((EARTH_RADIUS_POLES * Math.cos(Math.toRadians(latitude))), 2)));
Log.i("Earth Radius", Double.toString(earthRadius));
/*
* Calculating distance between 2 points on map using the Haversine formula (arctangent writing) with the following algorithm
* latDifference = latitude - lastLatitude;
* lngDifference = longitude - lastLongitude;
* a = (sin(latDifference/2))^2 + cos(lastLatitude) * cos(latitude) * (sin(lngDifference/2))^2
* c = 2 * atan2( sqrt(a), sqrt(1-a) )
* distance = earthRadius * c
*/
double latDifference = latitude - lastLatitude;
double lngDifference = longitude - lastLongitude;
double a = Math.pow((Math.sin(Math.toRadians(latDifference / 2))), 2) + (Math.cos(Math.toRadians(lastLatitude))
* Math.cos(Math.toRadians(latitude))
* Math.pow((Math.sin(Math.toRadians(lngDifference / 2))), 2));
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = earthRadius * c;
Log.i("New point distance", Double.toString(distance));
// Filter logic
// Make an initial location log
if ((!isInit) && (myAccuracy < ACCEPTED_ACCURACY))
{
isInit = true;
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
}
else
{
// Satellite lock (use of GPS) on the higher level
if (satellites == 0)
{
// Accuracy filtering at the second level
if (myAccuracy < ACCEPTED_ACCURACY)
{
if ((distance > ACCEPTED_DISTANCE))
{
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
Log.i("Location Logged", "No Sats");
/*
// Calculate speed in correlation to perceived movement
double speed = distance / (PREFERRED_INTERVAL / 1000); // TODO: Need to make actual time dynamic as the fused location does not have fixed timing
if (speed < ACCEPTED_SPEED)
{
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
} */
}
}
}
else if ((satellites < 4) && (signalPower > ACCEPTED_SIGNAL))
{
if (myAccuracy < (ACCEPTED_ACCURACY + 50))
{
logLocations(location);
Log.i("Location Logged", "With Sats");
}
}
else
{
if (myAccuracy < (ACCEPTED_ACCURACY + 100))
{
lastSpeed = mySpeed;
lastBearing = myBearing;
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
Log.i("Location Logged", "With Good Sats");
}
}
}
Log.i("END OF CYCLE", "END OF CYCLE");
}
private void logLocations(Location location)
{
String myprovider = "false";
String temp = timestampFormat.format(location.getTime());
MySQLiteHelper dbHelper = new MySQLiteHelper(getApplicationContext());
try
{
dbHelper.createEntry(latitude, longitude, allschemes, temp, mySpeed, myAccuracy, myBearing, myprovider, satellites);
}
catch (Exception e)
{
e.printStackTrace();
}
CheckAutoArrive(String.valueOf(latitude), String.valueOf(longitude));
}
Run Code Online (Sandbox Code Playgroud)
这是我认为可能需要的代码的一部分.我将所有过滤代码与数学一起留下来计算地球半径,给定纬度和地图上2点之间的距离.如果您需要,请随意使用.
与此相关的事实是,指南针应用程序实际上可以使系统获得卫星,而我的应用程序则无法.有没有办法真正强制读取位置服务?Fused Location是否可能实际使用GPS但位置管理器不知道它?
最后我想提一下,该应用程序已在其他设备(手机,而不是平板电脑)中使用不同版本的Android进行了测试,似乎运行正常.
任何想法都会受到欢迎.当然,继续问我可能忘记提及的任何事情.
编辑:我的实际问题隐藏在文本中,以便将它们列出来:
1)我们从Fused Location获得的位置数据和我们可以看到的其余GPS数据是否只能从位置管理器同步获得,或者是否有可能获得位置但是特定点的锁定卫星数量错误?
2)应用程序无法锁定卫星的奇怪行为背后的原因是什么,但如果锁来自另一个应用程序,那么应用程序似乎正确使用了它?为了使这更加奇怪,这种情况发生在Nexus 7(Android 6.0.1)上,而不是其他使用不同Android版本测试的设备.
Kla*_*und 14
根据我的理解:
每当客户端设备上的任何相关提供商(WiFi,CellTower,GPS,蓝牙)有新的读数时,FusedLocationApi都会返回一个新位置.该读数与先前的位置估计(可能使用扩展卡尔曼滤波器或类似物)融合.由此产生的位置更新是多个来源的融合估计,这就是为什么没有来自各个提供商的元数据.
因此,您从API获得的位置数据可能与从LocationManager获得的纯GPS读数一致(如果GPS是最新且相关的位置源),但它不必.因此,从上一次纯GPS读取获得的卫星数量可能适用于FusedLocationApi返回的最新位置,也可能不适用.
简而言之:
无法保证从LocationManager获取的位置读数与FusedLocationApi中的位置同步.
首先:要确定此问题的根本原因,您需要在多个位置测试多个设备.既然你问过
什么可能是背后的怪异的行为的原因是什么?
我将抛出一个理论:假设LocationManager和FusedLocationApi完全分开工作,LocationManager可能很难获得修复,因为你只依赖于GPS.除了GPS之外,尝试使用NETWORK_PROVIDER来加速首次修复的时间(从而使LocationManager能够使用辅助GPS).其他应用程序(如指南针应用程序)几乎肯定会这样做,这可以解释为什么他们得到更快的修复. 注意:开始接收GPS数据后,您当然可以取消注册网络提供商.或者你继续保持它,但只是忽略它的更新.
这是奇怪行为的一种可能解释.您可能知道位置行为取决于您所在的设备,操作系统,GPS芯片组,固件和位置 - 因此,如果您打算手动(即不使用FusedLocationApi),您将需要进行相当多的实验.
除了答案之外,让我对你的问题提出一个自以为是的看法(采取一些盐;-):我认为你正在尝试结合两个针对非常不同的用例而不是用过的东西意味着结合起来.
获得卫星数量是完全技术性的.没有最终用户会对这种信息感兴趣,除非您的应用程序教他们关于GNSS.如果您想将其记录为内部分析目的,那么您必须能够处理此信息不可用的情况.
场景1:出于某种原因,您决定绝对需要GPS读数的详细(技术)细节.在这种情况下,自己建立逻辑,旧学校的方式.即通过LocationManager请求GPS读数(并可能通过使用网络提供商加速此过程),然后自己融合这些东西.但是,在这种情况下,请勿触摸FusedLocationApi.即使它现在看起来过时而且神秘,但使用带有DIY融合逻辑的LocationManager仍然对少数用例非常有意义.这就是API仍然存在的原因.
场景2:您只是希望快速准确地更新客户端的位置.在这种情况下,请指定所需的更新频率和准确度,并让FusedLocationApi完成其工作.FusedLocationApi在过去几年已经走过了漫长的道路,现在很有可能在确定如何获取位置信息方面比任何DIY逻辑更快更好.这是因为获取位置信息是一个非常异质的问题,取决于客户端设备(芯片组,固件,操作系统,GPS,WiFi,GSM/LTE,蓝牙等)的功能,与物理环境(WiFi/CellTower)相同/蓝牙信号在附近,内部或外部,晴空或城市峡谷等.在这种情况下,请勿触摸手动提供程序.如果这样做,不要期望对各个提供者的读数与融合结果之间的关系做出任何有意义的推断.
两个最后的评论:
Android提供Location.distanceTo()和Location.distanceBetween(),因此无需在代码中实现Haversine公式.
如果您只需要从FusedLocationApi快速可靠地更新,我已经编写了一个名为LocationAssistant的小型实用程序类,它简化了设置并为您完成了大部分繁重工作.
| 归档时间: |
|
| 查看次数: |
4516 次 |
| 最近记录: |