[Résolu] Problème avec la précision de sin,cos, etc...

Bonjour à tous,

Je vous remercie d'avance pour vous pencher sur mon problème.

--Intro--

Je souhaiterais faire des calculs avec une précision de 0.000 000 000 (9 chiffres après la virgule) de précision car je calcule une distance et j'ai besoin de COS, SIN et ARCCOS.

--Problème--

Je fais mon calcul avec la calculette et Google (oui Google sait calculer) et je trouve le même résultats.

Je fais mon calcul avec la carte Arduino MEGA (Copié / Collé ) du calcul sur Google dans mon programme et les résultats sont pas du tout bon.

Je souhaiterais une précision exacte de 9 chiffres après la virgules, et aussi qu'il évite d'arrondir les 0.999999999 en 1.000000000. Et Cos, Sin et Arccos ne fonctionnent pas bien, les calculs aussi d'ailleurs.

Si quelqu'un a une solution a mon problème ça serait sympa. Merci 1000x d'avoir pris le temps de lire et 10000x si vous avez une solution :slight_smile:

--Exemple--

Sur la calculette ou sur Google :

cos(0.987654321) = 0.550649 3979

Sur la Arduino MEGA (lu via le Moniteur Série) :

cos(0.987654321) = 0.550649 4045

--Mon programme de teste--

#include <math.h>

double amg;

void setup()

{
Serial.begin(9600);
}

void loop()

{
amg = cos(0.987654321);
Serial.print("AMG: ");Serial.println(amg,10);

Est ce que l'angle passé en argument du cosinus est mesuré si précisément que ça ?

Bonjour à vous,

Tout d'abord je tiens à vous remercier personnellement pour votre team très actif :wink:

@Christian_R

Oui j'ai vraiment besoin d'une précision "énorme" car quand j'applique ma formule, je me retrouve tout le temps avec des écarts différents. Parfois je me retrouve avec 0 mètres après le calcule. J'ai décomposé tout mon calcul, j'ai remarqué qu'aucune erreur de calcul de la part de la Arduino MEGA, mais en fait il s'agissait d'une erreur venant de la précision des valeurs données ( à l'aide de la calculette Google et du Moniteur série, j'obtenais jamais le même résultat, juste les 6 chiffres après la virgule, et après tout est faussé ).

Du coup après des centaines de vérifications, c'était bien la précision qui n'allait pas ! Jusqu'à même a arrondir 0.999999 à 1.000000 alors que j'ai rien demandé.

@pepe

Merci d'avoir éclairci ce qui était un peu flou en moi, car je visitais de nombreux forums en anglais et je comprenais 1 mot sur 4. Mais l'essentiel est là.

Si vous pouvez m'expliquer comment utiliser CORDIC ça serait sympa, sinon je me débrouillerais seul. J'aimerais en fait juste faire mes calculs sans erreurs de 9 chiffres après la virgule. Et pour ce qui est du temps, tant que cela ne dépasse pas les 10 voire 20 secondes grand MAX, cela ne me dérange pas. Je vais quand même de mon côté faire mes recherches et travailler un peu..

En tout cas merci d'avoir éclairé ma piste un peu plus loin

Bon je vais être directe et clair avec vous,

j'en ai tellement bavé ces derniers jours pour trouver une solution à mon problème en surfant toute la toile, j'ai pas trouvé de tutoriel concret pour exploiter Taylor Series (10 chiffres après la virgule) ou bien Cordic 64Bits (15 chiffres après la virgule).

J'ai trouvé des morceaux de programmes que j'essaie de comprendre, mais une fois appliqué sur le programme Arduino j'ai pleins d'erreurs du genre manque x ou y library etc.. (sûrement pas compatible pour les calculs Arduino, mais pour programmes PC)

La dernière chose que j'ai trouvé pour Cordic c'est sa mais autant d'erreur que d'habitude :

https://people.sc.fsu.edu/~jburkardt/cpp_src/cordic/cordic.html

--

Sinon est ce que vous pouviez me montrer un bout de code en C compatible Arduino pour Cordic ou bien Taylor pour calculer un angle en radian avec 9 chiffres après la virgules au minimum. S'il vous plait, je suis au bout de mes capacités. Je vous en remercie d'avance, sincerement

Ah ouais quand même.. Je ne savais pas que des codes informatiques pouvaient être privés. Très intéressant tout ça, malgré que l'algorithme soit ouvert à tous. Je ne me servirais pas de mon programme a des fins professionnel ou publique. C'est de l'ordre d'usage privé.

Vous allez peut être me trouver un peu "chiant" ou bien "très chiant" :p, enfin bref,

Je ne suis pas très fort en mathématique (oui mathématique rime avec informatique), donc j'ai du mal a comprendre cet algorithme et de les comparer aux codes que je trouve sur internet (source Wiki : [CORDIC — Wikipédia](http://Wikipedia Cordic)) et que sur papier je peux le réaliser, mais sur clavier... Ca donne rien car je suis pauvre en code C / C++.

Mais je sais qu'au final je dois trouver un résultat qui est le cos, sin, tan avec des chiffres après la virgule exacte selon les bits que je choisis.

La formule pour laquelle je veux trouver ces valeurs exactes est la suivante :

arccos(sin(LATa)*sin(LATb) + cos (LATa)*cos(LATb)*cos(LONa-LONb))

--> Est ce qu'on peut utiliser cordic 3x pour Cos Sin et ArcCos ?

--> Est ce que si j'utilise ce code suivant ça pourrait fonctionner ? Et comment l'exploiter pour calculer mes 3 valeurs cos sin et arccos ?

Parce qu'ici ils donnent le gros morceau, mais je sais pas si c'est une library en .h ou bien un truc à insérer directement dans le programme. Et puis j'en fais quoi en fait une fois inséré dans le Arduino ? Comment faire pour qu'ils lisent et convertissent mes valeurs ?

Merci encore une fois a vous pour vous pencher sur mon problème et les explications :slight_smile:

URL d'origine : Fastest way to do sin(), cos() atan2() - #34 by MarkT - Programming Questions - Arduino Forum

MarkT:
CORDIC no not mentioned before, might be interesting to see the timing of those on Arduino....

430us for a 27bit resolution version...

long cordic_lookup [] = 

{
  0x20000000L,
  0x12E4051EL,
  0x09FB385BL,
  0x051111D4L,
  0x028B0D43L,
  0x0145D7E1L,
  0x00A2F61EL,
  0x00517C55L,
  0x0028BE53L,
  0x00145F2FL,
  0x000A2F98L,
  0x000517CCL,
  0x00028BE6L,
  0x000145F3L,
  0x0000A2FAL,
  0x0000517DL,
  0x000028BEL,
  0x0000145FL,
  0x00000A30L,
  0x00000518L,
  0x0000028CL,
  0x00000146L,
  0x000000A3L,
  0x00000051L,
  0x00000029L,
  0x00000014L,
  0x0000000AL,
  0x00000005L
};

#define ITERS 10000

void setup ()
{
  Serial.begin (57600) ;
  long  elapsed = micros () ;
  for (long i = 0 ; i < ITERS ; i++)
    test_cordic (i << 16, false) ;
  elapsed = micros () - elapsed ;
  Serial.print ("time taken for ") ; Serial.print (ITERS) ;
  Serial.print (" iterations = ") ; Serial.print (elapsed) ; Serial.println ("us") ;
  Serial.print (elapsed / ITERS) ; Serial.println (" us/iter") ;
  test_cordic (0x15555555L, true) ;
  test_cordic (0x95555555L, true) ;
}

void test_cordic (long aa, boolean printres)
{
  long  xx = 607252935L ;
  long  yy = 0L ;

if ((aa ^ (aa<<1)) < 0L)
  {
    aa += 0x80000000L ;
    xx = -xx ;
    yy = -yy ;
  }
 
  for (int i = 0 ; i <= 27 ; i++)
  {
    long  da = cordic_lookup [i] ;
    long  dx = yy >> i ;
    long  dy = -xx >> i ;
    if (aa < 0L)
    {
      aa += da ;
      xx += dx ;
      yy += dy ;
    }
    else
    {
      aa -= da ;
      xx -= dx ;
      yy -= dy ;
    }
  }
  if (!printres)
    return ;
  Serial.print ("end angle=") ; Serial.print (aa) ;
  Serial.print ("  end x = 0.") ; Serial.print (xx) ;
  Serial.print ("  end y = 0.") ; Serial.println (yy) ;
}

void loop ()
{
}

<< for (int i = 0 ; i <= 27 ; i++) >>

--> Je dois donc remplacer 27 par le nombre de bits voulu alors ?

--

"radians (entre -99,88° et +99,88°), "

--> Et pour le code, j'ai pas besoin d'autant en fait. vu que je fais LON et LAT * pi / 180 pour trouver en radian qui est souvent entre 0 et 1 pour être prudent je dirais -1 et 1.

Donc je dois adapter le code. Que dois je changer dans le code que j'ai cité au dessus ? Est ce que c'est le début en HEX que je dois changer ?

Ah ouiiii je comprends mieux maintenant ou se place les valeurs ok ok !!

Et juste encore une question, j'ai mes valeurs variables nommé LAT qui est exprimé en radian. Je dois le placer ou dans le programme ? Je peux utiliser directement << Resultat = cos(LAT) >> par exemple ?

OHHHHHWWWWWW OUIIIIIIIIII !!! Après moulte et moulte heures voire, jours j'ai essayé de décrypter un peu le code, comprendre et essayer de compiler tout ça.... SA MAAARCHEE ! Même plus précis que ma calculette scientifique.

Je sais pas comment vous remercier.. Je vous aurez bien payé un coup, voire 2 ou 3 sincèrement pour avoir donné un peu de votre temps. En tout cas, sachez que vous m'avez beaucoup aidé, je tiens a vous remercier encore.

Bon je ne dois pas crier victoire trop vite, car le boulot n'est pas terminé, je dois encore fusionner tout cela.

Arrgg je suis encore re coincé....

long long angle = _Fix48fromFloat( Angle );

long long angle2 = _Fix48fromFloat( LAT );

long long cosinus, sinus;
cordicCosSin( angle, &cosinus, &sinus );

char strout[23];

_Fix48toDecimal(angle, strout);
Serial.print(strout);
Serial.print(" -> ");

_Fix48toDecimal(cosinus, strout);
Serial.print(strout);
Serial.print(" -> ");

_Fix48toDecimal(sinus, strout);
Serial.println(strout);
Serial.println("\nESPACE");

_Fix48toDecimal(angle2, strout);
Serial.println(strout);

Je voudrais faire 2 calculs de sinus par exemple. Mais avant de faire ça j'ai testé juste d'afficher l'angle choisi qui est LAT (j'ai remplacé par LAT par 1 pour éviter de fusionner les 2 programmes tout de suite, car je suis encore en phase de test).

Donc

EXEMPLE:

---> Je me retrouve avec sa sur le sérial monitor quand j'envoie tout sur la Arduino :

Angle --> Cosinus --> Sinus
ESPACE
Sinus [au lieu de Angle2]

En explorant le code, je me demandais si je devais modifier tout ce qu'il y a au dessus pour pouvoir afficher plusieurs fois COS et plusieurs fois SIN ?

Bonsoir,

J'aurais pas dû crier victoire trop tôt :confused: En fait comme indiqué plus haut, vous m'avez conseillé d'utiliser + et - pour addition les 2 valeurs obtenus. Par exemple :

J'utilise cosinus x et sinus y. J'obtiens 2 valeurs sur le sérial. Mais en remontant le code, j'ai essayé d'additioner mais cela n'est pas possible car on obtient les valeurs en char soit en caractères. Donc du coup on obtient des erreurs ou bien les 2 valeurs l'un a côté de l'autre.

Voici un extrait de la fin de mon code :

long long angle = _Fix48fromFloat( 0.987654322 );

long long angle2 = _Fix48fromFloat( 1 );

long long cosinus, sinus;
cordicCosSin( angle, &cosinus, &sinus );

char strout[23];

_Fix48toDecimal(angle, strout);
Serial.print(strout);
Serial.print(" -> ");

_Fix48toDecimal(cosinus, strout);
Serial.println(strout);

Calculer les fonctions _Fix48... est impossible avec + et - car il indique que ce sont des caractères donc pas des nombres. Du coup.. Je ne vois pas trop d'alternative pour additionner. Il faut juste ne pas oublier qu'il y a en fait plusieurs calculs qui se chevauchent si on veut utiliser la formule suivante :

arccos (sin(xb)*sin(xa) + cos (xb)*cos(xa)*cos(yc-xc))

Excusez moi, j'ai un peu de mal a comprendre en fait. Je souhaiterais que cette opération soit réalisé à partir de cordic pour avoir un résultat convenable :

arccos (sin(xb)*sin(xa) + cos (xb)*cos(xa)*cos(yc-xc))

Et dans le programme, on obtient chaque résultats avec des "strout" qui sont les mêmes pour les trois. Donc si je crée une variable simple, et je calcule tout, le programme refuse car il y a plusieurs "strout" et ensuite il me dit qu'il n'arrive pas a faire de "+ - *" avec des chaines de caractères.

Mais si on convertis la chaine de caractère en nombres, on obtiendrais toujours des valeurs à 6 chiffres après la virgule et on reviendrai au début.

Donc est ce que c'est possible de calculer en chaine de caractère avec la correction Cordic pour avoir une précision de .000000001, pour un résultat qui sera affiché avec seulement des entiers ?

Donc à la fin le fix pour la précision peut être remplacé avec un float, voir un int soit :

arccos (sin(xb)*sin(xa) + cos (xb)*cos(xa)*cos(yc-xc)) = int

Car le calcul doit être calculé avec la précision, et le résultat final nécessitera juste un entier. Si je calcule tout sans la précision on se rapproche de 0

Si j'ai bien compris, (corrigez moi si je me suis trompé)

pour appliquer ceci :

Nouveau format( arccos (sin(xb)*sin(xa) + cos (xb)*cos(xa)*cos(yc-xc))) = (Float exploitable par la suite)

Il faut donc obtenir une variable exploitable dans le calcul, je peux donc utiliser le programme de conversion de chaine de char > en variable au nouveau format (9 chiffres après la v.) qui est celui ci :

long long _Fix48fromDecimal(char *str)
{
  char s = 0;
  long long val = 0LL;
  char c;
  for(;;) {
    c = *str++;
    if (c=='\0')
      return val;
    if (c=='-') {
      s ^= 1;
      continue;
    }
    if (c=='.')
      break;
    if (c=='+' || c==' ' || c=='\t')
      continue;
    if (c<'0' || c>'9')
      return val;

    unsigned int ival = c-'0';
    for(;;) {
      c = *str++;
      if (c=='.' || c==',')
        break;
      if (c<'0' || c>'9')
        return ((long long)(s ? -ival : ival))<<48;
      ival *= 10;
      ival += c-'0';
    }
    val = ((long long)ival)<<48;
    break;
  }
  long long dec = 1LL<<48;
  for(;;) {
    dec /= 10;
    c = *str++;
    if (c<'0' || c>'9')
      break;
    val += (c-'0')*dec;
  }
  return s ? -val : val;
}

Ensuite j'utilise ceci :

#define _Fix48toFloat(x) (((long)(((FIX48*)&(x))->DW[1]))/65536.0)

...

long long _rad = cordicArccos(_somme); // la dernière étape de ton calcul
float resultat = _Fix48toFloat(_rad); // résultat en radians, converti en float

Et je pourrais obtenir un résultat (float) à partir d'un calcul pré corrigé et exploitable comme une variable pour d'autres fonctions ! c'est bien cela ?

Je me sens vraiment perdu en fait:

  • Je ne vois pas comment séparer le cosinus et sinus du CordicCosSin pour pouvoir le calculer genre + - *

--> Parce que si je fais "long long = cosinus + sinus" défini dans cordicCosSin, ça me dit : 'cosinus' was not declared in this scope. Pour info j'ai tapé ça dans void loop() et non au dessus de void setup comme le reste. Testé au dessus c'est pareil.

  • Faut il que je fasse une partie fonction genre: long long _calculfinale {..code..} pour pouvoir faire mes calculs ?

Donc mon code ressemble à ça en ce moment(en raccourci). J'ai effacé tout ce qui est du Serial.monitor et des bout de code qui sont affiché comme faux / erreur:

{...} correspond au code initial sans modification.
{} correspond a rien, case vide

Code de base :

const long long tabCordic[49] {....}

const long long invGainCordic = 170926505739102LL;

void cordicCosSin(long long angle, long long *cosinus, long long *sinus) {...}

long long _Fix48fromFloat(float val) {...}

void _Fix48toDecimal(long long val, char *str23) {...}

void setup()
{}

void loop()
{}

Salut,

je n'arrive toujours pas a exploiter ce que vous m'avez donné. :sob:
Je ne sais pas comment et où remplacer mes variables de départ dans le code. J'ai essayé de travailler avec des collègues de programmations qui en savent un peu plus que moi, mais ils m'ont expliqué et aidé a faire fonctionner mais sans succès.

Nous avons testé plusieurs techniques pour exploiter le code mais sans succès. On ne peut pas définir xa xb xc et xd dans le programme avec int ou bien char avec la correction cordic.

Et maintenant que le calcul est effectué, comment on fait pour faire sortir le résultat de ce calcul en float ? Car le résultat est en radian et je suis bête voir trop bête j'ai oublié de multiplié toute la fonction arccos par le Rayon de la terre qui est de 6372 (environ) pour trouver le résultat en kmètre :(arccos (sin(xb)*sin(xa) + cos (xb)*cos(xa)*cos(yc-xc)))

En fait je comptais convertir directement avec *pi/180 pour l'avoir en radian directement. Et la formule avec le arccos s'obtient en radian, mais avec la multiplication de R = 6372 on obtient le résultat en mètre, comme sur Google Maps lorsque je fais une distance depuis 2 points, les résultats avec la calculette de Google est exactement la MEME.

Ahaha je sais, je sais que tout est la ! Je le vois bien, mais le fait que je comprends pas trop malgré vos explications fait que la logique ne peut pas fonctionner. (et j'en suis désolé, cela s'explique par mon niveau de math et informatique très nul donc j'ai du mal a associer)

Je vais pas encore vous demander, cela sera trop facile et en plus aucun interet si on comprend pas ce qu'on a, si vous estimez que toutes les informations nécessaires sont présentes pour pouvoir réaliser cela, alors je vais essayer de me pencher plus longtemps dessus.

Je vous donnerais des nouvelles dans 2/3 jours ou plus tôt si je réussi à exploiter le code :wink:
En tout cas, je vous remercie pour toutes les aides jusqu'à présent !

Après avoir suivi un cours particulier sur le C et C++, je me rends compte que c'était plus tôt facile du point de vue ou vous m'avez tout fourni et j'avais juste à compiler dans une bibliothèque. Il me fallait juste en fait savoir comment exploiter les données qui ne sont pas comme ceux que l'on peut retrouver sur des programmes simples chez Arduino. Donc après avoir su a quoi servaient chaque correcteurs, j'ai pu comprendre un peu l'algorithme à suivre ce qui m'a permis de faire mes débuts. Ensuite j'ai pu enfin avoir un résultat voire plusieurs résultats correctes exploitable via d'autres machines comme étant une variable float sans fix (résultat finale).

Donc tout fonctionne à la perfection ! Juste une chose : Vous êtes trop fort et merci d'avoir consacré un peu de votre temps. Cordialement !