デバイスの位置を追跡し、後で使用するためにデータを保持しようとしているプロジェクトに取り組んでいます。この問題について話す前に、背景を説明したいと思います。
StackExchange や Google などあらゆる場所を検索した結果、Fused Location API を使用して衛星に関する情報を取得することは事実上不可能であるという結論に達しました (Google ではよくできました)。
ほとんどの人が使用している方法は、GPS ステータスを取得するために Fused ロケーションと一緒に LocationManager を実際に使用することです。私の最初の質問は次のとおりです。LocationManager によって提供された数値が、Fused Location によって提供された数値と同期していることを 100% 確実にするにはどうすればよいでしょうか? Fused Location は Manager を内部的に使用しますか?
そして今問題。アプリは「常時オン」のスティッキー サービスを使用して、何があっても位置を取得します。衛星がない場合、すべてが意図したとおりに機能します。デバイスを衛星が見える位置に置くと、ロックされていないようです。デバッガーを使用すると、GpsStatus.getSatellites() によって空のリストが表示されます。ここで、デバイスを移動せずに、GPS タイプのコンパス スキームを備えたアプリ Compass (Catch.com が多数あるため) を起動します。それは衛星を非常に速くロックし、その瞬間から私のアプリでも衛星を報告します. コンパスが閉じている場合、アプリはコンパスが提供していた最後の数字で動かなくなります!!! 私が個人的にテストに使用しているデバイスは、最新の公式アップデート (Android 6.0.1) を適用した Nexus 7 2013 です。
ここにいくつかのコードがあります:
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));
}
これは、必要と思われるコードの一部です。地図上の 2 点間の緯度と距離から地球の半径を計算するための数学とともに、すべてのフィルタリング コードをそこに残します。必要な方はご自由にお使いください。
コンパスアプリは実際にシステムに衛星を取得させることができますが、私のアプリはできません. 実際に位置情報サービスを強制的に読み取る方法はありますか? Fused Location が実際に GPS を使用しているのに、Location Manager がそれを認識していない可能性はありますか?
最後に、このアプリケーションは異なるバージョンの Android を搭載した他のデバイス (タブレットではなくスマートフォン) でテストされており、正常に動作しているようです。
どんなアイデアでも大歓迎です。そしてもちろん、言い忘れたことは何でも聞いてください。
編集:私の実際の質問はテキストに隠されているので、それらをレイアウトします:
1) Fused Location から取得した位置データと、同期している Location Manager からのみ取得できる残りの GPS データは同期しているのか、それとも、位置を取得できる可能性があるが、特定のポイントのロックされた衛星の数が間違っているのか? ?
2) アプリケーションがサテライトへのロックを取得できないが、ロックが別のアプリケーションからのものである場合、アプリケーションによって適切に使用されているように見えるという奇妙な動作の背後にある理由は何ですか? これをさらに奇妙にするために、これは Nexus 7 (Android 6.0.1) で発生しますが、異なる Android バージョンでテストされた他のデバイスでは発生しません。