webentwicklung-frage-antwort-db.com.de

Sinussignal in C ohne Verwendung der Standardfunktion erzeugen

Ich möchte in C ein Sinussignal erzeugen, ohne die Standardfunktion sin () zu verwenden, um sinusförmige Änderungen in der Helligkeit einer LED auszulösen. Meine Grundidee war, eine Nachschlagetabelle mit 40 Punkten und Interpolation zu verwenden.

Hier ist mein erster Ansatz:

const int sine_table[40] = {0, 5125, 10125, 14876, 19260, 23170, 26509, 29196,
31163, 32364, 32767,  32364, 31163, 29196, 26509, 23170, 19260, 14876, 10125,
5125, 0, -5126, -10126,-14877, -19261, -23171, -26510, -29197, -31164, -32365,
-32768, -32365, -31164, -29197, -26510, -23171, -19261, -14877, -10126, -5126};

int i = 0;
int x1 = 0;
int x2 = 0;
float y = 0;

float sin1(float phase)
{
    x1 = (int) phase % 41;
    x2 = x1 + 1;
    y = (sine_table[x2] - sine_table[x1])*((float) ((int) (40*0.001*i*100) % 4100)/100 - x1) + sine_table[x1];
    return y;
}

int main()
{
    while(1)
    {
    printf("%f      ", sin1(40*0.001*i)/32768);
    i = i + 1;
    }
}

Leider liefert diese Funktion manchmal Werte, die viel größer als 1 sind. Darüber hinaus scheint die Interpolation nicht gut zu sein (ich habe dies verwendet, um sinusförmige Helligkeitsänderungen einer LED zu erzeugen, aber diese sind sehr unsauber).

Hat jemand eine bessere Idee, einen Sinusgenerator in C zu implementieren?

27
Peter123

Das Hauptproblem von OP besteht darin, den Index für die Tabellensuche zu generieren.

Der OP-Code versucht, auf ein externes Array sine_table[40] Zuzugreifen, was zu undefiniertem Verhalten führt. Repariere das zumindest.

const int sine_table[40] = {0, 5125, 10125, ...
    ...
    x1 = (int) phase % 41;                     // -40 <= x1 <= 40
    x2 = x1 + 1;                               // -39 <= x2 <= 41  
    y = (sine_table[x2] - sine_table[x1])*...  // bad code, consider x1 = 40 or x2 = 40,41

Vorgeschlagene Änderung

    x1 = (int) phase % 40;   // mod 40, not 41
    if (x1 < 0) x1 += 40;    // Handle negative values
    x2 = (x1 + 1) % 40;      // Handle wrap-around 
    y = (sine_table[x2] - sine_table[x1])*...  

Es gibt viel bessere Ansätze, um sich jedoch auf die OP-Methode zu konzentrieren, siehe unten.

#include <math.h>
#include <stdio.h>

const int sine_table[40] = { 0, 5125, 10125, 14876, 19260, 23170, 26509, 29196,
31163, 32364, 32767, 32364, 31163, 29196, 26509, 23170, 19260, 14876, 10125,
5125, 0, -5126, -10126, -14877, -19261, -23171, -26510, -29197, -31164, -32365,
-32768, -32365, -31164, -29197, -26510, -23171, -19261, -14877, -10126, -5126 };

int i = 0;
int x1 = 0;
int x2 = 0;
float y = 0;

float sin1(float phase) {
  x1 = (int) phase % 40;
  if (x1 < 0) x1 += 40;
  x2 = (x1 + 1) % 40;
  y = (sine_table[x2] - sine_table[x1])
      * ((float) ((int) (40 * 0.001 * i * 100) % 4100) / 100 - x1)
      + sine_table[x1];
  return y;
}

int main(void) {
  double pi = 3.1415926535897932384626433832795;
  for (int j = 0; j < 1000; j++) {
    float x = 40 * 0.001 * i;
    float radians = x * 2 * pi / 40;
    printf("%f %f %f\n", x, sin1(x) / 32768, sin(radians));
    i = i + 1;
  }
}

Ausgabe

         OP's     Reference sin()
0.000000 0.000000 0.000000
0.040000 0.006256 0.006283
0.080000 0.012512 0.012566
...
1.960000 0.301361 0.303035
2.000000 0.308990 0.309017
2.040000 0.314790 0.314987
...
39.880001 -0.020336 -0.018848
39.919998 -0.014079 -0.012567
39.959999 -0.006257 -0.006283

Besserer Code würde die Werte i, x1, x2, y Nicht als globale Variablen, sondern als Funktionsparameter oder Funktionsvariablen übergeben. Vielleicht ist das ein Artefakt beim Debuggen von OP.


Hat jemand eine bessere Idee, einen Sinusgenerator in C zu implementieren?

Das ist ziemlich breit. Besser als in Bezug auf Geschwindigkeit, Präzision, Code-Speicherplatz, Portabilität oder Wartbarkeit? sine() Funktionen sind einfach zu erstellen. Hochwertige nehmen mehr Kraft in Anspruch.

Obwohl es unscharf ist, ist die Verwendung einer kleinen Nachschlagetabelle durch OP ein guter Anfang - obwohl ich sehe, dass dies ohne Gleitkomma-Mathematik möglich ist. Ich empfehle OP, eine getestete und funktionierende Lösung zu konstruieren und sie für Verbesserungsvorschläge in Code Review zu veröffentlichen.

21
chux

... eine bessere Idee, einen Sinusgenerator in C zu implementieren?

Bearbeiten: Vorschlagen der ersten Lesung dieser Artikel um eine Einschätzung dessen zu erhalten, was OP verlangt.

Aus dem in Ihrer Frage gegebenen Kontext gehe ich davon aus, dass das Wort besser möglicherweise mit der Größe und Geschwindigkeit von zu tun hat kompilierter Code, der möglicherweise für die Ausführung auf einem kleinen Mikroprozessor erforderlich ist.

Der CORDIC-Algorithmus ( COordinate Rotation Digital Computer) eignet sich sehr gut für kleinere uP- und FPGA-Implementierungen mit eingeschränkten mathematischen Rechenkapazitäten, da er den Sinus und den Cosinus eines Werts berechnet mit nur Grundrechenarten (Addition, Subtraktion und Verschiebung). Weitere Informationen zu CORDIC und dessen Verwendung zur Erzeugung von Sinus/Cosinus eines Winkels werden hier bereitgestellt.

Es gibt auch mehrere Sites, die Beispiele für die Implementierung von Algorithmen enthalten. Simple CORDIC enthält ausführliche Erläuterungen zum Generieren einer Tabelle, die für die Verwendung auf Ihrem Zielgerät vorkompiliert werden kann, sowie zu testenden Code die Ausgabe der folgenden Funktion (die Festkomma-Mathematik verwendet):

(Siehe Dokumentation der folgenden und anderer Funktionen im Link)

#define cordic_1K 0x26DD3B6A
#define half_pi 0x6487ED51
#define MUL 1073741824.000000
#define CORDIC_NTAB 32
int cordic_ctab [] = {0x3243F6A8, 0x1DAC6705, 0x0FADBAFC, 0x07F56EA6, 0x03FEAB76, 0x01FFD55B, 
0x00FFFAAA, 0x007FFF55, 0x003FFFEA, 0x001FFFFD, 0x000FFFFF, 0x0007FFFF, 0x0003FFFF, 
0x0001FFFF, 0x0000FFFF, 0x00007FFF, 0x00003FFF, 0x00001FFF, 0x00000FFF, 0x000007FF, 
0x000003FF, 0x000001FF, 0x000000FF, 0x0000007F, 0x0000003F, 0x0000001F, 0x0000000F, 
0x00000008, 0x00000004, 0x00000002, 0x00000001, 0x00000000 };

void cordic(int theta, int *s, int *c, int n)
{
  int k, d, tx, ty, tz;
  int x=cordic_1K,y=0,z=theta;
  n = (n>CORDIC_NTAB) ? CORDIC_NTAB : n;
  for (k=0; k<n; ++k)
  {
    d = z>>31;
    //get sign. for other architectures, you might want to use the more portable version
    //d = z>=0 ? 0 : -1;
    tx = x - (((y>>k) ^ d) - d);
    ty = y + (((x>>k) ^ d) - d);
    tz = z - ((cordic_ctab[k] ^ d) - d);
    x = tx; y = ty; z = tz;
  }  
 *c = x; *s = y;
}

Edit:
Ich fand die Dokumentation zur Verwendung der Beispiele auf der Simple CORDIC Seite sehr einfach zu befolgen. Ein kleines Problem war jedoch, dass beim Kompilieren der Datei cordic-test.c Der Fehler auftrat: Verwendung der nicht deklarierten Kennung 'M_PI'. Es scheint, dass beim Ausführen der kompilierten gentable.c - Datei (die die cordic-test.c - Datei generiert) die Zeile:

#define M_PI 3.1415926535897932384626

obwohl in eigenen Deklarationen enthalten, nicht in den printf-Anweisungen enthalten, die zur Erstellung der Datei cordic-test.c verwendet wurden. Sobald dies behoben war, funktionierte alles wie angekündigt.

Wie dokumentiert, erzeugt der erzeugte Datenbereich 1/4 eines vollständigen Sinuszyklus (-π/2 - π/2). Die folgende Abbildung enthält eine Darstellung der tatsächlichen Daten, die zwischen den hellblauen Punkten erzeugt werden. Der Rest des Sinussignals wird durch Spiegeln und Transponieren des ursprünglichen Datenabschnitts hergestellt.

enter image description here

18
ryyker

Das Erzeugen einer genauen Sinusfunktion erfordert eine Menge an Ressourcen (CPU-Zyklen und Speicher), die in dieser Anwendung nicht gerechtfertigt sind. Ihr Ziel, eine "glatte" Sinuskurve zu erzeugen, besteht darin, die Anforderungen der Anwendung nicht zu berücksichtigen.

  • Während Sie beim Zeichnen der Kurve möglicherweise Unvollkommenheiten beobachten, erkennt das menschliche Auge diese Unvollkommenheiten überhaupt nicht, wenn Sie diese Kurve auf ein LED-PWM-Laufwerk anwenden.

  • Es ist auch nicht wahrscheinlich, dass das menschliche Auge den Helligkeitsunterschied zwischen benachbarten Werten in einer 40-stufigen Kurve wahrnimmt, so dass eine Interpolation nicht erforderlich ist.

  • Im Allgemeinen ist es effizienter, wenn Sie eine Sinusfunktion generieren, die die entsprechenden PWM-Antriebswerte direkt ohne Gleitkomma generiert. Tatsächlich wäre ein skalierter, erhöhter Cosinus geeigneter als eine Sinusfunktion, sodass eine Eingabe von Null eine Ausgabe von Null und eine Eingabe ergibt Eine Halbierung der Anzahl der Werte im Zyklus ergibt den Maximalwert für Ihren PWM-Antrieb.

Die folgende Funktion generiert eine angehobene Cosinuskurve für eine 8-Bit-FSD-PWM aus einer 16-Wert nd 16-Byte-) Suche, die einen 59-Schritt-Zyklus generiert. Im Vergleich zu Ihrer 40-Schritte-Fließkommaimplementierung ist dies sowohl speicher- als auch leistungseffizient.

#include <stdint.h>

#define LOOKUP_SIZE 16
#define PWM_THETA_MAX (LOOKUP_SIZE * 4 - 4)

uint8_t RaisedCosine8bit( unsigned n )
{
    static const uint8_t lookup[LOOKUP_SIZE] = { 0, 1, 5, 9,
                                                 14, 21, 28, 36,
                                                 46, 56, 67, 78,
                                                 90, 102, 114, 127} ;
    uint8_t s = 0 ;
    n = n % PWM_THETA_MAX ;

    if( n < LOOKUP_SIZE )
    {
        s = lookup[n] ;
    }
    else if( n < LOOKUP_SIZE * 2 - 1 )
    {
        s = 255 - lookup[LOOKUP_SIZE * 2 - n - 2] ;
    }
    else if( n < LOOKUP_SIZE * 3 - 2 )
    {
        s = 255 - lookup[n - LOOKUP_SIZE * 2 + 2] ;
    }
    else
    {
        s = lookup[LOOKUP_SIZE * 4 - n - 4] ;
    }

    return s ;
}

Für eine Eingabe von 0 <= theta < PWM_THETA_MAX Die Kurve sieht folgendermaßen aus:

--- (enter image description here

Welches ist ich viel glatt genug für die Beleuchtung vorschlagen.

In der Praxis können Sie es folgendermaßen verwenden:

for(;;)
{
    for( unsigned i = 0; i < PWM_THETA_MAX; i++ )
    {
        LedPwmDrive( RaisedCosine8bit( i ) ) ;
        Delay( LED_UPDATE_DLEAY ) ;
    }
}

Wenn Ihr PWM-Bereich nicht zwischen 0 und 255 liegt, skalieren Sie einfach die Ausgabe der Funktion. 8-Bit-Auflösung ist mehr als genug für die Aufgabe.

14
Clifford

Für eine LED könnte man wahrscheinlich mit 16 oder mehr Schritten ohne Interpolation auskommen. Das heißt, ich kann mindestens zwei seltsame Dinge in Ihrer sin1() -Funktion sehen:

1) Sie haben 40 Datenpunkte in sine_table, Aber Sie nehmen den Index x1 Modulo 41 der Eingabe. Dies scheint nicht der richtige Weg zu sein, um mit der Periodizität umzugehen, und lässt x1 Über den letzten Index des Arrays zeigen.
2) Dann addieren Sie +1, sodass x2 Die Grenzen des Arrays noch überschreiten kann.
3) Sie verwenden i in der Funktion, diese wird jedoch nur im Hauptprogramm festgelegt. Ich kann nicht sagen, was es tun soll, aber die Verwendung einer solchen globalen Funktion in einer einfachen Berechnungsfunktion scheint zumindest schmutzig zu sein. Vielleicht soll es den Bruchteil für die Interpolation liefern, aber sollten Sie dafür nicht phase verwenden.

Hier ist ein einfacher Interpolator, der zu funktionieren scheint. Nach Geschmack einstellen.

#include <assert.h>

int A[4] = {100, 200, 400, 800};    
int interpolate(float x)
{
    if (x == 3.00) {
        return A[3];
    }
    if (x > 3) {
        return interpolate(6 - x);
    }
    assert(x >= 0 && x < 3);
    int i = x;
    float frac = x - i;
    return A[i] + frac * (A[i+1] - A[i]);
}

Einige willkürliche Beispielausgaben:

interpolate(0.000000) = 100
interpolate(0.250000) = 125
interpolate(0.500000) = 150
interpolate(1.000000) = 200
interpolate(1.500000) = 300
interpolate(2.250000) = 500
interpolate(2.999900) = 799
interpolate(3.000000) = 800
interpolate(3.750000) = 500

(Ich überlasse es dem interessierten Leser, alle Vorkommen von 3 Durch eine richtig definierte symbolische Konstante zu ersetzen, die Funktion weiter zu verallgemeinern und auch die Berechnung der negativen Phase zu implementieren.)

6
ilkkachu

Haben Sie darüber nachgedacht, den Teil der Sinuskurve aus [0..PI] als Parabel zu modellieren? Wenn die Helligkeit der LED nur von einem menschlichen Auge beobachtet werden soll, sollten die Formen der Kurven so ähnlich sein, dass nur ein geringer Unterschied festgestellt werden kann.

Sie müssten nur die passende Gleichung herausfinden, um sie zu beschreiben.

Hmmm, ...

Scheitelpunkt bei (PI/2, 1)

X-Achsen-Schnittpunkte bei (0, 0) und (PI, 0)

f(x) = 1 - K * (x - PI/2) * (x - PI/2)

Wo K wäre ...

K = 4 / (PI * PI)
6
Sparky

Der klassische Hack, um einen Kreis zu zeichnen (und damit auch eine Sinuswelle zu erzeugen), ist Hakmem # 149 von Marvin Minsky . Z.B.,:

#include <stdio.h>

int main(void)
{
    float x = 1, y = 0;

    const float e = .04;

    for (int i = 0; i < 100; ++i)
    {
        x -= e*y;
        y += e*x;
        printf("%g\n", y);
    }
}

Es wird leicht exzentrisch sein, kein perfekter Kreis, und Sie können einige Werte leicht über 1 erhalten, aber Sie können sie anpassen, indem Sie durch das Maximum dividieren oder runden. Außerdem kann eine Ganzzahlarithmetik verwendet werden, und Sie können die Multiplikation/Division eliminieren, indem Sie eine negative Zweierpotenz für e verwenden, sodass stattdessen die Verschiebung verwendet werden kann.

6

Ich würde mit Bhaskara I Annäherung einer Sinusfunktion gehen. Mit Graden von 0 bis 180 können Sie den Wert wie folgt approximieren

float Sine0to180(float phase)
{
    return (4.0f * phase) * (180.0f - phase) / (40500.0f - phase * (180.0f - phase));
}

wenn Sie einen beliebigen Winkel berücksichtigen möchten, würden Sie hinzufügen

float sine(float phase)
{
    float FactorFor180to360 = -1 * (((int) phase / 180) % 2 );
    float AbsoluteSineValue = Sine0to180(phase - (float)(180 * (int)(phase/180)));
    return AbsoluteSineValue * FactorFor180to360;
}

Wenn Sie es im Bogenmaß tun möchten, würden Sie hinzufügen

float SineRads(float phase)
{
    return Sine(phase * 180.0f / 3.1416);
}

Hier ist eine Grafik, die die mit dieser Näherung berechneten Punkte sowie die mit der Sinusfunktion berechneten Punkte zeigt. Sie können kaum die Annäherungspunkte sehen, die unter den tatsächlichen Sinuspunkten hervorschauen.

enter image description here

5
AgapwIesu

Wenn Ihre Anwendung keine echte Präzision erfordert, lassen Sie sich nicht die Idee eines Algorithmus für eine 40-Punkt-Sinus- oder Cosinuswelle nehmen. Außerdem sollten die Werte in Ihrer Tabelle mit dem Bereich des PWM-Eingangs Ihrer LED übereinstimmen.

Trotzdem habe ich mir Ihren Code angesehen und festgestellt, dass Sie nicht zwischen Punkten interpoliert haben. Mit einer kleinen Modifikation habe ich das Problem behoben und der Fehler zwischen der Zeichenfunktion eines Excel und Ihrer ist um maximal etwa 0,0032 oder so entfernt. Die Änderung ist ziemlich einfach zu implementieren und wurde mit tcc getestet, meinem persönlichen Ansprechpartner für C-Algorithmus-Tests.

Zunächst habe ich Ihrem Sinus-Array einen weiteren Punkt hinzugefügt. Der letzte Punkt wird auf den gleichen Wert gesetzt wie das erste Element im Sinus-Array. Dies korrigiert die Berechnung in Ihrer Sinusfunktion, insbesondere wenn Sie x1 auf (int) phase% 40 und x2 auf x1 + 1 setzen. Das Hinzufügen des zusätzlichen Punkts ist nicht erforderlich, da Sie x2 auf (x1 + 1)% 40 setzen könnten, aber ich habe den ersten Ansatz gewählt. Ich zeige nur verschiedene Möglichkeiten auf, wie Sie dies erreichen können. Ich habe auch die Berechnung eines Restes hinzugefügt (Grundsätzlich Phase int) Phase). Ich benutze den Rest für die Interpolation. Ich habe auch einen temporären Sinuswerthalter und eine Delta-Variable hinzugefügt.

const int sine_table[41] = 
{0, 5125, 10125, 14876, 19260, 23170, 26509, 29196,
31163, 32364, 32767,  32364, 31163, 29196, 26509, 23170, 
19260, 14876, 10125, 5125, 0, -5126, -10126,-14877,
-19261, -23171, -26510, -29197, -31164, -32365, -32768, -32365,
-31164, -29197, -26510, -23171, -19261, -14877, -10126, -5126, 0};

int i = 0;
int x1 = 0;
int x2 = 0;
float y = 0;

float sin1(float phase)
{
    int tsv,delta;
    float rem;

    rem = phase - (int)phase;
    x1 = (int) phase % 40;
    x2 = (x1 + 1);

    tsv=sine_table[x1];
    delta=sine_table[x2]-tsv;

    y = tsv + (int)(rem*delta);
    return y;
}

int main()
{
    int i;  
    for(i=0;i<420;i++)
    {
       printf("%.2f, %f\n",0.1*i,sin1(0.1*i)/32768);
    }
    return 0;
}

Die Ergebnisse sehen ziemlich gut aus. Der Vergleich der linearen Approximation mit der Gleitkomma-Sinusfunktion des Systems ergab die unten gezeigte Fehlerdarstellung.

--- (Error Plot

Combined Error vs Sine graph

5
bill

Sie könnten die ersten Begriffe der Taylor-Reihen-Erweiterung von sin verwenden. Sie können so wenige Begriffe wie nötig verwenden, um die gewünschte Genauigkeit zu erreichen. Einige Begriffe, die über dem folgenden Beispiel liegen, stoßen an die Grenzen eines 32-Bit-Floats.

Beispiel:

#include <stdio.h>

// Please use the built-in floor function if you can. 
float my_floor(float f) {
    return (float) (int) f;
}

// Please use the built-in fmod function if you can.
float my_fmod(float f, float n) {
    return f - n * my_floor(f / n);
}

// t should be in given in radians.
float sin_t(float t) {
    const float PI = 3.14159265359f;

    // First we clamp t to the interval [0, 2*pi) 
    // because this approximation loses precision for 
    // values of t not close to 0. We do this by 
    // taking fmod(t, 2*pi) because sin is a periodic
    // function with period 2*pi.
    t = my_fmod(t, 2.0f * PI);

    // Next we clamp to [-pi, pi] to get our t as
    // close to 0 as possible. We "reflect" any values 
    // greater than pi by subtracting them from pi. This 
    // works because sin is an odd function and so 
    // sin(-t) = -sin(t), and the particular shape of sin
    // combined with the choice of pi as the endpoint
    // takes care of the negative.
    if (t >= PI) {
        t = PI - t;
    }

    // You can precompute these if you want, but
    // the compiler will probably optimize them out.
    // These are the reciprocals of odd factorials.
    // (1/n! for odd n)
    const float c0 = 1.0f;
    const float c1 = c0 / (2.0f * 3.0f);
    const float c2 = c1 / (4.0f * 5.0f);
    const float c3 = c2 / (6.0f * 7.0f);
    const float c4 = c3 / (8.0f * 9.0f);
    const float c5 = c4 / (10.0f * 11.0f);
    const float c6 = c5 / (12.0f * 13.0f);
    const float c7 = c6 / (14.0f * 15.0f);
    const float c8 = c7 / (16.0f * 17.0f);

    // Increasing odd powers of t.
    const float t3  = t * t * t;
    const float t5  = t3 * t * t;
    const float t7  = t5 * t * t;
    const float t9  = t7 * t * t;
    const float t11 = t9 * t * t;
    const float t13 = t9 * t * t;
    const float t15 = t9 * t * t;
    const float t17 = t9 * t * t;

    return c0 * t - c1 * t3 + c2 * t5 - c3 * t7 + c4 * t9 - c5 * t11 + c6 * t13 - c7 * t15 + c8 * t17;
}

// Test the output
int main() {
    const float PI = 3.14159265359f;
    float t;

    for (t = 0.0f; t < 12.0f * PI; t += (PI * 0.25f)) {
        printf("sin(%f) = %f\n", t, sin_t(t));
    }

    return 0;
}

Beispielausgabe:

sin(0.000000) = 0.000000
sin(0.785398) = 0.707107
sin(1.570796) = 1.000000
sin(2.356194) = 0.707098
sin(3.141593) = 0.000000
sin(3.926991) = -0.707107
sin(4.712389) = -1.000000
sin(5.497787) = -0.707098
sin(6.283185) = 0.000398
...
sin(31.415936) = 0.000008
sin(32.201332) = 0.707111
sin(32.986729) = 1.000000
sin(33.772125) = 0.707096
sin(34.557522) = -0.000001
sin(35.342918) = -0.707106
sin(36.128315) = -1.000000
sin(36.913712) = -0.707100
sin(37.699108) = 0.000393

Wie Sie sehen, gibt es immer noch Raum für Verbesserungen der Präzision. Ich bin kein Genie mit Fließkomma-Arithmetik, daher hängt ein Teil davon wahrscheinlich mit den floor/fmod -Implementierungen oder der spezifischen Reihenfolge zusammen, in der die mathematischen Operationen ausgeführt werden.

2
Teh JoE

Da Sie versuchen, ein Signal zu erzeugen, denke ich, dass die Verwendung einer Differentialgleichung keine schlechte Idee sein sollte! es gibt so etwas

#include <stdlib.h>
#include <stdio.h>

#define DT (0.01f) //1/s
#define W0 (3)     //rad/s

int main(void) {
    float a = 0.0f;
    float b = DT * W0;
    float tmp;

    for (int i = 0; i < 400; i++) {
        tmp = (1 / (1 + (DT * DT * W0 * W0))) * (2 * a - b);
        b = a;
        a = tmp;

        printf("%f\n", tmp);
    }
}

Noch die Amplitude und Frequenz des Signals einstellen, ist ein Schmerz im Nacken: /

2
alkaya

Es wäre hilfreich, wenn Sie erklären würden, warum Sie die eingebaute Funktion nicht wollen, aber wie andere gesagt haben, ist die Taylor-Reihe eine Möglichkeit, den Wert zu schätzen. Die anderen Antworten scheinen jedoch die Maclaurin-Reihe zu verwenden, nicht Taylor. Sie sollten eine Nachschlagetabelle für Sinus und Cosinus haben. Dann finde x, der x-Wert in Ihrer Nachschlagetabelle, der dem gewünschten x am nächsten kommt, und finden Sie d = x-x. Dann

sin (x) = sin (x) + cos (x) * d-sin (x) * d2/ 2-cos (x) * d3/ 6 + ...

Wenn Ihre Nachschlagetabelle so ist, dass d <.01 ist, erhalten Sie mehr als zwei Stellen Genauigkeit pro Term.

Eine andere Methode besteht darin, die Tatsache zu verwenden, dass wenn x = x+ d dann

sin (x) = sin (x) * cos (d) + cos (x) * sin (d)

Sie können eine Nachschlagetabelle verwenden, um sin (x zu erhalten) und cos (x), und verwenden Sie dann die Maclaurin-Reihe, um cos (d) und sin (d) zu erhalten.

1
Acccumulation