Trig Functions without math.h

This is a discussion on Trig Functions without math.h within the C Programming forums, part of the General Programming Boards category; I pose a problem, ladies and gentlemen. I need to do work with vectors on a Vex microcontroller, but the ...

  1. #1
    Registered User
    Join Date
    Dec 2007
    Posts
    14

    Trig Functions without math.h

    I pose a problem, ladies and gentlemen. I need to do work with vectors on a Vex microcontroller, but the problem is, it doesn't support double or float variables, so using the math.h trig functions is out. Specifically, I need tangent and arctangent (tan and atan). Any way of writing custom functions for these using integers?

    Basically, I'm trying to derive the angle and magnitude of the position of a control joystick with the x and y axes. Each axis has an unsigned character range (0-255), and 127 is center. I need to combine the positions of both axes and get the angle of the joystick (such as on a standard coordinate plane) and the distance of the joystick from the center (Pythagorean theorem, not worried about that part).

  2. #2
    Registered User carrotcake1029's Avatar
    Join Date
    Apr 2008
    Posts
    398
    There sure is. As a matter of fact, I wrote a function to calculate cosine a while back, let me see if I can tweak it for tangent, shouldnt be difficult.
    Cosine - Maclaurin Series
    I do include the math.h header, but it is only for the constant M_PI, which you could easily define yourself.

    Edit: The tangent Maclaurin series is pretty complicated, you could divide sin by cosine.
    Last edited by carrotcake1029; 12-27-2008 at 04:31 PM.

  3. #3
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,158
    Quote Originally Posted by daltore View Post
    I pose a problem, ladies and gentlemen. I need to do work with vectors on a Vex microcontroller, but the problem is, it doesn't support double or float variables, so using the math.h trig functions is out. Specifically, I need tangent and arctangent (tan and atan). Any way of writing custom functions for these using integers?

    Basically, I'm trying to derive the angle and magnitude of the position of a control joystick with the x and y axes. Each axis has an unsigned character range (0-255), and 127 is center. I need to combine the positions of both axes and get the angle of the joystick (such as on a standard coordinate plane) and the distance of the joystick from the center (Pythagorean theorem, not worried about that part).
    If you have the ROM for it, maybe a lookup table combined with interpolation? You could try to compute it using a series expansion with fixed point arithmetic, but I don't think it would be terribly accurate.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  4. #4
    Registered User
    Join Date
    Dec 2007
    Posts
    14
    Quote Originally Posted by carrotcake1029 View Post
    There sure is. As a matter of fact, I wrote a function to calculate cosine a while back, let me see if I can tweak it for tangent, shouldnt be difficult.
    Cosine - Maclaurin Series
    I do include the math.h header, but it is only for the constant M_PI, which you could easily define yourself.

    Edit: The tangent Maclaurin series is pretty complicated, you could divide sin by cosine.
    This still involves a lot of floating point variables, not to mention the long long function for factorials. Vex is a low-power microcontroller with ONLY character and integer variables.

    If you have enough ROM you could try a lookup table...
    I don't have enough ROM. I think the limit is something like 4 KB, and that's really pushing it. I'd have to make an instance for 256^2 (2^16) cases. That's a lot of ROM.

    Thanks for the suggestion though, I'll look into the Maclaurin series in the future for applications with more memory and processor speed.

  5. #5
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,158
    Quote Originally Posted by daltore View Post
    I don't have enough ROM. I think the limit is something like 4 KB, and that's really pushing it. I'd have to make an instance for 256^2 (2^16) cases. That's a lot of ROM.
    Well, it depends how small you want to make the table and how much interpolation error you can tolerate. Also, for something like arctan which is extremely flat over much of its range, you could approximate quite brutally and still have low absolute error.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Katy, Texas
    Posts
    2,309
    You could multiply all your integers by 10,000, and after any division, you get 4 decimal places, still in the integer, to round or discard as you please.
    Mac and Windows cross platform programmer. Ruby lover.

    Quote of the Day
    12/20: Mario F.:I never was, am not, and never will be, one to shut up in the face of something I think is fundamentally wrong.

    Amen brother!

  7. #7
    Registered User NeonBlack's Avatar
    Join Date
    Nov 2007
    Posts
    435
    Interpolation is probably your best bet. You might be surprised at how few points you're able to get away with.
    Using a taylor series is going to use a lot of cycles and if I remember correctly, the taylor series for arctan diverges for tan(x) > 1.
    ---
    Just a heads-up to save you some time in case you hadn't realized it yet:
    When you use atan(x/y), you lose some information (since -x/y==x/-y). So you're probably going to want to use atan2(x,y), which will let you recover the full 360 degrees of angles. With the regular atan, you are only going to get 180 degrees back.
    Last edited by NeonBlack; 12-27-2008 at 05:38 PM.
    I copied it from the last program in which I passed a parameter, which would have been pre-1989 I guess. - esbo

  8. #8
    Registered User
    Join Date
    Dec 2007
    Posts
    14
    Quote Originally Posted by NeonBlack View Post
    Just a heads-up to save you some time in case you hadn't realized it yet:
    When you use atan(x/y), you lose some information (since -x/y==x/-y). So you're probably going to want to use atan2(x,y), which will let you recover the full 360 degrees of angles. With the regular atan, you are only going to get 180 degrees back.
    Yeah, I'm taking Pre-Cal right now, that kind of info yells at me. But that may not actually be a bad thing because of what I'm using this for anyway. I need these vectors because I'm trying to write point-and-shoot driving code for a crab drive base (all 4 wheels of the base swivel in sync). The distance of the joystick from center is proportional to speed, and the angle of the joystick determines where the wheels are pointing. There's a potentiometer that measures where the wheels are, and because of the way it's all set up, the wheels can't swivel 360*, so I need to reverse the direction of their rotation at 180 anyway. Because of the reaction time you need for the crab drive and because there's no way to turn other than strafing, I really don't think a lookup table is the way to go. I think next I'll try law of sines or law of cosines to find the angle or something. Thank you for all the suggestions, though.

  9. #9
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,261
    I would definitely recommend the CORDIC method. It is the algorithm used for producing the results in hardware, as it can be done using entirely integer math. Here are some links:
    http://www.voidware.com/cordic.htm
    http://en.wikipedia.org/wiki/CORDIC
    http://www.andraka.com/cordic.htm
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  10. #10
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,261
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  11. #11
    Registered User
    Join Date
    Dec 2007
    Posts
    14
    Quote Originally Posted by iMalc View Post
    I would definitely recommend the CORDIC method. It is the algorithm used for producing the results in hardware, as it can be done using entirely integer math.
    Yeah, I found a CORDIC library for IFI controllers (of which Vex is a part), and I'm currently testing it.

    You could multiply all your integers by 10,000, and after any division, you get 4 decimal places, still in the integer, to round or discard as you please.
    The problem is not working with the numbers, there are fairly easy ways to deal with those numbers, the main problem is that all of the functions are written in object code as floats.

  12. #12
    Registered User
    Join Date
    Dec 2007
    Posts
    14
    Just an update, I have found a function which closely approximates the arctangent function that can be modified for integers for a precision of about 2 degrees or .01 radians. It uses ratios of x and y subtracted against each other in various arrangements that I can't quite understand at 3:17 AM, but here's the code:

    Code:
    int arctan2(int y, int x)
    {
       int angle, abs_y = y;
       if(abs_y<0) abs_y=0-y;
    
       if(x>=0) angle = (45 - (45 * (x - abs_y) / (x + abs_y)));
       else if(x<0) angle = (135 - (45 * (x + abs_y) / (abs_y - x)));
    
       if (y < 0) return(360-angle);     // negate if in quad III or IV
       else if(y >= 0) return(angle);
    }
    Enjoy, it works great. For more precision in the 2nd and 4th quadrants, which apparently have lower precision inherently with this setup, you can use these two lines, respectively:

    theta1=0.1963 * ((x + abs_y) / (abs_y - x))^3 - 0.9817 * ((x + abs_y) / (abs_y - x)) + pi/4 (2a)
    theta2=0.1963 * ((x - abs_y) / (x + abs_y))^3 - 0.9817 * ((x - abs_y) / (x + abs_y)) + 3*pi/4 (4a)

    Please note that the carret (^) is used as an exponent here, not the XOR, and cannot be used as such in the actual program. Also note that these are in radians, not degrees, because the original function was built for radians and I was to lazy to modify the two lines I didn't use. For more details, see the original website:

    http://dspguru.com/comp.dsp/tricks/alg/fxdatan2.htm

  13. #13
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Katy, Texas
    Posts
    2,309
    The bits in red can be removed:
    Code:
    int arctan2(int y, int x)
    {
       int angle, abs_y = y;
       if(abs_y<0) abs_y=0-y;
    
       if(x>=0) angle = (45 - (45 * (x - abs_y) / (x + abs_y)));
       else if(x<0) angle = (135 - (45 * (x + abs_y) / (abs_y - x)));
    
       if (y < 0) return(360-angle);     // negate if in quad III or IV
       else if(y >= 0) return(angle);
    }
    Mac and Windows cross platform programmer. Ruby lover.

    Quote of the Day
    12/20: Mario F.:I never was, am not, and never will be, one to shut up in the face of something I think is fundamentally wrong.

    Amen brother!

  14. #14
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,158
    Quote Originally Posted by daltore View Post
    Just an update, I have found a function which closely approximates the arctangent function that can be modified for integers for a precision of about 2 degrees or .01 radians. It uses ratios of x and y subtracted against each other in various arrangements that I can't quite understand at 3:17 AM, but here's the code:
    That's an extremely coarse rational-linear approximation (as you can tell by noting that the variable 'x' and 'y' don't get raised to any power anywhere). If it works well enough for you, I'd use it -- it's about as simple, small, and fast as you'll get.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Factory Functions HOWTO
    By GuardianDevil in forum Windows Programming
    Replies: 1
    Last Post: 05-01-2004, 01:41 PM
  2. Shell functions on Win XP
    By geek@02 in forum Windows Programming
    Replies: 6
    Last Post: 04-19-2004, 05:39 AM
  3. Inline functions and inheritance
    By hpy_gilmore8 in forum C++ Programming
    Replies: 3
    Last Post: 01-14-2004, 05:46 PM
  4. functions - please help!!!!
    By linkies in forum C Programming
    Replies: 1
    Last Post: 08-21-2002, 07:53 AM
  5. math.h
    By falconetti in forum C Programming
    Replies: 2
    Last Post: 11-17-2001, 01:37 PM

Tags for this Thread


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21