preserving as much precision as possible
tosjduenfs
Posts: 37
I've been working on code that converts a stellar objects RA/DEC coordinates to ALT/AZ coordinates. Basically I first calculate the number of days it has been since Jan 1, 2000 (J2000) and then through a series of calculations I can determine and track where an object should be in the sky given my geographical coordinates and my local time.
In my first attempt I calculated the time since J2000 in decimal days using the float32 object. Unfortunately due to the size of the numbers involved and the lack of precision of a 32 bit floating point number my program would only generate new ALT/AZ coordinates every ~50 seconds or so (when enough time had passed that the last digit of JD changed) rather than every second as the program is written.
So the plan now is to convert the code to use integer seconds rather than decimal days. I've successfully changed the time calculation to accurately give seconds as an integer rather than decimal days but now I am not sure on how to continue using integer math. The next step is to use the following formula to find Greenwich Mean Sidereal Time:
GMST = (280.46061837 + 360.9856473662862*D) mod 360 ' where D is days since J2000 and GMST is in degrees (as of writing this post D = 5036.795139 days)
This formula is accurate to 0.1 seconds over a century but all I really need is for it to be accurate to a few minutes of actual GMST and to have a precision of 1 second so that I can generate new ALT/AZ coordinates every second for tracking.
If I were still using floating point math I would have no problem converting the formula but I'm not exactly sure how to do it with integer math while preserving as much precision as possible.
Any tips would be appreciated. I can post my code later tonight when I get home if that would be more helpful.
Mike
In my first attempt I calculated the time since J2000 in decimal days using the float32 object. Unfortunately due to the size of the numbers involved and the lack of precision of a 32 bit floating point number my program would only generate new ALT/AZ coordinates every ~50 seconds or so (when enough time had passed that the last digit of JD changed) rather than every second as the program is written.
So the plan now is to convert the code to use integer seconds rather than decimal days. I've successfully changed the time calculation to accurately give seconds as an integer rather than decimal days but now I am not sure on how to continue using integer math. The next step is to use the following formula to find Greenwich Mean Sidereal Time:
GMST = (280.46061837 + 360.9856473662862*D) mod 360 ' where D is days since J2000 and GMST is in degrees (as of writing this post D = 5036.795139 days)
This formula is accurate to 0.1 seconds over a century but all I really need is for it to be accurate to a few minutes of actual GMST and to have a precision of 1 second so that I can generate new ALT/AZ coordinates every second for tracking.
If I were still using floating point math I would have no problem converting the formula but I'm not exactly sure how to do it with integer math while preserving as much precision as possible.
Any tips would be appreciated. I can post my code later tonight when I get home if that would be more helpful.
Mike
Comments
If GMST is in degrees, I assume you would want better resolution than that. If you want seconds(nav) then multiply everything by 3600.
GMST(nav sec) = (1009658.226132 + 1299476.33051863032 * D) MOD 1296000
Since you are using Julian seconds instead of days you would use:
GMST(nav sec) = (1009658.226132 + 15.040235306928591667 * S) MOD 1296000
That is off the top of my head. Does that final formula work ? If it does I can work out some integer math way to compute it.
Bean
Well I'd first convert the formula to use seconds rather than days - ie divide the 360.98... constant by 86400, replace D by S...
Then I'd select, as has been mentioned, a granularity for the GMST, and do that scaling for both constants (x 3600 for arc-seconds).
You then have a formula to calculate mod 1296000 (arc-seconds per revolution). The constant used to multiply S needs to
be approximated by a integer ratio (with at least one of numerator or denominator being full 32 bits for max precision).
So you'll need a 32x32->64 bit multiply and a 64bit modular reduction to compute it.
Alternatively you could choose your granularity to be 2^-32 revolutions, then the mod is for free, but you'd have to
scale the result back to arc-seconds or whatever.
That is a great idea. That is the path I would follow.
Bean
This makes sense to me, it hadn't occurred to me to use arc seconds for gmst. Arc seconds would be plenty accurate for my purpose.
I'm trying to wrap my head around this one. By granularity I'm assuming you mean that 1/(2^32) is the smallest increment that GMST could vary and then the mod would be"free" because 2^32 is the maximum size of a 32 bit integer?
Also I suppose it might be helpful to explain where it goes after this.
After getting GMST I calculate the Local Hour Angle:
LHA = GMST + Longitude - RA ' where LHA is degrees, Longitude is your geographical longitude also in degrees and RA is the right ascension of the object you are trying to locate also in degrees
Then you can calculate ALT/AZ
sin(ALT) = sin(DEC)*sin(LAT)+cos(DEC)*cos(LAT)*cos(LHA)
cos(Az) = [sin(DEC) - sin(ALT)*sin(LAT)] / [cos(ALT)*cos(LAT)] ' where Dec is the declination of the object you are locating, ALT is the altitude calculated above, and LAT is your geographical latitude
All of the inputs to the above equations had to be converted to radians first. Obviously Arcsin and ArcCos are applied to get the final results which are then converted back to degrees Which will be fed into a PI motor controller. Ultimately they will control two gear motors with encoders which will generate 67200 pulses per revolution of the telescope in ALT and AZ. Would it be best to work backwards from 67200?
The precision of the multiplier is 13 decimal digits, around 44 bits binary. If you use lower precision, error will accumulate over time and the granularity will increase. But at 31 bits, it should still be near 0.01 second per year, and at the 24 bits (precision of the floating point significand) it should be no more than 2 seconds per year. In light of that, I don't understand your statement, "Unfortunately due to the size of the numbers involved and the lack of precision of a 32 bit floating point number my program would only generate new ALT/AZ coordinates every ~50 seconds or so (when enough time had passed that the last digit of JD changed) rather than every second as the program is written. "
For better precision, the object F32.spin uses CORDIC for the trig functions out to full 24 bits. Float32 on the other hand uses linear interpolation to 16 bits in the 11 bit HUB table.
I will post my original code when I get home tonight so you can see the behavior I am talking about. I am using F32 now but I'm not sure If i was using it when I first had this problem so I will test that. I think GMST = 0 would mark the vernal equinox, 280.46... is the initial angle at j2000 and 360.98... is the angle the earth rotates in 24 hours.
What I was trying to say before is that my original code takes its input from the realtimeclock object and recalculates the Julian Day Number every second and then it calculates GMST then LHA then finds the ALT/AZ coordinates of the object I'm trying to track. For example, right now it has been about 5000 days since J2000 so with float32 I can get two or three decimal places (so it seams) after 5000 so 5000.000. The best precision I can get is a thousandth of a day, and with 86400 seconds in a day you can see that when I add 1 second to 5000.000 days it will get rounded to 5000.000. Only when enough seconds have passed will it get up to 5000.001 and thus change the rest of the calculations down the line. Combine that with other rounding errors throughout the code and I only get updated alt/az coordinates about every~50seconds even though they are recalculated, with updated time, every second.
If I could get 13 decimal digits of precision I think my code would run fine but that is not what I see when I send my variables to PST. I'll post the code when I get home tonight so you can see what I mean, maybe it will be a simple fix.
RA-DEC to ALT-AZ Converter 201310131437.spin
Float32.spin...
Here is the header...
''=============================================================================
'' Copyright (C) 2006-2007 Parallax, Inc. All rights reserved
''
'' @file Float32
'' @target Propeller
''
'' IEEE 754 compliant 32-bit floating point math routines.
'' This object supports the basic set of floating point routines using one cog.
''
'' ──Float32
''
'' @author Cam Thompson, Micromega Corporation
'' @version
'' V1.2 - March 26, 2007
'' - fixed Pow to handle negative base values
'' V1.1 - Oct 5, 2006
'' - corrected constant value for Radians and Degrees
'' V1.0 - May 17, 2006
'' - original version
''=============================================================================
I want super precision math too in a fifth order polynomial I am doing and just have to wrestle with the MAXIMUM number the float can return...
In the end can you use floats here??
When doing floating point addition it's easy to loose bits of precision if the inputs have different magnitudes. The given equation will suffer badly from this. (D*360.xxx >> 280.xxx) Also, Fmod( a, b) will have reduced precision when 'a' is much larger than 'b'. I'd try moving the modulus function inside the equation. Looks like this will require re-figuring the three constants of the equation though.
If you don't care much about speed, I've written a spin only floating point library with all the same functions as F32. It's a bit more precise than F32 with Log and Exp, but isn't standards compliant with all the edge cases. (see the links to FME.spin in my signature)
After making FME I've wondered weather it would be worth modifying it so most of the functions worked on unpacked floats pulled off of a stack? You could get ~30 bits of mantissa, expanded exponent range, plus a nice speed boost. The main cost is that floats would take 3-longs during equation evaluation. (and creating full precision constants would need some extra code)
Marty
And here is the output:
So 1 Julian second change resulted in a 15 arc-second change.
Bean
sum := jSec * 49846
sum += jSec ** 1597067845
so sum = (jsec * 49846) + (jsec ** 1597067845)
I'm not sure I understand the ** operation. It returns the upper 32 bits of a 32x32bit multiplication. So the upper 32 bits is the part that would normally be truncated? As in the upper 32 bits is the precision that is normally lost after the decimal? Also where does the 1597067845 come from and what exactly do these two lines accomplish?
I wish it were just as easy as going from GMST to encoder pulses but there are also trig functions these number must pass through first.
Again, thanks for your time.
First the whole value is multiplied.
sum := jSec * 49846
Then the fractional part. 0.3718463343
To multiply by a faction you take the fractional part and multiply it by 2^32 then use the ** operator.
So 0.3718463343 * 2^32 = 1597067845
When you multiply two 32 bit numbers you get a 64 bit result. The * operator gives the lower 32 bits, the ** gives the upper 32 bits (or the overflow).
The 49846.3718463343 comes from converting from 360 modulus to 2^32 modulus (and also going from days to seconds as the units).
So 360.9856473662 / 86400 because we are using seconds instead of days as the units.
Then (360.9856473662 / 86400) / 360 to use a modulus of 1 instead of 360
Then ((360.9856473662 / 86400) / 360) * 2^32 to use a modulus of 2^32 instead of 1
And that gives us the 49846.3718463343
Because the modulus is 2^32 we don't have to even think about it because all math is performed mod 2^32 by the propeller.
As far a ** math goes, you can think of 2^32 as 1.0000000000, so to get a different modulus you just high-multiply by the modulus you desire.
I hope that helps,
Bean
Think of it as multiplying two numbers together, followed by shifting right 32 bits. That is the same as dropping the low 32 bits from the result of a 32x32 mulitply.
Z := X ** F
is mathmatically equivalent to
' Z = X * Y = X * F / 232
where Y<1 and the factor F = Y * 232
The fraction Y has been well approximated by the fraction F/232
If Y has both an integer part and a fractional part, they have to be done separately,
Z := X * (integer part of Y) + X ** (fractional part of Y * 232)
There is caveat that involves negative numbers. To use ** in this way, both factors X and F have to be positive numbers, (less than or equal to posx:2147483647) or else the Prop's twos complement math returns the wrong answer when interpreted as fractional multiply. A trick for fractions greater than or equal to 0.5 is to separate it out like this:
Z = X * (integer part of Y) + 2*X ** (fractional part of Y/2 * 232)
ALT = arcsin[ sin(Declination) * sin(Latitude) + cos(Declination) * cos(Latitude) * cos(GMST + Longitude - Right Ascension) ]
Where Declination, Latitude, Longitude and Right Ascension are all given values.
When you say "given values" do you mean the are constants ? If they are then the "sin" and "cos" can be precalculated. Leaving just a cos and arcsin function which could probably be done with CORDIC (not sure about arcsin though).
Bean
arcsin(x) = arctan(x / (12 - x2))
Note that 1 is squared in the formula, a detail, but important in implementation through a process of normalization as done in floating point.
F32 uses 24 bit CORDIC for the atan function, but it uses HUB table lookup/interpolation to 16 bits at best for sin, cos and tan. Thinking about Lawson's comments, I'm unclear on how much precision that last formula for ALT needs once you have an accurate value of GMST.
Bean, thank you so much for taking the time to write out examples and for showing me how to get an accurate value from the GMST formula.
Not only does it work but my final values are accurate to better than 1/100th of a degree often times less than that. That is more than adequate for my application.
Thanks again.
Mike