jjzjj

android - Android-卫星信息(计数,信号等)的融合位置提供程序API问题

coder 2023-11-29 原文

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

通过在StackExchange和Google以及其他地方进行搜索,我得出的结论是,实际上几乎不可能使用Fused Location API(有关Google的信息)来获取有关卫星的信息。

大多数人使用的方法是在融合位置旁边实际使用LocationManager来获取GPS状态。我的第一个问题是:
我们如何100%确保LocationManager提供的数字与融合位置提供给我们的信息保持同步?融合地点是否在内部使用Manager?

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

}

这是我认为可能需要的代码部分。给定纬度和 map 上两点之间的距离,我将所有过滤代码和数学信息一起保留在那里。如果需要,可以随意使用它。

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

最后,我想提一提的是,该应用程序已在具有不同版本Android的其他设备(电话,而非平板电脑)中进行了测试,并且似乎可以正常运行。

任何想法都将受到欢迎。当然,请问任何我可能忘记提及的问题。

编辑:我的实际问题被隐藏在文本中,以便进行布局:

1)是我们从融合位置获得的位置数据,还是从同步管理器中获得的其余GPS数据,看似只能从位置管理器中获得,还是有可能获得位置但针对特定点的锁定卫星数错误?

2)奇怪的行为背后的原因是什么,即该应用程序无法获得卫星锁定,但如果该锁定来自另一个应用程序,则该应用程序似乎已正确使用了该锁定?为了使这个问题变得更奇怪,这发生在Nexus 7(Android 6.0.1)上,而不是其他使用不同Android版本进行测试的设备上。

最佳答案

据我了解:

1)

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

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

简而言之:

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

2)

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

What could be the reason behind the weird behavior?



我将提出一个理论:假设LocationManager和FusedLocationApi完全分开工作,由于您仅依赖GPS,因此LocationManager可能难以获得修复。
除了GPS外,还尝试使用NETWORK_PROVIDER来加快首次定位的时间(从而使LocationManager可以使用Assisted GPS)。几乎可以肯定其他应用程序(例如Compass应用程序)正在执行此操作,这可以解释为什么它们得到更快的修复。
注意:开始接收GPS数据后,您当然可以注销网络提供商。或者,您可以继续使用它,而只是忽略其更新。

这是怪异行为的一种可能解释。 您可能已经知道位置行为取决于您所在的设备,操作系统,GPS芯片组,固件和位置-因此,如果您打算手动操作(即不使用FusedLocationApi),则必须进行大量试验。

除了答案之外,让我对您的问题提供一个有根据的看法(用一粒盐;-):我认为您正在尝试将针对非常不同的用例而做出的两件事结合起来,而不是意味着要合并。

获得卫星的数量完全是技术性的。 ,除非您的应用程序教他们有关GNSS的信息,否则最终用户将永远不会对此类信息感兴趣。如果您想为内部分析目的记录它,那很好,但是您必须能够处理该信息不可用的情况。

场景1 :出于某种原因,您决定绝对需要GPS读数的详细(技术)细节。在这种情况下,自己构建逻辑,这是老式的方式。 IE。通过LocationManager要求获取GPS读数(并可能通过使用网络提供商来加快此过程),然后自行融合这些内容。但是,在这种情况下,请勿触摸FusedLocationApi。
即使在当今看来过时和不可思议,将位置管理器与DIY融合逻辑结合使用仍然对少数用例来说是非常合理的。这就是API仍然存在的原因。

方案2 :您只是想快速,准确地更新客户的位置。在这种情况下,指定所需的更新频率和准确性,并让FusedLocationApi进行其工作。在过去的几年中,FusedLocationApi取得了长足的进步,如今,比起任何DIY逻辑,现在它在找出如何获取位置信息方面将更加快捷,更好。这是因为获取位置信息是一个非常异构的问题,它取决于客户端设备(芯片组,固件,OS,GPS,WiFi,GSM/LTE,蓝牙等)的功能以及物理环境(WiFi/CellTower)的能力/附近,内部或外部,晴朗的天空或城市峡谷等的蓝牙信号)。
在这种情况下,请勿触摸手动提供程序。如果这样做,不要期望对各个提供者的读数与融合结果之间的关系做出任何有意义的推断。

最后两句话:
  • Android提供了Location.distanceTo()Location.distanceBetween(),因此无需在代码中实现Haversine公式。
  • 如果您只是需要FusedLocationApi的快速而可靠的更新,我已经写了a small utility class, called the LocationAssistant,可以简化设置并为您完成大部分繁重的工作。
  • 关于android - Android-卫星信息(计数,信号等)的融合位置提供程序API问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40162272/

    有关android - Android-卫星信息(计数,信号等)的融合位置提供程序API问题的更多相关文章

    1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

      大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

    2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    3. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

      我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

    4. ruby-on-rails - Ruby on Rails 计数器缓存错误 - 2

      尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot

    5. ruby - 使用多个数组创建计数 - 2

      我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

    6. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

      在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

    7. ruby-on-rails - Mandrill API 模板 - 2

      我正在使用Mandrill的RubyAPIGem并使用以下简单的测试模板:testastic按照Heroku指南中的示例,我有以下Ruby代码:require'mandrill'm=Mandrill::API.newrendered=m.templates.render'test-template',[{:header=>'someheadertext',:main_section=>'Themaincontentblock',:footer=>'asdf'}]mail(:to=>"JaysonLane",:subject=>"TestEmail")do|format|format.h

    8. ruby-on-rails - 在 Ruby (on Rails) 中使用 imgur API 获取图像 - 2

      我正在尝试使用Ruby2.0.0和Rails4.0.0提供的API从imgur中提取图像。我已尝试按照Ruby2.0.0文档中列出的各种方式构建http请求,但均无济于事。代码如下:require'net/http'require'net/https'defimgurheaders={"Authorization"=>"Client-ID"+my_client_id}path="/3/gallery/image/#{img_id}.json"uri=URI("https://api.imgur.com"+path)request,data=Net::HTTP::Get.new(path

    9. 安卓apk修改(Android反编译apk) - 2

      最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

    10. ruby - 正则表达式在哪个位置失败? - 2

      我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

    随机推荐