Forum: Mikrocontroller und Digitale Elektronik Sinustabelle für kontinuierliches Signal


von Kxx S. (kskolik)


Lesenswert?

Guten Morgen,

ich möchte gerne von einem kontinuierlich ansteigendem Eingangssignal 
den Sinus (sowie den Cosinus) bestimmen.

Das Eingangssignal kann man sich vereinfacht als Rampenfunktion bzw. 
eine Gerade mit der Steigung m vorstellen.

Aus Rechenzeitgründen möchte ich auf die Sinus und Cosinus Berechnung 
verzichten und stattdessen eine Tabelle verwenden.

Derzeit simuliere ich den Aufbau in Matlab.
Bei den Simulationen stellt sich jedoch immer ein periodischer Fehler 
ein.

Ich habe der Einfachheit halber zunächst einen einfachen Vektor 
angenommen mit 628 Elementen (1-629). Das sind die gerundeten Argumente 
von 0-2*pi*100).

Ich verwende folgenden Code:
1
 
2
% Signal nach Integer transformieren 
3
signal = signal * 100; 
4
signal = round(signal); 
5
6
if(signal < 0) 
7
    while(signal < 0)
8
        signal = signal + 628; 
9
    end
10
end
11
if(signal >= 628) 
12
    while(signal >= 628)
13
        signal = signal - 628; 
14
    end
15
end
16
17
% Offset für Array Index addieren (Wertebereich von 0-628 auf 1-629
18
% verschieben) 
19
signal = signal + 1; 
20
21
% Ausgabe des Signals 
22
signal_out = signal; 
23
24
% Abfrage des Tabellenwertes 
25
sind = sintab(signal); 
26
cosd = costab(signal);

sintab und costab sind dabei die Vektoren mit den Funktionswerten.

Jetzt meine Frage: Wie lässt sich der Fehler am besten eliminieren?
Der Fehler ist eine Verschiebung der sin/cos-Werte auf der x-Achse. Bei 
jeder Periode vergrößert sich die Verschiebung.

Jemand eine Idee? Ich schätze, es liegt daran, dass ich falsch auf die 
Vektor-Elemente zugreife.

Bin für jede Hilfe dankbar!

-Kai

von Karl H. (kbuchegg)


Lesenswert?

Wie hast du den die 'vectoren' definiert?

Dein größer werdender Fehler mit jeder Periode wird wahrscheinlich
darin begründet sein, dass 2*PI * 100 nun mal nicht 628 sind,
sondern ein klein wenig mehr.

Übrigens: Die If-Abfragen hier

if(signal < 0)
    while(signal < 0)

sind sinnlos. Wenn signal nicht kleiner als 0 ist, dann
würde auch die while Schleife nicht betreten werden. Es reicht
also völlig aus

    while(signal < 0)
        signal = signal + 628;
    end

    while(signal >= 628)
        signal = signal - 628;
    end

Die Vergleiche auf Gleichheit machen mich etwas stutzig.
Dir ist schon klar, dass du bei einem Wertebereich 0 bis 628
(jeweils inklusive) in Summe 629 Werte hast? Überleg es dir mal
mit kleineren Zahlen. Wenn du einen Wertebereich 0 bis 5 hast
(jweiles inklusive der Grenzen), dann sind das
  0, 1, 2, 3, 4, 5
6 Werte.

von ratgeber (Gast)


Lesenswert?

Bei mir funktioniert diese while >Konstruktion nur, wenn "signal" für 
alle Werte die Abfragebedingung erfüllt... und dann wird der ganze 
Vektor manipuliert. Ich weiß nicht, ob das so gewollt war, oder ob du 
nur einzelne Werte (Ausreißer nach oben oder unten) korrigieren 
wolltest.


test = zeros(1,10)
test(4)=4

while test<4
test=test+55
end

test bleibt 0 0 0 4 0 0 0 0 0 0

while test<5
test=test+55
end

test wird 55 55 55 59 55 55 55 55 55 55

Die Abfrage der Tabellen finde ich auch etwas merkwürdig.
Du übergibst den Wert als Index... bin mir nicht sicher ob das wirklich 
funktioniert. Müsste man deine Vektoren kennen.

von Kxx S. (kskolik)


Lesenswert?

Karl heinz Buchegger wrote:
> Wie hast du den die 'vectoren' definiert?

x = [0:1:628]';
sintab = sin(x/100);
Damit enthält der Vektor die Funktionswerte vom Sinus (in Abhängigkeit 
vom Index, allerdings mit "mangelhafter" Genauigkeit.

> Dein größer werdender Fehler mit jeder Periode wird wahrscheinlich
> darin begründet sein, dass 2*PI * 100 nun mal nicht 628 sind,
> sondern ein klein wenig mehr.

Wenn das der Fehler ist, wie kann man den am besten beheben? Wenn ich 
noch mehr Nachkommastellen einbeziehe, bleibt er ja weiterhin bestehen.


> Übrigens: Die If-Abfragen hier
>
> if(signal < 0)
>     while(signal < 0)
>
> sind sinnlos. Wenn signal nicht kleiner als 0 ist, dann
> würde auch die while Schleife nicht betreten werden. Es reicht
> also völlig aus
>
>     while(signal < 0)
>         signal = signal + 628;
>     end
>
>     while(signal >= 628)
>         signal = signal - 628;
>     end

Stimmt, war ein Denkfehler meinerseits.

> Die Vergleiche auf Gleichheit machen mich etwas stutzig.
> Dir ist schon klar, dass du bei einem Wertebereich 0 bis 628
> (jeweils inklusive) in Summe 629 Werte hast? Überleg es dir mal
> mit kleineren Zahlen. Wenn du einen Wertebereich 0 bis 5 hast
> (jweiles inklusive der Grenzen), dann sind das
>   0, 1, 2, 3, 4, 5
> 6 Werte.

Das stimmt. Matlab indiziert die einzelnen Elemente von 
Vektoren/Matrizen etc. beginnend mit 1. Das 0. Element existiert also 
nicht.

Da die sin-Werte von 0 und 2pi gleich sind, reicht es also nur einen der 
beiden zu betrachten.
Meine Idee: Sobald das Signal mindestens den Wert 2pi erreicht, ziehe 
ich 2pi (hier 628) ab.

Der Vektor hat 628 Elemente, da es das 0. nicht gibt.
Aus diesem Grund hab ich vor der Abfrage eine 1 zu dem Signal addiert. 
Damit entspricht der Signalwert 0 dem Vektorindex 1, 628 ergo 629.

Vielleicht gibts auch noch einen sinnvolleren Weg als meinen? Hab bis 
jetzt nur noch nichts passendes gefunden..

von Kxx S. (kskolik)


Lesenswert?

...

Gibt es vielleicht noch eine andere Möglichkeit, den Sinus von einem 
Signal zu bestimmen, welches den Wertebereich von 0-2pi überschreitet?

Bei dem Verfahren sollte natürlich kein Fehler auftreten, was jedoch 
nicht realisierbar ist. Der günstigeste Fall wäre ein statischer, oder 
bestimmbarer Fehler, den man nachträglich rausrechnen kann.

Bin für jede Hilfe und Anregungen sehr dankbar.

von eProfi (Gast)


Lesenswert?

Wie oft kann es denn zum Überschlag kommen (Wertebereich des Eingangs)?

Hier würde ich eine Multiplikation des Wertes mit einer Konstanten, 
sodass der Tabellenzugriff bei 2*pi wieder genau trifft, vorschlagen.

Berechnungen natürlich mit FixedPoint-Arithmetik.

z.B. so:
Eingangswert soll sich alle 628,318530 Werte wiederholen.
Sinus-Tabelle mit 512 Einträgen (damit der Faktor kleier als 1 bleibt).

Eingangswert mit 512/628,31853=0,8148733 fixedpoint multiplizieren.
Vom Ergebnis nur die unteren 9 Bits verwenden als Pointer in die 
Sinus-Tabelle.

Beispiel:
Zum Nachrechnen in Floating-Point:
Eingangs-Wert ist 2345 (0x0929)
*0,8148733 = 1910,8779087   gerundet 1911
modulo 512 = 375--> den 375. Wert der Sinus-Tabelle verwenden.

und nun in fixed und hex, damit sich der µC leichter tut:
0929 * D09C = 0776E4FC  (nur die oberen 16 Bits interessieren uns)
              0777      (gerundet = bit 15 dazuaddiert)
andiert mit   01FF      (gleichbedeutend mit modulo 512)
             ------
              0177      (ist 375 dez)

Jetzt nehmen wir zur Kontrolle ein Vielfaches (z.B. 100 x) von 
628,31853, das sollte ja wieder zum 0. Eintrag der Tabelle führen.

628,31853 * 100 = 62831,853   gerundet 62832   0xF570
F570 * D09C = C8009040
              c801      gerundet
andiert mit   01ff
             ------
              0001      naja, nach 100 Überschlägen greift er um 1 
daneben.
Wenn Dich das stört, musst Du mit mehr Bits rechnen.

Ist das in etwa, was Du wolltest? Wenn nicht, hab ich nicht verstanden, 
worauf es Dir ankommt. Dann bitte nochmal genauer erklären ;-)

von eProfi (Gast)


Lesenswert?

2 Sachen zum Nachtrag:
D09C ist 0,8148733 * 65536 = 53403,537154

Da man bei dieser Berechnung aufrundet, kann man zum Ausgleich bei der 
zweiten Berechnung (Multiplikation) abrunden, was im obigen Beispiel zum 
richtigen Ergebnis 0 führen würde.


Du kannst die Länge der Sinus-Tabelle beliebig machen, es bietet sich 
jedoch ein Vielfaches von 2 an, da man dann die Modulo-Funktion durch 
einen einfachen AND bewerkstelligen kann.

Erzähl doch bitte mal, wofür Du das brauchst...
Vielleicht gibt es eine noch einfachere Lösung.

von 3353 (Gast)


Lesenswert?

Da wurde einiges gelabbert. Also. Eine sinustabelle ist immer 2^N lang. 
also 256, 512, 1024, 2048, 4096. Am einfachsten ist 256. Das entspricht 
dann 2Pi.

von Kxx S. (kskolik)


Lesenswert?

eProfi wrote:
> Wie oft kann es denn zum Überschlag kommen (Wertebereich des Eingangs)?
>
> Hier würde ich eine Multiplikation des Wertes mit einer Konstanten,
> sodass der Tabellenzugriff bei 2*pi wieder genau trifft, vorschlagen.
>
> Berechnungen natürlich mit FixedPoint-Arithmetik.
>
> z.B. so:
> Eingangswert soll sich alle 628,318530 Werte wiederholen.
> Sinus-Tabelle mit 512 Einträgen (damit der Faktor kleier als 1 bleibt).
>
> Eingangswert mit 512/628,31853=0,8148733 fixedpoint multiplizieren.
> Vom Ergebnis nur die unteren 9 Bits verwenden als Pointer in die
> Sinus-Tabelle.
>
> Beispiel:
> Zum Nachrechnen in Floating-Point:
> Eingangs-Wert ist 2345 (0x0929)
> *0,8148733 = 1910,8779087   gerundet 1911
> modulo 512 = 375--> den 375. Wert der Sinus-Tabelle verwenden.
>
> und nun in fixed und hex, damit sich der µC leichter tut:
> 0929 * D09C = 0776E4FC  (nur die oberen 16 Bits interessieren uns)
>               0777      (gerundet = bit 15 dazuaddiert)
> andiert mit   01FF      (gleichbedeutend mit modulo 512)
>              ------
>               0177      (ist 375 dez)
>
> Jetzt nehmen wir zur Kontrolle ein Vielfaches (z.B. 100 x) von
> 628,31853, das sollte ja wieder zum 0. Eintrag der Tabelle führen.
>
> 628,31853 * 100 = 62831,853   gerundet 62832   0xF570
> F570 * D09C = C8009040
>               c801      gerundet
> andiert mit   01ff
>              ------
>               0001      naja, nach 100 Überschlägen greift er um 1
> daneben.
> Wenn Dich das stört, musst Du mit mehr Bits rechnen.
>
> Ist das in etwa, was Du wolltest? Wenn nicht, hab ich nicht verstanden,
> worauf es Dir ankommt. Dann bitte nochmal genauer erklären ;-)

Das hört sich schonmal nach einem recht sinnvollen Lösungsweg an. Werd 
das gleich mal programmieren und testen.

Wenn ich deine Angaben jetzt richtig verstanden habe, ist mit einem 
Fehler von 1/512 des Funktionswertes im Wertebereich von 0-200*pi zu 
rechnen. Ich denke, das ist ein akzeptabler Wert.
Das ganze benötige ich für einen Simulator, der auch im Dauerbetrieb 
eingesetzt wird.
EDIT: Und das Eingangssignal kann sich in bestimmten 
Betriebsfällen/Simulationsfällen kontinuierlich während des 
Dauerbetriebs erhöhen.
EDIT-ENDE

Aus diesem Grund sollte der Fehler statisch bleiben.

Mal sehen, was bei den Tests gleich rumkommt.

Vielen Dank

von Kxx S. (kskolik)


Lesenswert?

Perfekt!

Das Verfahren funktioniert.

Mit einer Tabellenauflösung von 512 Elementen bekomme ich einen 
periodisch schwingenden Fehler von 0 - 0.5%.
Bei 1024 Elementen ist der Fehler maximal 0.3%.

Also insgesamt verhält sich der Weg statisch!

Vielen Dank nochmal!

-Kai

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
Noch kein Account? Hier anmelden.