Julian Day (JD) number
Most astronomy based formulas use the Julian day number as an input.
I use the a modified 2000 Julian Day number, the number of days since mid-night of the 1st of January 2000:
double J2000(long Year,long Month,long Day,double UT) {
double JD;
// Julian (2000) Day Number
JD=(367*Year-(7*(Year+(Month+9)/12))/4+275*Month/9+Day);
JD=JD-730531.5;
JD=JD+UT/24.0;
return JD;
}
The formular is correct even for dated before 1/1/2000.
One of the inputs to the function is UT or Universal Time. UT is the time in Greenwich UK. I will write more about this later.
Note that Year, Month and Day are type long. Therefore integer division rules apply.
Reverse of the Julian Day number
I found a source for calculating the reverse of the 2000 Julian Day Number:
Source: http://mathforum.org/library/drmath/view/51907.htm
unsigned long JD,p,q,r,s,t,u,v; p=JD+2451545+68569; q=4*p/146097; r=p-(146097*q+3)/4; s=4000*(r+1)/1461001; t=r-1461*s/4+31; u=80*t/2447; v=u/11; Year=100*(q-49)+s+v; Month=u+2-12*v; Day=t-2447*u/80;
It may be useful when checking/calculating valid dates. I checked the code and it good from 1/1/2000 to 31/12/2099 but then the Julian Day number calculation is only good to 31/12/2099.
Greenwich Mean Sideral Time (GMST)
This is the time of the stars! You would be aware that there are 365.25 days in a solar year. But the stars rotate around the earth 366.25 times a year. The extra rotation is because the earth also rotates around the sun as well as rotating around it axis. Here is the GMST function:
double GMST(double JD,double UT) {
double gmst;
// Greenwich Mean Sidereal Time (~36625/36525)
gmst=18.697374558+24.06570982441908*(JD+UT/24.0);
gmst=fmod(fmod(gmst,24.0)+24.0,24.0);
return gmst;
}
We need GMST even for the sun as the sun is cosidered just another start. But the Right Ascension and Declination moves a lot more than ordinary stars.
Sun Right Ascension (RA) and Declination (Dec)
Now we can calculate the position of the sun amongst the stars. I use a series of formulas from my copy of the "The Astronomical Amanac (1986)" but I have seen the same formulas on the Internet:
void SunData(double JD,double *RA,double *Dec,double *EoT) {
double L,g,a,b,c;
/* Source: Page C24 from "The Astronomical Almanac" (1986) */
// Mean longitude of Sun, corrected for aberration
L=280.460+0.9856474*JD;
L=fmod(fmod(L,360.0)+360.0,360.0);
// Mean anomaly
g=357.528+0.9856003*JD;
g=fmod(fmod(g,360.0)+360.0,360.0);
// Ecliptic longitude
a=L+1.915*sin(g*M_PI/180)+0.020*sin(2*g*M_PI/180);
a=fmod(fmod(a,360.0)+360.0,360.0);
// Ecliptic latitude
b=0;
// Obliquity of ecliptic
c=23.439-0.0000004*JD;
c=fmod(fmod(c,360.0)+360.0,360.0);
// Right ascension
(*RA)=atan2(cos(c*M_PI/180)*sin(a*M_PI/180),cos(a*M_PI/180))*12/M_PI;
if ((*RA)<0) (*RA)=(*RA)+24.0;
// Equation of Time
(*EoT)=(L/15.0-(*RA));
// Declination
(*Dec)=asin(sin(c*M_PI/180)*sin(a*M_PI/180))*180/M_PI;
}
The procedure accepts modified Julian Day number (JD) and returns Right Ascension (RA), Declination (Dec) and the Equarion of Time (EoT). The units for RA and EoT are hours but the units for Declination is degrees.
Checking the formulas
It is really easy to make a minor mistake with these formulas so it is important to check the results against a worked example. The Internet has many calculators that you can check your results against.
Sunrise and sunset
Sunrise and sunset times are published on the Internet for any place that you live.
On the 20th of January 2018 in Perth Western Australia the Sunrise was 5:30 and Sunset was 19:24. Local noon was therefore 12:27. The local time is 8 hours ahead of UT.
Calculating longitude
The steps are:
- SunRise=5.50-8;
- SunSet=19.40-8;
- JD=J2000(Year,Month,Day,(SunRise+SunSet)/2.0);
- SunData(JD,&RA,&Dec,&EoT);
- Lon=15.0*(RA-GMST(JD,0.0));
The values were:
- Year 2018
- Month 1
- Day 20
- SunRise (LT) 5.5000
- SunSet (LT) 19.400
- SunRise (UT) -2.5000
- SunSet (UT) 11.4000
- JD 6593.68554687
- RA 20.14925956
- Dec -20.13604354
- EoT -0.18200683
- Longitude 115.9108
Which are correct.
Calculating latitude
Originally I used an iterative method but Jaromir Sukuba work out an exact answer:
double latitude(double SunRise,double SunSet,double Alt,double Dec) {
// The mathematics:
// Cos(15*DL/2)=Sin(Alt)/Cos(Dec)/Cos(Lat)-Tan(Dec)*Tan(Lat)
// Use this model:
// C=B*Cos(Lat)+A*Sin(Lat)
// D=Sqrt(A*A+B*B);
// Sin(U)=B/D;
// Sin(V)=C/D;
// U=ASIN(B/D);
// V=ASIN(C/D);
// Lat=V-U;
// Rearrange:
// Sin(Alt)/Cos(Dec)=Cos(15*DL/2)*Cos(Lat)+Tan(Dec)*Sin(Lat))
// Test data:
// SunRise 5.50 (need not be local time)
// SunSet 19.40 (need not be local time)
// DayLight 13.90
// Altitude -0.833 (definition for Sun rise/set)
// Declination -20.066095
// Latitude -31.9615789037
double DL,A,B,C,D,U,V,Lat;
if (fabs(Dec-Lat)>90.0) return -90.0; // Check if sun rises
if (fabs(Dec+Lat)>90.0) return +90.0; // Check if sun sets
DL=SunSet-SunRise; // 13.90
A=tan(Dec*PI/180); // -0.3652771781
B=cos(DL*PI/24); // -0.246153293
C=sin(Alt*PI/180)/cos(Dec*PI/180); // -0.015477611
D=sqrt(A*A+B*B); // 0.4404757207
if (fabs(C)<=D) { // Check if out of range
U=asin(B/D)*180/PI; // -33.9752753119
V=asin(C/D)*180/PI; // -2.0136964081
Lat=V-U; // -31.9615789037
if (Dec<0) Lat=-Lat; // -31.9615789037
} else {
Lat=-90;
}
return Lat;
}
If we apply our numbers to this function then the answer is:
6. Lat=latitude(SunRise,SunSet,Alt,Dec);
- Latitude -31.8655
Which is correct.
The only variable not discussed here is Alititude (Alt). Altitude is the angle from the horizon. For sunrise and sunset the offical altitude (nainlt due to reflaction) is -0.833 degrees (i.e. below the horizon), not 0 degrees.
Some code to try
Here is some arduino code:
double J2000(long Year,long Month,long Day,double UT) {
double JD;
// Julian (2000) Day Number
JD=(367*Year-(7*(Year+(Month+9)/12))/4+275*Month/9+Day);
JD=JD-730531.5;
JD=JD+UT/24.0;
return JD;
}
double GMST(double JD,double UT) {
double gmst;
// Greenwich Mean Sidereal Time (~36625/36525)
gmst=18.697374558+24.06570982441908*(JD+UT/24.0);
gmst=fmod(fmod(gmst,24.0)+24.0,24.0);
return gmst;
}
void SunData(double JD,double *RA,double *Dec,double *EoT) {
double L,g,a,b,c;
/* Source: Page C24 from "The Astronomical Almanac" (1986) */
// Mean longitude of Sun, corrected for aberration
L=280.460+0.9856474*JD;
L=fmod(fmod(L,360.0)+360.0,360.0);
// Mean anomaly
g=357.528+0.9856003*JD;
g=fmod(fmod(g,360.0)+360.0,360.0);
// Ecliptic longitude
a=L+1.915*sin(g*M_PI/180)+0.020*sin(2*g*M_PI/180);
a=fmod(fmod(a,360.0)+360.0,360.0);
// Ecliptic latitude
b=0;
// Obliquity of ecliptic
c=23.439-0.0000004*JD;
c=fmod(fmod(c,360.0)+360.0,360.0);
// Right ascension
(*RA)=atan2(cos(c*M_PI/180)*sin(a*M_PI/180),cos(a*M_PI/180))*12/M_PI;
if ((*RA)<0) (*RA)=(*RA)+24.0;
// Equation of Time
(*EoT)=(L/15.0-(*RA));
// Declination
(*Dec)=asin(sin(c*M_PI/180)*sin(a*M_PI/180))*180/M_PI;
}
double latitude(double SunRise,double SunSet,double Alt,double Dec) {
// The mathematics:
// Cos(15*DL/2)=Sin(Alt)/Cos(Dec)/Cos(Lat)-Tan(Dec)*Tan(Lat)
// Use this model:
// C=B*Cos(Lat)+A*Sin(Lat)
// D=Sqrt(A*A+B*B);
// Sin(U)=B/D;
// Sin(V)=C/D;
// U=ASIN(B/D);
// V=ASIN(C/D);
// Lat=V-U;
// Rearrange:
// Sin(Alt)/Cos(Dec)=Cos(15*DL/2)*Cos(Lat)+Tan(Dec)*Sin(Lat))
// Test data:
// SunRise 5.50 (need not be local time)
// SunSet 19.40 (need not be local time)
// DayLight 13.90
// Altitude -0.833 (definition for Sun rise/set)
// Declination -20.066095
// Latitude -31.9615789037
double DL,A,B,C,D,U,V,Lat;
DL=SunSet-SunRise; // 13.90
A=tan(Dec*PI/180); // -0.3652771781
B=cos(DL*PI/24); // -0.246153293
C=sin(Alt*PI/180)/cos(Dec*PI/180); // -0.015477611
D=sqrt(A*A+B*B); // 0.4404757207
if (fabs(C)<=D) { // Check if out of range
U=asin(B/D)*180/PI; // -33.9752753119
V=asin(C/D)*180/PI; // -2.0136964081
Lat=V-U; // -31.9615789037
if (Dec<0) Lat=-Lat; // -31.9615789037
} else {
Lat=0.0;
}
return Lat;
}
void setup() {
Serial.begin(9600);
while (!Serial);
// Day Light to Lat/Long
int Year,Month,Day;
double LTC,UT,JD,SunRise,SunSet,RA,Dec,EoT,Lat,Lon,Alt;
LTC=8.0; // Local Time Correction
Year=2018;
Month=1;
Day=20;
Alt=-0.833; // Sunrise and Sunset definition
SunRise=5.0+30.0/60.0-LTC; // Convert Local Time to Universal Time
SunSet=19.0+24.0/60.0-LTC; // Convert Local Time to Universal Time
// Problem parameters:
Serial.println("DayLight to Latitude:");
Serial.print("Year ");Serial.println(Year);
Serial.print("Month ");Serial.println(Month);
Serial.print("Day ");Serial.println(Day);
Serial.print("SunRise(UT) ");Serial.println(SunRise,4);
Serial.print("SunSet (UT) ");Serial.println(SunSet,4);
// Solve the problem:
JD=J2000(Year,Month,Day,(SunRise+SunSet)/2.0);
SunData(JD,&RA,&Dec,&EoT);
Lat=latitude(SunRise,SunSet,Alt,Dec);
Lon=15.0*(RA-GMST(JD,0.0));
// Debug:
Serial.print("JD ");Serial.println(JD,8);
Serial.print("RA ");Serial.println(RA,8);
Serial.print("Dec ");Serial.println(Dec,8);
Serial.print("EoT ");Serial.println(EoT,8);
// Solution:
Serial.print("Latitude ");Serial.println(Lat,4);
Serial.print("Longitude ");Serial.println(Lon,4);
Serial.println();
}
void loop(){
}
And here is the run:
DayLight to Latitude:
Year 2018
Month 1
Day 20
SunRise(UT) -2.5000
SunSet (UT) 11.4000
JD 6593.68554687
RA 20.14925956
Dec -20.13604354
EoT -0.18200683
Latitude -31.8655
Longitude 115.9108
Conclusion
For many places in the world if you can measure sunrse and sunset accuarately you can calculate your latitude and longitude accurately. There are places/dates where the sun does not set or rise, in these places/dates the mathematics does not work.
Magic
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.