Sunset in C language - c

I want to calculate the sunset for specific localization (latitude, longtitude) in C.
I modeled on: http://edwilliams.org/sunrise_sunset_algorithm.htm
For the given model I tried to calculate the sunrise - this value was good.
I still get the incorrect value for sunset: -11:-22.
Here's some code:
#include <stdio.h>
#include <math.h>
#define PI 3.1415926
#define ZENITH -.83
float calculateSunset(int year,int month,int day,float lat, float lng,int localOffset) {
//1. first calculate the day of the year
float N1 = floor(275 * month / 9);
float N2 = floor((month + 9) / 12);
float N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3));
float N = N1 - (N2 * N3) + day - 30;
//2. convert the longitude to hour value and calculate an approximate time
float lngHour = lng / 15;
float t = N + ((18 - lngHour) / 24); //if setting time is desired:
//3. calculate the Sun's mean anomaly
float M = (0.9856 * t) - 3.289;
//4. calculate the Sun's true longitude
float L = fmod(M + (1.916 * sin((PI/180)*M)) + (0.020 * sin(2 *(PI/180) * M)) + 282.634,360.0);
//5a. calculate the Sun's right ascension
float RA = fmod(180/PI*atan(0.91764 * tan((PI/180)*L)),360.0);
//5b. right ascension value needs to be in the same quadrant as L
float Lquadrant = floor( L/90) * 90;
float RAquadrant = floor(RA/90) * 90;
RA = RA + (Lquadrant - RAquadrant);
//5c. right ascension value needs to be converted into hours
RA = RA / 15;
//6. calculate the Sun's declination
float sinDec = 0.39782 * sin((PI/180)*L);
float cosDec = cos(asin(sinDec));
//7a. calculate the Sun's local hour angle
float cosH = (sin((PI/180)*ZENITH) - (sinDec * sin((PI/180)*lat))) / (cosDec * cos((PI/180)*lat));
if (cosH > 1) {
printf("the sun never rises on this location (on the specified date)");
return 0;
}
if (cosH < -1) {
printf("the sun never sets on this location (on the specified date)");
return 0;
}
//7b. finish calculating H and convert into hours
float H = acos(cosH); // if setting time is desired:
H = H / 15;
//8. calculate local mean time of rising/setting
float T = H + RA - (0.06571 * t) - 6.622;
//9. adjust back to UTC
float UT = fmod(T - lngHour,24.0);
//10. convert UT value to local time zone of latitude/longitude
return UT + localOffset;
}
void printSunset() {
float localT = calculateSunset(2018,10,4,51.8446,19.2094,2);
double hours;
float minutes = modf(localT,&hours)*60;
printf("Sunset: ");
printf("%.0f:%.0f",hours,minutes);
printf("\n");
}
int main()
{
printSunset();
return 0;
}
Could anyone help me? What am I doing wrong?

I think your main problem is that you expect fmod to return a positive result - that is not true, fmod preserves the sign i.e. if you are taking fmod of a negative value you'll get a (correct) negative result, if you use it on an positive value the (also correct) result will be positive. If you require an positive result you could for example simply add the divisor if the result is negative i.e.
float a = fmod(T, 360.0);
if(a < 0) { a = a + 360.0; }

Related

Print Value of pi. How many terms of this series do you have to use before you first get 3.14? 3.141? 3.1415? 3.14159?

#include <stdio.h>
#include <math.h>
int main(void) {
// Set the initial value of pi to 0
double pi = 0.0;
// Set the initial value of the term to 1
double term = 1.0;
// Set the initial value of the divisor to 1
double divisor = 1.0;
// Print the table header
printf("%10s%25s\n", "Number of terms", "Approximation of pi");
// Calculate and print the approximations of pi
for (int i = 1; i <= 20; i++) {
pi += term / divisor;
printf("%10d%25.10f\n", i, pi*4.0);
term *= -1.0;
divisor += 2.0;
}
return 0;
}
I tried to correct the code but still can't get closer to the value as it is ask by my teacher in our assignment...
The Question is..
Calculate the value of π from the infinite series. Print a table that
shows the value of π approximated by one term of this series, by two terms, by three terms,
and so on. How many terms of this series do you have to use before you first get 3.14?
3.141? 3.1415? 3.14159?
How many terms of this series do you have to use before you first get 3.14? 3.141? 3.1415? 3.14159?
The details of "first get 3.14" are a bit unclear. Below attempts something like OP's goal and illustrates the slow convergence as computation time is proportional to the number of terms.
The high number of terms, each incurring round-off errors in the division and addition eventually render this computation too inaccurate for high term count.
int main(void) {
double pi_true = 3.1415926535897932384626433832795;
double threshold = 0.5;
int dp = 0;
// Set the initial value of pi to 0
double pi = 0.0;
// Set the initial value of the term to 1
double term = 1.0;
// Set the initial value of the divisor to 1
double divisor = 1.0;
// Print the table header
printf("%7s %12s %-25.16f\n", "", "", pi_true);
printf("%7s %12s %-25s\n", "", "# of terms", "Approximation of pi");
// Calculate and print the approximations of pi
for (long long i = 1; ; i++) {
pi += term / divisor;
double diff = fabs(4*pi - pi_true);
if (diff <= threshold) {
printf("%7.1e %12lld %-25.16f %-25.*f\n", diff, i, pi * 4.0, dp++, pi * 4.0);
fflush(stdout);
threshold /= 10;
if (4*pi == pi_true) {
break;
}
}
term *= -1.0;
divisor += 2.0;
}
puts("Done");
return 0;
}
Output
3.1415926535897931
# of terms Approximation of pi
4.7e-01 2 2.6666666666666670 3
5.0e-02 20 3.0916238066678399 3.1
5.0e-03 200 3.1365926848388161 3.14
5.0e-04 2000 3.1410926536210413 3.141
5.0e-05 20000 3.1415426535898248 3.1415
5.0e-06 200001 3.1415976535647618 3.14160
5.0e-07 2000001 3.1415931535894743 3.141593
5.0e-08 19999992 3.1415926035897974 3.1415926
5.0e-09 199984633 3.1415926585897931 3.14159266
5.0e-10 1993125509 3.1415926540897927 3.141592654
5.0e-11 19446391919 3.1415926536397927 3.1415926536
...
Ref 3.1415926535897931
On a 2nd attempt, perhaps this is closer to OP's goal
int main(void) {
double pi_true = 3.1415926535897932384626433832795;
double threshold_lo = 2.5;
double threshold_hi = 3.5;
double error_band = 0.5;
int dp = 0;
// Set the initial value of pi to 0
double pi = 0.0;
// Set the initial value of the term to 4
double term = 4.0;
// Set the initial value of the divisor to 1
double divisor = 1.0;
// Print the table header
printf("%12s %-25.16f\n", "", pi_true);
printf("%12s %-25s\n", "# of terms", "Approximation of pi");
// Calculate and print the approximations of pi
for (long long i = 1;; i++) {
pi += term / divisor;
if (pi > threshold_lo && pi < threshold_hi) {
printf("%12lld %-25.16f %-25.*f\n", i, pi, dp++, pi);
fflush(stdout);
char buf[100] = "3.1415926535897932384626433832795";
buf[dp + 2] = 0;
error_band /= 10.0;
double target = atof(buf);
threshold_lo = target - error_band;
threshold_hi = target + error_band;
}
term *= -1.0;
divisor += 2.0;
}
puts("Done");
return 0;
}
Output
3.1415926535897931
# of terms Approximation of pi
2 2.6666666666666670 3
12 3.0584027659273332 3.1
152 3.1350137774059244 3.14
916 3.1405009508583017 3.141
7010 3.1414500002381582 3.1415
130658 3.1415850000208838 3.14159
866860 3.1415915000009238 3.141592
9653464 3.1415925500000141 3.1415926
116423306 3.1415926450000007 3.14159265
919102060 3.1415926525000004 3.141592653
7234029994 3.1415926534500005 3.1415926535

Arduino calculating angle accuracy problem?

I'm trying to calculate the angle of a point I'm measuring.
Using: https://www.calculator.net/triangle-calculator.html?vc=90&vx=50&vy=50&va=&vz=&vb=&angleunits=d&x=101&y=9
My a = 50, b = 50 and c = unknown (varies along with a), I am currently testing it with a fixed distance of 50.
See the link for visualisation, it will probably help a lot.
I am using the following code:
float calculateAngle(float distance)
{
float a = TSThorizontalDistanceToProjector - (distance + TSTdistanceFromFrontToProjection);
float b = TSTverticalDistanceToProjector;
float C = 90;
float c = sqrt(pow(TSTverticalDistanceToProjector, 2) + pow(a,2) - ((2 * b * a) * cos(C)));
Serial.println("float c is: ");
Serial.println(c);
float A = acos((pow(c,2) + pow(b,2) - pow(a,2)) / (2 * c * b));
Serial.println("float A is: ");
Serial.println(A);
}
I first calculate c so I can calculate the A angle. I only want the A angle however, so if I can calculate it right away please say so. The problem with this code is however that it outputs the wrong numbers.
According to the site linked above angle A should be: 45 degrees and side c should be 70.711. My code outputs angle A = 0.40 and side C = 68.88. Both are wrong, am I using the formula incorrectly? Or does it have to do something with the variables I'm using?
EDIT:
I'm now using the following code:
float calculateAngle(float distance)
{
float a = TSThorizontalDistanceToProjector - (distance + TSTdistanceFromFrontToProjection); //120 - (70 + 20) = 30
Serial.println(a);
float b = TSTverticalDistanceToProjector;
float C = 90;
float c = sqrt(pow(a,2) + pow(b,2));
Serial.println("float c: ");
Serial.println(c);
float tanX = (a / b);
Serial.println("float tanX is: ");
Serial.println(tanX);
float tanXresult = atan(tanX);
Serial.println("float tanXresult: ");
Serial.println(tanXresult);
}
I also saw that a = 30 and not 50 but b still is 50.
I can now calculate c with enough accuracy, the angle of A is still a problem though.
Don't use degrees as input to trigonmic functions. Use radians!
Also if a and b join at 90° and you know a and b, c is simply sqrt(a^2+b^2) as
c^2 = a^2 + b^2
Not sure what that cos is supposed to do here.
As you already know a and b you can simply calculate A via the arcustangens or a/b. You don't need c for that.
I suggest you revisit basic trigonometry.
I had to convert my atan from radians to degrees.
float calculateAngle(float distance)
{
float a = TSThorizontalDistanceToProjector - (distance + TSTdistanceFromFrontToProjection); //120 - (70 + 20) = 30
//Serial.println(a);
float b = TSTverticalDistanceToProjector;
float C = 90;
float c = sqrt(pow(a,2) + pow(b,2));
Serial.println("float c: ");
Serial.println(c);
float tanX = (a / b);
Serial.println("float tanX is: ");
Serial.println(tanX);
float tanXresult = ((atan(tanX)) * 180) / Pi;
Serial.println("float tanXresult: ");
Serial.println(tanXresult);
}
I did that with:
float tanXresult = ((atan(tanX)) * 180) / Pi;
printed result: 30.96 degrees, which is correct!

Animating a sine wave infinitely

I need a function to animate a sine wave infinitely over time. The sine wave moves to the left.
My sine wave is built using the following equation:
A * sin(B * x + C) + D
Now to animate the sine wave as if it is moving to the left, I simply increase C by 1 everytime I refresh the screen. Now this is all fine and dandy for a few minutes but I need to have that animation run for hours. I can't just have an integer build up 60 times a second forever. How does someone deal with this? Do I just try to find a point where the sine wave crosses 0 and then restart the animation from 0?
I just need to have the logic of something like this explained.
EDIT #1
I forgot to mention that there's a randomized component to my sine. The sine is not continuously the same. A and D are sinusoidal functions tied to that integer at the moment. The sine needs to look random with varying periods and amplitudes.
EDIT #2
Edited see Edit 3
EDIT #3
#Potatoswatter I tried implementing your technique but I don't think I'm getting it. Here's what I got:
static double i = 0;
i = i + (MPI / 2);
if ( i >= 800 * (MPI / 2) ) i -= 800 * (MPI / 2);
for (k = 0; k < 800; ++k)
{
double A1 = 145 * sin((rand1 * (k - 400) + i) / 300) + rand3; // Amplitude
double A2 = 100 * sin((rand2 * (k - 400) + i) / 300) + rand2; // Amplitude
double A3 = 168 * sin((rand3 * (k - 400) + i) / 300) + rand1; // Amplitude
double B1 = 3 + rand1 + (sin((rand3 * k) * i) / (500 * rand1)); // Period
double B2 = 3 + rand2 + (sin((rand2 * k) * i) / 500); // Period
double B3 = 3 + rand3 + (sin((rand1 * k) * i) / (500 * rand3)); // Period
double x = k; // Current x
double C1 = 10 * i; // X axis move
double C2 = 11 * i; // X axis move
double C3 = 12 * i; // X axis move
double D1 = rand1 + sin(rand1 * x / 600) * 4; // Y axis move
double D2 = rand2 + sin(rand2 * x / 500) * 4; // Y axis move
double D3 = rand3 + cos(rand3 * x / 400) * 4; // Y axis move
sine1[k] = (double)A1 * sin((B1 * x + C1) / 400) + D1;
sine2[k] = (double)A2 * sin((B2 * x + C2) / 300) + D2 + 100;
sine3[k] = (double)A3 * cos((B3 * x + C3) / 500) + D3 + 50;
}
How do I modify this to make it work?
Halp!
Sine has a period of 2 pi, meaning that sin(x) = sin(x + 2 * M_PI), for any x.
So, you could just increase C by, say, pi/n where n is any integer, as you refresh the screen, and after 2n refreshes, reset C (to 0, or whatever).
Edit for clarity: the integer n is not meant to change over time.
Instead, pick some n, for example, let's say n = 10. Now, every frame, increase x by pi / 10. After 20 frames, you have increased x by a total of 20 * pi / 10 = 2 * pi. Since sin(x + 2 * pi) = sin(x), you may as well just reset your sin(...) input to just x, and start the process over.
sin is periodic, with a period of 2π. Therefore, if the argument is greater than 2π, you can subtract 2 * M_PI from it and get the same answer.
Instead of using a single variable k to compute all waves of various speeds, use three variables double k1, k2, k3, and keep them bound in the range from 0 to 2π.
if ( k2 >= 2 * M_PI ) k2 -= 2 * M_PI;
They may be individually updated by adding some value each frame. If the increment may be more than 2π then subtracting a single 2π won't bring them back into range, but you can use fmod() instead.
I decided to change my course of action. I just drive i with the system's monotonic clock like so:
struct timespec spec;
int ms;
time_t s;
static unsigned long long etime = 0;
clock_gettime(CLOCK_MONOTONIC, &spec);
s = spec.tv_sec;
ms = spec.tv_nsec / 10000000;
etime = concatenate((long)s, ms);
Then I simply changed i to etime in my sine equations. Here's the concatenating function I used for this purpose:
unsigned concatenate(unsigned x, unsigned y) {
x = x * 100;
return x + y;
}

Numerical Evaluation of Pi

I would like to evaluate Pi approximately by running the following code which fits a regular polygon of n sides inside a circle with unit diameter and calculates its perimeter using the function in the code. However the output after the 34th term is 0 when long double variable type is used or it increases without bounds when double variable type is used. How can I remedy this situation? Any suggestion or help is appreciated and welcome.
Thanks
P.S: Operating system: Ubuntu 12.04 LTS 32-bit, Compiler: GCC 4.6.3
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <stdlib.h>
#define increment 0.25
int main()
{
int i = 0, k = 0, n[6] = {3, 6, 12, 24, 48, 96};
double per[61] = {0}, per2[6] = {0};
// Since the above algorithm is recursive we need to specify the perimeter for n = 3;
per[3] = 0.5 * 3 * sqrtl(3);
for(i = 3; i <= 60; i++)
{
per[i + 1] = powl(2, i) * sqrtl(2 * (1.0 - sqrtl(1.0 - (per[i] / powl(2, i)) * (per[i] / powl(2, i)))));
printf("%d %f \n", i, per[i]);
}
return 0;
for(k = 0; k < 6; k++)
{
//p[k] = k
}
}
Some ideas:
Use y = (1.0 - x)*( 1.0 + x) instead of y = 1.0 - x*x. This helps with 1 stage of "subtraction of nearly equal values", but I am still stuck on the next 1.0 - sqrtl(y) as y approaches 1.0.
// per[i + 1] = powl(2, i) * sqrtl(2 * (1.0 - sqrtl(1.0 - (per[i] / powl(2, i)) * (per[i] / powl(2, i)))));
long double p = powl(2, i);
// per[i + 1] = p * sqrtl(2 * (1.0 - sqrtl(1.0 - (per[i] / p) * (per[i] / p))));
long double x = per[i] / p;
// per[i + 1] = p * sqrtl(2 * (1.0 - sqrtl(1.0 - x * x)));
// per[i + 1] = p * sqrtl(2 * (1.0 - sqrtl((1.0 - x)*(1.0 + x)) ));
long double y = (1.0 - x)*( 1.0 + x);
per[i + 1] = p * sqrtl(2 * (1.0 - sqrtl(y) ));
Change array size or for()
double per[61+1] = { 0 }; // Add 1 here
...
for (i = 3; i <= 60; i++) {
...
per[i + 1] =
Following is a similar method for pi
unsigned n = 6;
double sine = 0.5;
double cosine = sqrt(0.75);
double pi = n*sine;
static const double mpi = 3.1415926535897932384626433832795;
do {
sine = sqrt((1 - cosine)/2);
cosine = sqrt((1 + cosine)/2);
n *= 2;
pi = n*sine;
printf("%6u s:%.17e c:%.17e pi:%.17e %%:%.6e\n", n, sine, cosine, pi, (pi-mpi)/mpi);
} while (n <500000);
Subtracting 1.0 from a nearly-1.0 number is leading to "catastrophic cancellation", where the relative error in a FP calculation skyrockets due to the loss of significant digits. Try evaluating pow(2, i) - (pow(2, i) - 1.0) for each i between 0 and 60 and you'll see what I mean.
The only real solution to this issue is reorganizing your equations to avoid subtracting nearly-equal nonzero quantities. For more details, see Acton, Real Computing Made Real, or Higham, Accuracy and Stability of Numerical Algorithms.

sunrise sunset times in c

In my C application, I want to calculate sunrise/sunset times for a given date, latitude and longitude. i have been searching on the net but i can not find a working sample.
I tried to implement this sample:
http://souptonuts.sourceforge.net/code/sunrise.c.html
But this sample didnt work correctly.
Is there a simple C source code or method which i can easly implement in my application?
Edit:
I implement the code on this link but it gave me the wrong sunset/sunrise values. Also i tried the Saul's link here but it gave me the wrong result either.
I have 41N, 28E location. When i try the codes, both sample says that sunrise value is aproximately 10:13 and sunset is 23:24. But the correct values are 06:06, 20:13.
I can not understand the problem.
Ten simple steps to follow to calculate sunrise / sunset time given the date, latitude and longitude
first calculate the day of the year
N1 = floor(275 * month / 9)
N2 = floor((month + 9) / 12)
N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3))
N = N1 - (N2 * N3) + day - 30
convert the longitude to hour value and calculate an approximate time
lngHour = longitude / 15
if rising time is desired:
t = N + ((6 - lngHour) / 24)
if setting time is desired:
t = N + ((18 - lngHour) / 24)
calculate the Sun's mean anomaly
M = (0.9856 * t) - 3.289
calculate the Sun's true longitude
L = M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634
NOTE: L potentially needs to be adjusted into the range [0,360) by adding/subtracting 360
5a. calculate the Sun's right ascension
RA = atan(0.91764 * tan(L))
NOTE: RA potentially needs to be adjusted into the range [0,360) by adding/subtracting 360
5b. right ascension value needs to be in the same quadrant as L
Lquadrant = (floor( L/90)) * 90
RAquadrant = (floor(RA/90)) * 90
RA = RA + (Lquadrant - RAquadrant)
5c. right ascension value needs to be converted into hours
RA = RA / 15
calculate the Sun's declination
sinDec = 0.39782 * sin(L)
cosDec = cos(asin(sinDec))
7a. calculate the Sun's local hour angle
cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
if (cosH > 1)
the sun never rises on this location (on the specified date)
if (cosH < -1)
the sun never sets on this location (on the specified date)
7b. finish calculating H and convert into hours
if if rising time is desired:
H = 360 - acos(cosH)
if setting time is desired:
H = acos(cosH)
H = H / 15
calculate local mean time of rising/setting
T = H + RA - (0.06571 * t) - 6.622
adjust back to UTC
UT = T - lngHour
NOTE: UT potentially needs to be adjusted into the range [0,24) by adding/subtracting 24
convert UT value to local time zone of latitude/longitude
localT = UT + localOffset
This seems quite easy to implement:
http://edwilliams.org/sunrise_sunset_algorithm.htm
Using this guide (which was first posted by #BenjaminMonate and I assume is the same one #Geetha used), I built this C function which seems to work correctly.
#include <math.h>
#define PI 3.1415926
#define ZENITH -.83
float calculateSunrise(int year,int month,int day,float lat, float lng,int localOffset, int daylightSavings) {
/*
localOffset will be <0 for western hemisphere and >0 for eastern hemisphere
daylightSavings should be 1 if it is in effect during the summer otherwise it should be 0
*/
//1. first calculate the day of the year
float N1 = floor(275 * month / 9);
float N2 = floor((month + 9) / 12);
float N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3));
float N = N1 - (N2 * N3) + day - 30;
//2. convert the longitude to hour value and calculate an approximate time
float lngHour = lng / 15.0;
float t = N + ((6 - lngHour) / 24); //if rising time is desired:
//float t = N + ((18 - lngHour) / 24) //if setting time is desired:
//3. calculate the Sun's mean anomaly
float M = (0.9856 * t) - 3.289;
//4. calculate the Sun's true longitude
float L = fmod(M + (1.916 * sin((PI/180)*M)) + (0.020 * sin(2 *(PI/180) * M)) + 282.634,360.0);
//5a. calculate the Sun's right ascension
float RA = fmod(180/PI*atan(0.91764 * tan((PI/180)*L)),360.0);
//5b. right ascension value needs to be in the same quadrant as L
float Lquadrant = floor( L/90) * 90;
float RAquadrant = floor(RA/90) * 90;
RA = RA + (Lquadrant - RAquadrant);
//5c. right ascension value needs to be converted into hours
RA = RA / 15;
//6. calculate the Sun's declination
float sinDec = 0.39782 * sin((PI/180)*L);
float cosDec = cos(asin(sinDec));
//7a. calculate the Sun's local hour angle
float cosH = (sin((PI/180)*ZENITH) - (sinDec * sin((PI/180)*lat))) / (cosDec * cos((PI/180)*lat));
/*
if (cosH > 1)
the sun never rises on this location (on the specified date)
if (cosH < -1)
the sun never sets on this location (on the specified date)
*/
//7b. finish calculating H and convert into hours
float H = 360 - (180/PI)*acos(cosH); // if if rising time is desired:
//float H = acos(cosH) // if setting time is desired:
H = H / 15;
//8. calculate local mean time of rising/setting
float T = H + RA - (0.06571 * t) - 6.622;
//9. adjust back to UTC
float UT = fmod(T - lngHour,24.0);
//10. convert UT value to local time zone of latitude/longitude
return UT + localOffset + daylightSavings;
}
void printSunrise() {
float localT = calculateSunrise(/*args*/);
double hours;
float minutes = modf(localT,&hours)*60;
printf("%.0f:%.0f",hours,minutes);
}
code provided by scottmrogowski was useful,
two issues however
//float H = (180/PI)*acos(cosH) // if setting time is desired:
float localT=fmod(24 + calculateSunrise(/* args */),24.0); //in printSunrise function
Cheers
Marek
There is a c solution with an objective c wrapper for sunrise/set here: https://github.com/berkley/ObjectiveCUtil
Maybe try this piece of code. It is tested and works.
Hope you like it...
#include "stdafx.h"
#include <iostream>
#include <math.h>
#include <time.h>
using namespace std;
//STANDARD CONSTANTS
double pi = 3.1415926535; // Pi
double solarConst = 1367; // solar constant W.m-2
// Function to convert radian to hours
double RadToHours (double tmp)
{
//double pi = 3.1415926535; // Pi
return (tmp * 12 / pi);
}
// Function to convert hours to radians
double HoursToRads (double tmp)
{
//double pi = 3.1415926535; // Pi
return (tmp * pi / 12);
}
// Function to calculate the angle of the day
double AngleOfDay (int day, // number of the day
int month, // number of the month
int year // year
)
{ // local vars
int i, leap;
int numOfDays = 0; // number of Day 13 Nov=317
int numOfDaysofMonths[12] = {0,31,28,31,30,31,30,31,31,30,31,30}; // Number of days per month
int AllYearDays; // Total number of days in a year 365 or 366
double DayAngle; // angle of the day (radian)
//double pi = 3.1415926535; // Pi
// leap year ??
leap = 0;
if ((year % 400)==0)
{ AllYearDays = 366;
leap = 1;
}
else if ((year % 100)==0) AllYearDays = 365;
else if ((year % 4)==0)
{ AllYearDays = 366;
leap = 1;
}
else AllYearDays = 365;
// calculate number of day
for (i=0;i<month;i++) numOfDays += numOfDaysofMonths[i];
if ( (month > 2) && leap) numOfDays++;
numOfDays += day;
// calculate angle of day
DayAngle = (2*pi*(numOfDays-1)) / AllYearDays;
return DayAngle;
}
// Function to calculate declination - in radian
double Declination (double DayAngle // angle day in radian
)
{
double SolarDeclination;
// Solar declination (radian)
SolarDeclination = 0.006918
- 0.399912 * cos (DayAngle)
+ 0.070257 * sin (DayAngle)
- 0.006758 * cos (2*DayAngle)
+ 0.000907 * sin (2*DayAngle)
- 0.002697 * cos (3*DayAngle)
+ 0.00148 * sin (3*DayAngle);
return SolarDeclination;
}
// Function to calculate Equation of time ( et = TSV - TU )
double EqOfTime (double DayAngle // angle day (radian)
)
{
double et;
// Equation of time (radian)
et = 0.000075
+ 0.001868 * cos (DayAngle)
- 0.032077 * sin (DayAngle)
- 0.014615 * cos (2*DayAngle)
- 0.04089 * sin (2*DayAngle);
// Equation of time in hours
et = RadToHours(et);
return et;
}
// Calculation of the duration of the day in radian
double DayDurationRadian (double _declination, // _declination in radian
double lat // latitude in radian
)
{
double dayDurationj;
dayDurationj = 2 * acos( -tan(lat) * tan(_declination) );
return dayDurationj;
}
// Function to calculate Day duration in Hours
double DayDuratInHours (double _declination // _declination in radian
, double lat // latitude in radian
)
{
double dayDurationj;
dayDurationj = DayDurationRadian(_declination, lat);
dayDurationj = RadToHours(dayDurationj);
return dayDurationj;
}
// Function to calculate the times TSV-UTC
double Tsv_Tu (double rlong // longitude en radian positive a l est.
,double eqOfTime // Equation of times en heure
)
{
double diffUTC_TSV; double pi = 3.1415926535; // Pi
// diffUTC_TSV Solar time as a function of longitude and the eqation of time
diffUTC_TSV = rlong * (12 / pi) + eqOfTime;
// difference with local time
return diffUTC_TSV;
}
// Calculations of the orbital excentricity
double Excentricity(int day,
int month,
int year)
{
double dayAngleRad, E0;
// calculate the angle of day in radian
dayAngleRad = AngleOfDay(day, month, year);
// calculate the excentricity
E0 = 1.000110 + 0.034221 * cos(dayAngleRad)
+ 0.001280 * sin(dayAngleRad)
+0.000719 * cos(2*dayAngleRad)
+0.000077 * sin(2*dayAngleRad);
return E0;
}
// Calculate the theoretical energy flux for the day radiation
double TheoreticRadiation(int day, int month, int year,
double lat // Latitude in radian !
)
{
double RGth; // Theoretical radiation
double decli; // Declination
double E0;
double sunriseHourAngle; // Hour angle of sunset
// Calculation of the declination in radian
decli = Declination (AngleOfDay(day, month, year));
// Calcuate excentricity
E0 = Excentricity(day, month, year);
// Calculate hour angle in radian
sunriseHourAngle = DayDurationRadian(decli, lat) / 2;
// Calculate Theoretical radiation en W.m-2
RGth = solarConst * E0 * (cos(decli)*cos(lat)*sin(sunriseHourAngle)/sunriseHourAngle + sin(decli)*sin(lat));
return RGth;
}
// Function to calculate decimal hour of sunrise: result in local hour
double CalclulateSunriseLocalTime(int day,
int month,
int year,
double rlong,
double rlat)
{
// local variables
int h1, h2;
time_t hour_machine;
struct tm *local_hour, *gmt_hour;
double result;
// Calculate the angle of the day
double DayAngle = AngleOfDay(day, month, year);
// Declination
double SolarDeclination = Declination(DayAngle);
// Equation of times
double eth = EqOfTime(DayAngle);
// True solar time
double diffUTC_TSV = Tsv_Tu(rlong,eth);
// Day duration
double dayDurationj = DayDuratInHours(SolarDeclination,rlat);
// local time adjust
time( &hour_machine ); // Get time as long integer.
gmt_hour = gmtime( &hour_machine );
h1 = gmt_hour->tm_hour;
local_hour = localtime( &hour_machine ); // local time.
h2 = local_hour->tm_hour;
// final result
result = 12 - fabs(dayDurationj / 2) - diffUTC_TSV + h2-h1;
return result;
}
// Function to calculate decimal hour of sunset: result in local hour
double CalculateSunsetLocalTime(int day,
int month,
int year,
double rlong,
double rlat)
{
// local variables
int h1, h2;
time_t hour_machine;
struct tm *local_hour, *gmt_hour;
double result;
// Calculate the angle of the day
double DayAngle = AngleOfDay(day, month, year);
// Declination
double SolarDeclination = Declination(DayAngle);
// Equation of times
double eth = EqOfTime(DayAngle);
// True solar time
double diffUTC_TSV = Tsv_Tu(rlong,eth);
// Day duration
double dayDurationj = DayDuratInHours(SolarDeclination,rlat);
// local time adjust
time( &hour_machine ); // Get time as long integer.
gmt_hour = gmtime( &hour_machine );
h1 = gmt_hour->tm_hour;
local_hour = localtime( &hour_machine ); // local time.
h2 = local_hour->tm_hour;
// resultat
result = 12 + fabs(dayDurationj / 2) - diffUTC_TSV + h2-h1;
return result;
}
// Function to calculate decimal hour of sunrise: result universal time
double CalculateSunriseUniversalTime(int day,
int month,
int year,
double rlong,
double rlat)
{
double result;
// Calculate the angle of the day
double DayAngle = AngleOfDay(day, month, year);
// Declination
double SolarDeclination = Declination(DayAngle);
// Equation of times
double eth = EqOfTime(DayAngle);
// True solar time
double diffUTC_TSV = Tsv_Tu(rlong,eth);
// Day duration
double dayDurationj = DayDuratInHours(SolarDeclination,rlat);
// resultat
result = 12 - fabs(dayDurationj / 2) - diffUTC_TSV;
return result;
}
// Function to calculate decimal hour of sunset: result in universal time
double CalculateSunsetUniversalTime(int day,
int month,
int year,
double rlong,
double rlat)
{
double result;
// Calculate the angle of the day
double DayAngle = AngleOfDay(day, month, year);
// Declination
double SolarDeclination = Declination(DayAngle);
// Equation of times
double eth = EqOfTime(DayAngle);
// True solar time
double diffUTC_TSV = Tsv_Tu(rlong,eth);
// Day duration
double dayDurationj = DayDuratInHours(SolarDeclination,rlat);
// resultat
result = 12 + fabs(dayDurationj / 2) - diffUTC_TSV;
return result;
}
// Function to calculate the height of the sun in radians the day to day j and hour TU
double SolarHeight (int tu, // universal times (0,1,2,.....,23)
int day,
int month,
int year,
double lat, // latitude in radian
double rlong // longitude in radian
)
{
// local variables
double pi = 3.1415926535; // Pi
double result, tsvh;
// angle of the day
double DayAngle = AngleOfDay(day, month, year);
// _declination
double decli = Declination(DayAngle);
// eq of time
double eq = EqOfTime(DayAngle);
// calculate the tsvh with rlong positiv for the east and negative for the west
tsvh = tu + rlong*180/(15*pi) + eq;
// hour angle per hour
double ah = acos( -cos((pi/12)*tsvh) );
// final result
result = asin( sin(lat)*sin(decli) + cos(lat)*cos(decli)*cos(ah) );
return result;
}
///////////EXTRA FUNCTIONS/////////////////////////////
//Julian day conversion for days calculations
//Explanation for this sick formula...for the curious guys...
//http://www.cs.utsa.edu/~cs1063/projects/Spring2011/Project1/jdn-explanation.html
int julian(int year, int month, int day) {
int a = (14 - month) / 12;
int y = year + 4800 - a;
int m = month + 12 * a - 3;
if (year > 1582 || (year == 1582 && month > 10) || (year == 1582 && month == 10 && day >= 15))
return day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;
else
return day + (153 * m + 2) / 5 + 365 * y + y / 4 - 32083;
}
int _tmain(int argc, _TCHAR* argv[])
{
int day = 14;
int month = 11;
int year = 2013;
double lat = 39.38;
double lon = 22.75;
double rlat = 39.38 * pi/180;
double rlong = 22.75 * pi/180;
double _AngleOfDay = AngleOfDay ( day , month , year );
cout << "Angle of day: " << _AngleOfDay << "\n";
double _Declinaison = Declination (_AngleOfDay);
cout << "Declination (Delta): " << _Declinaison << "\n";
double _EqOfTime = EqOfTime (_AngleOfDay);
cout << "Declination (Delta): " << _EqOfTime << "\n";
double _DayDuratInHours = DayDuratInHours (_Declinaison, rlat);
cout << "Day duration: " << _DayDuratInHours << "\n";
double _Excentricity = Excentricity(day, month, year);
cout << "Excentricity: " << _Excentricity << "\n";
double _TheoreticRadiation = TheoreticRadiation(day, month, year, rlat);
cout << "Theoretical radiation: " << _TheoreticRadiation << "\n";
double _CalclulateSunriseLocalTime = CalclulateSunriseLocalTime
(day, month, year, rlong, rlat);
cout << "Sunrise Local Time: " << _CalclulateSunriseLocalTime << "\n";
double _CalculateSunsetLocalTime = CalculateSunsetLocalTime
(day, month, year, rlong, rlat);
cout << "Sunrise Local Time: " << _CalculateSunsetLocalTime << "\n";
return 0;
}
I ported Tomoyose's C code to C#. It works fine, though a manual offset fix was required due to a consistent discrepancy of about 4 minutes with online sources. This may be due to a difference in what constitutes a sunset or rise, though officially it should be when the sun is just touching the horizon from underneath it. I expect that detail is buried in the constant parameters somewhere - beyond me to attempt a proper fix. If others manage it I'd like to know ;-)
It uses NodaTime which is Jon Skeet's implementation of Java's JodaTime. NodaTime is available in nuGet.
using System;
using NodaTime;
namespace YourNamespaceHere
{
public static class MySunset
{
// Based on Tomoyose's
// http://stackoverflow.com/questions/7064531/sunrise-sunset-times-in-c
private static DateTimeZone mUtcZone = DateTimeZoneProviders.Tzdb["Etc/UTC"];
private static int mSecondsInDay = 24 * 60 * 60;
// Convert radian to hours
private static double RadiansToHours(double radians)
{
return (radians * 12.0 / Math.PI);
}
// Convert hours to radians
private static double HoursToRadians(double hours)
{
return (hours * Math.PI / 12.0);
}
// Calculate the angle of the day
private static double AngleOfDay(int year, int month, int day)
{
DateTime date = new DateTime(year, month, day);
int daysInYear = DateTime.IsLeapYear(year) ? 366 : 365;
// Return angle of day in radians
return (2.0 * Math.PI * ((double)date.DayOfYear - 1.0)) / (double)daysInYear;
}
// Calculate declination in radians
private static double SolarDeclination(double angleOfDayRadians)
{
// Return solar declination in radians
return 0.006918
- 0.399912 * Math.Cos(angleOfDayRadians)
+ 0.070257 * Math.Sin(angleOfDayRadians)
- 0.006758 * Math.Cos(2.0 * angleOfDayRadians)
+ 0.000907 * Math.Sin(2.0 * angleOfDayRadians)
- 0.002697 * Math.Cos(3.0 * angleOfDayRadians)
+ 0.00148 * Math.Sin(3.0 * angleOfDayRadians);
}
// Calculate Equation of time ( eot = TSV - TU )
private static double EquationOfTime(double angleOfDayRadians)
{
// Equation of time (radians)
double et = 0.000075
+ 0.001868 * Math.Cos(angleOfDayRadians)
- 0.032077 * Math.Sin(angleOfDayRadians)
- 0.014615 * Math.Cos(2.0 * angleOfDayRadians)
- 0.04089 * Math.Sin(2.0 * angleOfDayRadians);
// Return equation-of-time in hours
return RadiansToHours(et);
}
// Calculate the duration of the day in radians
private static double DayDurationRadians(double declinationRadians, double latitudeRadians)
{
return 2.0 * Math.Acos(-Math.Tan(latitudeRadians) * Math.Tan(declinationRadians));
}
// Calculate day duration in hours
private static double DayDurationHours(double declinationRadians, double latitudeRadians)
{
return RadiansToHours(
DayDurationRadians(declinationRadians, latitudeRadians)
);
}
// Calculate the times TSV-UTC
private static double Tsv_Tu(double longitudeRadians, double equationOfTime)
{
// Solar time as a function of longitude and the equation of time
return longitudeRadians * (12.0 / Math.PI) + equationOfTime;
}
private static void GetDayParameters(int year, int month, int day, double latitude, double longitude,
out double dayDuration, out double diffUTC_TSV)
{
double latitudeRadians = latitude * Math.PI / 180.0;
double longitudeRadians = longitude * Math.PI / 180.0;
// Calculate the angle of the day
double dayAngle = AngleOfDay(year, month, day);
// Declination
double solarDeclination = SolarDeclination(dayAngle);
// Equation of times
double equationOfTime = EquationOfTime(dayAngle);
// True solar time
diffUTC_TSV = Tsv_Tu(longitudeRadians, equationOfTime);
// Day duration
dayDuration = DayDurationHours(solarDeclination, latitudeRadians);
}
// Calculate decimal UTC hour of sunrise.
private static double CalculateSunriseUTC(int year, int month, int day, double latitude, double longitude)
{
double dayDuration;
double diffUTC_TSV;
GetDayParameters(year, month, day, latitude, longitude, out dayDuration, out diffUTC_TSV);
return 12.0 - Math.Abs(dayDuration / 2.0) - diffUTC_TSV;
}
// Calculate decimal UTC hour of sunset.
private static double CalculateSunsetUTC(int year, int month, int day, double latitude, double longitude)
{
double dayDuration;
double diffUTC_TSV;
GetDayParameters(year, month, day, latitude, longitude, out dayDuration, out diffUTC_TSV);
return 12.0 + Math.Abs(dayDuration / 2.0) - diffUTC_TSV;
}
// Public methods to return sun rise and set times.
public static Tuple<ZonedDateTime, ZonedDateTime> GetSunRiseSet(LocalDate dateAtLocation, DateTimeZone locationZone, double latitude, double longitude)
{
// latitude-longitude must lie within zone of locationZone
// Get UTC rise and set
double dayDuration;
double diffUTC_TSV;
GetDayParameters(dateAtLocation.Year, dateAtLocation.Month, dateAtLocation.Day, latitude, longitude, out dayDuration, out diffUTC_TSV);
double sunriseUtcDecimal = 12.0 - Math.Abs(dayDuration / 2.0) - diffUTC_TSV;
double sunsetUtcDecimal = 12.0 + Math.Abs(dayDuration / 2.0) - diffUTC_TSV;
// Convert decimal UTC to UTC dates
// If a UTC time is negative then it means the date before in the UTC timezone.
// So if negative need to minus 1 day from date and set time as 24 - decimal_time.
LocalDateTime utcRiseLocal;
LocalDateTime utcSetLocal;
if (sunriseUtcDecimal < 0)
{
LocalDate utcDateAdjusted = dateAtLocation.PlusDays(-1);
// Normalize() is important here; otherwise only have access to seconds.
Period utcTimeAdjusted = Period.FromSeconds((long)((24.0 + sunriseUtcDecimal) / 24.0 * mSecondsInDay)).Normalize(); // + a negative
utcRiseLocal = new LocalDateTime(utcDateAdjusted.Year, utcDateAdjusted.Month, utcDateAdjusted.Day,
(int)utcTimeAdjusted.Hours, (int)utcTimeAdjusted.Minutes, (int)utcTimeAdjusted.Seconds);
}
else
{
Period utcTime = Period.FromSeconds((long)(sunriseUtcDecimal / 24.0 * mSecondsInDay)).Normalize();
utcRiseLocal = new LocalDateTime(dateAtLocation.Year, dateAtLocation.Month, dateAtLocation.Day,
(int)utcTime.Hours, (int)utcTime.Minutes, (int)utcTime.Seconds);
}
if (sunsetUtcDecimal < 0) // Maybe not possible?
{
LocalDate utcDateAdjusted = dateAtLocation.PlusDays(-1);
Period utcTimeAdjusted = Period.FromSeconds((long)((24.0 + sunsetUtcDecimal) / 24.0 * mSecondsInDay)).Normalize();
utcSetLocal = new LocalDateTime(utcDateAdjusted.Year, utcDateAdjusted.Month, utcDateAdjusted.Day,
(int)utcTimeAdjusted.Hours, (int)utcTimeAdjusted.Minutes, (int)utcTimeAdjusted.Seconds);
}
else
{
Period utcTime = Period.FromSeconds((long)(sunsetUtcDecimal / 24.0 * mSecondsInDay)).Normalize();
utcSetLocal = new LocalDateTime(dateAtLocation.Year, dateAtLocation.Month, dateAtLocation.Day,
(int)utcTime.Hours, (int)utcTime.Minutes, (int)utcTime.Seconds);
}
// FIX: always about 4 minutes later/earlier than other sources
utcRiseLocal = utcRiseLocal.PlusMinutes(-4);
utcSetLocal = utcSetLocal.PlusMinutes(4);
// Get zoned datetime from UTC local datetimes
ZonedDateTime utcRiseZoned = new LocalDateTime(utcRiseLocal.Year, utcRiseLocal.Month, utcRiseLocal.Day, utcRiseLocal.Hour, utcRiseLocal.Minute, utcRiseLocal.Second).InZoneLeniently(mUtcZone);
ZonedDateTime utcSetZoned = new LocalDateTime(utcSetLocal.Year, utcSetLocal.Month, utcSetLocal.Day, utcSetLocal.Hour, utcSetLocal.Minute, utcSetLocal.Second).InZoneLeniently(mUtcZone);
// Return zoned UTC to zoned local
return new Tuple<ZonedDateTime, ZonedDateTime>
(
new ZonedDateTime(utcRiseZoned.ToInstant(), locationZone),
new ZonedDateTime(utcSetZoned.ToInstant(), locationZone)
);
}
public static Tuple<ZonedDateTime, ZonedDateTime> GetSunRiseSet(int year, int month, int day, string locationZone, double latitude, double longitude)
{
return GetSunRiseSet(new LocalDate(year, month, day), DateTimeZoneProviders.Tzdb[locationZone], latitude, longitude);
}
public static Tuple<ZonedDateTime, ZonedDateTime> GetSunRiseSet(ZonedDateTime zonedDateAtLocation, double latitude, double longitude)
{
return GetSunRiseSet(zonedDateAtLocation.LocalDateTime.Date, zonedDateAtLocation.Zone, latitude, longitude);
}
}
}
I recently wrote a library to do this. It has both C and Python APIs exposed - https://github.com/adonmo/daylight
Here is an excerpt of how sunrise time is calculated:
time_t Sunclock::sunrise(time_t date) {
date = date + tz_offset * 60 * 60;
struct tm *t = gmtime(&date);
double _time_of_day = time_of_day(date);
double _julian_day = julian_day(t, _time_of_day, tz_offset);
double _julian_century = julian_century(_julian_day);
double _mean_obliq_ecliptic = mean_obliq_ecliptic(_julian_century);
double _mean_long_sun = mean_long_sun(_julian_century);
double _mean_anom_sun = mean_anom_sun(_julian_century);
double _sun_eq_of_centre = sun_eq_of_centre(_mean_anom_sun, _julian_century);
double _sun_true_long = sun_true_long(_mean_long_sun, _sun_eq_of_centre);
double _obliq_corr = obliq_corr(_mean_obliq_ecliptic, _julian_century);
double _sun_app_long = sun_app_long(_sun_true_long, _julian_century);
double _eccent_earth_orbit = eccent_earth_orbit(_julian_century);
double _var_y = var_y(_obliq_corr);
double _eq_of_time =
eq_of_time(_var_y, _mean_long_sun, _eccent_earth_orbit, _mean_anom_sun);
double _declination = declination(_obliq_corr, _sun_app_long);
double _hour_angle_sunrise = hour_angle_sunrise(_declination);
double noon_decimal_day =
(720 - 4 * longitude - _eq_of_time + tz_offset * 60) / 1440;
double decimal_day = noon_decimal_day - _hour_angle_sunrise * 4 / 1440;
return time_from_decimal_day(date, decimal_day) - tz_offset * 60 * 60;
}
The above code should give a rough idea, but code for other functions and in fact the complete code can be read from here: https://github.com/adonmo/daylight/blob/master/source/Sunclock.cpp
The sample code appears to work in VC++ 2010 with a few minor changes:
Compile it as a C++ file and not C.
Remove the #include <sys/time.h> line.
Add a #define _USE_MATH_DEFINES at the top of the file in order for M_PI to be defined.
Change the two %T in the strftime() calls to %X.
Now that you have a working sample you can debug the working version and your version to see where the calculation begins to differ and narrow in on the issue. Either step through the program or make liberal use of temporary printf() calls much like the sample does.
If you want specific help you will have to post your code (either a link to the entire file or particular snippets you need help with).
Pure C implementations are apparently scarce but if You are willing to port either from C++ or C# then there are a couple of options:
C++ Sunrise/Sunset Calculations
C# Get sunrise and sunset time based on latitude and longitude
//a practical approximation for a microcontroller using lookup table
//seems to only use a small fraction of the resources required by the trigonometric functions
//ignores daylight saving: idea is for the device to approximate and trigger actual sunrise, sunset event as opposed to actual (politically correct local hhmm)
//using globals gps.Lat gps.LatDir(0:S 1:N) gps.Day (day of month) gps.Mth
//needs solar noon offset,latitude,dayOfMonth,Month
//LocalNoon-SolarNoon (may be off +/- up to ?? 2 hours) depending on local timezone and longitude (? typical discrepancy about 20min)
void SunriseSunsetCalculations(u8 SolarNoonOffsetInDeciHours,u8 SolarNoonOffsetDirection){
if(SolarNoonOffsetInDeciHours>20){SolarNoonOffsetInDeciHours=0;}//limit offest to 2 hours: discard offset on error
//--------references:
//https://www.orchidculture.com/COD/daylength.html
//http://aa.usno.navy.mil/data/docs/Dur_OneYear.php
//SolarTime is up two two hours off in either direction depending on timezone:
//http://blog.poormansmath.net/how-much-is-time-wrong-around-the-world/
//------------------
//lookUpTable Of daylength in decihours(6min)(fits well in u8) - 15day And 5 DegLat resolution
//SolarNoonOffsetDirection: 0: negative(b4 local noon), 1: +ve (solar noon after local noon)
u8 a[13][12]={//length of day in decihours
// Dec Nov Oct Sep Aug Jul
//Jan Feb Mar Apr May June
{120,120,120,120,120,120,120,120,120,120,120,120}, //lat 0____0
{126,125,124,123,122,121,119,118,116,115,115,114}, //lat 10____1
{129,128,127,125,123,121,119,117,115,113,112,111}, //lat 15____2
{132,131,129,127,124,121,118,115,113,110,109,108}, //lat 20____3
{135,134,131,128,125,122,118,114,111,108,106,105}, //lat 25____4
{139,137,134,130,127,122,117,113,108,105,102,101}, //lat 30____5
{143,141,137,133,128,123,117,111,106,102, 98, 97}, //lat 35____6
{148,145,141,135,130,123,116,109,103, 98, 94, 92}, //lat 40____7
{154,150,145,138,132,124,115,107,100, 93, 88, 86}, //lat 45____8
{161,157,150,142,134,124,114,105, 96, 88, 82, 79}, //lat 50____9
{170,165,156,146,137,125,113,102, 91, 81, 73, 69}, //lat 55___10
{183,176,165,152,140,126,112, 98, 84, 72, 61, 56}, //lat 60___11
{200,185,171,152,134,121,101, 84, 65, 53, 40, 33} //lat 65___12
};
u8 b[]={6,12,17,22,27,32,37,42,47,52,57,62,90}; // latitude limit cutoffs to get index of lookUpTable
u8 lat=gps.Lat/10000000; //lat stored in u32 to 7 decimals resolution (32bit unsigned integer)
u8 i=0; while(b[i]<lat){i++;} //get row index for daylength table
u8 k,ix; //k: 15 day offset; ix: column index for daylength table
k=gps.Day/15;if(k){k=1;}//which half of the month (avoid k=2 eg:31/15)
if(!gps.LatDir){ //0:southern latitudes
if(gps.Mth<7){
ix=(gps.Mth-1)*2+k; //2 fields per month (k to select)
}else{ //beyond june, use row in reverse
ix=11-(gps.Mth-7)*2-k; //2 fields per month (k to select)
}
}else{ //1:northern latitudes
if(gps.Mth<7){ //with first six month read rows in reverse
ix=11-(gps.Mth-1)*2-k; //2 fields per month (k to select)
}else{ //beyond june, use same row forward
ix=(gps.Mth-7)*2+k; //2 fields per month (k to select)
}
}
//debug only:...dcI("\r\ni", i ,Blue,Red,1);dcI("ix", ix ,Blue,Red,1);dcI("a[i][ix]", a[i][ix] ,Blue,Red,1);
u8 h=a[i][ix]/2; //HalfTheDayLightLength in deciHours
u8 j[]={-1,1}; //multiplier: 0:(-) 1:(+)
u8 sn=120+SolarNoonOffsetInDeciHours*j[SolarNoonOffsetDirection]; //Solar noon
u8 srdh=sn-h; //sunrise in deciHours = solarNoon minus HalfTheDayLightLength
u8 ssdh=sn+h; //sunset in deciHours = solarNoon plus HalfTheDayLightLength
//debug only:...dcI("\r\nSunRiseDeciHours", srdh ,Blue,Red,1);dcI("SunSetDeciHours", ssdh ,Blue,Red,1);
gps.HmSunRise=deciHourTohhmm(srdh);
gps.HmSunSet =deciHourTohhmm(ssdh);
}
u16 deciHourTohhmm(u8 dh){ //return unsigned integer from 0 to 2400
u16 h=(dh/10)*100; //hours: hh00
u8 r= dh%10; //fraction hour remainder to be converted to minutes
u8 m= 6*r; //60*r/10
return(h+m);
}
/*
Example Output: (!!! solarNoonOffset kept at 0(ignored) for the below example)
:_(08474300)___:_(A)___:_(381234567)___:_(S)___:_(1431234567)___:_(E)___
:_(GPS OK)___:_(Sat)___:_(12)___:_(Aug)___:_(2017)___hhmm:_(847)___
//...........
i:_(7)___ix:_(9)___a[i][ix]:_(98)___
SunRiseDeciHours:_(71)___SunSetDeciHours:_(169)___
HmSunRise:_(706)___
HmSunSet:_(1654)___
//............same as above but change LatDir to 1 (northern hemisphere)
i:_(7)___ix:_(2)___a[i][ix]:_(141)___
SunRiseDeciHours:_(50)___SunSetDeciHours:_(190)___
HmSunRise:_(500)___
HmSunSet:_(1900)___
..........ignore dcI(...) it's just a custom function printing through the serial port
*/
This is adaptable and is quite accurate. You'll get all the components and then all you need to calculate is the arc cosine for the Zenith to Horizon angle. Yes there are simpler ways but don't you basically want to track the Sun? It could come in handy some day.
http://www.nrel.gov/midc/spa/
De program "zon" will calculate and display sun rise and set for your location. See https://github.com/Aygath/zon . .deb and .rpm available.
Not a library, but a command line utility, written in C.
Take location coordinates from /usr/share/zoneinfo/zone1970.tab on linux. Alternatively, click right on Google Maps, copy the coordinates and do a one-time conversion to degrees and minutes (and optionally seconds) somewhere online.

Resources