Forum: Digitale Signalverarbeitung / DSP / Machine Learning Effiziente Implementierung von ARCTAN fuer uCs


von Edi M. (Gast)


Lesenswert?

Ich suche eine effiziente Implementierung von ARCTAN fuer einen kleinen 
Microcontroller. CORDIC ist mir zuviel. Es muss klein, schnell und ohne 
Divisionen sein, Eingangsbereich Z.B. 32 Bit im 16.16 Format, um die 
0,000x bei grossen X darstellen zu können, Rechnung in einem Quadranten, 
Ausgang Winkel -/- 255, d.h. es reicht eine Genauigkeit von einem halben 
Grad.
Die Rechnung müsste dann wohl auf ein Viertel Grad genau sein.
Tabelle würde Ich vermeiden wollen,

von WS (Gast)


Lesenswert?

Wikipedia kennst Du?
x(1+0,28x*x) und so?
Viel einfacher geht es wohl nicht.

Beitrag #5115144 wurde vom Autor gelöscht.
von Michael W. (Gast)


Lesenswert?

Eine Tabelle wäre aber wohl das Einfachste bei den wenigen Werten.

Den ARCTAN kann man auch mit einer Wurzel nachbilden, aber die ist auch 
nicht einfacher zu berechnen. Nach mehreren Iterationen gibt es einen 
Wert, der dann doch nur Ungenau ist.

Welcher Controller?

von Der Zahn der Zeit (Gast)


Lesenswert?

WS schrieb:
> x(1+0,28x*x) und so?
Interessant. Aber in Wikipedia steht x/(1+0,28x*x) (für |X| <= 1, andere 
Bereiche ähnlich), und die Division tut dem Meister offensichtlich weh. 
Aber es ist nur eine Division.

von Dirk B. (dirkb2)


Lesenswert?

Wie kommst du an den TAN (also den Einganganswert für den ARCTAN)?

von Detlef _. (detlef_a)


Lesenswert?

Ja, wenn man x und y kennt ( oder Real- und Imaginärteil), dann geht das 
einfacher:

Beitrag "Goertzel-Algorithmus und Signed Fractional Format"

Cheers
Detlef

von J. S. (engineer) Benutzerseite


Lesenswert?

CORDIC ist aber doch gerade für die lumpigen Rechner DIE Methode 
schlechthin.

Was wäre, mit reduzierter Auflösung = weniger Rotationen zu agieren? Mit 
4-5 Schritten müsste sich das doch machen lassen.
http://bsvi.ru/uploads/CORDIC--_10EBA/cordic.pdf

Ansonsten heuristisch annähern, anhand der eigenen Anforderungen, sprich 
lokal erlaubten Abweichungen und der benötigten Bereiche, wobei es sehr 
viel knapper kaum gehen wird. Die billigste Lösung ist wohl die von den 
Telekomikern/Iren hier:

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.1.4931&rep=rep1&type=pdf

Weitere Denkanstösse hier:
http://www.convict.lu/Jeunes/Math/arctan.htm

Bei Näherungen muss man nur aufpassen, dass es keine minimalen Knicke 
gibt und die Kurve monoton bleibt! Je nach Verwendungszweck muss sogar 
auch die Ableitung monoton und vor allem stetig sein, daher darf es 
keine Plateaus geben. Da fallen viele auf die Nase!

von Edi M. (Gast)


Lesenswert?

Ich sehe nicht den Grund und Vorteil für die Nutzung des Cordic, wenn 
Ich dann doch wieder auf gespeicherte Werte zurückgreifen muss, die ich 
aus Tabllen lesen soll. Dann kann ich gleich nähern.

von Possetitjel (Gast)


Lesenswert?

Meister E. schrieb:

> Ich sehe nicht den Grund und Vorteil für die Nutzung
> des Cordic, wenn Ich dann doch wieder auf gespeicherte
> Werte zurückgreifen muss, die ich aus Tabllen lesen soll.

Es ist Dir also völlig egal, ob Du eine Tabelle mit 512
Werten brauchst oder eine mit nur 16? Merkwürdig.

> Dann kann ich gleich nähern.

Dann mach doch. Ganz ohne Spott: Ich wäre gespannt auf
Deine Lösung.

von J. S. (engineer) Benutzerseite


Lesenswert?

Mit dem CORDIC wird es eben am schnellsten genau. Natürlich musst Du 
mehrfach auf eine Tabelle zugreifen, aber das müsste man mit einer 
diskreten Abschnittstabelle eben auch. Bei binärem hashing und Bei 512 
Werten mindestens 9x. In der Zeit arbeitest Du auch 3-4 CORDIC 
Iterationen ab.

Klar kann man mit noch weniger Stellen operieren und interpolieren, wenn 
es die Anwendung hergibt, aber man darf eben nicht erwarten, dass man 
mit dem Winkel nochmal irgendwelche Cosinüsse bilden und z.B. Realteil 
errechnen kann zur weiteren Verwendung. Solche simplen Näherungen sind 
i.d.R. finale Operationen für Anzeigen oder für Schätzungen am Besten 
geeignet.

Ansonsten sind das Sonderfälle:

Ich habe mal eine Implementierung in VHDL gemacht, die sehr schnell sein 
müsste und wobei der CORDIC-CORE einfach zu langsam war. Allerdings war 
die Anwendung recht grob und diente nur der Schätzung einer 
Winkelinformation. Ich habe einfach Werte <1.0 per Division gespiegelt, 
um sie in den anderen Ast zu bekommen und den Ausgang der Division, die 
ja als pipeline die bits von oben herunter sukzessive liefert, zur 
Ansteuerung der hash table genutzt. Dann musste Ich nicht "warten" bis 
das Divisionsergebnis komplett feststeht, sondern hat schon nach 2 
Takten die erste Information, in welcher (lokalen) Hälfte der Tabelle 
das Ergebnis liegt. So kann man das auch für Wurzeln machen.

Der Hauptvorteil war, dass man nur sehr geringe Auflösungen und auch 
keine Multiplier für die Rechnung brauchte.

Die Methode ist aber definitiv mal nichts für Microcontroller.

von T.U.Darmstadt (Gast)


Lesenswert?

Dirk B. schrieb:
> Wie kommst du an den TAN (also den Einganganswert für den ARCTAN)?

Das war damals auch mein Problem ;-) Den nimmt man über einen 
Tabellenwert. Das ist ja der Trick:
Beitrag "Cordic - Sinussignal"

von Edi M. (Gast)


Lesenswert?

Possetitjel schrieb:
> Meister E. schrieb:
> Es ist Dir also völlig egal, ob Du eine Tabelle mit 512
> Werten brauchst oder eine mit nur 16? Merkwürdig.

Nein, es ist nicht egal. Bei der vorliegenden Applikation habe Ich nur 
ein bissl ein Problem, viele Daten schnell hin und her zu bekommen, weil 
Ich alles erst umständlich aus einem etnfernten RAM, wahrscheinlich ROM 
heranschauffeln müsste.

von Possetitjel (Gast)


Lesenswert?

Meister E. schrieb:

> Possetitjel schrieb:
>> Meister E. schrieb:
>> Es ist Dir also völlig egal, ob Du eine Tabelle mit 512
>> Werten brauchst oder eine mit nur 16? Merkwürdig.
>
> Nein, es ist nicht egal. Bei der vorliegenden Applikation
> habe Ich nur ein bissl ein Problem, viele Daten schnell
> hin und her zu bekommen, weil Ich alles erst umständlich
> aus einem etnfernten RAM, wahrscheinlich ROM heranschauffeln
> müsste.

Naja, das spricht doch für CORDIC; dort kann man mMn alles
mit Direktoperanden erschlagen, wenn man die Schleife abrollt.
Insofern verstehe ich Deine Argumente nicht.

von WS (Gast)


Lesenswert?

Edi M. schrieb:
> weil
> Ich alles erst umständlich aus einem etnfernten RAM, wahrscheinlich ROM
> heranschauffeln müsste.

Die wenigen Werte lassen sich eigentlich gut lokal speichern. Was ist 
das für ein Controller, der arctan machen soll aber kaum Speicher hat?

von Edi M. (Gast)


Lesenswert?

Das wissen wir noch nicht genau, was für ein Controller es wird. Aber es 
wird eine kleiner, mickriger mit wenig Platz und wenig Strom.

von Possetitjel (Gast)


Lesenswert?

Edi M. schrieb:

> Aber es wird eine kleiner, mickriger mit wenig
> Platz und wenig Strom.

Naja, Kernfrage ist, ob das Ding schnell multiplizieren
kann oder nicht.

Wenn nicht, wird's auf CORDIC hinauslaufen. Bangemachen
gilt nicht :)
Beim Algorithmus für ATAN kann ich nicht helfen; trotz
der i.d.R. beschissenen Erklärungen habe ich die
sin/cos-Variante verstanden -- wie man aber den ATAN mit
CORDIC berechnet, ist mir unklar.

Falls jedoch ein Multiplizierer vorhanden ist, gibt's
andere Möglichkeiten.

von W.S. (Gast)


Lesenswert?

Was ist denn mit einer stückweisen quadratischen Annäherung? Etwas 
rechnen können, wird auch der winzigste Winzigkontrolleur.

von Detlef _. (detlef_a)


Lesenswert?

Possetitjel schrieb:

> Beim Algorithmus für ATAN kann ich nicht helfen; trotz
> der i.d.R. beschissenen Erklärungen habe ich die
> sin/cos-Variante verstanden -- wie man aber den ATAN mit
> CORDIC berechnet, ist mir unklar.

Das Prinzip für die ATAN Berechnung mit Cordic muss so sein, denke ich:
Du zerlegst Deinen Winkel in die Komponenten 45°, 22.5°, 11.25°, 5.65°, 
2.8125°, 1.40625°, 0.703125° und 0.3515625°, das sollte reichen für das 
geforderte halbe Grad Auflösung. Dann nimmst Du die komplexe Zahl 1+j*0 
und drehst sie um diese Winkel nach dem Muster hier, hab ich schon mal 
referenziert:

Beitrag "Goertzel-Algorithmus und Signed Fractional Format"

Die Drehungen normierst Du so, dass der Realteil 1 bleibt. Der 
Imaginärteil nach den Drehungen ist der gesuchte Wert.

Wäre ein grosser Spass, das mal so zu versuchen.

Cheers
Detlef

von Detlef _. (detlef_a)


Lesenswert?

Nochmal nachgedacht, das geht viel einfacher. Du rufst einfach die 
Routine aus
Beitrag "Goertzel-Algorithmus und Signed Fractional Format"
so auf:
IntAtan2(foo,1). Eingangsdynamik sind allerdings 16Bit, weiß nicht, ob 
das dem TO reicht.

Cheers
Detlef

von Possetitjel (Gast)


Lesenswert?

W.S. schrieb:

> Was ist denn mit einer stückweisen quadratischen Annäherung?

Geht beschissen, wegen der horizontalen Asymptote. Man muss
in ewig viele Teilintervalle zerlegen.

(Gut... ich hatte kubische Splines betrachtet. Macht keinen
großen Unterschied in der Kernaussage.)

> Etwas
> rechnen können, wird auch der winzigste Winzigkontrolleur.

Ein alter Vorschlag von gjlayde sagt, für Winkel über 45°
die Beziehung atan(1/x) = 90 - atan(x) auszunutzen. Das
ist clever, setzt aber effiziente Berechnung von 1/x voraus.

von Edi M. (Gast)


Lesenswert?

Diese Vereinfachungen kenne Ich natürlich, aber mit einer Division bin 
Ich genau so weit. Ich denke nochmal etwas nach ...

von Possetitjel (Gast)


Lesenswert?

Edi M. schrieb:

> Diese Vereinfachungen kenne Ich natürlich, aber mit einer
> Division bin Ich genau so weit.

Irgendwie ist das ermüdend.

Für den Kehrwert gibt es eine schnelle, gut konvergente
Iteration - aber die setzt voraus, dass man schnell
multiplizieren kann.
Fällt Dir jetzt vielleicht auf, warum ich weiter oben
nach dem Multiplizieren gefragt habe?

von Possetitjel (Gast)


Lesenswert?

Detlef _. schrieb:

> Nochmal nachgedacht, das geht viel einfacher. Du rufst
> einfach die Routine aus
> Beitrag "Goertzel-Algorithmus und Signed Fractional Format"
> so auf:
> IntAtan2(foo,1).

Ja, danke.

Hatte wohl ein gewaltiges Brett vor dem Kopf. Der Unterschied
liegt offenbar nur in Startwert, Drehrichtung und Abbruch-
kriterium -- der Algorithmus bleibt genau gleich.

Wenn man von (0;1) ausgeht und so dreht, dass phi gegen den
gewünschten Winkel geht, bekommt man (cos(phi);sin(phi)).

Geht man aber von (1;y) aus und wählt die Drehungen so,
dass y gegen Null geht, ist der Gesamt-Drehwinkel gerade
atan(y).

von Possetitjel (Gast)


Lesenswert?

Possetitjel schrieb:

> Wenn man von (0;1) ausgeht

Quatsch: von (1;0) natürlich.

von Jack (Gast)


Lesenswert?

Edi M. schrieb:
> Diese Vereinfachungen kenne Ich natürlich, aber mit einer Division
> bin
> Ich genau so weit. Ich denke nochmal etwas nach ...

Von nichts kommt nichts! Man kann kein Omelett machen ohne Eier zu 
zerbrechen. Einen Tod muss man sterben.

Nahezu ohne Division würde eine altmodische Taylor-Reihe funktionieren.

arctan(x) = x - (x^3)/3 + (x^5)/5 - ... ; für |x| <= 1

Aber nur nahezu, man braucht die erwähnte Vereinfachung:

arctan(x) = pi/2 - arctan(1/x); für |x| > 1

Die Division durch die Konstanten kann man vermeiden wenn man 1, 1/3, 
1/5, 1/7 als Konstanten ablegt und multipliziert. In den Konstanten kann 
man noch das Vorzeichen einarbeiten, dann wird die Iterationsschleife 
etwas einfacher 1, -1/3, 1/5, -1/7.

Die Reihe konvergiert langsam. Entweder nimmt man dass in Kauf, man 
verzichtet auf Genauigkeit oder nimmt etwas anderes, z.b. eine 
Tschebyscheff-Approximation. Die habe ich aber nicht im Kopf.

Ich würde noch mal über das Eingangs erwähnte

arctan(x) = x / (1 + 0,28125 x^2); für |x| <= 1

nachdenken. Auf 
http://www.embedded.com/design/other/4216719/Performing-efficient-arctangent-approximation 
dröselt das jemand schön auf. Hat man aus der Signalverarbeitung I, Q 
Werte und braucht man arctan(Q/I) gilt:

arctan(Q/I)
= (Q / I) / (1 + 0,28125 * (Q / I)^2)
= (Q * I) / (I^2 + 0,28125 * Q^2)

D.h. die Division Q/I muss man nicht ausführen. Statt dessen hat man 
zwei weitere Multiplikationen. Es bleibt aber die ursprüngliche Division 
auf der rechten Seite.

Der nächste Trick ist dann (siehe Link), dass

0,28125 * Q^2 = (1/4 + 1/32) * Q^2 = Q^2/4 + Q^2/32

Hat man einen angestaubten Prozessor bei dem Schieben schneller als 
Multiplizieren ist und ein Zahlensystem, bei dem nach Rechts schieben 
einer Division durch 2 entspricht, bekommt man

Q^2/4 + Q^2/32 = (Q^2) >> 2 + (Q^2) >> 5

D.h. die Multiplikation mit 0,28125 kann man durch zwei Mal schieben 
ersetzen.

arctan(Q/I)
= (Q * I) / (I^2 + (Q^2) >> 2 + (Q^2) >> 5)

Zum Schluss, das schreibe ich nicht ab (siehe Link), kann man Q/I noch 
in acht Oktanten aufteilen um die Division 1/x in

arctan(x) = pi/2 - arctan(1/x); für |x| > 1

zu ungehen! D.h. die Division kann man sich auch sparen. Es bleibt aber 
eine Division auf der rechten Seite. Einen Tod muss man sterben.

von W.S. (Gast)


Lesenswert?

Jack schrieb:
> arctan(Q/I)
> = (Q / I) / (1 + 0,28125 * (Q / I)^2)
> = (Q * I) / (I^2 + 0,28125 * Q^2)

Das finde Ich interessant!

Ich hatte bereits vor Tagen genau darauf hingewiesen:
Beitrag "Re: Effiziente Implementierung von ARCTAN fuer uCs"

Allerdings kannte Ich bisher nur die 0,28 aus der Wikipedia.

Wie ist die Abweichung zu 0,28125 erklären?

von W.S. (Gast)


Lesenswert?

Ach jetzt sehe Ich es:

Jack schrieb:
> Der nächste Trick ist dann (siehe Link), dass
>
> 0,28125 * Q^2 = (1/4 + 1/32) * Q^2 = Q^2/4 + Q^2/32

Ein wahrlich toller Trick, einfach die Kommastellen 3 und folgende 
wegzulassen, um binär rechnen zu können.

von Edi M. (Gast)


Lesenswert?

Jack schrieb:
> Nahezu ohne Division würde eine altmodische Taylor-Reihe funktionieren.

Ist noch aufwendiger, läuft auch zu sehr vom Ergebnis weg, alles 
getestet.

Wahrscheinlich wird es eine stückweise Approximation, wo Ich nur linear 
rechnen muss. Keine Division, keine grosse Tabelle.

von Martin O. (ossi-2)


Lesenswert?

Mein Cordic-artiger Algorithmus für atan2(x,y) wobei der Rückgabewinkel 
von 0=0Grad bis 255=360Grad geht.

1
#include <avr/io.h>
2
#include <avr/pgmspace.h>
3
4
5
// generated by scaling with 65536.0 :: CORDIC1.PAS
6
int32_t iCordicSinTab[5]={25080L,12785L,6424L,3216L,1608L} ;
7
int32_t iCordicCosTab[5]={60547L,64277L,65220L,65457L,65516L} ;
8
9
10
int16_t CordicAmplitude ;
11
12
int16_t GetCordicAmplitude(){
13
  return CordicAmplitude  ;
14
  }
15
16
uint8_t iCordic(int32_t x, int32_t y) {
17
// compute angle and radius of point (x,y)
18
// angle is valid from 0 to 255 (256 would mean 2*Pi )
19
// angle is stored in phi and returned via function
20
// radius is stored in Amplitude and mus be lower than 2^15-1
21
// warning, if x near 2^15 and y near 2^15 than radius is too big !
22
23
  int32_t s,c,x0,y0,x1,y1,tmp ;
24
  uint8_t sign,phi,phi1,phiTest,k_cordic ;
25
26
// first rotate until x0,y0 are in octant 0    
27
// 
28
  x0=x ; y0=y ;
29
  phi=0 ;
30
  if ( y0<0 ) { x0=-x0 ; y0=-y0 ; phi=128 ; }
31
  if ( x0<0 ) { tmp=x0 ; x0=y0 ; y0=-tmp ; phi +=  64 ; }
32
  sign=0 ;
33
  if (y0>x0) { sign=1 ; phi += 64 ; tmp=x0 ; x0=y0 ; y0=tmp ; }
34
  // now x0 >= y0 >=0
35
 
36
 // warning: this scales amplitude !! so leave out
37
 // while (x0>0x7fff) { x0=x0 >> 1 ; y0=y0 >> 1 ; }
38
 // now rotate in 4 SAR steps with phiTest=16,8,4,2,1
39
 // and check that x0,y0 stays in octant 0
40
41
  phiTest=16 ;
42
  phi1=0 ;
43
  k_cordic=0 ;
44
  while (k_cordic<=4){
45
    // get cosine(phiTest)=c and sine(phiTest)=ss from table
46
    c=iCordicCosTab[k_cordic] ;
47
    s=iCordicSinTab[k_cordic] ;
48
    // rotate (x0,y0) by phi into (x1,y1)
49
    x1=( c*x0+s*y0) / 65536L ;
50
    y1=(-s*x0+c*y0) / 65536L ;
51
    // change x0,y0 if valid rotation step meaning that we stay in octant 0
52
    if (y1>=0) { x0=x1 ; y0=y1 ; phi1 += phiTest; }
53
    phiTest = phiTest >> 1 ;
54
  k_cordic++ ;
55
    }
56
  CordicAmplitude=x0 ;
57
  if (sign) { phi -= phi1 ; } else { phi += phi1 ; }
58
  return phi ;
59
  }

von Jack (Gast)


Lesenswert?

W.S. schrieb:
> Allerdings kannte Ich bisher nur die 0,28 aus der Wikipedia.
>
> Wie ist die Abweichung zu 0,28125 erklären?

So wie ich es gelernt habe wurde 0,28125 bewusst so gewählt, dass man 
den Trick mit dem Schieben, bei entsprechendem Zahlensystem, anwenden 
kann. Genaueres müsste im Original-Paper stehen. R. Lyons, "Another 
contender in the arctangent race".

Allerdings habe ich das Paper nicht, daher vom Hörensagen: Die 0,28 sind 
abgerundet. Ein genauerer Wert der Konstante ist 0,28086. Such man nach 
einer in der Nähe von 0,28086 liegenden Zahl, bei der man Multiplizieren 
durch Schieben ersetzen kann, landet man bei 1/4 + 1/32 = 0,28125.

0,28125 liegt sogar näher an 0,28086 als 0,28. Der maximale Fehler bei 
0,28125 statt 0,28086 ist nur unwesentlich größer.

Auf der vergeblichen Suche nach dem Paper habe ich noch etwas gefunden:

arctan(x) = pi/4 * x; für |x| <= 1;

Viel einfacher dürfte es kaum gehen. Der Fehler soll in der zweiten 
Stelle hinter dem Komma liegen. Navigieren würde ich damit nicht :-)

von Detlef _. (detlef_a)


Angehängte Dateien:

Lesenswert?

R. Lyons: Understanding Digital Signal Processing

Die arctan Approx. von Lyons steht auf S547, Kap. 13.21

Cheers
Detlef

von Detlev T. (detlevt)


Lesenswert?

Hier eine Implementierung von mir, die ich vor längerer Zeit einmal für 
einen at89C2051 (8051 Core) geschrieben habe. Mit dem sdcc Compiler 
benötigte diese Routine 307 Byte und bei 12Mhz Taktfrequenz 1.2ms. 
Interne Rechengenauigkeit sind 16Bit. Die sollte man sich gönnen, auch 
wenn das Ergebnis nur 8Bit betragen soll.
1
const unsigned int tabelle[12] = {8192, 4836, 2555, 1297, 651, 325,
2
163,  81,   41,   20,   10,  5};
3
unsigned char cordic_steps = 8; /* Zahl der Schritte im Cordic-Verfahren,
4
kann während  des Programmablaufs verändert werden.*/
5
unsigned  int cordic_abs;  /* Länge des Vektors nach der letzten Berechnung.*/
6
unsigned int cordic(int x, int y)
7
{
8
  unsigned char i;
9
  int temp = x;
10
  unsigned int angle = 32768;
11
  if (y < 0)
12
  {
13
    x = - y;
14
    y = temp;
15
    angle += 16384;
16
  }
17
  else
18
  {
19
    x      = y;
20
    y      = -temp;
21
    angle -= 16384;
22
  }
23
  for (i = 0; i < cordic_steps; ++i)
24
  {
25
    temp = x;
26
    if (y < 0)
27
      {
28
        x     -= y >> i;
29
        y     += temp >> i;
30
        angle -= tabelle[i];
31
      }
32
    else
33
      {
34
        x      += y >> i;
35
        y      -= temp >> i;
36
        angle  += tabelle[i];
37
      }
38
  }
39
  cordic_abs = x;
40
  return angle;
41
}

von W.S. (Gast)


Lesenswert?

Jack schrieb:
> Ein genauerer Wert der Konstante ist 0,28086.

Ok, Danke!

von Oplas (Gast)


Lesenswert?

Detlef _. schrieb:
> R. Lyons: Understanding Digital Signal Processing
>
> Die arctan Approx. von Lyons steht auf S547, Kap. 13.21
>
> Cheers
> Detlef

Ist das von dir verlinkte PDF eine Raubkopie?

von Possetitjel (Gast)


Lesenswert?

Jack schrieb:

> Nahezu ohne Division würde eine altmodische Taylor-Reihe
> funktionieren.
>
> arctan(x) = x - (x^3)/3 + (x^5)/5 - ... ; für |x| <= 1

Ja. Allerdings sind die Taylor-Koeffizienten für diesen
Zweck nicht optimal. Der Fehler ist sehr ungleichmäßig
übers Intervall verteilt.

> Aber nur nahezu, man braucht die erwähnte Vereinfachung:
>
> arctan(x) = pi/2 - arctan(1/x); für |x| > 1

Ja. Kehrwert ist aber auch gut iterativ berechenbar.


> Die Reihe konvergiert langsam.

Naja... man kommt mit einem kubischen Polynom aus,
wenn man die Koeffizienten von Hand optimiert.
Restfehler im Intervall [0;1] ungefähr 0.3%.

von Possetitjel (Gast)


Lesenswert?

Oplas schrieb:

> Ist das von dir verlinkte PDF eine Raubkopie?

Sehr unwahrscheinlich.

Da von "... Gewalt gegen eine Person oder [...] Androhung
einer gegenwärtigen Gefahr für Leib und Leben..." mit
hoher Wahrscheinlichkeit keine Rede sein kann, ist der
Tatbestand des Raubes nicht gegeben.

von Edi M. (Gast)


Lesenswert?

Wo ist das PDf denn her?

von T.U.Darmstadt (Gast)


Lesenswert?

Detlev T. schrieb:
> Hier eine Implementierung von mir, die ich vor längerer Zeit einmal für
> einen at89C2051 (8051 Core) geschrieben habe.

Die Tabelle ist - gerade im kleinen Winkelbereich - zu grob, denke Ich.

Warum keine rekursive Iteration?

von Bernd K. (prof7bit)


Lesenswert?

Je nachdem was Du machen willst wär vielleicht auch mein hemdsärmeliger 
Vier-Quadranten-Phasenfehler interessant:

https://gist.github.com/prof7bit/e7d2c7ca6eaa0240e13244bacb915d14

Diese äußerst innovative Funktion meinerseits (prior art!) braucht nur 
eine einzige Division und eine Multiplikation, ist zwar nicht ganz 
linear, geht dafür aber über alle 4 Quadranten und reicht locker für so 
Sachen wie ne simple Tan-Lock-Loop, hab ich zuverlässig in ner 
70m-Lichtschranke mit Software PLL am Werkeln bei 160kSamples/s (40kHz) 
auf nem kleinen Cortex M0+.

von J. S. (engineer) Benutzerseite


Lesenswert?

Wie groß ist denn da der phase error? Den Scale Factor kann ich nicht 
sehen.

Und warum hast Du den error nicht nochmal korrigiert? Der ist doch quasi 
linear und damit abziehbar.

Ich komme nochmals zu meiner Darstellung weiter oben zurück: Näherung 
gut und schön, aber aufpassen mit den Oberwellen. Wenn der ATAN genutzt 
werden soll, um irgendwelche Winkel zurückzurechnen, auf die dann 
geregelt werden soll, dann sind solche langsamen seichten Wellen, wie 
sie in der phase error Funktion drin sind, mitunter tödlich, weil sie 
Schwingungen vorgeben, die nicht drin sind und wenn dann irgendwo im 
System die richtige Frequenz daherkommt, will heißen, ein Anstieg mit 
dU/dt, dann gibt es Resonanzanregung und -verstärkung.

von Bernd K. (prof7bit)


Lesenswert?

Jürgen S. schrieb:
> Wie groß ist denn da der phase error?

kann man mit PLL_GAIN einstellen, ist proportional zu PLL_GAIN und in 
erster Näherung proportional zum Winkel den der Vektor (q,i) mit der 
q-Achse einschließt. Es ist invariant gegenüber der Amplitude.

> Den Scale Factor kann ich nicht sehen.

Sehen kann man das Ergebnis unten im Plot.

> Und warum hast Du den error nicht nochmal korrigiert? Der ist doch quasi
linear und damit abziehbar.

Was ist da linear und abziehbar? Was meinst Du?

> dann sind solche langsamen seichten Wellen, wie
> sie in der phase error Funktion drin sind, mitunter tödlich, weil sie
> Schwingungen vorgeben, die nicht drin sind und wenn dann irgendwo im
> System die richtige Frequenz daherkommt, will heißen, ein Anstieg mit
> dU/dt, dann gibt es Resonanzanregung und -verstärkung.

Ich sagte ja: "je nachdem was Du machen willst"

Ich selbst benutze das für eine PLL. Eine leichte Nichtlinearität im 
Phasendetektor ist dort vollkommen irrelevant, man kann man die 
Regelschleife problemlos so gutmütig einstellen daß sie nicht zum 
Schwingen neigt. Schau Dir z.B. nur mal die Kennlinien anderer 
gebräuchlicher Phasendetektoren an, manche sind sogar 
amplitudenabhängig. Diese hier nicht!

> mitunter tödlich [...] Schwingungen vorgeben [...] Frequenz daherkommt

Geb mal ein konkretes Beispiel was Du meinst, das Gesagte erscheint mir 
etwas nebulös formuliert, hast Du diese oder eine vergleichbare Funktion 
schon mal in einer Phasenregelschleife eingesetzt? Ich finde keinerlei 
Hinweise in der auffindbaren Literatur daß ein vergleichbares 
Husarenstück jemals versucht wurde, deshalb hat das Ding scheinbar auch 
noch keinen Namen. Die Performance in der Praxis zeigt alle 
Eigenschaften einer "echten" 4Q-Tanlock-Loop.

Der einzige Grund für die Existenz dieses unortodoxen Hacks anstelle 
eines echten 4Q-arctan() ist der geringe Rechenaufwand auch für 
schwachbrüstige CPUs, Ziel der [meiner] Übung war es einen 4Q 
Phasendetektor mit streng monotoner Kennlinie mit dem geringstmöglichen 
Rechenaufwand zu implementieren weil alles andere in dem Fall die zur 
Verfügung stehenden Ressourcen gesprengt hätte, damit ich damit eine 
DPLL bauen kann die noch sparsam nebenher im Interrupt läuft.

: Bearbeitet durch User
von Detlev T. (detlevt)


Lesenswert?

Thomas U. schrieb:
> Die Tabelle ist - gerade im kleinen Winkelbereich - zu grob, denke Ich.

Bist du sicher, dass du das Verfahren kennst/verstanden hast? Die 
Auflösung geht bis 0.01% des Vollkreises. Das sollte für eine 
Berechnung, die als Parameter nur mit integer gespeist wird, doch 
reichen.

> Warum keine rekursive Iteration?
Hast du damit eine konkrete Implementierung oder ist das nur so dahin 
geworfen? Das von mir angegebene Verfahren ist an die Leistung eines 
8051 angepasst und verwendet daher nur Addition/Subtraktion und Shift. 
Das ist der Vorteil dieses Verfahrens.

von J. S. (engineer) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Jürgen S. schrieb:
>> Wie groß ist denn da der phase error?
> kann man mit PLL_GAIN einstellen,
Das war ja die Frage: Wie groß ist der eingestellt?

>> Den Scale Factor kann ich nicht sehen.
> Sehen kann man das Ergebnis unten im Plot.
Ich sehe da nur einen unskalierten Wert von etwa 2. Was heißt das? 2%?


>> Und warum hast Du den error nicht nochmal korrigiert? Der ist doch quasi
> linear und damit abziehbar.
> Was ist da linear und abziehbar? Was meinst Du?
Wenn der Fehler so verläuft, wie in der roten Kurve, dann ist das so 
ziemlich eine Gerade. Das bedeutet, die Differenz zwischen Falsch und 
Richtig wäre abschnittsweise linear. Damit wäre sie sehr leicht zu 
eliminieren, indem die Näherung noch ein Additiv mit k x X bekommt.


> Geb mal ein konkretes Beispiel was Du meinst, das Gesagte erscheint mir
> etwas nebulös formuliert, hast Du diese oder eine vergleichbare Funktion
> schon mal in einer Phasenregelschleife eingesetzt?
Ja und das Problem ergibt sich sowohl praktisch, als auch theoretisch:

Die Funktion ATAN ist dem Wesen nach monoton, hat also keine lokalen 
Maxima. Baut man in Näherungen keine Knicke, sondern versucht Bögen zu 
bekommen, erhält man mitunter solche Extremstellen. Bei Durchfahren der 
Kennlinie wird dann nicht ein Fehlersignal erster- sondern zweiter 
Ordnung in das System injiziert, welche je nach Dämpfung zu 
Regelschwingungen führt, bzw diese verschärft.

> Ziel der Übung war es einen 4Q
> Phasendetektor mit streng monotoner Kennlinie

Die Fehlerkennlinie scheint dieser Monotonie aber zu widersprechen. ?

von Bernd K. (prof7bit)


Lesenswert?

Jürgen S. schrieb:
> Bernd K. schrieb:
>> Jürgen S. schrieb:
>>> Wie groß ist denn da der phase error?
>> kann man mit PLL_GAIN einstellen,
> Das war ja die Frage: Wie groß ist der eingestellt?

Den stellst Du genau so ein wie Du ihn brauchst: Groß genug daß das 
Ergebnis genügend Auflösung hat und gerade noch klein genug daß das 
Ergebnis in jedem Falle ohne Überlauf in Deinen Integertypen passt, 14 
Bit sollte reichen, das passt noch gut in nen signed short und hat 
massig genug Auflösung. Das übliche Spielchen halt.

>>> Den Scale Factor kann ich nicht sehen.
>> Sehen kann man das Ergebnis unten im Plot.
> Ich sehe da nur einen unskalierten Wert von etwa 2. Was heißt das? 2%?
1
phase_error ≈ 2 * PLL_GAIN * Phasenwinkel/180°

>>> Und warum hast Du den error nicht nochmal korrigiert? Der ist doch quasi
>> linear und damit abziehbar.
>> Was ist da linear und abziehbar? Was meinst Du?
> Wenn der Fehler so verläuft, wie in der roten Kurve, dann ist das so
> ziemlich eine Gerade. Das bedeutet, die Differenz zwischen Falsch und
> Richtig wäre abschnittsweise linear. Damit wäre sie sehr leicht zu
> eliminieren, indem die Näherung noch ein Additiv mit k x X bekommt.

Schreibs bitte vollständig hin und zeig nen Plot vom Deiner Variante, 
ich kann Deinen Ausführungen ansonsten beim besten Willen nicht nicht 
folgen, sie erscheinen mir unvollständig und scheinen keinen Sinn zu 
ergeben. Gib Dir bitte etwas mehr Mühe.

> Die Funktion ATAN ist dem Wesen nach monoton, hat also keine lokalen
> Maxima. Baut man in Näherungen keine Knicke, sondern versucht Bögen zu
> bekommen, erhält man mitunter solche Extremstellen. Bei Durchfahren der
> Kennlinie wird dann nicht ein Fehlersignal erster- sondern zweiter
> Ordnung in das System injiziert, welche je nach Dämpfung zu
> Regelschwingungen führt, bzw diese verschärft.

Nein.

Wenn die Regelung schwingt stellst Du mit Fingerspitzengefühl die 
Regelkonstanten in geeigneter Weise ein, so lange bis sie dem 
gewünschten Verhalten ausreichend nahe kommt. Fertig.

>> Ziel der Übung war es einen 4Q
>> Phasendetektor mit streng monotoner Kennlinie
>
> Die Fehlerkennlinie scheint dieser Monotonie aber zu widersprechen. ?

sie ist streng monoton steigend und stetig im gesamten 
Definitionsbereich von [-180°, +180°[ wie unschwer am Plot zu erkennen 
ist.

: Bearbeitet durch User
von J. S. (engineer) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> phase_error ≈ 2  PLL_GAIN  Phasenwinkel/180°

Heiland! Die Formel sehe Ich auch. Die entscheidende Frage ist, wie es 
hier im konkreten Beispiel eingestellt war.

Die Skala für die rote Fehlerkurve hat keine Einheit. Sie geht nur von 0 
.. 2.

Was heisst hier 2?

2 Grad Phasenfehler?  Hoffentlich nicht.
2% Phasenfehler. Hoffentlich nicht.

2 Digit nehme ich an?


Bernd K. schrieb:
> Wenn die Regelung schwingt stellst Du mit Fingerspitzengefühl die
> Regelkonstanten in geeigneter Weise ein, so lange bis sie dem
> gewünschten Verhalten ausreichend nahe kommt. Fertig.
Jaha, das belastet aber die Regelreserve und Stabilität. Es gibt ja noch 
mehr Schwingungen im System, die in die Regelung eingeprägt werden und 
Regelbedarf erzeugen - nicht zuletzt die von Außen und jede 
Nichtlinearität des Systems erzeugt ein zusätzliches Problem und da sind 
die Schwingungen erster und vor allem zweiter Ordnung in vielen Systemen 
ein Punkt.

Eine Näherung mag im Proportional monoton sein, ist es aber schon bei 
der ersten Ableitung nicht mehr.

von Bernd K. (prof7bit)


Lesenswert?

Jürgen S. schrieb:
> Heiland! Die Formel sehe Ich auch. Die entscheidende Frage ist, wie es
> hier im konkreten Beispiel eingestellt war.

Steht doch alles schwarz auf weiß da, guggst Du: PLL_GAIN=1, hast Du 
eine Sehschwäche oder willst Du trollen? Langsam kommts mir so vor.

> Die Skala für die rote Fehlerkurve hat keine Einheit. Sie geht nur von 0
> .. 2.

Erstens geht sie von -2 bis 2 und zweitens ist die Einheit volkommen 
irrelevant denn die interessiert keinen denn die kann man sich 
einstellen wie man sie braucht, am besten so daß die Integer immer gut 
gefüllt sind, der Wert hat die Dimension eines Winkels und bei der 
Konfiguration im Beispiel wäre die Einheit 1 "Viertelkreis", Also geht 
der Wertebereich von -Halbkreis bis +Halbkreis, oder allgemeiner ist die 
Einheit Kreis/(4*PLL_GAIN) oder meinetwegen auch pi/(2*PLL_GAIN) oder 
90°/PLL_GAIN

> Was heisst hier 2?

Mein Gott, das steht doch da! 2 heißt in dem Fall 180° hinterher, -2 
heißt 180° voraus und 0 heißt "der Zeiger steht so wie er soll" und wäre 
zum Beispiel der Sollwert eines Phasenreglers, der Fehler wäre null. Ich 
hab nen schönen Plot hinmalen lassen hast Du den nicht gesehen?

Was ist jetzt mit Deiner verbesserten Formel wo Du irgendwas von 
irgendwo abziehen willst um es linear zu bekommen, wo kann man sich die 
mal anschauen?

Außerdem will der OP gar keine PLL bauen sondern irgendwas anderes 
machen, also schweifen wir sowieso vom Thema ab.

: Bearbeitet durch User
von T.U.Darmstadt (Gast)


Lesenswert?

Detlev T. schrieb:
> Bist du sicher, dass du das Verfahren kennst/verstanden hast? Die
> Auflösung geht bis 0.01% des Vollkreises. Das sollte für eine
> Berechnung, die als Parameter nur mit integer gespeist wird, doch
> reichen.

Ich denke, daß Ich das verstanden habe, ja, Ich meine nur, daß es noch 
genauer sein könnte, wenn die Tabelle genauer wäre, als mehr Werte hätte 
oder mit einer größeren Auflösung arbeiten würde.

von J. S. (engineer) Benutzerseite


Angehängte Dateien:

Lesenswert?

Bernd K. schrieb:
> Steht doch alles schwarz auf weiß da, guggst Du: PLL_GAIN=1,

Es ging mir nur um die Sicherstellung, dass Ich Deine Grafik richtig 
interpretiere.

Üblicherweise enthalten Diagramme im wissenschaftlichen Bereich eine 
Skalierung der Y-Achse oder einen sonstigen Hinweis, damit man das 
eindeutig ablesen kann und hier konkret ergibt sich nicht zwangsläufig, 
dass Fehler unskaliert dargestellt sind, weil sie in der Regel so klein 
sind, dass man sie 10fach oder 100fach einblenden muss, um den Verlauf 
zu sehen.

Dass sich das aus dem PLL Gain errechnen lässt, konnte Ich nicht 
ersehen, zumal Ich nirgendwo etwas von einem Setting mit "1" erspähen 
konnte.
Momentan kann man gar nichts lesen, weil die Grafik verschwunden ist - 
jedenfalls führt der link auf den Ich mich bezog ins Nichts. Ist das 
wieder weggenommen worden?

Wie auch immer, Ich entnehme Deiner Darstellung dass es skalierbar ist.

Ich hatte mich innerlich etwas gewehrt, die Zahl 2 so zu lesen, dass es 
Digit sind.

Fragen wir daher mal anders herum: Wie weit ließe sich das denn 
verbessern? Erzähle mir jetzt itte aber nicht, dass Ich den PLL-GAIN 
hochstellen muss.

Vielleicht frage Ich deshalb mal gleich direkter, dass Dir das Problem 
etwas deutlicher wird:

Wie genau muss man Deine Lösung machen und wie umfangreich wird sie 
dabei, um an die Qualität der Definition aus den Wiki heranzukommen?

Zu Deiner anderen Frage:

Falls Du meinen Vorschlag der abschnittsweisen Annäherung meinst: Diese 
ist skalierbar, arbeitet also mit eine genügenden Anzahl von Schritten 
und zwar so, dass der lokale Fehler optimiert wird. Ein Fehler von z.B. 
2 Grad benötigt mit der einfachen Interpolation (aus der Erinnerung) so 
etwa 12 Bereiche / Stützstellen und damit 4 Abfragen ("If Then"s)

Falls Du meinen Vorschlag der weiteren Unterteilung der Wiki-Annäherung 
meinst: Man kann (und sollte!) speziell den Übergangsbereich zwischen 
den Formeln um die Werte -0,28 und 0,28 jeweils mit einer Quadratischen 
Kurve überbrücken / glätten. Das macht dann 5 Terme / Bereiche statt 3 
beseitigt aber der Sprung an den Übergängen.

von Bernd K. (prof7bit)


Lesenswert?

Jürgen S. schrieb:

> Dass sich das aus dem PLL Gain errechnen lässt, konnte Ich nicht
> ersehen,

Das ist doch offensichtlich wenn man sich die Funktion anschaut.

> zumal Ich nirgendwo etwas von einem Setting mit "1" erspähen
> konnte.

Steht da schwarz auf weiß, dick und fett, willst Du mich verschaukeln? 
Sprechen wir überhaupt von der selben Sache? Wieso kritisierst Du etwas 
das Du gar nicht verstanden, bzw überhaupt noch nicht mal gesehen 
geschweige denn gelesen hast?

von Lukas (Gast)


Lesenswert?

Ich glaube das Missverständnis kommt vom gewählten Namen phase error mit 
welchem hier nicht der tatsächliche Fehler zu atan2 gemeint ist.
Ich hab das mal kurz ausprobiert und komme mit Bernds funktion auf einen 
maximalen Winkelfehler von +- 4°

von Michael W. (Gast)


Lesenswert?

Detlev T. schrieb:
> Hier eine Implementierung von mir, die ich vor längerer Zeit einmal für
> einen at89C2051 (8051 Core) geschrieben habe. Mit dem sdcc Compiler
> benötigte diese Routine 307 Byte und bei 12Mhz Taktfrequenz 1.2ms.
Das ist der ganz normale Cordic, oder?

Wäre mal ein Vergleich der Verfahren interessant.

von J. S. (engineer) Benutzerseite


Lesenswert?

Lukas schrieb:
> Ich glaube das Missverständnis kommt vom gewählten Namen phase error mit
> welchem hier nicht der tatsächliche Fehler zu atan2 gemeint ist.
Das ist das Problem, daher hatte Ich in meinem Beitrag der ersten 
Nachfrage schon konkret nach dem realen Fehler gefragt.


> Ich hab das mal kurz ausprobiert und komme mit Bernds funktion auf einen
> maximalen Winkelfehler von +- 4°
Ja, soweit lässt sich das grafisch ablesen (+2/-2).

Aber damit ist die Frage immer noch offen:

Was ist der minimale Fehler?

Die Genauigkeit kann man nicht beliebig steigern, auch nicht durch den 
PLL-Gain, weil - wenn man sich die "phase-Funktion" mal anschaut und 
gedanklich nach unten klappt - irgendwann dahin kommt, dass die 
Welligkeit relevant wird und die Monotonie verletzt.

von Bernd K. (prof7bit)


Lesenswert?

Jürgen S. schrieb:
>> Ich hab das mal kurz ausprobiert und komme mit Bernds funktion auf einen
>> maximalen Winkelfehler von +- 4°
> Ja, soweit lässt sich das grafisch ablesen (+2/-2).

Nein, das ist der Rückgabewert der Funktion. Der Winkelfehler wäre die 
Differenz zwischen diesem und einem echten atan2() wenn man man sie 
beide auf den selben Wertebereich skalieren und vergleichen würde.

ich habe sie damals phase_error() genannt weil das den Zweck beschreibt 
für den ich sie damals eingesetzt habe: Der Zweck war: wie weit und in 
welche Richtung ist das abgetastete Signal phasenverschoben zu einem 
festen Sollwert, also in welche Richtung und wie stark muss ich den 
Oszillator nachführen. Das "wie weit" ist einfach eine Zahl, beliebig 
skaliert, einzige 2 Bedingungen: sie soll 0 erreichen wenn die 
Phasenlage genau dem Soll entspricht und sie soll halbwegs 
proportional zur Abweichung vom Sollwert sein. Skaliert wird der Wert 
per Fingerspitzengefühl so daß sich die Regelschleife nicht daran 
verschluckt und daß das Regelverhalten zufriedenstellend ist, also 
welche absoluten Zahlen dabei herauskommen ist letztendlich vollkommen 
egal, Hauptsache sie passen größenordnungsmäßig zur Verstärkung der 
Regelschlefe so daß man den Regler so justieren kann daß es vernünftig 
funktioniert. Sobald es sich eingeregelt hat hängt es dann eh nur noch 
bei +- null herum.

von J. S. (engineer) Benutzerseite


Lesenswert?

Ich denke, das Prinzip der Methode ist hinreichend verstanden. Eine 
ähnliche Herangehensweise findet sich z.B. bei analogen Frequenzteilern 
und Suboszillatoren in Synthesizern.

Im Prinzip macht die PLL eigentlich das, was der CORDIC auch tut, er 
berechnet den Wert aufgrund der aktuellen Annahme des Fortschritts und 
"regelt" den Fehler auf Null. Der CORDIC geht den Weg halt zu Ende und 
gibt den finalen Wert erst nach Erreichen des konvergierten Zielwertes 
aus, die PLL gibt permanent einen Wert aus.

Was eben interessant wäre, ist die tatsächliche Abweichung gegenüber 
sich z.B. dynamisch ändernden Eingangswerten. Die PLL entwickelt ja ihr 
Eigenleben bis hin zur Resonanz, wenn dem Eingangsignal ein 
entsprechendes Spektrum überlagert ist.

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.