Hallo,
ich habe ein Funktion zur Berechnung der Wegstrecke zwischen 2
GPS-Koordinaten geschrieben. Diese verwendet double-Datentypen. Da GCC
aber anscheinend keinen echten double verwendet, habe ich Probleme mit
der Genauigkeit von "nur" 6 Stellen.
Wenn die Koordinaten eng beisammen sind, ergibt sich bereits ein relativ
großer Fehler. Beim Aufaddieren der Abstände zwischen den einzelnen
Koordinaten ergibt sich folglich ein erheblicher Fehler (Bsp. 300
Koordinaten, Länge am PC errechnet: 750m, Länge am AVR berechnet:
312m!!!).
Gibt es eine Möglichkeit, die Genauigkeit im GCC zu erhöhen oder hat
jemand eine optimierte Formel zur Berechnung der Differenz?
Ich verwende folgende Formel:
d = 2 * asin(sqrt((sin((lat1 - lat2) / 2)) ^ 2 + cos(lat1) * cos(lat2) *
(sin((lon1 - lon2) / 2)) ^ 2))
Das Ergebnis ist die Länge in nautischen Meilen.
Danke für eure Hilfe.
mfg
Andreas
Diese Formel ist bezüglich der Rundungsfehler schon optimiert. Da hängt
es eher an der internen Implementation der trigonometrischen Funktionen
im AVR-GCC.
Was spricht dagegen, die Gesamtstrecke nicht als Summe der einzelnen
Teilstrecken, sondern als Entfernung Startpunkt-Zielpunkt zu berechnen?
Hallo Oliver,
genau das ist das Problem. Bei einer geraden Strecke würde der Fehler
erträglich sein, aber durch das Aufaddieren entsteht ein relativ großer
Fehler. Bin gerade auf der Suche nach Fixed-Point-Berechnungen für
sin/cos/asin.
mfg
Andreas
Ist jetzt nur eine Idee ohne zu wissen ob es hier was bringt, aber
vielleicht sollte man sich die Taylor Reihen der einzelnen Funktionen
mal ansehen und gucken ob man die Formel dann noch etwas vereinfachen
kann.
Gruß Philipp
Hallo,
hab jetzt ziemlich viel Zeit investiert, um eine höhere Genauigkeit zu
erreichen. Leider ohne Erfolg. Ich habe in FixedPoint (8.24)
sin/cos/sqrt implementiert, aber trotzdem reicht die Genauigkeit nicht
für die Berechnung kleiner Wegstrecken aus. Bei einer Distanz von
mehreren km ist es kein Problem. Ist der Abstand aber kleiner 10m, so
kommt bei den Berechnungen immer 0 raus.
Wenn man zu Fuß unterwegs ist, wird man pro Sekunde (GPS-Daten kommen
1x/s) nicht viel Meter zurücklegen. Somit habe ich als Summe der
Wegstrecke immer 0.
Jetzt ist die Frage, ob ein anderer Compiler double precision
unterstützt. Vielleich IAR oder so?
Hat da jemand Erfahrung damit?
mfg
Andreas
Den GCC selbst auf 64-bit-double zu bringen, ist sicher auch gar kein
großer Akt. Allerdings müsste sich eben jemand hinsetzen und eine
libm.a für 64 bit FP bauen.
"Bei einer Distanz von mehreren km ist es kein Problem. Ist der Abstand
aber kleiner 10m, so kommt bei den Berechnungen immer 0 raus."
Was benutzt Du denn für ein GPS. Normalerweise liegt der
Positionsfehler bei GPS im günstigsten Fall bei 15m, kann aber leicht
auch mal bis >50m hochgehen. Differential-GPS geht runter bis 1-3m
erfordert aber einen zusätzlichen terrestrischen Empfangskanal.
Wo liegt also das Problem mit Abständen von 10m?
R.
@Reiner: das merkt man auch, wenn der Empfänger am selben Ort liegt,
aber lt. Koordinaten die Position sehr stark abweicht (hatte schon ca.
50m bei schlechterem Empfang!).
Laut Rückrechnung ergibt sich bei einer Differenz von 1m eine Änderung
der Longitude von 0,00000952". Da ich aber nur 0,0001" Auflösung habe,
habe ich sowieso ein Problem...
mfg
Andreas
8.24 kannst du nur nutzen, wenn du die 360 Grad in 256 umrechnest, inkl.
den Nachkommastellen. Dies kann unter umstaenden die Berechnungen sehr
beschleunigen. Ich habe das mit fixed-point mal gemacht, mit einer
multiplication sowie ein paar shifts/additionen, fuer die Rundungen.
Grundsaetzlich habe ich damals 16.16 Float genommen, auch weil ich
sin usw. im bereich von 0-1 hatte (16bit table lookup+interpolation).
Habe aber auch ein Scaling gemacht, aber das war Applikationsspezifisch.
> Jetzt ist die Frage, ob ein anderer Compiler double precision> unterstützt. Vielleich IAR oder so?
Der macht das problemlos und rechnet sehr schnell.
> Allerdings müsste sich eben jemand hinsetzen und eine> libm.a für 64 bit FP bauen.
Das macht bekanntlich niemand, da 'float' in 'Fachkreisen' immer wieder
schlechtgeredet wird und 'double' sowieso niemand braucht.
GPS ist nun mal ne Anwendung, wo man sich nicht mehr um 'double'
herummogeln kann.
Michael wrote:
>> Allerdings müsste sich eben jemand hinsetzen und eine>> libm.a für 64 bit FP bauen.> Das macht bekanntlich niemand, da 'float' in 'Fachkreisen' immer wieder> schlechtgeredet wird und 'double' sowieso niemand braucht.
Quark. Es gibt hinreichend viele Requests dafür, nur keinen, der sich
bislang der Aufgabe hätte annehmen wollen.
Hab ich auch noch nie davon gehört. Irgendwo auf meiner Platte rotiert
aber noch ein angefangenes Fixedpoint-Klassentemplate für avr-g++ mit
per Template-Argument wählbarer Anzahl an Vor- und Nachkommabits. Gips
aber keine trigonometrischen Funktionen für, und die Klasse ist auch
weder fertig, noch getestet. Muss ich mal bei Gelegenheit dran
weiterbasteln.
> Quark. Es gibt hinreichend viele Requests dafür, nur keinen, der sich> bislang der Aufgabe hätte annehmen wollen.
Das ist ja auch kein Wunder. Wenn hier (Forum) ein Problem auftritt, das
sich einfachst mit 'float' lösen ließe, wird gleich geschrieen, daß man
das nicht machen darf, weil es viel zu langsam sei, .....
Man nehme gefälligst 'int' und schlimmstenfalls 'long' und skaliere bis
zum Abwinken.
Das ist keine Motivation für irgendeinen , die Grundrechenarten als
'double' zu implementieren. Für sin(), cos(), usw. gib es ja die
entsprechenden Routinen in C, mit recht effizienten Reihen.
Michael wrote:
> Das ist keine Motivation für irgendeinen , die Grundrechenarten als> 'double' zu implementieren. Für sin(), cos(), usw. gib es ja die> entsprechenden Routinen in C, mit recht effizienten Reihen.
Eben um die Implementierung dieser Routinen ging es mir aber. Die
gibt es nicht ,,in C'', sondern die müsste jemand für avr-libc's
Mathematikbibliothek in 64 bit implementieren. Andernfalls wäre das
mögliche GCC-Feature ziemlich sinnfrei. Die Implementierung des
GCC-Features trau ich mir beinahe zu (ist letztlich ein Mischmasch
zwischen 64-bit int und 32-bit float), aber eine effiziente Reihe für
den AVR zu entwickeln ist jenseits dessen, was ich aus dem Handgelenk
schüttele. (Die GCC-Implementierung bietet natürlich auch noch
genügend Brot in den Details, man will schließlich wohl am Ende einen
Schalter -mdouble32 haben analog zu -mint8.)
Diejenigen, die potenziell in der Lage sind, obiges zu erledigen,
werden sich kaum vom gelegentlichen Gequake ,,double nimmt man einfach
nicht'' abhalten lassen. Eher von der fehlenden Zeit, das anzugehen.
Naja, letztlich sollte es schon für den AVR optimiert sein. Wenn
ich mir das so angucke fürchte ich, dass das ein ziemlicher Klopper
wird. Ich häng dir mal zum Vergleich die jetzige Implementierung
von atan2() der libm aus der avr-libc an.
Klopper hin, Klopper her. Die uralte Routine habe ich auf die Schnelle
gefunden. Bei anderen GNU-Quellen hatte ich auch mehr C-Quellen für
trigon.+andere Funktionen gefunden, allerdings nicht mehr aktuell auf
der Platte verfügbar. Das wird sich alles wiederfinden lassen.
Um sich nicht gleich Knüppel zwischen die Beine zu werfen, würde ich
vorschlagen, je nach Compiler-Schalter entweder nur 32bit oder nur 64bit
rechnen zu lassen: gleiche Größe für 'float' und 'double'.
Ferner wird man einen Prozessor mit wohl 32k Flash benötigen. Wenn ich
meine Mega128-Programme ansehe, da ist Platz im Flash noch und nöcher.
Und wenn ein sin() vielleicht 2-3ms dauert, wer es braucht, kann damit
leben. Kopfrechnen dauert länger.
Dazu bräuchte man
1. die Grundrechenarten einschließlich Rundung und Vergleichsoperationen
2. eine Compilerversion, die alle float/double mit 64bit verarbeitet.
3. eine libm64.a, die die notwendigen Funktionen zur Verfügung stellt.
Punkt 1 müßte in Assembler erledigt werden, da hiervon die
Verarbeitungsgeschwindigkeit maßgeblich abhängt. Die Routinen sollten
rekursiv und reentrant verwendbar sein.
Punkt 2 müßte in der Compilerquelle angepaßt werden.
Punkt 3 müßte die C-Quellen für alle Funktionen durchnudeln und eine lib
erzeugen.
Gretchenfrage: wer macht Punkt 1 ?
Danke für eure interessanten Ideen. Ich habe versucht, die Berechnung
mit FixedPoint (64 Bit) zu lösen. Doch das scheitert an mehreren
Punkten:
1. ich bräuchte für *, / einen 128 Bit Datentyp, oder aufwändige
Schiebeoperationen
2. die Implementierung in C ist extrem (!!) speicherintensiv. Ein
ATmega32 reicht hier bei weitem nicht aus. Implementierung in Assembler
ist mir zu mühsam.
3. Kosten für anderen Compiler sind zu hoch
Deshalb werde ich einfach einen anderen Controller einsetzen, dessen
Compiler einen echten double unterstützt (Renesas M16C). Den haben wir
im Haus und kostet somit nichts.
mfg
Andreas
Wegen Numerikproblemen den Mikrocontroller wechseln? Bißchen
zweifelhaft, die Strategie.
Du machst in Deiner Formel asin(sqrt(sin . Ich nehme an, daß das
numerisch ungünstig ist. In
Beitrag "Re: GPS - MOUSE - MINI- NAVIGATOR (Assembler) ATmega8" habe ich mal nen
Excelsheet gepostet, dessen Berechnungsmethode vielleicht numerisch
günstiger ist.
Michaels atan2.c und Jörgs atan2.s sind nicht fair vergleichbar, weil in
atan2.c der atan selbst mitberechnet wird, atan.c selbst ist ne
Vorverarbeitung, welche den atan selbst als Funktion nur aufruft.
Für die Freunde des atan2: angehängt ne C-Routine, die aus 2 short
Real/imag einen short mit Cordic nen Winkel macht. Genau wie sau und hat
für meine Zwecke immer gelangt.
Cheers
Detlef
Hallo Detlef,
danke für die Infos. Der Wechsel des Controllers ist kein Problem, da
ich noch in der Evaluierungsphase bin und Librarys (RS232, etc.) für
beide Controller habe. Mal schauen, ob nach der Machbarkeitsstudie ein
Go für das Projekt rauskommt...
mfg
Andreas