如何计算与不良浮点支持的处理器上GPS坐标之间的距离? [英] How do I calculate distance between GPS co-ordinates on a processor with poor floating point support?

查看:146
本文介绍了如何计算与不良浮点支持的处理器上GPS坐标之间的距离?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要计算GPS坐标之间的距离来计算行驶距离。我都试过了半正矢和Vincenty算法,我的台式电脑上正常工作,但是当我口code到dsPIC,他们为接近(几米以内)点,由于缺乏返回0浮点precision和sin和cos的差的实现。

I need to calculate the distance between GPS co-ordinates to calculate distance traveled. I've tried both the Haversine and Vincenty algorithms, which work fine on my desktop PC, but when I port the code to the dsPIC, they return 0 for points that are close (within several meters) due to a lack of floating point precision and poor implementations of sin and cos.

有关我的使用情况下,我的积分将不超过10米拆开,将全部落在对方10公里。我试过以下算法和结果似乎确定:

For my use case, my points will be no more than 10 meters apart and will all fall within 10km of each other. I've tried the following algorithm and the results seem ok:

double dist(double latA, double lonA, double latB, double lonB)
{
    double latD = fabs(latA - latB) * 111.3;
    double lonD = fabs(lonA - lonB) * 111.3 * cos(latA * 3.14159265 / 180);

    return sqrt(latD*latD + lonD*lonD) * 1000;
}

假设的距离每增加1°为111.3公里,我用勾股定理计算距离。有没有改善我的算法没有简单的方法?还是有不依赖于高度精确的正弦/余弦?

Assuming the distance for every 1° is 111.3km, I use the pythagorean theorem to calculate distance. Is there any easy way to improve my algorithm? Or are there any other algorithms that don't depend on a highly accurate sin/cos?

推荐答案

在接受算法在船舶AIS系统的使用(以IEC61193-4规定)是的恒向线算法。我已经成功地使用这种实现对目标的安东尼·威廉姆斯的定点数学库,它使用 CORDIC 算法,将我相信一般给了软件浮点回合5倍的性能提升。

The accepted algorithm for use in Marine AIS systems (specified in IEC61193-4) is the Rhumb Line algorithm. I have successfully implemented this on a target using Anthony Williams' fixed point maths library, which uses the CORDIC algorithm, and will I believe typically give a bout 5x performance improvement over software floating point.

然而,库是C ++而不是C,这使得它易于使用由于大量的运算符重载,而不是也许你在找什么。值得使用C ++编译对C code然而,就为这个图书馆的利益考虑。与该问题当然是,Microchip的C31编译器奇怪的是不支持C ++。

However the library in is C++ rather than C, which makes it easy to use due to extensive operator overloading, but is not perhaps what you are looking for. Worth considering using C++ compilation for your C code however, just for the benefit of this library. The problem with that of course is that Microchip's C31 compiler bizarrely does not support C++.

一个警告然而是对日志()函数的查找表是由一个值过短,并在与零值的端需要一个附加的元件。这是由安东尼证实后,我发现了它,但我不相信他已更新下载。

One caveat however is that the look-up table for the log() function is too short by one value and needs an additional element at the end with value zero. This was confirmed by Anthony after I found it, but I do not believe that he has updated the download.

无论哪种方式,答案很可能是使用固定点,CORDIC。

Either way, the answer is probably to use fixed point, and CORDIC.

要解决经度或赤道弧1m时,您将需要8位precision的,所以单precision持股量将不足,采用双precision将大大减缓事。检查MikroElectronica的C用户手册显示,编译器只支持单precision - 浮动双击长双都是32位的,所以没有办法达到你需要使用精度内置的FP类型与该编译器无论如何。

To resolve to 1m of longitude or equatorial arc, you will need 8 digits of precision, so a single precision float will be insufficient, using double precision will slow things considerably. Checking MikroElectronica's C User Manual reveals that the compiler only supports single precision - float, double, and long double are all 32-bit, so there is no way to achieve the accuracy you need using the built-in FP types in any case with this compiler.

如果它是任何使用,这里是用安东尼的图书馆我的恒向线code:

If it is of any use, here is my Rhumb Line code using Anthony's library:

标题:

#if !defined cRhumbLine_INCLUDE
#define cRhumbLine_INCLUDE

#include "fixed.hpp"

//! Rhumb Line method for distance and bearing between two geodesic points
class cRhumbLine
{
public:

    //! @brief Default constructor
    //!
    //! Defines a zero length line, bearing zero
    cRhumbLine() : m_update_bearing(false), m_update_distance(false), m_distance(0), m_bearing(0) {} 

    //! @brief Constructor defining a line
    //!
    //! @param startLatDeg  Start latitude in degrees, negative values are south of equator
    //! @param startLonDeg  Start longitude in degrees, negative values are west of meridian.
    //! @param endLatDeg    End latitude in degrees, negative values are south of equator
    //! @param endLonDeg    End longitude in degrees, negative values are west of meridian.
    cRhumbLine( fixed startLatDeg, fixed startLonDeg, fixed endLatDeg, fixed endLonDeg ) 
    {
        define( startLatDeg, startLonDeg, endLatDeg, endLonDeg ) ;
    }

    //! @brief Define a start/ent point
    //!
    //! @param startLatDeg  Start latitude in degrees, negarive values are south of equator
    //! @param startLonDeg  Start longitude in degrees, negative values are west of meridian.
    //! @param endLatDeg    End latitude in degrees, negarive values are south of equator
    //! @param endLonDeg    End longitude in degrees, negative values are west of meridian.
    void define( fixed startLatDeg, fixed startLonDeg, fixed endLatDeg, fixed endLonDeg ) ;

    //! @brief Get start-end distance in nautical miles
    //! @return Start-end distance in nautical miles.
    fixed distanceNm() { return distanceMetres() / ONE_NM_IN_METRE ; }

    //! @brief Get start-end distance in metres.
    //! @return Start-end distance in metres.
    fixed distanceMetres()  ;

    //! @brief Get start-end bearing in degreed.
    //! @return Start-end bearing in degreed (0 <= x < 360).
    fixed bearingDeg()  ;

private:
    static const int ONE_NM_IN_METRE = 1852 ;

    bool m_update_bearing ;
    bool m_update_distance ;
    fixed m_distance ;
    fixed m_bearing ;

    fixed m_delta_phi ;
    fixed m_delta_lon ;
    fixed m_delta_lat ;
    fixed m_lat1_radians ;
} ;

#endif

正文:

#include "cRhumbLine.h"

void cRhumbLine::define( fixed startLatDeg, fixed startLonDeg, fixed endLatDeg, fixed endLonDeg )
{
    fixed lat1 = startLatDeg / 180 * fixed_pi ;
    fixed lon1 = startLonDeg / 180 * fixed_pi ;
    fixed lat2 = endLatDeg / 180 * fixed_pi ;
    fixed lon2 = endLonDeg / 180 * fixed_pi ;

    m_update_bearing = true ;
    m_update_distance = true ;

    m_delta_phi = log( tan( lat2 / 2 + fixed_quarter_pi ) / tan( lat1 / 2 + fixed_quarter_pi ) ) ;
    m_delta_lat = lat1 - lat2 ;
    m_delta_lon = lon1 - lon2 ;
    m_lat1_radians = lat1 ;

    // Deal with delta_lon > 180deg, take shorter route across meridian
    if( m_delta_lon.abs() > fixed_pi )
    {
        m_delta_lon = m_delta_lon > 0 ? -(fixed_two_pi - m_delta_lon) : (fixed_two_pi + m_delta_lon) ;
    }
} 


fixed cRhumbLine::distanceMetres()
{
    if( m_update_distance )
    {
        static const fixed mean_radius = 6371009 ; // in metres. Source: International Union of Geodesy and Geophysics (IUGG)

        fixed q = m_delta_phi != 0 ? m_delta_lat / m_delta_phi : cos( m_lat1_radians ) ; // Deal with lines with constant latitude

        m_distance = sqrt( ( sqr(m_delta_lat) + sqr(q) * sqr(m_delta_lon) ) ) * mean_radius ;
        m_update_distance = false ;
    }

    return m_distance ;
}


fixed cRhumbLine::bearingDeg()
{
    if( m_update_bearing )
    {
        static const fixed mean_radius = 6371009 ; // in metres. Source: International Union of Geodesy and Geophysics (IUGG)

        m_bearing = atan2( m_delta_lon, m_delta_phi ) / fixed_pi * 180 ;
        if( m_bearing == -180 )
        {
            m_bearing == 0 ;
        }
        else if( m_bearing < 0 )
        {
            m_bearing += 360 ;
        }
        m_update_bearing = false ;
    }

    return m_bearing ;
}

这篇关于如何计算与不良浮点支持的处理器上GPS坐标之间的距离?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆