Hallo, ich habe eine Dampfdrucktabelle mit Stammwerten in definierten Abständen Temp----------Sättigung 10"C -------- 12% 20"C -------- 19% 30°C -------- .... Der Zusammenhang ist nicht linear. Was mache ich, wenn ich Zwischenwerte brauche - z.B. 23°C? Lege ich dann einen kubischen Spline zwischen die Werte?
Vincent schrieb: > Lege ich dann einen kubischen Spline zwischen die Werte? Wenn die Kurve insgesamt einer 3. Potenz folgt wäre das ok. Für einen kleinen µC aber eben auch rechenaufwendig. > Lege ich dann einen kubischen Spline zwischen die Werte? Kommt drauf an, welche Abweichung du dir erlauben kannst und natürlich auch, wie zuverlässig der Messwert ermittelt worden ist bzw. werden kann. Gerade bei Sachen mit Feuchtemesseung würde ich annehmen, dass du mit simpler linearer Interpolation da gut im tolerablen Rahemn landen wirst. Denn die Senosren gehenmigen sich da gern mal 5% Toleranz...
Vincent schrieb: > Der Zusammenhang ist nicht linear. > > Was mache ich, wenn ich Zwischenwerte brauche - z.B. 23°C? Lege ich dann > einen kubischen Spline zwischen die Werte? Kann man machen. Man muss allerdings die erste Ableitung an den Stützpunkten kennen. Kubischer Spline für f über einem Invervall [a, b] hat 4 Kontrollpunkte: (a, f(a)), (b, f(b)) und für die beiden anderen Kontrollpunkte gilt, dass deren x-Koordinaten das Intervall im Verhältnis 1:2 bzw. 2:1 teilen unanhängig von der darzustellenden Funktion. Die y-Koordinaten ergeben sich dadurch, dass die Punkte auf Tangenten an f liegen: Der Kontrollpunkt, dessen x-Koordinate näher bei a liegt, liegt auf der Tangenten durch (a, f(a)). Der andere Kontrollpunkt liegt dann auf der Tangenten durch (b, f(b)). Auswertung geht zum Beispiel per deCasteljau https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm was die nette Eigenschaft hat, dass als Zwischenwerte nur x-Werte in [a, b] auftreten und nur y-Werte in [f(a), f(b)]. Dadurch ist die Rechnung frei von internen Überläufen und auch gut für Fixed-Point geeignet.
Sind die gerundeten Tabellenwerte das Ergebnis der genannten Clausius-Clapeyron-Gleichung, oder gibt es da noch andere Berechnungsformeln?
:
Bearbeitet durch User
Lothar M. schrieb: > Gerade bei Sachen mit Feuchtemesseung würde ich annehmen, dass du mit > simpler linearer Interpolation da gut im tolerablen Rahemn landen wirst. > Denn die Senosren gehenmigen sich da gern mal 5% Toleranz ok, das bedeutet dann, dass ich einfach linear zwischen den Punkten mit einfachem Dreisatz die Zwischenwerte berechnen kann, da die Genauigkeit der Sensoren eh so groß ist. Danke für die vielen Tipps.
Johann L. schrieb: > (a, f(a)), (b, f(b)) und für die beiden anderen Kontrollpunkte gilt, > dass deren x-Koordinaten das Intervall im Verhältnis 1:2 bzw. 2:1 teilen > unanhängig von der darzustellenden Funktion. Wo steht das? Was soll ein 2:1 geteiltes Intervall sein? Kubische Splines funktionieren unabhängig davon, wie die Kontrollpunkte das Intervall teilen. Allenfalls ist die von dir irgendwie etwas unklar beschriebene Anforderung an die Lage der Kontrollpunkte eine Beschränkung durch den verwendeten Algorithmus.
:
Bearbeitet durch User
> ich einfach linear zwischen den Punkten mit > einfachem Dreisatz die Zwischenwerte berechnen kann, ???Also oben schreibste das der Zusammenhang nicht-linear sei und unten lässte dir ne lineare Interpolation aufschwatzen? -> Wieder ein Beispiel für kognitive Dissonanz. Ausserden nützt eine Interpolationsformal relativ wenig, weil deren Wert gerundet werden muss, um nicht eine bessere Genauigkeit als an den gegebenen Stützstellen vorzugaukeln. Vorschlag: Mach eine LookUpTable bspw. 10° 20° 12% 13 14 14 15 15 16 17 18 18 19% Unter der Annahme das die Temp. der Eingangswert ist, musste diesen erst auf den nächsten Wert runden (bspw.: 12.4 -> 12) und dann in die Tabelle schauen. Ungültige Eingangswerte werden mit einem Fehlercode quittiert. Und natürlich solltest Du mehr Stützstellen generieren, beispielsweise durch einen Kalibrierlauf. Oder durch Auswahl einer passenden Modell-Kennlinie.
Emil S. schrieb: > Ausserden nützt eine Interpolationsformal relativ wenig, weil deren Wert > gerundet werden muss, um nicht eine bessere Genauigkeit als an den > gegebenen Stützstellen vorzugaukeln. Die Interpolationsformel mit dem richtige Modell kann auf dem kompletten Datensatz basieren und liefert damit bessere Werte, als jeder einzelne Messpunkt (Stichwort: Minimierung der Fehlerquadrate). Das Residuum darf rauschen. Da muss nichts gerundet werden. Die Genauigkeit wird von den Sensorspezifikationen und den Messfehlern vorgegeben, nicht von der Wertetabelle.
:
Bearbeitet durch User
Rainer W. schrieb: > Johann L. schrieb: >> (a, f(a)), (b, f(b)) und für die beiden anderen Kontrollpunkte gilt, >> dass deren x-Koordinaten das Intervall im Verhältnis 1:2 bzw. 2:1 teilen >> unanhängig von der darzustellenden Funktion. > > Wo steht das? Ergibt sich mit Papier und Bleistift: Gegeben sei eine Funktion ƒ → R², ƒ(t) = (x(t), y(t)), die für t in [a, b] durch einen kubischen Bézier B anzunähern ist. Eine Möglichkeit B festzulegen sind die Bedingungen: * B(a) = ƒ(a) * B(b) = ƒ(b) * B'(a) || ƒ'(a) * B'(b) || ƒ'(b) * B"(a) || ƒ"(a) * B"(b) || ƒ"(b) wobei || für "parallel" steht. Wählt man die Darstellung von B als 4-Tupel von Kontrollpunkten, dann ergeben sich die Koordinaten der Kontrollpunkte als Lösung eines 4×4 linearen Systems. (Die ersten beiden Bedingungen ergeben unmittelbar P0 und P3. Die letzten 4 Bedingungen ergeben P1 und P2). Möchte man lediglich ƒ → R approximieren, so hat man den Spezialfall x(t) = t mit x'=1 und x"=0; und für diesen Spezialfall ergibt sich die genannte Folgerung für die x-Koordinaten der Kontrollpunkte. Zum Beispiel gilt P1.x = (2a + b) / 3, was das Intervall [a, b] im Verhältnis 1:2 teilt. Unten als Beispiel in C99, wo sin(x) in [0, π/2] durch einen kubischen Spline interpoliert wird. Die (betragsmäßig) größte Abweichung ergibt sich wie erwartet in der Mitte des Intervalls und liegt bei ca. 1%. Zum Vergleich: Taylor vom Grad 3 um 0 hat in π/2 eine Abweichung von 7.5%. Die Ausgabe des Programms ist:
1 | Approximiere sin in [0.00000, 1.57080]: |
2 | Alle y Zwischenergebnisse liegen in [+0.00000, +1.00000] |
3 | 0: (0.00000, 0.00000) @ 0.00000% |
4 | 1: (0.09817, 0.09752) @ -0.05003% |
5 | 2: (0.19635, 0.19330) @ -0.17915% |
6 | 3: (0.29452, 0.28672) @ -0.35675% |
7 | 4: (0.39270, 0.37714) @ -0.55402% |
8 | 5: (0.49087, 0.46395) @ -0.74485% |
9 | 6: (0.58905, 0.54650) @ -0.90669% |
10 | 7: (0.68722, 0.62418) @ -1.02132% |
11 | 8: (0.78540, 0.69635) @ -1.07572% |
12 | 9: (0.88357, 0.76238) @ -1.06274% |
13 | 10: (0.98175, 0.82165) @ -0.98176% |
14 | 11: (1.07992, 0.87353) @ -0.83937% |
15 | 12: (1.17810, 0.91738) @ -0.64985% |
16 | 13: (1.27627, 0.95258) @ -0.43566% |
17 | 14: (1.37445, 0.97851) @ -0.22783% |
18 | 15: (1.47262, 0.99452) @ -0.06628% |
19 | 16: (1.57080, 1.00000) @ 0.00000% |
C99 Code
1 | #include <math.h> |
2 | #include <stdio.h> |
3 | |
4 | typedef struct |
5 | {
|
6 | double x0, x1, x2, x3; |
7 | double y0, y1, y2, y3; |
8 | } spline_t; |
9 | |
10 | // Sei g Gerade durch P0 = (x0,y0) mit Steigung m in P0.
|
11 | // Berechne g(x).
|
12 | static inline |
13 | double gerade (double x0, double y0, double m, double x) |
14 | {
|
15 | return y0 + m * (x - x0); |
16 | }
|
17 | |
18 | // Lineare Interpolation zwischgen a und b.
|
19 | static inline |
20 | double mix (double a, double b, double t) |
21 | {
|
22 | return a + t * (b - a); |
23 | }
|
24 | |
25 | // Auswertung des Splines an der Stelle x = mix (a, b, t).
|
26 | // Die Auswertung erfolgt nach dem Algorithmus von deCasteljau, was den Vorteil
|
27 | // hat, dass Zwischenergebnisse immer im Intervall liegen, das durch min(y_i)
|
28 | // und max(y_i) begrenzt wird. Mit 6 Multplikationen und 12 Additionen ist der
|
29 | // Aufwand allerdings höher als der der Auswertung eines kubischen Polynoms
|
30 | // nach Horner mit 3 Multiplikationen und 3 Additionen.
|
31 | // In der Praxis lässt sich der Aufwand um 3 Subtraktionen verringern, indem
|
32 | // nicht nur die y-Koordinaten der Kontrollpunkte gespeichert werden sondern
|
33 | // auch deren Differenzen y1-y0, y2-y1 und y3-y2.
|
34 | double eval (const spline_t *s, double t) |
35 | {
|
36 | double y01 = mix (s->y0, s->y1, t); |
37 | double y12 = mix (s->y1, s->y2, t); |
38 | double y23 = mix (s->y2, s->y3, t); |
39 | |
40 | double y012 = mix (y01, y12, t); |
41 | double y123 = mix (y12, y23, t); |
42 | |
43 | return mix (y012, y123, t); |
44 | }
|
45 | |
46 | static inline double min (double x, double y) { return x < y ? x : y; } |
47 | static inline double max (double x, double y) { return x > y ? x : y; } |
48 | |
49 | typedef double (*func_t)(double); |
50 | |
51 | // Approximiere f(x) in [a, b] an n+1 Stellen. df ist die Ableitung von f.
|
52 | void approx (const char *name, func_t f, func_t df, double a, double b, int n) |
53 | {
|
54 | double x1 = mix (a, b, 1.0/3.0); |
55 | double x2 = mix (a, b, 2.0/3.0); |
56 | spline_t spline = |
57 | {
|
58 | // x-Koordinaten der Kontrollpunkte werden nicht gebraucht
|
59 | // und sind nur zur Vollständigkeit angegeben.
|
60 | .x0 = a, .x1 = x1, .x2 = x2, .x3 = b, |
61 | // y-Koordinaten der Kontrollpunkte.
|
62 | .y0 = f(a), |
63 | .y1 = gerade (a, f(a), df(a), x1), |
64 | .y2 = gerade (b, f(b), df(b), x2), |
65 | .y3 = f(b) |
66 | };
|
67 | |
68 | printf ("Approximiere %s in [%.5f, %.5f]:\n", name, a, b); |
69 | printf ("Alle y Zwischenergebnisse liegen in [%+.5f, %+.5f]\n", |
70 | min (min (spline.y0, spline.y1), min (spline.y2, spline.y3)), |
71 | max (max (spline.y0, spline.y1), max (spline.y2, spline.y3))); |
72 | for (int i = 0; i <= n; ++i) |
73 | {
|
74 | double t = (double) i / n; |
75 | double y = eval (&spline, t); |
76 | double x = mix (a, b, t); |
77 | printf ("% 3d: (%.5f, %.5f) @ % .5f%%\n", i, x, y, 100 * (y - f(x))); |
78 | }
|
79 | }
|
80 | |
81 | int main (void) |
82 | {
|
83 | double pi2 = acos (0.0); // pi / 2 |
84 | approx ("sin", sin, cos, 0.0, pi2, 16); |
85 | |
86 | // double x = pi2;
|
87 | // printf ("Taylor: %.5f%%\n", 100 * (x * (1 - x*x/6) - sin (x)));
|
88 | return 0; |
89 | }
|
In der Praxis wird man splint_t extern vorberechnen und als Konstante vorhalten. > Kubische Splines funktionieren unabhängig davon, wie die Kontrollpunkte > das Intervall teilen. Die Kontrollpunkte selbst teilen das Intervall nicht, weil sie idR nicht auf der x-Achse liegen. Alle Kontrollpunkte auf die x-Achse zu legen gäbe ziemlich langweilige Funktionen. > Allenfalls ist die von dir irgendwie etwas unklar beschriebene > Anforderung an die Lage der Kontrollpunkte eine Beschränkung > durch den verwendeten Algorithmus. Es ist keine Anforderung and die Kontrollpunkte bzw. deren x-Koordinaten, sondern es ergibt sich diese Eigenschaft mit der spezielle Wahl ƒ(t) = (t, y(t)). Im übrigen teilt x = (ua + vb) / (u + v) mit u ≥ 0, v ≥ 0, a ≤ b das Intervall [a, b] im Verhältnis v : u. Wobei u und v nicht beide Null sein dürfen. Ist umgekehrt x in [a, b] gegeben, dann teilt x das Intervall im Verhältnis x-a : b-x. Beispielsweise teilt 7 das Intervall [5, 11] im Verhältnis 2:4 = 1:2.
:
Bearbeitet durch User
> Da muss nichts gerundet werden. Die Genauigkeit wird von den > Sensorspezifikationen und den Messfehlern vorgegeben, nicht von der > Wertetabelle. Mal Butter bei die Fische: Wertpaar 1: 1, 10 Wertpaar 2: 4, 20 Was wäre die korrekte Ausgabe für einen linear Interpolierten Wert bei x = 3 ? : ? 16 ? 16.6 ? 16.7 ? 16.66 ? 16.67 .. Variante der Aufgabenstellung die beiden Wertepaare sein mit [1, 10.0] und [4, 20.0] gegeben. Es geht halt nicht ohne Runden/Abschneiden auf die zulässige gültiger Anzeige-Ziffern, es sei den das Ausgabegerät ist /dev/null ;-) > Die Interpolationsformel mit dem richtige Modell kann auf dem kompletten > Datensatz basieren und liefert damit bessere Werte, als jeder einzelne > Messpunkt (Stichwort: Minimierung der Fehlerquadrate). Nein, ein Modell ist nur eine Abstraktion, die einige Eigenschaften korrekt, aber andere falsch darstellt. Prominentes Beispiel: Drehung Merkur-Perihel. Die Astronomen vor Albert (bspw. 1855 Le Verrier) haben schon korrekt und lang genug gemessen, aber erst Albert konnte ihnen 1915 sagen, das der Isaac den kleinen Term, der aus der Relativitätstheorie folgt, nicht auf dem Zettel hatte und deshalb das der Bahnberechnung tugrundeliegende Modell 'Murks' ist.
Johann L. schrieb: > Die (betragsmäßig) größte Abweichung [mit kubischen Splines] ergibt > sich wie erwartet in der Mitte des Intervalls und liegt bei ca. 1%. > Zum Vergleich: Taylor vom Grad 3 um 0 hat in π/2 eine Abweichung > von 7.5%. Die Köningin der Approximation durch Polynome (oder rationale Funktionen) ist naturlich die MiniMax-optimierte Approximation, d.h. diese erfüllt nachweislich
und ist in diesem Sinne optimal. Wermutstropfen ist dabei lediglich, dass solche Approximationen etwas aufwändiger zu bestimmen sind, während zur Bestimmung eines kubischen Splines Kenntnis von Funktionswert und Ableitung an den Stützstellen genügt. Bevor ich den Code poste, hier wieder eine Approximation von sin in [0, π/2] bzw. die Ausgabe des Programms:
1 | Approximiere sin in [0.00000, 1.57080]: |
2 | 0: (0.00000, -0.00137) @ -0.13671% |
3 | 1: (0.09817, 0.09850) @ 0.04823% |
4 | 2: (0.19635, 0.19636) @ 0.12742% |
5 | 3: (0.29452, 0.29159) @ 0.13048% |
6 | 4: (0.39270, 0.38354) @ 0.08522% |
7 | 5: (0.49087, 0.47156) @ 0.01673% |
8 | 6: (0.58905, 0.55504) @ -0.05341% |
9 | 7: (0.68722, 0.63331) @ -0.10802% |
10 | 8: (0.78540, 0.70576) @ -0.13506% |
11 | 9: (0.88357, 0.77173) @ -0.12837% |
12 | 10: (0.98175, 0.83059) @ -0.08837% |
13 | 11: (1.07992, 0.88169) @ -0.02263% |
14 | 12: (1.17810, 0.92442) @ 0.05356% |
15 | 13: (1.27627, 0.95811) @ 0.11675% |
16 | 14: (1.37445, 0.98213) @ 0.13489% |
17 | 15: (1.47262, 0.99586) @ 0.06707% |
18 | 16: (1.57080, 0.99863) @ -0.13671% |
19 | Taylor: -7.51678% |
Der absolute Fehler liegt bei weniger als 0.14%, was nochmals eine deutliche Verbesserung gegenüber dem kubischen Spline bedeutet, der einen Fehler von ca. 1% hat. Kubische Splines sind m.E. hier etwas deplatziert: Sie dienen vor allem für optisch ansprechende Darstellungen und einfache Darstellung bzw. "Kontrolle" via Kontrollpunkte, sind aber im Sinne des resultierenden Approximationsfehlers nicht optimal. Das Minimax-Polynom hingegen ist optimal, d.h. kein Polynom vom Grade höchstens 3 liefert bessere Ergebnisse (im Sinne des absoluten Fehlers, genommen über das Zielintervall). Hier der Code, diesmal in GNU-C99 so dass M_PI verfügbar ist:
1 | #include <math.h> |
2 | #include <stdio.h> |
3 | |
4 | // Approximiere sin(x) in [0, pi/2].
|
5 | double approx_sin_0_pi2 (double x) |
6 | {
|
7 | // Koeffizienten berechnet mit Remez-Algorithmus
|
8 | // für sin (x + pi/4) mit |x| <= pi/4.
|
9 | double a0 = +0.70575621; |
10 | double a1 = +0.70601911; |
11 | double a2 = -0.33577563; |
12 | double a3 = -0.11250597; |
13 | x -= M_PI / 4; |
14 | |
15 | // fma = float multiply-add: fma (x, y, z) = x * y + z.
|
16 | double y = a3; |
17 | y = fma (y, x, a2); |
18 | y = fma (y, x, a1); |
19 | y = fma (y, x, a0); |
20 | return y; |
21 | }
|
22 | |
23 | // Approximiere sin(x) in [0, pi/2] an n+1 Stellen.
|
24 | void print_approx_sin (int n, double a, double b) |
25 | {
|
26 | printf ("Approximiere sin in [%.5f, %.5f]:\n", a, b); |
27 | |
28 | for (int i = 0; i <= n; ++i) |
29 | {
|
30 | double t = (double) i / n; |
31 | double x = a + t * (b - a); |
32 | double y = approx_sin_0_pi2 (x); |
33 | printf ("% 3d: (%.5f, %.5f) @ % .5f%%\n", i, x, y, 100 * (y-sin(x))); |
34 | }
|
35 | }
|
36 | |
37 | int main (void) |
38 | {
|
39 | double b = M_PI / 2; |
40 | print_approx_sin (16, 0.0, b); |
41 | |
42 | printf ("Taylor: %.5f%%\n", 100 * (b * (1 - b*b/6) - sin (b))); |
43 | return 0; |
44 | }
|
Anmerkung: Die Approximante ist zentriert auf das Zielintervall, so dass zur Berechnung nur x-Werte mit |x| ≤ (b-a)/2 auftreten statt a ≤ x ≤ b. Ziel ist, die auftretenden Zwischenergebnisse betragsmäßig möglichst klein zu halten. Für double spielt das keine Rolle, für Fixed-Point aber durchaus schon.
Johann L. schrieb: > Die Köningin der Approximation durch Polynome (oder rationale > Funktionen) ist naturlich die MiniMax-optimierte Approximation, d.h. > diese erfüllt nachweislich Die Königin der Approximation ist ein mathematisches Modell, dass die Physik hinter dem Sensor beschreibt und mit entsprechend wenig Parametern auskommt, meist nicht irgendein Polynom.
Rainer W. schrieb: > Johann L. schrieb: >> Die Köningin der Approximation durch Polynome (oder rationale >> Funktionen) ist naturlich die MiniMax-optimierte Approximation, d.h. >> diese erfüllt nachweislich > > Die Königin der Approximation ist ein mathematisches Modell, dass die > Physik hinter dem Sensor beschreibt und mit entsprechend wenig > Parametern auskommt, meist nicht irgendein Polynom. Die Dampdruckkurve ist doch gegeben und nur von der entsprechenden Flüssigkeit abhängig bzw. deren physikalischen Eigenschaften? Die Abbildung ƒ: Temperatur → Sättigung ist also hinreichend genau bekannt. Die Frage ist dann: Wie genau ist ƒ zu approximieren, wenn die Eingabedaten einen (Mess-)Fehler ΔT aufweisen? Und wie genau sind die Ergebnisse? Wir haben ƒ(T+ΔT) ≈ f(T) + ΔT·ƒ'(T) und |ƒ(x) - g(x)| ≤ M also ist ƒ(x) ≈ g(x) ± M und ƒ(T+ΔT) ≈ g(T) ± M + ΔT·ƒ'(T) Bei einer sinnvolle Approximation sind beide Fehler in etwa von gleicher Größe(nordnung), also: M ≈ ΔT·|max ƒ'| und die Ergebnisse sind dann mit einem Fehler der Größenordnung ΔSättigung ≈ 2·ΔT·|max ƒ'| behaftet, wobei hier noch keine Rundungsfehler aufgrund endlicher Rechengenauigkeit beücksichtigt sind.
Johann L. schrieb: > Die Frage ist dann: Wie genau ist ƒ zu approximieren, wenn die > Eingabedaten einen (Mess-)Fehler ΔT aufweisen? Und wie genau sind die > Ergebnisse? Welche Messfehler? Der TO hat eine Tabelle, die (hoffentlich) auf hinreichend bekannten Stoffeigenschaften und einem zum Prozess passenden physikalischen Modell basiert. Daher ist davon auszugehen, dass die Fehler im Rahmen der Darstellungsgenauigkeit der Werte statistisch verteilt sind. Durch Approximation mit Minimierung der Fehlerquadrate bekommt man die Modellwerte also wieder erheblich genauer raus, als die Einzelwerte der Tabelle suggerieren.
> Welche Messfehler? > Der TO hat eine Tabelle, Man kann aber auch davon ausgehen, das der TO hat, was er zeigt. Und er zeigt lediglich zwei Wertepaare und die Aussage, das der Zusammenhang nichtlinear ist. Und da es sich um physikalisch messbare Größen handelt (Temperatur) sind diese wohl experimentell und nicht aus einem mathematischen Modell ermittelt worden.
Emil S. schrieb: > Man kann aber auch davon ausgehen, das der TO hat, was er zeigt. Ich gehe davon aus, dass er deutlich mehr also dies zwei Wertepaar hat. Woher weiß er sonst, dass die Werte sich nichtlinear verhalten. Zeigen tut er diese Tabelle bisher nicht. Bei einer Dampfdrucktabelle würde ich außerdem eher Angaben von Temperatur und Druck erwarten.
Vielleicht klärt Vincent ersma darüber auf, ob es um eine Approximation geht oder um eine Interpolation.
Pragmatische Lösung (da ich mir fast sicher bin, dass der TO mit dem gezeigten Formelwerk überfordert ist - und angesichts der Tatsache, dass die Sensorik eh mit Fehlern um die 5% daher kommt), würde ich good'old Excel bemühen und per Curve Fitting weitere Stütz-Werte erzeugen, wie z.B. in https://engineerexcel.com/nonlinear-curve-fitting-in-excel/ gezeigt. Für den uC, also um die erzeugte Tabelle von Stütz-Werten 'anzuwenden', ergibt sich dann optimierter Code (Division wird vermieden), wenn man sich in der Anzahl der Stützstellen auf (2^n)+1, also ..., 9, 17, 33, 65, ... beschränkt. Folgenden c-Code habe ich vor Urzeiten bei einem Projekt eingesetzt um die an einem NTC gemessenen Werte (adc_value) in eine Temperatur-Skala umzuwandeln.
1 | int NTC_table[17] = { |
2 | 4982, 3866, 2930, 2409, |
3 | 2040, 1748, 1499, 1277, |
4 | 1071, 873, 677, 476, |
5 | 261, 19, -278, -710, |
6 | -1454 |
7 | };
|
8 | |
9 | |
10 | /*
|
11 | * Mit p1 und p2 werden die Stützpunkte direkt vor und nach dem
|
12 | * ADC Wert ermittelt. Zwischen beiden Stützpunkten wird linear
|
13 | * interpoliert. Der Code ist sehr klein und schnell.
|
14 | * Es wird lediglich eine Ganzzahl-Multiplikation verwendet.
|
15 | * Die Division kann vom Compiler durch eine Schiebeoperation
|
16 | * ersetzt werden.
|
17 | *
|
18 | * \param adc_value Das gewandelte ADC Ergebnis - 10-bit
|
19 | * \return Die Temperatur in 0.0625 °C
|
20 | *
|
21 | */
|
22 | |
23 | |
24 | int NTC_ADC2Temperature(unsigned int adc_value){ |
25 | |
26 | int p1, p2; |
27 | unsigned int delta; |
28 | |
29 | /* Stützpunkt vor und nach dem ADC Wert ermitteln. */
|
30 | p1 = NTC_table[ (adc_value >> 4) ]; |
31 | p2 = NTC_table[ (adc_value >> 4)+1]; |
32 | |
33 | /* Zwischen beiden Punkten linear interpolieren. */
|
34 | |
35 | delta = (p1-p2) * (adc_value & 0x000F); |
36 | return p1 - (delta >> 4); |
37 | };
|
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.