Fonction mathématique Coordonnées GPS

Bonsoir à tous,

Toujours dans le développement de mon module GPS, je bloque sur un truc peut être très basique pour certains.

J'ai cette fonction en JavaSript qui permet de calculer la distance en kms entre deux points GPS.

function distance(lat_a, lon_a, lat_b, lon_b){
a = Math.PI / 180;
lat1 = lat_a * a;
lat2 = lat_b * a;
lon1 = lon_a * a;
lon2 = lon_b * a;
t1 = Math.sin(lat1) * Math.sin(lat2);
t2 = Math.cos(lat1) * Math.cos(lat2);
t3 = Math.cos(lon1 - lon2);
t4 = t2 * t3;
t5 = t1 + t4;
rad_dist = Math.atan(-t5/Math.sqrt(-t5 * t5 +1)) + 2 * Math.atan(1);
return (rad_dist * 3437.74677 * 1.1508) * 1.6093470878864446;
}

Et utilisée comme ceci :

alert(distance(43.838489, 4.349213, 44.131957, 4.085541));

Je voudrais la transcrire pour l'Arduino.

J'ai trouvé les fonctions math de l'Arduino ici : http://www.arduino.cc/en/Math/H

Mais pour autant ça marche pas, quelle horreur cette fonction :grin:

Je continue d'essayer, si vous y arrivez, tenez moi au courant je gagnerai surement du temps :slight_smile:

float a;
float lat1;
float lat2;
float lon1;
float lon2;
float t1;
float t2;
float t3;
float t4;
float t5;
float rad_dist;

void setup() {
Serial.begin(9600); // use the serial port
}

void loop() {

distance(43.838489, 4.349213, 44.131957, 4.085541);
delay(1000);

}
void distance(float lat_a, float lon_a, float lat_b, float lon_b){

a = 3.141592654 / 180;
lat1 = lat_a * a;
lat2 = lat_b * a;
lon1 = lon_a * a;
lon2 = lon_b * a;
t1 = sin(lat1) * sin(lat2);
t2 = cos(lat1) * cos(lat2);
t3 = cos(lon1 - lon2);
t4 = t2 * t3;
t5 = t1 + t4;
rad_dist = atan(-t5/sqrt(-t5 * t5 +1)) + 2 * atan(1);
Serial.println ((rad_dist * 3437.74677 * 1.1508) * 1.6093470878864446);

}

Bon ça marche, mais j'ai 250 mètres d'écart je comprends pas pourquoi, j'ai juste remplacé la fonction Math.PI par la valeur la plus précise, mais ça a rien changé.

Après plusieurs tests, j'ai parfois 40 mètres d'écart et parfois 300 mètres... C'est bête.

A croire que les fonctions Arduino ne sont pas conçues de la même manière que celles en Javascript, mais quelles sont les plus précises ??!! :slight_smile:

Bon en fait c'est l'Arduino qui permet pas ces calculs :frowning:

L'Arduino me donne 3 kilomètre alors que parfois il y a juste 120 mètres, le Javascript est lui très précis ! Ca fait galérer...

Salut !

Essaie de comparer les valeurs à chaque étapes du calcul à quel moment il y a une différence, mais à bon avis ça vient de la précision des nombres flottants.

Essaye avec cette formule qui à l'air un peu plus simple que ton code repris sur un JS XD :

http://www.zeguigui.com/weblog/archives/2006/05/calcul-de-la-di.php

Oliv4945:
Salut !

Essaie de comparer les valeurs à chaque étapes du calcul à quel moment il y a une différence, mais à bon avis ça vient de la précision des nombres flottants.

Déjà tous les résultats sont différents (comprend pas), à quelques millièmes près mais bon du coup à la fin ça fait n'importe quoi...
Puis j'ai lu dans la doc Arduino, qu'il s'arrêtait à la précision float, même si il propose de déclarer des variables double...

@Jean-François :

La première étape consiste à convertir nos degrés en radians. Ca c'est facile, il suffit de tout multiplier par 2?/360.

Eih ?

d = R * (Pi/2 - ArcSin( sin(destLat) * sin(sourceLat) + cos(destLong - sourceLong) * cos(destLat) * cos(sourceLat)))

Pi je peux peut être le remplacer en dur car il existe pas de fonction PI pour l'Arduino.

l'Arduino n'a pas non plus ArcSin :frowning:

Si tu inclus Math.h, tu as la constante M_PI.

Arcsin c'est asin

fdufnews:
Si tu inclus Math.h, tu as la constante M_PI.

Arcsin c'est asin

D'accord, merci de ces précieuses infos, je testerai tout ça :wink:

arduimat:
Eih ?

Radian

Unité de mesure des angles. Abréviation : rad.
Un angle plein vaut 2 PI rad ; un angle plat vaut PI rad ; un angle droit vaut PI/ 2 rad.
Pour convertir des degrés en radians : on multiplie par PI et on divise par 180 ;
pour convertir des radians en degrés : on multiplie par 180 et on divise par PI.

http://jpm-chabert.perso.neuf.fr/maths/Lexique/radian.html

Pour des petites distances, une bonne approximation:
1 minute de latitude vaut 1852m (c'est le mille marin)
1 minute de longitude vaut 1852*cos(latitude)
Donc
D² = ((Lat2-Lat1) + ((Longi2-Longi1)*cos(Lat))²) * 1852

Alors j'ai testé cette formule :

d = R * (Pi/2 - ArcSin( sin(destLat) * sin(sourceLat) + cos(destLong - sourceLong) * cos(destLat) * cos(sourceLat)))

Et exactement mêmes résultat qu'avec la toute première formule, pour des moyennes distances c'est pas trop mal, mais quand le JavaScript m'annonce 120 mètres (qui est juste), l'Arduino me donne 3.11 kms...

Voilà le code :

#include <Math.h>

double lat1;
double lat2;
double lon1;
double lon2;
double d;

void setup() {
 Serial.begin(9600);       // use the serial port
}
void loop() {
  distance(43.965218, 4.22383, 43.964423, 4.22486);
  
}

void distance(double lat_a, double lon_a, double lat_b,  double lon_b){ 
    lat1 = lat_a * (2 * M_PI / 360);
    lat2 = lat_b * (2 * M_PI / 360);
    lon1 = lon_a * (2 * M_PI / 360);
    lon2 = lon_b * (2 * M_PI / 360);
    d = 6378 * (M_PI/2 - asin(sin(lat2) * sin(lat1) + cos(lon2 - lon1) * cos(lat2) * cos(lat1)));
    Serial.println(d,12);
}

patsol:
Pour des petites distances, une bonne approximation:
1 minute de latitude vaut 1852m (c'est le mille marin)
1 minute de longitude vaut 1852*cos(latitude)
Donc
D² = ((Lat2-Lat1) + ((Longi2-Longi1)*cos(Lat))²) * 1852

Merci pour la proposition mais je n'arrive pas à reconstituer votre formule sur l'Arduino, le dernier Lat c'est le 1 ou le 2 ?

Pourquoi mettre un ² sur le nom de la variable ?

Merci :slight_smile:

J'ai essayé ça :

d = ((lat2-lat1) + (((lon2-lon1)*cos(lat2))*((lon2-lon1)*cos(lat2)))) * 1852;

Mais ça me donne un nombre négatif -0.02... enfin pas bon quoi.

arduimat:
Pourquoi mettre un ² sur le nom de la variable ?

C'est l'exposant, pour indiquer sa puissance, la deux c'est carrée... la trois c'est cubique ...etc XD

Pour les puissances :
http://arduino.cc/en/Reference/Pow

Pour la racine carrée :
http://arduino.cc/en/Reference/Sqrt

regarde ici :

Orthodromie

Soit la formule :

d=2*asin(sqrt(pow((sin((lat1-lat2)/2)),2) + cos(lat1)*cos(lat2)*pow((sin((lon1-lon2)/2)),2)));

Non, toujours pas :frowning:

Le JavaScript me donne 81 kilomètres.

Et l'Arduino avec le code suivant me retourne 0.012818629741...

#include <Math.h>

double lat1;
double lat2;
double lon1;
double lon2;
double d;

void setup() {
 Serial.begin(9600);       // use the serial port
}
void loop() {
  distance(43.965218, 4.22383, 43.976016, 3.203429);
  delay(2000);
  
}

void distance(double lat_a, double lon_a, double lat_b,  double lon_b){ 
    lat1 = lat_a * (2*M_PI/360);
    lat2 = lat_b * (2*M_PI/360);
    lon1 = lon_a * (2*M_PI/360);
    lon2 = lon_b * (2*M_PI/360);
    d=2*asin(sqrt(pow((sin((lat1-lat2)/2)),2) + cos(lat1)*cos(lat2)*pow((sin((lon1-lon2)/2)),2)));
    Serial.println(d,12);
}

Celle ci de fonction (dessous), elle fonctionne bien SAUF que sur des distances d'environ moins de 6 kilomètres, elle renvoi n'importe quoi, elle va renvoyer 3 kilomètres pour 500 mètres, 4 kilomètres pour 200 mètres etc.

d = 6378 * (M_PI/2 - asin(sin(lat2) * sin(lat1) + cos(lon2 - lon1) * cos(lat2) * cos(lat1)));

Ahh ça y est ça fonctionne :grin:

Avec cette fonction :

float calc_dist(float flat1, float flon1, float flat2, float flon2)
{
float dist_calc=0;
float dist_calc2=0;
float diflat=0;
float diflon=0;

diflat=radians(flat2-flat1);
flat1=radians(flat1);
flat2=radians(flat2);
diflon=radians((flon2)-(flon1));

dist_calc = (sin(diflat/2.0)*sin(diflat/2.0));
dist_calc2= cos(flat1);
dist_calc2*=cos(flat2);
dist_calc2*=sin(diflon/2.0);
dist_calc2*=sin(diflon/2.0);
dist_calc +=dist_calc2;

dist_calc=(2*atan2(sqrt(dist_calc),sqrt(1.0-dist_calc)));

dist_calc*=6371000.0; //Converting to meters
//Serial.println(dist_calc);
return dist_calc;
}

Merci à vous tous, à force ça marche :slight_smile:

J'avais trouvé ça :

 void distFrom(float lat1, float lng1, float lat2, float lng2) {
    double earthRadius = 3958.75;
    double dLat = Math.toRadians(lat2-lat1);
    double dLng = Math.toRadians(lng2-lng1);
    double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
               Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
               Math.sin(dLng/2) * Math.sin(dLng/2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    double dist = earthRadius * c;

    int meterConversion = 1609;
 Serial.println(dist*meterConversion/1000);
    }

Trop tard.... XD

Pour la peine, je te fais bientôt un emballage de renom... :slight_smile:

arduimat:
Pour la peine, je te fais bientôt un emballage de renom... :slight_smile:

J'y compte XD