Getting arccosine and arctan using CORDIC functions
DiverBob
Posts: 1,108
in Propeller 2
Using Spin2 has anyone created a functions to get arccosine and arctan values on the P2? With some great help on the forum I’ve now got good examples of the QSin, qcos, polxy, xypol, and rotxy math methods. Between these I was able to calculate tangent also. Still learning about the CORDIC and wanted to see if anyone has already figured out arccosine and arctan for the P2.
Bob
Comments
Based on my studies there isn’t any simple way to come up with arccosine. I did find some interesting approaches to solving for an angle over in Stack Overflow website so here are some code examples I found in case anyone else is interested. I have not tried these approaches out or converted them to Spin2 yet (Original code examples is C++).
The notes indicate it can have an error of up to 10.31 degrees, most likely that is at both ends of the range
This next one is via Nvidia, comments stated it is pretty accurate
Trey Reynolds posted this version:
I’ll try these out and see how they do. I want to convert everything over so I’m only doing integer math for speed, have to see what that does to accuracy.
Bob
The 90's 3d raycast code creates tables at the start. Maybe that's the way to do it. Very fast...
Another way, might be guess and then iteration to improve result. Might be slow, but more precise.
Or, mix table and iteration methods together...
I’ll try out the examples above and see what kind of speed I get out of them. I only need 0.1 degree precision so I may just end up using a lookup table with interpolation. Gives me a goal for tomorrow!
Is an expansion of a Taylor series an option?
For arctan, you can use the second return value of
XYPOL(x, y)
, which is equal toatan(y / x)
.XYPOL
is equivalent to what might be expressed in other languages as(hypot(x, y), atan2(y, x))
.arctan basically is xypol, if you think about it. It's easier to see with the atan2(y, x) function, which finds the angle between point (x, y) and the x axis -- this is exactly what xypol does. arctan(x) is just atan2(y, 1).
arcsin(x) and arccos(x) are similarly derivable from atan2 by looking at the triangles, e.g. arcsin(z) is the angle a such that sin(a) = z, which means it's the angle in the triangle whose height (y coordinate) is z and which has a unit hypotenuse: so it is atan2(z, sqrt(1-z*z)). arccos(z) is the same except it's the x coordinate which is z.
Thanks, I didn’t realize that was what xypol delivered. I ran some test entries with the method but didn’t stop to figure out what the results represented.
The Taylor series appears to output the most accurate values but it very costly in cpu cycles. I want to see if what kind of accuracy I can get with some of the faster methods available. Since I only need 0.5 degree accuracy for my robot I should be able to get away with a simpler computation.
Here's an implementation of arcsin and arccos in Spin2. Tested with flexspin, but it should compile with Chip's compiler (except maybe you'll have to replace SmartSerial with a different serial object). Change SCALE to get different number of places after the decimal point. For example with SCALE = 1_0000 there are 4 places after the decimal point, so 0.5 is represented as 5000, 0.25 as 2500, 41.3 as 413000, and so on.
That is great Eric! I will go through the math and figure out how you did it. I was looking at xypol yesterday but hadn’t thought about using it that way. This looks much cleaner than some of the interpolations I found yesterday and it uses the built-in CORDIC method. I was talking with Chip and the group at last nights meeting about this and there were a couple of ideas but no real solutions. I can’t wait to try this one out.
Slowly creating a library of SPIN2 trigonometry methods based on integer math with a lot of help from everyone. Eric's asin and acos worked great but somehow I managed to break them when I was working on the atan and atan2 items. Eric's code is good, it's something that I did but I just can't seem to find the error. acos and asin should be returning the original angle value (60 degrees) and it was working correctly for most of the afternoon. I think I'm too close to the problem and can't see the forest for the trees at this point. I've been running this code in the Prop Tool with Debug for the testing output.
In the current code that you posted you are feeding the asin and acos routines variables x and y instead of s1 and s2. When I put s1 and s2 into the asin and acos calls, they do indeed return values close to 60 degs.
So the asin and acos routines do appear to work, atan still needs more work...
Thanks for the fresh set of eyes, don’t know how I missed that.
Atan is still a work in progress, I had been tweaking that when I noticed I’d messed up the asin() and acos(). I’m still deciphering Eric’s comments on how to use xypol() to derive atan and atan2.
You can get arctan from arcsin, but you have to do a square root to get there....
I’ve been experimenting with coding for several hours and not getting the expected results. Tan(60) = ~1.732 so atan(1.732) should return ~60 degrees back (when working in DEG vs RAD). Don’t really understand how XYPOL(x,y) fits in here when x and y are position values and I’m supplying tan(angle) value. I tried some trig substitutions but didn’t get good results from that either.
Next problem, XYPOL(x,y) expects a x,y value so it seems to fit right in with the atan2(y,x) definition. Using 3000 and 4000 (using integer math by scaling everything by 1_000) for y, x entered into _,angle := XYPOL(4000,3000) gives the expected result of 53_130 degrees but changing the x value to 4000 so the expected angle is 45_000, however the output was still 53_130. Since both X and y are positive I am only dealing with quadrant 1 at this point.
When I tried it I got the expected value of 45000 for atan2(4000, 4000). Also, if you have atan2 then you can implement atan(a) as atan2(a, SCALE). Here's my complete program:
and here's the output it produces
Thanks Eric, I had the right formula but after a day’s re-coding I was using the wrong variables coming into the atan2() code so I was getting bad output based on what I was expecting.
Here is a library of Spin2 CORDIC based trig functions (sin, cos, tan, asin, acos, atan, atan2) that use scaling for integer-based calculations. All angle inputs and outputs are in Degrees, not Radians.
There are also a couple of examples of using polxy() and rotxy() included that work but haven't been fully tested for all angle combinations
Glad you all figured this out! Turns out I need inverse cosine to have my robot dog calculate its own servo angles.
The above looks perfect for this. Guessing that's why @DiverBob needed it too...
@DiverBob: Some months ago I was chatting with Chip about math functions and he showed me a couple tricks that you may want to use -- the power() method from Chip always runs in 760 ticks (including call and return) while your looped version (which is where I started, too), is always slower and the size of the exponent will compound that. For projects like Ray's robot this may be important.
That's good info, Jon, thanks.
I created a related topic as well - https://forums.parallax.com/discussion/173878/qrotate-and-qvector-made-simple/p1
Your posting helped me a lot in really understanding how the P2 dealt with angles.
Jon, as always you (and Chip!) come up with some great insights on P2 coding. Never even looked at qexp() as an option. Thank you for the information, now I get to dig back into the math again! Actually I really needed an excuse to get back programming, been taking a 3 month break from the robot and programming, about time to get moving on it again!
welcome back Bob!
I'm thinking about trying to adjust the functions so they take angles in degrees*scale instead of degrees.
Maybe that would improve accuracy?
Why don't you use binary radians everywhere? They're the only angle units that don't suffer discontinuities when you go around a whole circle, and they are (for this very reason) already the native units of the P2's CORDIC.
Binary radian - Never thought to call it that ... looking up Wikipedia, yeah, it's right but, as is the case with many names in computing, it's a repurposed naming.