Принятым алгоритмом для использования в морских системах AIS (определенным в МЭК 61193-4) является алгоритм Rhumb Line .Я успешно реализовал это на цели, используя библиотеку математики Энтони Уильямса с фиксированной точкой , которая использует алгоритм CORDIC , и я верю, что, как правило, дает увеличение производительности примерно в 5 раз по сравнению с программной плавающей точкой,
Тем не менее, библиотека в C ++, а не C, что делает ее простой в использовании из-за большой перегрузки операторов, но, возможно, это не то, что вы ищете.Однако стоит подумать об использовании компиляции C ++ для вашего кода на C, просто для пользы этой библиотеки.Проблема с этим, конечно, в том, что компилятор C31 Microchip странным образом не поддерживает C ++.
Однако одно предостережение заключается в том, что таблица поиска для функции log () слишком короткая на одно значение и нуждается в дополнительном элементев конце со значением ноль.Это было подтверждено Энтони после того, как я его нашел, но я не верю, что он обновил загрузку.
В любом случае, ответ, вероятно, заключается в использовании фиксированной точки и CORDIC.
Дляпри разрешении до 1 м по долготе или экваториальной дуге вам понадобится 8 цифр точности, поэтому поплавка с одинарной точностью будет недостаточно, а использование двойной точности значительно замедлит процесс.Проверка MikroElectronica's C User Manual показывает, что компилятор поддерживает только одинарную точность - float
, double
и long double
все 32-битные, поэтому нет никакого способа достичь необходимой точности, используя встроенные типы FPв любом случае с этим компилятором.
Если он нужен, вот мой код Rhumb Line с использованием библиотеки Энтони:
Заголовок:
#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
Body:
#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 ;
}