8 votes

Calculer atan2 sans les fonctions std ou C99

Je calcule des angles à partir d'un accéléromètre à 3 axes, mais mon compilateur n'a pas de fonction atan ou atan2. Il a un emplacement mémoire réservé, mais il appelle une fonction que je ne trouve dans aucun fichier.

Mon compilateur est Keil µVision 4 qui utilise le compilateur ARMCC. La compilation contient le fichier math.h, mais la fonction est extern et n'existe pas :

  extern _ARMABI double atan2(double /*y*/, double /*x*/);

Existe-t-il une librairie ou une fonction que je puisse inclure et qui intègre la fonction arctan ? Ou existe-t-il une fonction alternative pour calculer les angles à partir de l'accéléromètre ? J'ai besoin d'un étalonnage complet des angles sur 3 axes.

Modification : j'espérais éviter un tableau rempli de valeurs précalculées.

14voto

rcor Points 121

Le code suivant utilise un approximation rationnelle pour obtenir l'arctangente normalisée à l'intervalle [0 1) (vous pouvez multiplier le résultat par Pi/2 pour obtenir l'arctangente réelle)

normalised_atan(x) ~ (b x + x^2) / (1 + 2 b x + x^2)

où b = 0,596227

L'erreur maximale est de 0,1620º.

#include <stdint.h>
#include <math.h>

// Approximates atan(x) normalized to the [-1,1] range
// with a maximum error of 0.1620 degrees.

float normalized_atan( float x )
{
    static const uint32_t sign_mask = 0x80000000;
    static const float b = 0.596227f;

    // Extract the sign bit
    uint32_t ux_s  = sign_mask & (uint32_t &)x;

    // Calculate the arctangent in the first quadrant
    float bx_a = ::fabs( b * x );
    float num = bx_a + x * x;
    float atan_1q = num / ( 1.f + bx_a + num );

    // Restore the sign bit
    uint32_t atan_2q = ux_s | (uint32_t &)atan_1q;
    return (float &)atan_2q;
}

// Approximates atan2(y, x) normalized to the [0,4) range
// with a maximum error of 0.1620 degrees

float normalized_atan2( float y, float x )
{
    static const uint32_t sign_mask = 0x80000000;
    static const float b = 0.596227f;

    // Extract the sign bits
    uint32_t ux_s  = sign_mask & (uint32_t &)x;
    uint32_t uy_s  = sign_mask & (uint32_t &)y;

    // Determine the quadrant offset
    float q = (float)( ( ~ux_s & uy_s ) >> 29 | ux_s >> 30 ); 

    // Calculate the arctangent in the first quadrant
    float bxy_a = ::fabs( b * x * y );
    float num = bxy_a + y * y;
    float atan_1q =  num / ( x * x + bxy_a + num );

    // Translate it to the proper quadrant
    uint32_t uatan_2q = (ux_s ^ uy_s) | (uint32_t &)atan_1q;
    return q + (float &)uatan_2q;
} 

Si vous avez besoin de plus de précision, il existe une fonction rationnelle d'ordre 3 :

normalised_atan(x) ~ ( c x + x^2 + x^3) / ( 1 + (c + 1) x + (c + 1) x^2 + x^3)

où c = (1 + sqrt(17)) / 8

qui présente une erreur d'approximation maximale de 0,00811º.

9voto

Pavan Manjunath Points 10853

Il n'est pas très difficile de mettre en œuvre votre propre arctan2 . Convertir arctan2 a arctan en utilisant cette formule. Vous pouvez alors calculer arctan en utilisant ceci série infinie . Si vous additionnez un nombre suffisant de termes de cette série infinie, vous obtiendrez un résultat très proche de la fonction de bibliothèque arctan2 ne.

Voici un exemple similaire la mise en œuvre pour exp() que vous pouvez utiliser comme référence.

4voto

Eric Postpischil Points 36641

Il existe une implémentation open source d'atan aquí .

0voto

Les implémentations effectives des fonctions mathématiques (ou les stubs vers la HWFPU si elle existe) devraient se trouver dans libm. Avec GCC, cela est indiqué en passant -lm au compilateur, mais je ne sais pas comment cela se passe avec vos outils spécifiques.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X