<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=95.208.157.192</id>
	<title>Mikrocontroller.net - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=95.208.157.192"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/95.208.157.192"/>
	<updated>2026-04-13T13:43:02Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Arithmetik/Sinus_und_Cosinus_(Lineare_Interpolation)&amp;diff=65871</id>
		<title>AVR Arithmetik/Sinus und Cosinus (Lineare Interpolation)</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Arithmetik/Sinus_und_Cosinus_(Lineare_Interpolation)&amp;diff=65871"/>
		<updated>2012-04-22T21:43:12Z</updated>

		<summary type="html">&lt;p&gt;95.208.157.192: Die -1 hatten mich erst auch verwirrt, daher meine Änderung. Dann hab ich&amp;#039;s wieder rückgängig gemacht, weil eben Q-Format. Hiermit Versuch es etwas klarer zu formulieren&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von [[Benutzer:gjlayde|gjlayde]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:1em;&amp;quot;&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|+ &#039;&#039;&#039;Lineare Interpolation: Ressourcenverbrauch&#039;&#039;&#039;&lt;br /&gt;
|- bgcolor=&amp;quot;#ffddbb&amp;quot;&lt;br /&gt;
! Größe || maximaler Verbrauch&lt;br /&gt;
|-&lt;br /&gt;
| Laufzeit Sinus || 60 Ticks (incl. CALL+RET)&lt;br /&gt;
|-&lt;br /&gt;
| Laufzeit Cosinus || 74 Ticks (incl. CALL+RET)&lt;br /&gt;
|-&lt;br /&gt;
| RAM (statisch) || 0 Bytes&lt;br /&gt;
|-&lt;br /&gt;
| RAM (Stack) || 2&amp;amp;ndash;3 Bytes&amp;lt;ref name=&amp;quot;pc&amp;quot;&amp;gt;Abhängig von der Größe des PC&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Flash || 300 Bytes&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;&amp;gt;Übersetzt mit avr-gcc 3.4.6 für einen ATmega88&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|   || 318 Bytes (avr-gcc 4.3.3 für ATmega88)&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Die AVR-Implementierung der linearen Interpolation von Sinus und Cosinus braucht mit 300 Bytes etwas mehr Platz als [[AVR Arithmetik/Sinus und Cosinus (CORDIC)|CORDIC]], arbeitet dafür aber deutlich schneller. Das Eingabeformat ist allerdings ein anderes als bei der obigen CORDIC-Implementierung. Die Werte streuen weniger als beim CORDIC, und sowohl Maximalfehler als auch Standardabweichungen sind kleiner.&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus ist in GNU-C implementiert, greift jedoch auf Inline-Assembler zurück. Dennoch dürfte er leichter zu portieren und zu verstehen sein als eine reine Assembler-Implementierung. Er verwendet Befehle wie MUL, setzt also einen AVR aus der ATmega-Familie voraus.&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus braucht für keine Berechnung (inclusive CALL und RET) länger als 60 Ticks&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;/&amp;gt; für den Sinus bzw. 74 Ticks&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;/&amp;gt; für den Cosinus. Für avr-gcc 4.x kommen u.U einige Ticks hinzu.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Ein- und Ausgabe ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe ist so skaliert, daß ein Grad aus 256 Inkrementen besteht. Gültige Eingaben liegen also in einem Bereich von 0° bis 255.99° bzw. 0&amp;amp;middot;256...255&amp;amp;middot;256+255. Für die Berechnung von sin(45°) ist der Aufruf beispielsweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// si = sin (45°)&lt;br /&gt;
// co = cos (45°)&lt;br /&gt;
int16_t si = linsin (45*256);&lt;br /&gt;
int16_t co = lincos (45*256);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Ausgabe gilt das gleiche wie für [[AVR Arithmetik/Sinus und Cosinus (CORDIC)#Ein- und Ausgabe|CORDIC]]: Es sind vorzeichenbehaftete Werte im 1.15 Q-Format, wobei der kritische Wert 0x8000 (entspricht &amp;amp;minus;1 im Q-Format) nicht produziert wird. Kritisch u.A. deshalb, weil er nicht negiert werden kann.&lt;br /&gt;
&lt;br /&gt;
== Quellcode ==&lt;br /&gt;
&lt;br /&gt;
=== linsin.h ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef LINSIN_H&lt;br /&gt;
#define LINSIN_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int16_t linsin (uint16_t);&lt;br /&gt;
int16_t lincos (uint16_t);&lt;br /&gt;
#endif // LINSIN_H&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== linsin.c ===&lt;br /&gt;
&lt;br /&gt;
{{Scrollbox|40ex;|&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;linsin.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Nachguck-Tabelle für eine Sinus-Interpolante.&lt;br /&gt;
// Die Stützstellen sind volle °-Werte von 0°...90°.&lt;br /&gt;
// Die Werte sind so gewählt, daß die Polygonzug-Interpolante&lt;br /&gt;
// den Maximalfehler minimiert; daher sind die Werte in der&lt;br /&gt;
// Tabelle nicht die Funktionswerte des Sinus an den &lt;br /&gt;
// entsprechenden Stellen.&lt;br /&gt;
static const int16_t linsin_tab[91] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
	0x0000, 0x023c, 0x0478, 0x06b3, 0x08ee, 0x0b28, 0x0d61, 0x0f9a, 0x11d1, 0x1406,&lt;br /&gt;
	0x163a, 0x186d, 0x1a9d, 0x1ccb, 0x1ef8, 0x2121, 0x2348, 0x256d, 0x278e, 0x29ad,&lt;br /&gt;
	0x2bc8, 0x2ddf, 0x2ff4, 0x3204, 0x3410, 0x3619, 0x381d, 0x3a1d, 0x3c18, 0x3e0f,&lt;br /&gt;
	0x4001, 0x41ed, 0x43d5, 0x45b7, 0x4794, 0x496c, 0x4b3d, 0x4d09, 0x4ecf, 0x508e,&lt;br /&gt;
	0x5248, 0x53fb, 0x55a7, 0x574d, 0x58eb, 0x5a83, 0x5c14, 0x5d9e, 0x5f20, 0x609b,&lt;br /&gt;
	0x620f, 0x637a, 0x64df, 0x663b, 0x678f, 0x68db, 0x6a1f, 0x6b5b, 0x6c8e, 0x6db9,&lt;br /&gt;
	0x6edb, 0x6ff5, 0x7106, 0x720e, 0x730d, 0x7403, 0x74f0, 0x75d4, 0x76af, 0x7781,&lt;br /&gt;
	0x7849, 0x7908, 0x79bd, 0x7a69, 0x7b0c, 0x7ba5, 0x7c34, 0x7cb9, 0x7d35, 0x7da7,&lt;br /&gt;
	0x7e0f, 0x7e6e, 0x7ec2, 0x7f0d, 0x7f4e, 0x7f85, 0x7fb1, 0x7fd4, 0x7fed, 0x7ffc,&lt;br /&gt;
	0x7fff&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Ein Word per post-Increment aus dem Flash lesen.&lt;br /&gt;
// Dies ist besser als  pgm_read_word (p++)&lt;br /&gt;
#define pgm_read_word_inc(addr)             \&lt;br /&gt;
(__extension__({                            \&lt;br /&gt;
    uint16_t __result;                      \&lt;br /&gt;
    __asm__                                 \&lt;br /&gt;
    (                                       \&lt;br /&gt;
        &amp;quot;lpm %A0, Z+&amp;quot;   &amp;quot;\n\t&amp;quot;              \&lt;br /&gt;
        &amp;quot;lpm %B0, Z+&amp;quot;                       \&lt;br /&gt;
        : &amp;quot;=r&amp;quot; (__result), &amp;quot;+z&amp;quot; (addr)      \&lt;br /&gt;
    );                                      \&lt;br /&gt;
    __result;                               \&lt;br /&gt;
}))&lt;br /&gt;
&lt;br /&gt;
// Multiply-Accumulate &lt;br /&gt;
// return c + a*b&lt;br /&gt;
// c = 1.15 signed Q-Format&lt;br /&gt;
// a = 1.15 signed Q-Format&lt;br /&gt;
// b = 0.8 unsigned Q-Format&lt;br /&gt;
static inline int16_t fmac16_8 (int16_t c, int16_t a, uint8_t b)&lt;br /&gt;
{&lt;br /&gt;
    asm (&amp;quot;; fmac16_8: %B[c]:%A[c] += %B[a]:%A[a] * %[b]&amp;quot;  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;mulsu  %B[a], %[b]&amp;quot;       &amp;quot;\n\t&amp;quot;      // ( (signed)ah * (unsigned)b ) &amp;lt;&amp;lt; 1&lt;br /&gt;
        &amp;quot;add    %A[c], R0&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;adc    %B[c], R1&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;mul    %[b], %A[a]&amp;quot;       &amp;quot;\n\t&amp;quot;      // ( (unsigned) b * al ) &amp;lt;&amp;lt; 1&lt;br /&gt;
        &amp;quot;add    %A[c], R1&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;clr    __zero_reg__&amp;quot;      &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;adc    %B[c], __zero_reg__&amp;quot;&lt;br /&gt;
        : [c] &amp;quot;+r&amp;quot; (c)&lt;br /&gt;
        : [a] &amp;quot;a&amp;quot; (a), [b] &amp;quot;a&amp;quot; (b)&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sinus&lt;br /&gt;
// phi in [0°,256°) wobei 1° = 256&lt;br /&gt;
int16_t linsin (uint16_t phi)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t neg = 0;&lt;br /&gt;
    uint16_t _180_grad = (uint16_t) 180*256;&lt;br /&gt;
    // Wir wollen diese Konstante in einem Register&lt;br /&gt;
    asm (&amp;quot;&amp;quot; : &amp;quot;+r&amp;quot; (_180_grad));&lt;br /&gt;
        &lt;br /&gt;
    if (phi &amp;gt;= _180_grad)&lt;br /&gt;
    {&lt;br /&gt;
        // sin(x) = -sin(x+pi)&lt;br /&gt;
        // phi in [0°,180°)&lt;br /&gt;
        phi -= _180_grad;&lt;br /&gt;
        neg = 1;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (phi &amp;gt; 90*256)&lt;br /&gt;
        // sin(x) = sin(pi-x)&lt;br /&gt;
        // phi in [0°,90°]&lt;br /&gt;
        phi = _180_grad - phi;&lt;br /&gt;
    &lt;br /&gt;
    // Stützpunkte aus Tabelle lesen.&lt;br /&gt;
    // Für phi = 90° ist das formal nicht 100% korrekt, &lt;br /&gt;
    // da über das Tabellenende hinausgelesen wird.&lt;br /&gt;
    // In diesem Falle ist aber phi.lo=0, so daß unten &lt;br /&gt;
    // dann data1-data0 mit 0 multipliziert wird.&lt;br /&gt;
    // Ausserdem gibt&#039;s auf AVR keine Segmentation Faults ;-)&lt;br /&gt;
    const int16_t * p = &amp;amp; linsin_tab[phi &amp;gt;&amp;gt; 8];&lt;br /&gt;
    uint16_t data0  = pgm_read_word_inc (p);&lt;br /&gt;
    uint16_t data1  = pgm_read_word (p);&lt;br /&gt;
    &lt;br /&gt;
    // sin = data0 + (data1-data0) * phi_lo&lt;br /&gt;
    int16_t si = fmac16_8 (data0, data1-data0, phi);&lt;br /&gt;
    &lt;br /&gt;
    return neg ? -si : si;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Cosinus&lt;br /&gt;
// phi in [0°,256°) wobei 1° = 256&lt;br /&gt;
int16_t lincos (uint16_t phi)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t _90_grad = (uint16_t) 90*256;&lt;br /&gt;
    // Wir wollen diese Konstante in einem Register&lt;br /&gt;
    asm (&amp;quot;&amp;quot; : &amp;quot;+r&amp;quot; (_90_grad));&lt;br /&gt;
    &lt;br /&gt;
    if (phi &amp;lt;= _90_grad)&lt;br /&gt;
        // cos(x) = sin(pi/2 - x)&lt;br /&gt;
        return linsin (_90_grad - phi);&lt;br /&gt;
    else&lt;br /&gt;
        // cos(x) = -sin (x - pi/2)&lt;br /&gt;
        return -linsin (phi - _90_grad);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Aufbau der Tabelle ===&lt;br /&gt;
&lt;br /&gt;
Zur Erstellung der Tabelle gehen wir von äquidistanten Stützstellen im Abstand &amp;amp;delta; aus. Entwickeln wir die zu interpolierende Funktion in eine [http://de.wikipedia.org/wiki/Taylorreihe Taylor-Reihe] um den Mittelpunkt eines Teilintervalles der Breite &amp;amp;delta;, so erhalten wir für den absoluten Fehler&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\Delta = \frac{f&#039;&#039;(\xi)}2 \cdot \left(\frac\delta 2\right)^2 + \mathcal O(f&#039;&#039;&#039;\cdot\delta^3)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Indem wir durch eine Sekante annähern anstatt durch &amp;amp;ndash; wie durch die Taylor-Reihe gegeben &amp;amp;ndash; eine Tangente, können wir die Größenordnung des Fehlers halbieren und erhalten:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
|\Delta| \approx \tfrac1{16}\cdot |f&#039;&#039;|\cdot \delta^2 &lt;br /&gt;
 \;\leqslant\; \tfrac1{16} \cdot \delta^2 &lt;br /&gt;
 \;\stackrel!\leqslant\; 2^{-15}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Weil mit der Tabelle das Intervall [0..&amp;amp;pi;/2] interpoliert werden soll, brauchen wir mindestens&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
16\pi\sqrt{2} \approx 71&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Teilintervalle. Wir machen also die naheliegende Wahl von 90 Teilintervallen bzw. 91 Stützstellen von 0°...90° und damit&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\delta = \tfrac1{90} \cdot \tfrac\pi2 \approx 0.01745 = 571.9\cdot 2^{-15}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Damit wäre ein Eingabeformat von 512=2&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; Inkrementen pro Grad zwar naheliegend (die Ableitung des Sinus/Cosinus ist höchstens 1), aus rechentechnischen Erwägungen unterteilen wir ein Grad jedoch nur in 256=2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; Inkremente und kommen damit zum vorliegenden Eingabeformat.&lt;br /&gt;
&lt;br /&gt;
== Genauigkeit ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:1em;&amp;quot;&amp;gt;&lt;br /&gt;
{| {{Tabelle|text-align:center;}} &lt;br /&gt;
|+ &#039;&#039;&#039;Lineare Interpolation: Genauigkeit&#039;&#039;&#039;&lt;br /&gt;
|- bgcolor=&amp;quot;#ffddbb&amp;quot;&lt;br /&gt;
! Größe || Standardabweichung || Maximaler Fehler&lt;br /&gt;
|-&lt;br /&gt;
! sin   &lt;br /&gt;
| 2.2&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! cos&lt;br /&gt;
| 2.3&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Radius&lt;br /&gt;
| 2.9&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! &amp;amp;phi; &lt;br /&gt;
| 1.5&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 3.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Die Güte der Ergebnisse kann aus der nebenstehenden Tabelle entnommen werden.&lt;br /&gt;
&lt;br /&gt;
Die Ergebnisse sind recht gut normalverteilt um das korrekte Ergebnis. Der maximale absolute Fehler liegt bei 2&amp;amp;nbsp;Inkrementen bzw. 0.00006, was einer effektiven Auflösung von 14&amp;amp;nbsp; Nachkomma-Bits bzw. insgesamt 15 Bits entspricht.&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zum CORDIC sind die Ergebnisse der linearen Interpolation per Konstruktion &#039;&#039;monoton&#039;&#039;: Im Intervall 0...&amp;amp;pi;/2 ist die Sinus-Funktion z.&amp;amp;nbsp;B. monoton wachsend, und das gilt auch für die hier berechneten Werte. Bei CORDIC sind die Ergebnisse &amp;quot;verrauscht&amp;quot;, so daß es auch bei etwas größerer Eingabe zu einem kleineren Wert kommen kann.&lt;br /&gt;
&lt;br /&gt;
Die untenstehenden Diagramme zeigen die Verteilungen der absoluten Fehler. In x-Richtung ist der Fehler aufgetragen, wieder in Einheiten von 1/32767. In y-Richtung ist die Anzahl der Werte aufgetragen, die mit dem entsprechenden absoluten Fehler behaftet sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery perrow=&amp;quot;2&amp;quot; caption=&amp;quot;Standard- und Maximalabweichungen in Einheiten von &amp;amp;nbsp;1/32767&amp;quot; widths=&amp;quot;260&amp;quot; heights=&amp;quot;130&amp;quot;&amp;gt;&lt;br /&gt;
Bild:Linsin-dsin.png|Sinus&lt;br /&gt;
Bild:Linsin-dcos.png|Cosinus&lt;br /&gt;
Bild:Linsin-dr.png|Radius&lt;br /&gt;
Bild:Linsin-dphi.png|Winkel&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-CORDIC]]&lt;br /&gt;
* [[AVR Arithmetik/Sinus und Cosinus (CORDIC)|Sinus und Cosinus mit CORDIC]]&lt;br /&gt;
&lt;br /&gt;
== Anmerkungen ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Arithmetik|S]]&lt;/div&gt;</summary>
		<author><name>95.208.157.192</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Arithmetik/Sinus_und_Cosinus_(Lineare_Interpolation)&amp;diff=65865</id>
		<title>AVR Arithmetik/Sinus und Cosinus (Lineare Interpolation)</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Arithmetik/Sinus_und_Cosinus_(Lineare_Interpolation)&amp;diff=65865"/>
		<updated>2012-04-22T15:55:52Z</updated>

		<summary type="html">&lt;p&gt;95.208.157.192: Änderung 65864 von 95.208.157.192 (Diskussion) wurde rückgängig gemacht.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von [[Benutzer:gjlayde|gjlayde]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:1em;&amp;quot;&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|+ &#039;&#039;&#039;Lineare Interpolation: Ressourcenverbrauch&#039;&#039;&#039;&lt;br /&gt;
|- bgcolor=&amp;quot;#ffddbb&amp;quot;&lt;br /&gt;
! Größe || maximaler Verbrauch&lt;br /&gt;
|-&lt;br /&gt;
| Laufzeit Sinus || 60 Ticks (incl. CALL+RET)&lt;br /&gt;
|-&lt;br /&gt;
| Laufzeit Cosinus || 74 Ticks (incl. CALL+RET)&lt;br /&gt;
|-&lt;br /&gt;
| RAM (statisch) || 0 Bytes&lt;br /&gt;
|-&lt;br /&gt;
| RAM (Stack) || 2&amp;amp;ndash;3 Bytes&amp;lt;ref name=&amp;quot;pc&amp;quot;&amp;gt;Abhängig von der Größe des PC&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Flash || 300 Bytes&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;&amp;gt;Übersetzt mit avr-gcc 3.4.6 für einen ATmega88&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|   || 318 Bytes (avr-gcc 4.3.3 für ATmega88)&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Die AVR-Implementierung der linearen Interpolation von Sinus und Cosinus braucht mit 300 Bytes etwas mehr Platz als [[AVR Arithmetik/Sinus und Cosinus (CORDIC)|CORDIC]], arbeitet dafür aber deutlich schneller. Das Eingabeformat ist allerdings ein anderes als bei der obigen CORDIC-Implementierung. Die Werte streuen weniger als beim CORDIC, und sowohl Maximalfehler als auch Standardabweichungen sind kleiner.&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus ist in GNU-C implementiert, greift jedoch auf Inline-Assembler zurück. Dennoch dürfte er leichter zu portieren und zu verstehen sein als eine reine Assembler-Implementierung. Er verwendet Befehle wie MUL, setzt also einen AVR aus der ATmega-Familie voraus.&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus braucht für keine Berechnung (inclusive CALL und RET) länger als 60 Ticks&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;/&amp;gt; für den Sinus bzw. 74 Ticks&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;/&amp;gt; für den Cosinus. Für avr-gcc 4.x kommen u.U einige Ticks hinzu.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Ein- und Ausgabe ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe ist so skaliert, daß ein Grad aus 256 Inkrementen besteht. Gültige Eingaben liegen also in einem Bereich von 0° bis 255.99° bzw. 0&amp;amp;middot;256...255&amp;amp;middot;256+255. Für die Berechnung von sin(45°) ist der Aufruf beispielsweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// si = sin (45°)&lt;br /&gt;
// co = cos (45°)&lt;br /&gt;
int16_t si = linsin (45*256);&lt;br /&gt;
int16_t co = lincos (45*256);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Ausgabe gilt das gleiche wie für [[AVR Arithmetik/Sinus und Cosinus (CORDIC)#Ein- und Ausgabe|CORDIC]]: Es sind vorzeichenbehaftete Werte im 1.15 Q-Format, wobei der kritische Wert 0x8000 bzw. &amp;amp;minus;1 nicht produziert wird.&lt;br /&gt;
&lt;br /&gt;
== Quellcode ==&lt;br /&gt;
&lt;br /&gt;
=== linsin.h ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef LINSIN_H&lt;br /&gt;
#define LINSIN_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int16_t linsin (uint16_t);&lt;br /&gt;
int16_t lincos (uint16_t);&lt;br /&gt;
#endif // LINSIN_H&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== linsin.c ===&lt;br /&gt;
&lt;br /&gt;
{{Scrollbox|40ex;|&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;linsin.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Nachguck-Tabelle für eine Sinus-Interpolante.&lt;br /&gt;
// Die Stützstellen sind volle °-Werte von 0°...90°.&lt;br /&gt;
// Die Werte sind so gewählt, daß die Polygonzug-Interpolante&lt;br /&gt;
// den Maximalfehler minimiert; daher sind die Werte in der&lt;br /&gt;
// Tabelle nicht die Funktionswerte des Sinus an den &lt;br /&gt;
// entsprechenden Stellen.&lt;br /&gt;
static const int16_t linsin_tab[91] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
	0x0000, 0x023c, 0x0478, 0x06b3, 0x08ee, 0x0b28, 0x0d61, 0x0f9a, 0x11d1, 0x1406,&lt;br /&gt;
	0x163a, 0x186d, 0x1a9d, 0x1ccb, 0x1ef8, 0x2121, 0x2348, 0x256d, 0x278e, 0x29ad,&lt;br /&gt;
	0x2bc8, 0x2ddf, 0x2ff4, 0x3204, 0x3410, 0x3619, 0x381d, 0x3a1d, 0x3c18, 0x3e0f,&lt;br /&gt;
	0x4001, 0x41ed, 0x43d5, 0x45b7, 0x4794, 0x496c, 0x4b3d, 0x4d09, 0x4ecf, 0x508e,&lt;br /&gt;
	0x5248, 0x53fb, 0x55a7, 0x574d, 0x58eb, 0x5a83, 0x5c14, 0x5d9e, 0x5f20, 0x609b,&lt;br /&gt;
	0x620f, 0x637a, 0x64df, 0x663b, 0x678f, 0x68db, 0x6a1f, 0x6b5b, 0x6c8e, 0x6db9,&lt;br /&gt;
	0x6edb, 0x6ff5, 0x7106, 0x720e, 0x730d, 0x7403, 0x74f0, 0x75d4, 0x76af, 0x7781,&lt;br /&gt;
	0x7849, 0x7908, 0x79bd, 0x7a69, 0x7b0c, 0x7ba5, 0x7c34, 0x7cb9, 0x7d35, 0x7da7,&lt;br /&gt;
	0x7e0f, 0x7e6e, 0x7ec2, 0x7f0d, 0x7f4e, 0x7f85, 0x7fb1, 0x7fd4, 0x7fed, 0x7ffc,&lt;br /&gt;
	0x7fff&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Ein Word per post-Increment aus dem Flash lesen.&lt;br /&gt;
// Dies ist besser als  pgm_read_word (p++)&lt;br /&gt;
#define pgm_read_word_inc(addr)             \&lt;br /&gt;
(__extension__({                            \&lt;br /&gt;
    uint16_t __result;                      \&lt;br /&gt;
    __asm__                                 \&lt;br /&gt;
    (                                       \&lt;br /&gt;
        &amp;quot;lpm %A0, Z+&amp;quot;   &amp;quot;\n\t&amp;quot;              \&lt;br /&gt;
        &amp;quot;lpm %B0, Z+&amp;quot;                       \&lt;br /&gt;
        : &amp;quot;=r&amp;quot; (__result), &amp;quot;+z&amp;quot; (addr)      \&lt;br /&gt;
    );                                      \&lt;br /&gt;
    __result;                               \&lt;br /&gt;
}))&lt;br /&gt;
&lt;br /&gt;
// Multiply-Accumulate &lt;br /&gt;
// return c + a*b&lt;br /&gt;
// c = 1.15 signed Q-Format&lt;br /&gt;
// a = 1.15 signed Q-Format&lt;br /&gt;
// b = 0.8 unsigned Q-Format&lt;br /&gt;
static inline int16_t fmac16_8 (int16_t c, int16_t a, uint8_t b)&lt;br /&gt;
{&lt;br /&gt;
    asm (&amp;quot;; fmac16_8: %B[c]:%A[c] += %B[a]:%A[a] * %[b]&amp;quot;  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;mulsu  %B[a], %[b]&amp;quot;       &amp;quot;\n\t&amp;quot;      // ( (signed)ah * (unsigned)b ) &amp;lt;&amp;lt; 1&lt;br /&gt;
        &amp;quot;add    %A[c], R0&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;adc    %B[c], R1&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;mul    %[b], %A[a]&amp;quot;       &amp;quot;\n\t&amp;quot;      // ( (unsigned) b * al ) &amp;lt;&amp;lt; 1&lt;br /&gt;
        &amp;quot;add    %A[c], R1&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;clr    __zero_reg__&amp;quot;      &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;adc    %B[c], __zero_reg__&amp;quot;&lt;br /&gt;
        : [c] &amp;quot;+r&amp;quot; (c)&lt;br /&gt;
        : [a] &amp;quot;a&amp;quot; (a), [b] &amp;quot;a&amp;quot; (b)&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sinus&lt;br /&gt;
// phi in [0°,256°) wobei 1° = 256&lt;br /&gt;
int16_t linsin (uint16_t phi)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t neg = 0;&lt;br /&gt;
    uint16_t _180_grad = (uint16_t) 180*256;&lt;br /&gt;
    // Wir wollen diese Konstante in einem Register&lt;br /&gt;
    asm (&amp;quot;&amp;quot; : &amp;quot;+r&amp;quot; (_180_grad));&lt;br /&gt;
        &lt;br /&gt;
    if (phi &amp;gt;= _180_grad)&lt;br /&gt;
    {&lt;br /&gt;
        // sin(x) = -sin(x+pi)&lt;br /&gt;
        // phi in [0°,180°)&lt;br /&gt;
        phi -= _180_grad;&lt;br /&gt;
        neg = 1;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (phi &amp;gt; 90*256)&lt;br /&gt;
        // sin(x) = sin(pi-x)&lt;br /&gt;
        // phi in [0°,90°]&lt;br /&gt;
        phi = _180_grad - phi;&lt;br /&gt;
    &lt;br /&gt;
    // Stützpunkte aus Tabelle lesen.&lt;br /&gt;
    // Für phi = 90° ist das formal nicht 100% korrekt, &lt;br /&gt;
    // da über das Tabellenende hinausgelesen wird.&lt;br /&gt;
    // In diesem Falle ist aber phi.lo=0, so daß unten &lt;br /&gt;
    // dann data1-data0 mit 0 multipliziert wird.&lt;br /&gt;
    // Ausserdem gibt&#039;s auf AVR keine Segmentation Faults ;-)&lt;br /&gt;
    const int16_t * p = &amp;amp; linsin_tab[phi &amp;gt;&amp;gt; 8];&lt;br /&gt;
    uint16_t data0  = pgm_read_word_inc (p);&lt;br /&gt;
    uint16_t data1  = pgm_read_word (p);&lt;br /&gt;
    &lt;br /&gt;
    // sin = data0 + (data1-data0) * phi_lo&lt;br /&gt;
    int16_t si = fmac16_8 (data0, data1-data0, phi);&lt;br /&gt;
    &lt;br /&gt;
    return neg ? -si : si;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Cosinus&lt;br /&gt;
// phi in [0°,256°) wobei 1° = 256&lt;br /&gt;
int16_t lincos (uint16_t phi)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t _90_grad = (uint16_t) 90*256;&lt;br /&gt;
    // Wir wollen diese Konstante in einem Register&lt;br /&gt;
    asm (&amp;quot;&amp;quot; : &amp;quot;+r&amp;quot; (_90_grad));&lt;br /&gt;
    &lt;br /&gt;
    if (phi &amp;lt;= _90_grad)&lt;br /&gt;
        // cos(x) = sin(pi/2 - x)&lt;br /&gt;
        return linsin (_90_grad - phi);&lt;br /&gt;
    else&lt;br /&gt;
        // cos(x) = -sin (x - pi/2)&lt;br /&gt;
        return -linsin (phi - _90_grad);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Aufbau der Tabelle ===&lt;br /&gt;
&lt;br /&gt;
Zur Erstellung der Tabelle gehen wir von äquidistanten Stützstellen im Abstand &amp;amp;delta; aus. Entwickeln wir die zu interpolierende Funktion in eine [http://de.wikipedia.org/wiki/Taylorreihe Taylor-Reihe] um den Mittelpunkt eines Teilintervalles der Breite &amp;amp;delta;, so erhalten wir für den absoluten Fehler&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\Delta = \frac{f&#039;&#039;(\xi)}2 \cdot \left(\frac\delta 2\right)^2 + \mathcal O(f&#039;&#039;&#039;\cdot\delta^3)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Indem wir durch eine Sekante annähern anstatt durch &amp;amp;ndash; wie durch die Taylor-Reihe gegeben &amp;amp;ndash; eine Tangente, können wir die Größenordnung des Fehlers halbieren und erhalten:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
|\Delta| \approx \tfrac1{16}\cdot |f&#039;&#039;|\cdot \delta^2 &lt;br /&gt;
 \;\leqslant\; \tfrac1{16} \cdot \delta^2 &lt;br /&gt;
 \;\stackrel!\leqslant\; 2^{-15}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Weil mit der Tabelle das Intervall [0..&amp;amp;pi;/2] interpoliert werden soll, brauchen wir mindestens&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
16\pi\sqrt{2} \approx 71&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Teilintervalle. Wir machen also die naheliegende Wahl von 90 Teilintervallen bzw. 91 Stützstellen von 0°...90° und damit&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\delta = \tfrac1{90} \cdot \tfrac\pi2 \approx 0.01745 = 571.9\cdot 2^{-15}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Damit wäre ein Eingabeformat von 512=2&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; Inkrementen pro Grad zwar naheliegend (die Ableitung des Sinus/Cosinus ist höchstens 1), aus rechentechnischen Erwägungen unterteilen wir ein Grad jedoch nur in 256=2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; Inkremente und kommen damit zum vorliegenden Eingabeformat.&lt;br /&gt;
&lt;br /&gt;
== Genauigkeit ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:1em;&amp;quot;&amp;gt;&lt;br /&gt;
{| {{Tabelle|text-align:center;}} &lt;br /&gt;
|+ &#039;&#039;&#039;Lineare Interpolation: Genauigkeit&#039;&#039;&#039;&lt;br /&gt;
|- bgcolor=&amp;quot;#ffddbb&amp;quot;&lt;br /&gt;
! Größe || Standardabweichung || Maximaler Fehler&lt;br /&gt;
|-&lt;br /&gt;
! sin   &lt;br /&gt;
| 2.2&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! cos&lt;br /&gt;
| 2.3&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Radius&lt;br /&gt;
| 2.9&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! &amp;amp;phi; &lt;br /&gt;
| 1.5&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 3.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Die Güte der Ergebnisse kann aus der nebenstehenden Tabelle entnommen werden.&lt;br /&gt;
&lt;br /&gt;
Die Ergebnisse sind recht gut normalverteilt um das korrekte Ergebnis. Der maximale absolute Fehler liegt bei 2&amp;amp;nbsp;Inkrementen bzw. 0.00006, was einer effektiven Auflösung von 14&amp;amp;nbsp; Nachkomma-Bits bzw. insgesamt 15 Bits entspricht.&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zum CORDIC sind die Ergebnisse der linearen Interpolation per Konstruktion &#039;&#039;monoton&#039;&#039;: Im Intervall 0...&amp;amp;pi;/2 ist die Sinus-Funktion z.&amp;amp;nbsp;B. monoton wachsend, und das gilt auch für die hier berechneten Werte. Bei CORDIC sind die Ergebnisse &amp;quot;verrauscht&amp;quot;, so daß es auch bei etwas größerer Eingabe zu einem kleineren Wert kommen kann.&lt;br /&gt;
&lt;br /&gt;
Die untenstehenden Diagramme zeigen die Verteilungen der absoluten Fehler. In x-Richtung ist der Fehler aufgetragen, wieder in Einheiten von 1/32767. In y-Richtung ist die Anzahl der Werte aufgetragen, die mit dem entsprechenden absoluten Fehler behaftet sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery perrow=&amp;quot;2&amp;quot; caption=&amp;quot;Standard- und Maximalabweichungen in Einheiten von &amp;amp;nbsp;1/32767&amp;quot; widths=&amp;quot;260&amp;quot; heights=&amp;quot;130&amp;quot;&amp;gt;&lt;br /&gt;
Bild:Linsin-dsin.png|Sinus&lt;br /&gt;
Bild:Linsin-dcos.png|Cosinus&lt;br /&gt;
Bild:Linsin-dr.png|Radius&lt;br /&gt;
Bild:Linsin-dphi.png|Winkel&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-CORDIC]]&lt;br /&gt;
* [[AVR Arithmetik/Sinus und Cosinus (CORDIC)|Sinus und Cosinus mit CORDIC]]&lt;br /&gt;
&lt;br /&gt;
== Anmerkungen ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Arithmetik|S]]&lt;/div&gt;</summary>
		<author><name>95.208.157.192</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Arithmetik/Sinus_und_Cosinus_(Lineare_Interpolation)&amp;diff=65864</id>
		<title>AVR Arithmetik/Sinus und Cosinus (Lineare Interpolation)</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Arithmetik/Sinus_und_Cosinus_(Lineare_Interpolation)&amp;diff=65864"/>
		<updated>2012-04-22T15:50:27Z</updated>

		<summary type="html">&lt;p&gt;95.208.157.192: /* Ein- und Ausgabe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von [[Benutzer:gjlayde|gjlayde]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:1em;&amp;quot;&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|+ &#039;&#039;&#039;Lineare Interpolation: Ressourcenverbrauch&#039;&#039;&#039;&lt;br /&gt;
|- bgcolor=&amp;quot;#ffddbb&amp;quot;&lt;br /&gt;
! Größe || maximaler Verbrauch&lt;br /&gt;
|-&lt;br /&gt;
| Laufzeit Sinus || 60 Ticks (incl. CALL+RET)&lt;br /&gt;
|-&lt;br /&gt;
| Laufzeit Cosinus || 74 Ticks (incl. CALL+RET)&lt;br /&gt;
|-&lt;br /&gt;
| RAM (statisch) || 0 Bytes&lt;br /&gt;
|-&lt;br /&gt;
| RAM (Stack) || 2&amp;amp;ndash;3 Bytes&amp;lt;ref name=&amp;quot;pc&amp;quot;&amp;gt;Abhängig von der Größe des PC&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Flash || 300 Bytes&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;&amp;gt;Übersetzt mit avr-gcc 3.4.6 für einen ATmega88&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|   || 318 Bytes (avr-gcc 4.3.3 für ATmega88)&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Die AVR-Implementierung der linearen Interpolation von Sinus und Cosinus braucht mit 300 Bytes etwas mehr Platz als [[AVR Arithmetik/Sinus und Cosinus (CORDIC)|CORDIC]], arbeitet dafür aber deutlich schneller. Das Eingabeformat ist allerdings ein anderes als bei der obigen CORDIC-Implementierung. Die Werte streuen weniger als beim CORDIC, und sowohl Maximalfehler als auch Standardabweichungen sind kleiner.&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus ist in GNU-C implementiert, greift jedoch auf Inline-Assembler zurück. Dennoch dürfte er leichter zu portieren und zu verstehen sein als eine reine Assembler-Implementierung. Er verwendet Befehle wie MUL, setzt also einen AVR aus der ATmega-Familie voraus.&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus braucht für keine Berechnung (inclusive CALL und RET) länger als 60 Ticks&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;/&amp;gt; für den Sinus bzw. 74 Ticks&amp;lt;ref name=&amp;quot;3.4.6&amp;quot;/&amp;gt; für den Cosinus. Für avr-gcc 4.x kommen u.U einige Ticks hinzu.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Ein- und Ausgabe ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe ist so skaliert, daß ein Grad aus 256 Inkrementen besteht. Gültige Eingaben liegen also in einem Bereich von 0° bis 255.99° bzw. 0&amp;amp;middot;256...255&amp;amp;middot;256+255. Für die Berechnung von sin(45°) ist der Aufruf beispielsweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// si = sin (45°)&lt;br /&gt;
// co = cos (45°)&lt;br /&gt;
int16_t si = linsin (45*256);&lt;br /&gt;
int16_t co = lincos (45*256);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Ausgabe gilt das gleiche wie für [[AVR Arithmetik/Sinus und Cosinus (CORDIC)#Ein- und Ausgabe|CORDIC]]: Es sind vorzeichenbehaftete Werte im 1.15 Q-Format, wobei der kritische Wert 0x8000 bzw. &amp;amp;minus;32768 nicht produziert wird.&lt;br /&gt;
&lt;br /&gt;
== Quellcode ==&lt;br /&gt;
&lt;br /&gt;
=== linsin.h ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef LINSIN_H&lt;br /&gt;
#define LINSIN_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int16_t linsin (uint16_t);&lt;br /&gt;
int16_t lincos (uint16_t);&lt;br /&gt;
#endif // LINSIN_H&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== linsin.c ===&lt;br /&gt;
&lt;br /&gt;
{{Scrollbox|40ex;|&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;linsin.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Nachguck-Tabelle für eine Sinus-Interpolante.&lt;br /&gt;
// Die Stützstellen sind volle °-Werte von 0°...90°.&lt;br /&gt;
// Die Werte sind so gewählt, daß die Polygonzug-Interpolante&lt;br /&gt;
// den Maximalfehler minimiert; daher sind die Werte in der&lt;br /&gt;
// Tabelle nicht die Funktionswerte des Sinus an den &lt;br /&gt;
// entsprechenden Stellen.&lt;br /&gt;
static const int16_t linsin_tab[91] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
	0x0000, 0x023c, 0x0478, 0x06b3, 0x08ee, 0x0b28, 0x0d61, 0x0f9a, 0x11d1, 0x1406,&lt;br /&gt;
	0x163a, 0x186d, 0x1a9d, 0x1ccb, 0x1ef8, 0x2121, 0x2348, 0x256d, 0x278e, 0x29ad,&lt;br /&gt;
	0x2bc8, 0x2ddf, 0x2ff4, 0x3204, 0x3410, 0x3619, 0x381d, 0x3a1d, 0x3c18, 0x3e0f,&lt;br /&gt;
	0x4001, 0x41ed, 0x43d5, 0x45b7, 0x4794, 0x496c, 0x4b3d, 0x4d09, 0x4ecf, 0x508e,&lt;br /&gt;
	0x5248, 0x53fb, 0x55a7, 0x574d, 0x58eb, 0x5a83, 0x5c14, 0x5d9e, 0x5f20, 0x609b,&lt;br /&gt;
	0x620f, 0x637a, 0x64df, 0x663b, 0x678f, 0x68db, 0x6a1f, 0x6b5b, 0x6c8e, 0x6db9,&lt;br /&gt;
	0x6edb, 0x6ff5, 0x7106, 0x720e, 0x730d, 0x7403, 0x74f0, 0x75d4, 0x76af, 0x7781,&lt;br /&gt;
	0x7849, 0x7908, 0x79bd, 0x7a69, 0x7b0c, 0x7ba5, 0x7c34, 0x7cb9, 0x7d35, 0x7da7,&lt;br /&gt;
	0x7e0f, 0x7e6e, 0x7ec2, 0x7f0d, 0x7f4e, 0x7f85, 0x7fb1, 0x7fd4, 0x7fed, 0x7ffc,&lt;br /&gt;
	0x7fff&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Ein Word per post-Increment aus dem Flash lesen.&lt;br /&gt;
// Dies ist besser als  pgm_read_word (p++)&lt;br /&gt;
#define pgm_read_word_inc(addr)             \&lt;br /&gt;
(__extension__({                            \&lt;br /&gt;
    uint16_t __result;                      \&lt;br /&gt;
    __asm__                                 \&lt;br /&gt;
    (                                       \&lt;br /&gt;
        &amp;quot;lpm %A0, Z+&amp;quot;   &amp;quot;\n\t&amp;quot;              \&lt;br /&gt;
        &amp;quot;lpm %B0, Z+&amp;quot;                       \&lt;br /&gt;
        : &amp;quot;=r&amp;quot; (__result), &amp;quot;+z&amp;quot; (addr)      \&lt;br /&gt;
    );                                      \&lt;br /&gt;
    __result;                               \&lt;br /&gt;
}))&lt;br /&gt;
&lt;br /&gt;
// Multiply-Accumulate &lt;br /&gt;
// return c + a*b&lt;br /&gt;
// c = 1.15 signed Q-Format&lt;br /&gt;
// a = 1.15 signed Q-Format&lt;br /&gt;
// b = 0.8 unsigned Q-Format&lt;br /&gt;
static inline int16_t fmac16_8 (int16_t c, int16_t a, uint8_t b)&lt;br /&gt;
{&lt;br /&gt;
    asm (&amp;quot;; fmac16_8: %B[c]:%A[c] += %B[a]:%A[a] * %[b]&amp;quot;  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;mulsu  %B[a], %[b]&amp;quot;       &amp;quot;\n\t&amp;quot;      // ( (signed)ah * (unsigned)b ) &amp;lt;&amp;lt; 1&lt;br /&gt;
        &amp;quot;add    %A[c], R0&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;adc    %B[c], R1&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;mul    %[b], %A[a]&amp;quot;       &amp;quot;\n\t&amp;quot;      // ( (unsigned) b * al ) &amp;lt;&amp;lt; 1&lt;br /&gt;
        &amp;quot;add    %A[c], R1&amp;quot;         &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;clr    __zero_reg__&amp;quot;      &amp;quot;\n\t&amp;quot;&lt;br /&gt;
        &amp;quot;adc    %B[c], __zero_reg__&amp;quot;&lt;br /&gt;
        : [c] &amp;quot;+r&amp;quot; (c)&lt;br /&gt;
        : [a] &amp;quot;a&amp;quot; (a), [b] &amp;quot;a&amp;quot; (b)&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    return c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sinus&lt;br /&gt;
// phi in [0°,256°) wobei 1° = 256&lt;br /&gt;
int16_t linsin (uint16_t phi)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t neg = 0;&lt;br /&gt;
    uint16_t _180_grad = (uint16_t) 180*256;&lt;br /&gt;
    // Wir wollen diese Konstante in einem Register&lt;br /&gt;
    asm (&amp;quot;&amp;quot; : &amp;quot;+r&amp;quot; (_180_grad));&lt;br /&gt;
        &lt;br /&gt;
    if (phi &amp;gt;= _180_grad)&lt;br /&gt;
    {&lt;br /&gt;
        // sin(x) = -sin(x+pi)&lt;br /&gt;
        // phi in [0°,180°)&lt;br /&gt;
        phi -= _180_grad;&lt;br /&gt;
        neg = 1;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (phi &amp;gt; 90*256)&lt;br /&gt;
        // sin(x) = sin(pi-x)&lt;br /&gt;
        // phi in [0°,90°]&lt;br /&gt;
        phi = _180_grad - phi;&lt;br /&gt;
    &lt;br /&gt;
    // Stützpunkte aus Tabelle lesen.&lt;br /&gt;
    // Für phi = 90° ist das formal nicht 100% korrekt, &lt;br /&gt;
    // da über das Tabellenende hinausgelesen wird.&lt;br /&gt;
    // In diesem Falle ist aber phi.lo=0, so daß unten &lt;br /&gt;
    // dann data1-data0 mit 0 multipliziert wird.&lt;br /&gt;
    // Ausserdem gibt&#039;s auf AVR keine Segmentation Faults ;-)&lt;br /&gt;
    const int16_t * p = &amp;amp; linsin_tab[phi &amp;gt;&amp;gt; 8];&lt;br /&gt;
    uint16_t data0  = pgm_read_word_inc (p);&lt;br /&gt;
    uint16_t data1  = pgm_read_word (p);&lt;br /&gt;
    &lt;br /&gt;
    // sin = data0 + (data1-data0) * phi_lo&lt;br /&gt;
    int16_t si = fmac16_8 (data0, data1-data0, phi);&lt;br /&gt;
    &lt;br /&gt;
    return neg ? -si : si;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Cosinus&lt;br /&gt;
// phi in [0°,256°) wobei 1° = 256&lt;br /&gt;
int16_t lincos (uint16_t phi)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t _90_grad = (uint16_t) 90*256;&lt;br /&gt;
    // Wir wollen diese Konstante in einem Register&lt;br /&gt;
    asm (&amp;quot;&amp;quot; : &amp;quot;+r&amp;quot; (_90_grad));&lt;br /&gt;
    &lt;br /&gt;
    if (phi &amp;lt;= _90_grad)&lt;br /&gt;
        // cos(x) = sin(pi/2 - x)&lt;br /&gt;
        return linsin (_90_grad - phi);&lt;br /&gt;
    else&lt;br /&gt;
        // cos(x) = -sin (x - pi/2)&lt;br /&gt;
        return -linsin (phi - _90_grad);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Aufbau der Tabelle ===&lt;br /&gt;
&lt;br /&gt;
Zur Erstellung der Tabelle gehen wir von äquidistanten Stützstellen im Abstand &amp;amp;delta; aus. Entwickeln wir die zu interpolierende Funktion in eine [http://de.wikipedia.org/wiki/Taylorreihe Taylor-Reihe] um den Mittelpunkt eines Teilintervalles der Breite &amp;amp;delta;, so erhalten wir für den absoluten Fehler&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\Delta = \frac{f&#039;&#039;(\xi)}2 \cdot \left(\frac\delta 2\right)^2 + \mathcal O(f&#039;&#039;&#039;\cdot\delta^3)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Indem wir durch eine Sekante annähern anstatt durch &amp;amp;ndash; wie durch die Taylor-Reihe gegeben &amp;amp;ndash; eine Tangente, können wir die Größenordnung des Fehlers halbieren und erhalten:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
|\Delta| \approx \tfrac1{16}\cdot |f&#039;&#039;|\cdot \delta^2 &lt;br /&gt;
 \;\leqslant\; \tfrac1{16} \cdot \delta^2 &lt;br /&gt;
 \;\stackrel!\leqslant\; 2^{-15}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Weil mit der Tabelle das Intervall [0..&amp;amp;pi;/2] interpoliert werden soll, brauchen wir mindestens&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
16\pi\sqrt{2} \approx 71&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Teilintervalle. Wir machen also die naheliegende Wahl von 90 Teilintervallen bzw. 91 Stützstellen von 0°...90° und damit&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\delta = \tfrac1{90} \cdot \tfrac\pi2 \approx 0.01745 = 571.9\cdot 2^{-15}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Damit wäre ein Eingabeformat von 512=2&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; Inkrementen pro Grad zwar naheliegend (die Ableitung des Sinus/Cosinus ist höchstens 1), aus rechentechnischen Erwägungen unterteilen wir ein Grad jedoch nur in 256=2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; Inkremente und kommen damit zum vorliegenden Eingabeformat.&lt;br /&gt;
&lt;br /&gt;
== Genauigkeit ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:1em;&amp;quot;&amp;gt;&lt;br /&gt;
{| {{Tabelle|text-align:center;}} &lt;br /&gt;
|+ &#039;&#039;&#039;Lineare Interpolation: Genauigkeit&#039;&#039;&#039;&lt;br /&gt;
|- bgcolor=&amp;quot;#ffddbb&amp;quot;&lt;br /&gt;
! Größe || Standardabweichung || Maximaler Fehler&lt;br /&gt;
|-&lt;br /&gt;
! sin   &lt;br /&gt;
| 2.2&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! cos&lt;br /&gt;
| 2.3&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Radius&lt;br /&gt;
| 2.9&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 6.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! &amp;amp;phi; &lt;br /&gt;
| 1.5&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt; || 3.1&amp;amp;middot;10&amp;lt;sup&amp;gt;-5&amp;lt;/sup&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Die Güte der Ergebnisse kann aus der nebenstehenden Tabelle entnommen werden.&lt;br /&gt;
&lt;br /&gt;
Die Ergebnisse sind recht gut normalverteilt um das korrekte Ergebnis. Der maximale absolute Fehler liegt bei 2&amp;amp;nbsp;Inkrementen bzw. 0.00006, was einer effektiven Auflösung von 14&amp;amp;nbsp; Nachkomma-Bits bzw. insgesamt 15 Bits entspricht.&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zum CORDIC sind die Ergebnisse der linearen Interpolation per Konstruktion &#039;&#039;monoton&#039;&#039;: Im Intervall 0...&amp;amp;pi;/2 ist die Sinus-Funktion z.&amp;amp;nbsp;B. monoton wachsend, und das gilt auch für die hier berechneten Werte. Bei CORDIC sind die Ergebnisse &amp;quot;verrauscht&amp;quot;, so daß es auch bei etwas größerer Eingabe zu einem kleineren Wert kommen kann.&lt;br /&gt;
&lt;br /&gt;
Die untenstehenden Diagramme zeigen die Verteilungen der absoluten Fehler. In x-Richtung ist der Fehler aufgetragen, wieder in Einheiten von 1/32767. In y-Richtung ist die Anzahl der Werte aufgetragen, die mit dem entsprechenden absoluten Fehler behaftet sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery perrow=&amp;quot;2&amp;quot; caption=&amp;quot;Standard- und Maximalabweichungen in Einheiten von &amp;amp;nbsp;1/32767&amp;quot; widths=&amp;quot;260&amp;quot; heights=&amp;quot;130&amp;quot;&amp;gt;&lt;br /&gt;
Bild:Linsin-dsin.png|Sinus&lt;br /&gt;
Bild:Linsin-dcos.png|Cosinus&lt;br /&gt;
Bild:Linsin-dr.png|Radius&lt;br /&gt;
Bild:Linsin-dphi.png|Winkel&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-CORDIC]]&lt;br /&gt;
* [[AVR Arithmetik/Sinus und Cosinus (CORDIC)|Sinus und Cosinus mit CORDIC]]&lt;br /&gt;
&lt;br /&gt;
== Anmerkungen ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Arithmetik|S]]&lt;/div&gt;</summary>
		<author><name>95.208.157.192</name></author>
	</entry>
</feed>