Need help with a Math problem

I have to turn a AZ/EL rotor system into a XY system.

For the X part this is the formula where az and el are the Azimuth and Elevation values.

Y = Degrees ( ArcSin ( Sin ( Radians ( -az ) * Cos ( Radians ( -el )))) + 90.0

In arduino code this would look like:

#include "Arduino.h"
#include <SoftwareSerial.h>
#include <Streaming.h>
#include <math.h>

  long _XX;
  long _YY;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  delay(1000);
  _xyConvert(36000, 19700);
}

void loop() {
  
}

void _xyConvert(long el, long az)
{
  double azFloat = az;
  azFloat = azFloat /100;
  double elFloat = el;
  elFloat = elFloat /100;

    _XX = atan(cos(-az) / tan(-el)) + 90;
    
    _YY = asin(sin(-az) * cos(-el) ) + 90;
  
      Serial << _FLOAT(_XX,2) << " - " << _FLOAT(_YY,2) << endl;
}

But the result in both equals zero. Any help would be wolcome.

Moderator edit: Tags hopefully corrected

Well, the standard math libraries work in radians, not degrees.

I see that you're using degrees. All trigonometric math functions use radians. Meaning convert your degrees to radians (90 degrees == pi/2 radians). Second, making a variable a float or double doesn't make the math float/double. Here is a example:

float x = 90 * 1.1;

Won't necessarily give you 99 because the number 90 is an integer. Type this instead to fix the problem:

float x = 90.0 * 1.1;

By putting a ".0" in front of the number you automatically turned it from an integer to a float.

I'm not home, otherwise I would have tried your code...

I now have changed the code by including the radians to degrees conversion by declaring a static

  static const double DegreeToRadian = 0.0174532925;  // PI/180

and included in the two calculations

    _XX = DegreeToRadian * ( atan(cos(-az) / tan(-el) ) ) + 90.0;
    _YY = DegreeToRadian * ( asin(sin(-az) * cos(-el) ) ) + 90.0;

But now the result is in both 90.0

But now the result is in both 90.0

So, print some intermediate results...

It's not the output of tan(), sin(), cos(), etc. that needs to be converted. Its the INPUT.

You got it backward... Try this instead...

az = az * DegreeToRadian;
el = el * DegreeToRadian;
    _XX = (1 / DegreeToRadian) * ( atan(cos(-az) / tan(-el) ) ) + 90.0;
    _YY = (1 / DegreeToRadian) * ( asin(sin(-az) * cos(-el) ) ) + 90.0;

After postin i figured that out as wel en started to output every line of calculation. This is the result of that:

az(RAD) = 23000(4.01) and el(RAD) = 5100(0.89) Calculate Azimuth >> X cos(-azFloat) = -0.64 tan(-azFloat) = -1.23 cos(-azFloat)/tan(-azFloat) = 0.52 atan(cos(-azFloat)/tan(-azFloat)) = 0.48 atan(cos(-azFloat)/tan(-azFloat))*RadianToDegree = 27.50 (atan(cos(-azFloat)/tan(-azFloat))*RadianToDegree)+90.0 = 117.50 Calculate Elevation >> Y sin(-azFloat) = 0.77 cos(-elFloat) = 0.63 sin(-azFloat)*cos(-elFloat) = 0.48 asin(sin(-azFloat)*cos(-elFloat)) = 0.50 (asin(sin(-azFloat)*cos(-elFloat)))*RadianToDegree = 28.82 ((asin(sin(-azFloat)*cos(-elFloat)))*RadianToDegree)+90 = 118.82

Made by this code and gives the corect values.

void _xyConvert(long az, long el)
{
  double azFloat = az;
  azFloat = azFloat /100*DegreeToRadian;
  double elFloat = el;
  elFloat = elFloat /100*DegreeToRadian;

    _XX = (atan(cos(-azFloat) / tan(-elFloat) )*RadianToDegree) + 90.0;
    _YY = (asin(sin(-azFloat) * cos(-elFloat) )*RadianToDegree) + 90.0;
}

But your code example gives a different answer _XX =75 and _YY=117

some observations: 1) make az and el float, that prevents truncating here -> azFloat /100*DegreeToRadian;

2) note there are several division by zero possible

3) style (is a matter of taste) double azFloat = az; azFloat = azFloat /100*DegreeToRadian;

can be a one liner without loosing readability

double azFloat = az / 100.0 * DegreeToRadian;

@Rob, following your advise i have changed the routine to this.

void _xyConvert(float az, float el)  {

  double azFloat = az /100*DegreeToRadian;
  double elFloat = el /100*DegreeToRadian;

 _XX =  (asin(sin(-azFloat) * cos(-elFloat) )*RadianToDegree) + 90.0;
  if(el < 0.1) { 
    el = 0.1;
  } 
  else {
    _YY = (atan(cos(-azFloat) / tan(-elFloat) )*RadianToDegree) + 90.0;
  }
}

According to the theoritical guys there are some differences between there results on a sacel-ruler en this routine.
How i can i prefent/tactle the divisions by 0?

How i can i prefent/tactle the divisions by 0?

The obvious one that I see is the atan(y/x) problem. Since tan(a) = sin(a)/cos(a) then tan becomes asymptopic at PI/2 and multiples of PI thereafter (3PI/2 etc)

I am not sure how safe the maths library is for using atan2(x,y) or whether it may be better to write your own arctan procedure in order to get the angle in the correct quadrant.

Here is a link that describes the problem clearly and simply:

http://hyperphysics.phy-astr.gsu.edu/hbase/ttrig.html

baselsw: float x = 90 * 1.1;

Won't necessarily give you 99 because the number 90 is an integer

It will never give 99, but it will always give 99.0 !!

It does because the result of an operator will be widened to float if any of its operands are float. Having the 1.1 is enough to make the RHS float. However it is easy to make mistakes when mixing integer and floating point values, such as with

  float a = 2000 / 13 * 4.5 ;

This is parsed as (2000 / 13) * 4.5 so that the coercion to floating point doesn't happen till after the divide has happened, so integer (truncating) divide would be used. It is thus good practice to always use floating point constants for floating point values - it also makes your intent clear in the code to those that have to work with it later.

Some came with a other aproach and while constructing it i created this:

  _XX = 90 - ( atan( sin( azFloat ) / tan( elFloat ) ) * RadianToDegree );
  _YY = 90 - ( atan( ( cos(azFloat) * cos(elFloat) ) / sqrt( 1 - ( cos(azFloat) * cos(elFloat) ) * cos(azFloat) * cos(elFloat) ) ) * RadianToDegree );

So i think better to use some extra brackets than to litle

gharryh: I now have changed the code by including the radians to degrees conversion by declaring a static

  static const double DegreeToRadian = 0.0174532925;  // PI/180

and included in the two calculations

    _XX = DegreeToRadian * ( atan(cos(-az) / tan(-el) ) ) + 90.0;
    _YY = DegreeToRadian * ( asin(sin(-az) * cos(-el) ) ) + 90.0;

But now the result is in both 90.0

You need to adjust your angle, not the output. Example (pseudo-code):

Wrong: ``` x = sin(45) / 57.29578; print x;    (yields 0.014851, expected 0.707107) ```

Right: ``` x = sin(45 / 57.29578); print x;      (yields 0.707107) ```

(btw, 57.29578 is 180 / pi).

And you can generate PI like this: ``` double pi = 4 * atan(1); ```

I did some rework on the sketch using the internal Arduino PI value

static const double DegreeToRadian = PI/180 ;  // PI/180
static const double RadianToDegree = 1/DegreeToRadian;    // 1 / ( PI / 180 )

And also som rewriting on the xyConverter:

void _xyConvert(float az, float el)  {

  double azFloat = az /100*DegreeToRadian;
  
  if(el == 90.0) { 
    el = 89.9;
  }
  if(el < 0.1)   { 
    el = 0.1;
  }

double elFloat = el /100*DegreeToRadian;

  _XX = 90-(atan(sin(azFloat)/tan(elFloat))*RadianToDegree);
  _YY = 90-(atan((cos(azFloat)*cos(elFloat))/sqrt(1-(cos(azFloat)*cos(elFloat))*cos(azFloat)*cos(elFloat)))*RadianToDegree);
}
cos(azFloat)*cos(elFloat))*cos(azFloat)*cos(elFloat)

sin() and cos() are not fast functions. Why call them more than you need to?

PaulS: cos(azFloat)*cos(elFloat))*cos(azFloat)*cos(elFloat)

sin() and cos() are not fast functions. Why call them more than you need to?

I havent done any speed optimizing yet. Its done this way to make some people clear that what they asked me to do is done.

Its done this way to make some people clear that what they asked me to do is done.

float cosAZ = cos(azFloat)
float sinAZ = sin(azFloat);

float cosEL = cos(elFloat);
float sinEL = sin(elFloat);

  _XX = 90-(atan(sinAZ/tan(elFloat))*RadianToDegree);
  _YY = 90-(atan((cosAZ*cosEL)/sqrt(1-(cosAZ*cosEL)*cosAZ*cosEL))*RadianToDegree);

would accomplish the same thing, calculating each value only once.

Well that calculates sinEL which is never used and computes cosAZ*cosEL twice ;)

Well that calculates sinEL which is never used...

big chance the compiler will optimize it (would be an interesting test what the limits of its optimizing algo is).

I don't think it can optimise away function calls, since it cannot be sure of side effects. (But it can call them in any order.)