Sinuswaarden berekenen met Taylor reeksen

Ik probeer sinuswaarden te berekenen met Taylor, maar krijg niet het juiste antwoord. Dit is een diepte van 5, heb niet meer nodig. Ik wil van 0 - 90 graden, dus 0 - 255.

sin(x) = x - x^3/3! + x^5/5!

Ik moet dan naar bytes, dus.

x - x^3/3! + x^5/5! * 255. (afgeronde waarden)

Als ik bijv. sin(20) bereken kom ik op 0.34 (dan maal 255 = 87 afgerond), maar krijg dat niet voor elkaar met de Taylor reeks.

RES

invullen in radialen, niet graden :)

20*((2*pi)/360)=0,3491
0,3491 - 0,3491^3/3! + 0,3491^5/5! = 0,3420

De taylor reeks is voor computers niet een handige manier om de sinus te berekenen. Voor kleine hoeken gaat het nog wel maar voor grote hoeken heb je heel veel temen nodig.
Zoek maar eens op het cordic algoritme.
Voor microprocessors is een tabel en interpoleren daartussen het handigst.

Op 9 augustus 2007 22:46:22 schreef xantus:
invullen in radialen, niet graden :)

20*((2*pi)/360)=0,3491
0,3491 - 0,3491^3/3! + 0,3491^5/5! = 0,3420

Wat ik niet begrijp is, hoe kan ik met deze Taylor reeks die sinuswaarden berekenen? Ik heb geen tabel, ik kan niet 0.3491 invullen.
Ik wil van 0 - 90 graden (0 - pi/2 rad?), dus 0 - 255. Een kwart sinus. sin(20)*255 is 87. sin(20) heb ik niet, ook geen sin(x) functie. Ik heb alleen die taylor reeks.
Ik weet dat het met 16-bit getallen kan worden gedaan (zie link), maar weet niet hoe.

Hier doet iemand het met Forth.

http://www.forth.hccnet.nl/sicotekst.html
Sinus met behulp van breuken in Forth zonder floating point

Forth kan ik helaas niet lezen.

RES

Als ik je taylor formule invul voor het bereik dat je wil hebben kom ik op iets als:

sin(x) = x*4.45 - (x*x*x)/4427 + (x*x*x*x*x)/2421767

Dat werkt wel aardig voor kleine waarden van x (tot een graad of 25), maar om tot 90 graden een sinus netjes te benaderen red je het niet met deze taylor ontwikkeling!

Kun je niet iets doen met een lookup table en interpolatie ofzo? iets als:

code:


int sin(int x)
{
    int table[] = {0, 22, 44, 65, 87, 107, 127, 146, 164, 180, 195, 208, 220, 231, 239, 246, 251, 254, 255, 255};
    int i = x/5, j = x%5;

    return (table[i] * (5-j) + table[i+1] * j)/5;
}

[Bericht gewijzigd door KaRaMBa op vrijdag 10 augustus 2007 01:35:23 (34%)

Ja, ho, wacht ff.

Taylor is een benadering! Je hebt veel termen en dito grote getallen (decimalen) nodig voor een beetje precisie.

Vanwege de herhaling in de sin functie breng je normaal gesproken eerst de hoek terug totdat deze tussen 0 en 90 graden valt. Rest is er via symmetrie eruit te halen.

Daarna kun je het via taylor berekenen, maar zelfs die machten en delingen zijn niet fijn op een uC. Eigenlijk altijd pak je idd een tabel en interpoleer je. Zelfs op PC's wordt dat veel gedaan, omdat de sinus nauwkeurig uitrekenen via Taylor zelfs daar veel tijd kost.

[Bericht gewijzigd door dexter op vrijdag 10 augustus 2007 03:25:29 (16%)

It's a good day for science!
joopv

Golden Member

Misschien heb je iets aan het standaardwerk Handbook of Mathematical Functions van Abramowitz and Stegun. Dat is public domain en kun je op internet helemaal inzien:

http://www.math.sfu.ca/~cbm/aands/

Voor jou is vooral pagina 76 interessant.

Op 9 augustus 2007 22:29:47 schreef RES:
sin(x) = x - x^3/3! + x^5/5!

Even voor de goede orde, dit is niet de taylor reeks.... De taylor reeks is:

code:


sin(x) = x - x^3/3! + x^5/5! ... 

Voor kleine waarden van x, gaat x^n nogal snel klein worden. Ga je hem dan nog delen door n! dan wordt ie nog sneller kleiner.

Is x echter wat groter, bijvoorbeeld 20 (uit je eerste posting, als dat radialen zijn, zoals ik eerst aannam) dan wordt x^n snel groot, en moet je een zwik meer termen meenemen om ze wat kleiner te maken.....

Kortom, de taylor reeks is bruikbaar voor een benadering, maar dan moet je wel zorgen dat je voldoende termen meeneemt voor de range van x-waarden die je gaat gebruiken.

Wil je een circel tekenen, gebruik dan:

code:


short x,y; 

x=0x7ff0;
y=0;
while (1) {
   plot (x>>8,y>>8);
   y += x >> 8;
   x -= y >> 8;
}

Dit is ook te gebruiken om sinus uit te rekenen. Zoals ik het nu heb, doet hij 1 rad per 256 stapjes. Mooi he?

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Ik hoop dat je hier iets mee kan.

Benadering via een 2e graads polynoom.
Naar een idee van http://www.dspguru.com/comp.dsp/tricks/alg/sincos.htm

Benader de sinus m.b.v een tweede graads polynoom:

y(x)= a0 + a1*x + a2*x2 [1]

de afgeleide hiervan:

y'(x)= a1*2a2*x [2]

We weten de waarde sin(x=0) = 0.
Deze invullen in [1] waaruit volgt dat:
a0=0.

De afgeleide van sin(x)= cos(x).
Bij de waarde x=0 is dat dus 1;
Invullen in [2] waaruit volgt dat:
a1=1

Bij een hoek x van 90 graden (Pi/2) is de waarde van sin(x)=1.

Deze voorwaarde invullen in [1] waardoor a2 berekend kan worden.

Resultaat sin(x)approx = x-0.23134*x2

Is tot 90 graden (pi/2 rad) behoorlijk nauwkeurig (grootste afwijking 0.0728).

Zelf de juiste eenheden uitwerken

*edit*

Klopt dit?
Nee, even nagekeken maar is niet nauwkeurig genoeg
Grootste fout geeft al bijna 5 graden afwijking
Heb je dus niks aan.

BelleBlazer4Life

Wil je een circel tekenen, gebruik dan:

code:


short x,y; 

x=0x7ff0;
y=0;
while (1) {
   plot (x>>8,y>>8);
   y += x >> 8;
   x -= y >> 8;
}

Dit is ook te gebruiken om sinus uit te rekenen. Zoals ik het nu heb, doet hij 1 rad per 256 stapjes. Mooi he?

Welke taal is dit? En hoe haal ik hier sinuswaarden uit?
Ziet er simpel uit, maar kan het niet volgen.

RES

ziet er uit als C, maar met dat plot lijkt het ook wel een beetje op mathlab.

Maar zie niet zo snel hoe je daar makkelijk de sinus uit kan halen. Het beschijft de baan van
x = 255*sin(i*(2*pi())/256)
y = 255*cos(i*(2*pi())/256)

Als je dan een hoek van 90 graden wilt berekenen moet je eerst 64x dat algoritme doorlopen om vervolgens y>>8 te nemen. Dan lijkt een lookup table me toch iets makkelijker.

Op 10 augustus 2007 18:54:45 schreef xantus:
ziet er uit als C, maar met dat plot lijkt het ook wel een beetje op mathlab.

Maar zie niet zo snel hoe je daar makkelijk de sinus uit kan halen. Het beschijft de baan van
x = 255*sin(i*(2*pi())/256)
y = 255*cos(i*(2*pi())/256)

http://selland.boisestate.edu/jbrennan/images/circle_sine.gif

Als je dan een hoek van 90 graden wilt berekenen moet je eerst 64x dat algoritme doorlopen om vervolgens y>>8 te nemen. Dan lijkt een lookup table me toch iets makkelijker.

Een lookup tabel is veel simpeler, alleen kan ik met een routine, in die routine dingen veranderen zodat de sinus veranderd (frekwentie, amplitude en dergelijken), met een tabel moet ik als nog weer een routine gaan schrijven om die sinuswaarden te veranderen. Om deze reden wil ik geen tabel.

RES
JoWi

Special Member

Op 10 augustus 2007 21:58:51 schreef RES:
[...]
Een lookup tabel is veel simpeler, alleen kan ik met een routine, in die routine dingen veranderen zodat de sinus veranderd (frekwentie, amplitude en dergelijken), met een tabel moet ik als nog weer een routine gaan schrijven om die sinuswaarden te veranderen. Om deze reden wil ik geen tabel.

Een sinustabel is toch een sinustabel, wat heeft dat met frequentie/amplitude te maken ?
De bedoeling is toch zo snel mogelijk dan wel met zo min mogelijk code een sinus uit te rekenen ?
Daar is een Taylor reeks volgens mij niet de goede oplossing voor.

Ignorance is bliss

Een lookup tabel is veel simpeler, alleen kan ik met een routine, in die routine dingen veranderen zodat de sinus veranderd (frekwentie, amplitude en dergelijken), met een tabel moet ik als nog weer een routine gaan schrijven om die sinuswaarden te veranderen. Om deze reden wil ik geen tabel.

welke taal schrijf je het? In C zou je door de preprocessor een const array kunnen laten vullen aan de hand van een formule. Dan kan je gewoon een lookup table gebruiken en hoef je tijdens het draaien van je programma de frequentie/amplitude te veranderen.

Een sinustabel is toch een sinustabel, wat heeft dat met frequentie/amplitude te maken ?

een sin(x) lookup table is niet zo handig als je bijvoorbeeld 20*sin(10*x) wilt hebben.

JoWi

Special Member

Op 10 augustus 2007 22:30:08 schreef xantus:
een sin(x) lookup table is niet zo handig als je bijvoorbeeld 20*sin(10*x) wilt hebben.

???? x = x*10, lookup, output * 20. Wat is het probleem ?

Ignorance is bliss

minstens 2 instructies meer dan als je de lookup table voor 20*sin(10*x) al had gemaakt ;). En denk dan eens aan komma getallen. Dan krijg je vaak nog meer instructies.

[Bericht gewijzigd door xantus op vrijdag 10 augustus 2007 23:04:35 (30%)

@belleblaas: Een 2e graads taylorbenadering werkt heel aardig, mits je het een klein beetje aanpast.

De benadering rond 0.7 nemen, en dan nog een beetje foezelen. Ik kom op max. 1 graden verschil uit. met de functie:

-0.322109 (-3.86218 + x) (-0.0675259 + x)

Hogepriester in het genootschap der mexicaanse hond. // // Aan 2% van de mensen is 50% van het bezit ; 1% van het bezit is aan 50% van de mensen.
JoWi

Special Member

Op 10 augustus 2007 22:30:08 schreef xantus:
[...]
een sin(x) lookup table is niet zo handig als je bijvoorbeeld 20*sin(10*x) wilt hebben.

IMHO probeert de TS op de verkeerde manier een sinusgenerator (toonfiets) te maken (zie ook een andere draad op dit forum) met PWM en doet ie dat op de verkeerde manier. De berekening die jij noemt heb je dan niet nodig.

Ignorance is bliss

Op 10 augustus 2007 22:11:13 schreef JoWi:
[...]Een sinustabel is toch een sinustabel, wat heeft dat met frequentie/amplitude te maken ?
De bedoeling is toch zo snel mogelijk dan wel met zo min mogelijk code een sinus uit te rekenen ?
Daar is een Taylor reeks volgens mij niet de goede oplossing voor.

Probleem met een sinustabel is dat als de frekwentie heel laag wordt dat heel weinig samples overhoudt bij dezelfde samplerate en je kantelfrekwentie van je filter aan de hoge kant omlaag moet. Ik heb nu een bereik van 20Hz t/m 14kHz.
Met een Taylor berekening en een vaste samplerate kom je op een maximum aantal samples voor dat hele frekwentiegebied.
Hoef alleen maar het aantal samples te berekenen en de frekwentie rolt eruit. Samplerate is 31250Hz, dus een nauwkeurigheid van 32 usec. (ook nog eens 10-bit sampleresolutie, dus amplitude regelbaar in 1024 stapjes.)

RES

Ja, mijn stukje code is in C.

Ik had een functie "plot (x, y)" bedacht als je circels had willen tekenen.

Mijn voorbeeld functie is handig als je sin (en evt cos) achter mekaar nodig hebt, bijvoorbeeld omdat je een circel aan het tekenen bent, of omdat je een sinus signaal wilt uitsturen.

Als je 10 bits nodig hebt, kan je x initializeren op 511, en alle bits van de x (of y) variabele gebruiken.

Als je meer dan 256 stapjes nodig hebt per radiaal, dan moet je meer shiften voordat je ze er bij optelt. Als je minder stapjes nodig hebt, moet je minder shiften.

Is echt super simpel aan te passen voor wat je nodig hebt.

Als ik begrijp dat je met 10 bits PWM een audio signaal wil maken, en je systeem loopt op 20MHz, dan kan je een volgende sample na 50 us aanleveren. Hmm. 20kHz. nog niet zo gek. Als je een 1 kHz signaal wil maken, heb je dus 20 puntjes op de circel nodig. Ongeveer 3 puntjes per radiaal. Als je dan met 16 stapjes per radiaal (dus x += y>> 4) gaat werken, en dan de juiste "ongeveer 5 stapjes" neemt, zal dat behoorlijk nauwkeurig genoeg zijn, hoor.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/