当前位置: 首页 > 知识库问答 >
问题:

Android-融合了卫星信息(计数、信号等)的位置提供商API问题

葛子昂
2023-03-14

我正在做一个项目,我们试图跟踪设备的位置并保留数据以供以后使用。在我谈论这个问题之前,我想提供一些背景知识。

通过在StackExchange和Google以及其他任何地方进行搜索,我得出了这样的结论,即使用融合位置API几乎不可能获得有关卫星的信息(Google做得好)。

大多数人使用的方法是在融合的位置旁边使用LocationManager来获取GPS状态。我的第一个问题是:我们如何才能百分之百地确保LocationManager提供的数字与融合后的位置所提供的数据同步?熔合位置是否在内部使用管理器?

现在是问题。该应用程序使用“始终在线”的粘性服务来获取职位。当没有卫星时,一切都按预期工作。将设备放置在可以看到卫星的位置,似乎没有锁。使用调试器GpsStatus。getSatellites()带来一个空列表。现在,在不移动设备的情况下,我启动了具有GPS类型指南针方案的应用程序Compass(by 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));

}

这是我认为可能需要的代码部分。我把所有的过滤代码和数学一起放在那里,根据地图上的纬度和两点之间的距离来计算地球的半径。如果你需要的话,可以随意使用。

事实上,Compass应用程序实际上可以让系统获得卫星,而我的应用程序却不能。有没有办法强制读取定位服务?融合后的位置是否可能实际使用GPS,但位置管理器不知道?

最后,我想提及的是,该应用程序已经在使用不同版本Android的其他设备(手机,而不是平板电脑)上进行了测试,似乎工作正常。

任何想法都非常受欢迎。当然,去问任何我可能忘记提到的问题。

编辑:我的实际问题隐藏在文本中,以便将它们列出:

1)我们从融合位置获得的位置数据和我们似乎只能从位置管理器获得的其余GPS数据是否同步,或者是否有可能获得一个位置,但特定点的锁定卫星数量错误?

2)应用程序无法锁定卫星但如果锁定来自另一个应用程序,应用程序似乎正确使用了它的奇怪行为背后的原因是什么?更奇怪的是,这种情况发生在Nexus 7(Android 6.0.1)上,但不会发生在使用不同Android版本测试的其他设备上。

共有1个答案

田冥夜
2023-03-14

据我了解:

每当客户端设备(WiFi、CellTower、GPS、蓝牙)上有来自任何相关提供商的新读数时,FusedLocationApi都会返回一个新位置。此读数与之前的位置估计进行融合(可能使用扩展卡尔曼滤波器或类似方法)。由此产生的位置更新是对多个来源的融合估计,这就是为什么没有附加来自各个提供商的元数据。

因此,您从API获得的位置数据可能与从LocationManager获得的纯GPS读数一致(如果GPS是最新和相关的位置源),但不必如此。因此,从最后一次纯GPS读数获得的卫星数量可能适用于FusedLocationApi返回的最新位置,也可能不适用。

简而言之:

无法保证从LocationManager获得的位置读取与来自FusedLocationApi的位置同步。

首先:要查明此问题的根本原因,您需要在多个位置使用多个设备进行测试。既然你问了

这种奇怪行为背后的原因是什么?

我将抛出一个理论:假设LocationManager和FusedLocationApi完全分开工作,LocationManager可能很难获得修复,因为您只依赖GPS。尝试在GPS之外使用NETWORK\u PROVIDER来加快首次修复的时间(从而使LocationManager能够使用辅助GPS)。其他应用程序(如Compass应用程序)几乎肯定也在这样做,这就解释了为什么它们得到更快的修复。注意:开始接收GPS数据后,您当然可以注销网络提供商。或者你保持它,但只是忽略它的更新。

这是奇怪行为的一种可能解释。您可能知道位置行为取决于您所在的设备、操作系统、GPS芯片组、固件和位置——因此,如果您计划使用手动(即不使用FusedLocationApi),您将不得不进行大量实验。

除了答案之外,让我对你的问题提出一个固执己见的看法(对此持半信半疑的态度;-):我认为你试图结合两个为非常不同的用例而制作的东西,而不是为了结合起来。

获取卫星数量完全是技术性的。没有最终用户会对此类信息感兴趣,除非您的应用程序教给他们有关GNSS的知识。如果您想出于内部分析的目的记录它,这很好,但是您必须能够处理这些信息不可用的情况。

场景1:出于某种原因,您决定绝对需要GPS读数的详细(技术)细节。在这种情况下,你可以自己构建逻辑,按照老派的方式。也就是说,通过LocationManager请求GPS读数(并可能通过使用网络提供商来加快这一过程),然后自己融合这些数据。然而,在这种情况下,永远不要接触FusedLocationApi。尽管现在看起来有些过时和神秘,但将LocationManager与DIY fusion逻辑结合使用对于少数用例来说仍然是非常有意义的。这就是API仍然存在的原因。

场景2:您只想快速准确地更新客户端的位置。在这种情况下,请指定您想要的更新频率和准确性,让FusedLocationApi完成它的工作。FusedLocationApi在过去几年中取得了长足的进步,现在它很可能比任何DIY逻辑都更快更好地弄清楚如何获取位置信息。这是因为获取位置信息是一个非常多样化的问题,它取决于客户端设备的功能(芯片组、固件、操作系统、GPS、WiFi、GSM/LTE、蓝牙等)以及物理环境(WiFi/CellTower/蓝牙信号附近、内部或外部、晴朗的天空或城市峡谷等)。在这种情况下,不要接触手动提供者。如果你这样做了,不要期望对单个提供者的读数和融合结果之间的关系做出任何有意义的推断。

最后两点意见:

>

  • Android提供了Location.distanceTo()和Location.distance之间(),因此无需在代码中实现Haversine公式。

    如果您只是需要FusedLocationApi快速可靠的更新,我编写了一个名为LocationAssistant的小型实用程序类,它简化了设置并为您完成了大部分繁重的工作。

  •  类似资料:
    • 我正在检查移动设备上是否启用了位置服务,如果是,请监听构建GoogleAppClient并监听位置更改。我面临的问题是,一旦在mobile中打开位置并重新启用,我就无法获得位置(纬度/长)。位置始终为空。 isLocationEnabled检查位置是否已打开

    • 我正在使用GoogleApiClient请求位置更新。我正在获取纬度、经度、速度和其他信息。与此同时,我还想获取卫星计数。我如何实现这一点? 请在这方面帮助我。

    • 我一直在尝试使用andriod中的融合位置应用程序接口获取位置(lat/lon)。当wifi和gps都打开时,我的代码运行良好。如果我关闭gps,那么我不会得到任何位置更新。 我正在使用BALANCED_POWER_ACCURACY进行位置请求。我在清单中添加了Access_FINE_Location权限,并在我的build.gradle文件中使用了播放服务版本9.2.1。 我正在真实设备上进行测

    • 我在MainActivity类中有一个提供纬度和经度值的融合位置提供程序代码,它使用persistableBundle传递给JobService类。当用户使用应用程序时,它工作正常(即应用程序位于前台)。一旦应用程序被刷出或销毁,MainActivity的最后更新值就会一直通过作业调度器重复上传(即,作业调度器始终获得相同的值,融合的位置提供程序不工作)。即使应用程序不在前台,我应该怎么做才能使其

    • 说明 获取平台等位配置信息 请求地址 http://api.dc78.cn/Api/wwconf 请求方式 GET 请求参数 URL参数 描述 无 POST参数 描述 无 返回 { "status": "1", "tabset": [ { "type": "A", "name": "小台(2-4人)" }, { "type": "B", "name": "中台(5-7人)" }, { "type"

    • 请求参数说明 参数 描述 必填 示例值 类型 最大长度 action 接口参数组 是 object └action 需要调用的接口名称 是 wwconf string 请求示例 { "action": { "action": "wwconf" } } 响应参数说明 参数 描述 必填 示例值 类型 最大长度 status 返回状态。接口成功时为1,出错为0 是 1 number tabset