Hallo,
bin seit Stunden bei einer Fehlersuche und komme leider nicht weiter.
Als Anlage 2 Zeilen Code in C.
Habe die varriablen zum Testen durch Konstanten ersetzt.
Frage: Warum ist hier e immer positiv ??
Gruß
Bitte nächstes mal als Text und nicht als Bild posten.
32767 * 2000000 = 65534000000 -> viel zu groß für long auf allen
gängigen Plattformen. Das Überlauffverhalten für vorzeichenbehaftete
Werte ist nicht definiert. Damit darf also ein beliebiges Ergebnis
rauskommen.
Dirk F schrieb:> Habe die varriablen zum Testen durch Konstanten ersetzt.
Wenn man jetzt wüßte, was an der Ganzzahlrechnung wirklich variable
Größen sind, könnte man auch über sinnvolle alternative Formulierungen
nachdenken, um explosionsartige Anforderungen an die Zahl der gültigen
Bits in der Rechnung zu vermeiden.
Hallo,
das ging ja schnell mit den Antworten.
Die Platform ist PIC32 mit dem XC32 compiler.
Ich denke, INT ist da 32 Bit und long 64 Bit ?
Das Problem:
Der 24 Bit wert vom DAC (Skaliert auf 0...2000000) soll mit
Sollwertvorgabe vom 16 Bit DAC (Bereich 0....65535) verglichen werden.
Also muss ich ja erst eine der beiden Werte auf den anderen Bereich
skalieren.
Hier der aktuelle code in Textform:
if((e>1)&&(PI_corr>-650))PI_corr--;// um 1 Digit vom 16-Bit DAC korregieren
3
if((e<-1)&&(PI_corr<650))PI_corr++;// wird ausgef max 1 % in 6,5 s ausregeln
--
Und das nächste Mal liest Du auch noch den Text, der über dem
Eingabefeld steht, und verwendest die [ c ] [ /c ] -Tags, dann kann man
den Kram .. naja .. besser lesen.
-rufus
Dirk F schrieb:> Der 24 Bit wert vom DAC (Skaliert auf 0...2000000)
Blöde Idee.
Die Sache wird aber einfacher, wenn du 65536 auf 2000030 oder 2000031
skalierst. Und der Genauigkeitsfehler verschwindet im
Digitalisierungsrauschen vom 16-Bit ADC.
> Hier der aktuelle code in Textform:
Du darfst das gerne mit "pre" taggen.
>> e = (long) 900000 - (((long) 32767 * (long) 2000000 )/ (long)> 65535 ) ; // ( ( voltage <<16) /2000000) - 32000; //data ; //
Und was davon ist jetzt wirklich konstant?
Rolf Magnus schrieb:> 32767 * 2000000 = 65534000000 -> viel zu groß für long auf allen> gängigen Plattformen.
Ohne den üblichen Streit starten zu wollen möchte ich doch anmerken,
dass 64-Bit Linux durchaus als gängige Plattform betrachtet werden darf.
Und da ist "long" 64 Bits breit (LP64 Modell). Dass Microsoft sich das
nicht getraut hat (LLP64 Modell) liegt wohl an der Historie von deren
APIs und den darin enthaltenen Typdefinitionen.
>>>Digitalisierungsrauschen vom 16-Bit ADC.
Verwende einen 24 Bit ADC . Habe eine rauschfreie Auflösung 0.5 PPM
erreicht.
>>>Und was davon ist jetzt wirklich konstant?
Hier der code, der funktioniert:
e = (long) voltage - (((long long) data_roh * (long long ) 2000000 )/
(long long ) 65535 ) ;
Eine sehr gute Näherung für 2000000/65535 ist 26215/859 (der relative
Fehler beträgt nur 1.5E-8). Damit kannst du das Ganze auch ohne
64-Bit-Arithmetik rechnen.
Die Division kannst du ebenfalls einsparen, wenn du sowohl den Ausdruck
für e als auch die Fehlergrenzen (±2) jeweils mit 859 multiplizierst.
Dann sieht der Code folgendermaßen aus:
1
e=voltage*859-data_roh*26215;
2
if((e>=2*859)&&(PI_corr>-650))PI_corr--;
3
if((e<=-2*859)&&(PI_corr<650))PI_corr++;
Bei dieser Methode entfällt zudem der Rundungsfehler durch die
ganzzahlige Division.
Da mir solche Divisionen durch 65535 (oder durch 255, ...) überhaupt
nicht gefallen, verschiebe ich den 'Rundungsfehler' lieber an das untere
Ende des Messwertes.
z.B.:
Messwert ist 1023 -> Messwert + 1 -> 1024 -> und jetzt, falls es hier
nötig ist, schon was anzupassen: durch 4, statt 1023 / 4 -> jetzt noch
minus eins und somit hat der Compiler die Möglichkeit, die Division
durch Bitschieben zu ersetzen.
In dem vorliegenden Fall könnte man dann mit 15625 / 512 rechnen.
(Falls die Genauigkeit 7.9e-5 reicht, auch mit 7812 / 256)
>>>Damit kannst du das Ganze auch ohne 64-Bit-Arithmetik rechnen.
Diese Berechnung wird jede 250 ms ausgeführt. Aus Prozessorsicht eine
Ewigkeit. Ich denke, eine 64-Bit Berechnung wird die Gesamt Performance
vom System nicht sehr stark beeinträchtigen.
>>>Eine sehr gute Näherung für 2000000/65535 ist 26215/859
Danke für den guten Hinweis. Aber ein Argument spricht dagegen: Um die
Wartung des Programms zu verbessern, ist die
Lesbarkeit/Nachvollziehbarkeit nach z.B. 1 Jahr mit der ersten Methode
besser.
Danke für eure Hilfe!
Gruß
DirkF schrieb:> Danke für den guten Hinweis. Aber ein Argument spricht dagegen: Um die> Wartung des Programms zu verbessern, ist die> Lesbarkeit/Nachvollziehbarkeit nach z.B. 1 Jahr mit der ersten Methode> besser.
Man kann alles übertreiben.
Irgendwie sollte es doch möglich sein, zwei Programmzeilen auch nach
einem Jahr noch zu überblicken. Und zur Not gibt es die Möglichkeit, in
solchen Fällen durch einen Kommentar Licht ins Dunkle zu bringen und so
den Sinn solcher Formulierung zu erhellen.
Ich würde empfehlen in zukunft die <stdint.h> zu includieren und dann
mit diesen Datentypen zu arbeiten. Dann gibt's kein "Ich dachte long
sein 64bit" mehr, einfach int64_t.
DirkF schrieb:> Diese Berechnung wird jede 250 ms ausgeführt.>>>>Eine sehr gute Näherung für 2000000/65535 ist 26215/859>> Danke für den guten Hinweis. Aber ein Argument spricht dagegen: Um die> Wartung des Programms zu verbessern, ist die> Lesbarkeit/Nachvollziehbarkeit nach z.B. 1 Jahr mit der ersten Methode> besser.
Ok, da stimme ich dir zu. Wenn weder Rechenzeit noch der Programm-
speicherverbrauch kritisch sind, ist es besser, die übersichtlichere
Lösung zu wählen.
Wolfgang schrieb:> Man kann alles übertreiben.>> Irgendwie sollte es doch möglich sein, zwei Programmzeilen auch nach> einem Jahr noch zu überblicken.
Den obigen Näherungswert habe ich über die Kettenbruchentwicklung von
2000000/65535 bestimmt. Wird nun aus irgendeinem Grund die Skalierung
der Werte geändert, so dass der Wertebereich von voltage nicht mehr
nur bis 2000000, sondern bspw. bis 3000000 reicht, müsste die Näherung
neu berechnet werden. Im anderen Fall genügt es, im Programm einfach den
Wert 2000000 durch 3000000 zu ersetzen.