I need to determine Sunset (and Sunrise) for arbitrary locations. I don’t want to use floating-point math, due to the code size penalty. I don’t need to know the times to better than +/- 5 minutes or so.
If I create a lookup table for a specific location, is there some way to use that information to determine the times for other locations (keeping in mind the no-floating-point constraint)?
I guess my question wasn’t clear - if I have a table (for every day of the year) of sunset times for a specific lat/lon, can I use that information to calculate the times for any other location?
If you are looking for a pecific calculation, that math is beyond me. Though it strikes me that the NOAA calculator is probably public domain, and you might be able to get hold of the source code for it.
Thanks, but I already have a working Sunrise/Sunset calculator.
The issue is that I’m moving to a smaller microcontroller, and the code I have uses a lot of floating-point math, including a great deal of trig. If I can avoid doing the calculations (say, by pre-calculating a table of times for a specific location), I can not only save the space required for the calculation routine itself, but even more important, the space taken by the math libraries, which are pretty huge.
I did find an integer SIN function which uses fixed-point math, and I may look at using that, but if I could use the look-up table approach, I think that would be the most efficient.
Why the limitation of no floating point math? I’m guessing you have a small embedded processor such as PIC with limited FP capabilities.
You’re going to be using floating point math for the latitude and longitude values, unless you’re willing to be off by about 70 miles in any direction.
You might try using formulas found in Practical Astronomy with your Calculator by Peter Duffett-Smith. The math is pretty clearly laid out and easy to understand. You might be able to reverse-engineer the formula for sunset at your known location to get the time, and then that gets plugged into the formula for sunset at your unknown location. If I recall correctly, these calculations are within six minutes of accuracy.
The problem with using a lookup table is your unknown location could be north or south, east or west, both of which affects the sunset time, and those times change throughout the year. For example, regardless of latitude, sunset is the same for all locations on the same longitude on the days of the equinoxes. However, on the days of the solstices longitude comes into play as a major factor. In other words, on the equinox, all cities on the same north-south line have the same sunset, but on the summer solstice (northern hemisphere) that line will tilt to the northeast: the further north you are, the later the sunset, which would be at the same time for a city the the southwest of you. I hope that’s not too confusing!
It’s pretty trivial to calculate how many hours and minutes *east *or *west *of a location you are - it was nice of those old guys to divide the earth into 360 degrees, 15 to the hour, or 4 minutes per degree. So if you were at the same latitude you’d be set.
The issue, as you know, is different latitudes experience sunset at different times at different points of the year.
How big can your lookup table be? If you had lookups for one longitude (say, Greenwich) for *several *latitudes for 365 days of the year - maybe 17 of them (80 degrees south, 70 degrees south, … 60 north, 70 north, 80 north) you might get close via simple interpolation. On a better computer you could test that - calculate according to spherical trig, compare to the table lookup answer plus adjustments for thousands of trials.
The SIN function has a precision of 1/16,384 degree (at least, that’s what it takes as a parameter). I don’t know what the overall precision I would end up with after I converted everything to fixed point, but it’s probably worth the effort to see.
How are you getting your locations, and how are you storing your starting point? Is it just Lattitude and Longitude? Since you’re avoiding floating point, are you just dropping the decimal and storing those numbers as integers?
I think carrying latitude and longitude to the nearest degree will not be precise enough to guarantee that the sunset time will be accurate to within five minutes. If city A is off by almost half a degree and city B is off by almost half a degree in the opposite direction, you can have almost one degree of error. One degree of error in longitude is equivalent to 4 minutes of time. A similar one-degree error in latitude can have an even bigger effect especially at higher latitudes.
What do your formulas look like? If they involve multiplication of constants by trig functions, and if you have access to logical shift operators, something like this may work though it may be to complicated to bother with:
You can build a lookup table for the sine and cosine functions (from 0 to 90 degrees only, to avoid the complications of negative entries), using integers as entries instead of floating point or fixed-point numbers. The entry corresponding to angle x would not be sin(x) but rather the integer approximation of (2^n)*sin(x) for whatever value of n you choose. E.g., if you choose n=10, sin (22 deg) which is really about 0.3746 would be entered on the table as the integer 384 because 384/(2^10) is about 0.3746. Every time your formula requires multiplication by sin(x), multiply by the (integer) table entry instead but at the same time augment a counter i (initially zero) by n (n=10 in this example). Of course the final product will be too big by a factor of 2^i. After your calculations are done, (and if the product is positive) you can right-shift the result by i bits which has the effect of floor division by 2^i. Be careful because if the product result is negative, a logical right shift is not the same as floor division (in two’s complement representation). If at some intermediate point you are in danger of integer overflow or if the large multiplicands are likely to slow you down too much, you can right-shift an intermediate result by m bits (with the loss of some precision), remembering to decrement the counter i by m.
Other non-integer factors besides trig functions can be accounted for this way too. Pi is nearly 3217/(2^10), the square root of 2 is about 1448/(2^10), etc.
We currently use whole number of degrees for both Lat and Lon, and it’s close enough.
Even being off by 5 minutes from that would be fine (this code is used to turn on outdoor lighting, and nobody is that picky).
I’m going to have to look at the fixed-point solution. I could also externally calculate the table, but I’d have to figure out a reasonable way for an end user to upload it into the device.
Taking a completely different tack towards a solution …
A table of lat & long at 1 degree intervals requires 180*360 ~= 64K entries. That times 365 days requires ~=23M entries. Each value ranges from 0 to 1440, so 11 bits is enough for each entry. Trivial to calculate the whole table once at your factory, but probably impractical to embed it all in your microcontroller.
But SR/SS times don’t change that much from day to day. So you don’t really need 365 daily entries. You could get away with 180 or even 90 daily entries and still meet your *+/-5 minutes of actual *goal.
With a bit more analysis, you’ll see that times change at different rates at different times of the year; fastest around the equinox and slowest around the solstice. You’ll also see they change differently depending on latitude; fastest at high latitude and slowest at low latitude.
If you went through the full 23M-entry table and quantized out just the entries which differ by >=5 minutes from the previous quantized entry, you might well get down to more like (major WAG here) 1M entries. Which table *might *be small enough to simply store the whole thing in every microcontroller from the factory. Yes, the table search process would be a bit messier, but nothing difficult or overly time consuming, especially for something that’s only expected to trigger 2x/day.
You could further trim the table by simply deciding not to support ops beyond, say, 70N or 70S. The impact on your target market size will be negligible, and since the polar areas of the globe have some of the most dynamic SR/SS times, you’ll maximize the amount of table-size trimming you get per degree of latitude you don’t cover.
The same idea could be applied to longitude as well, although less easily. e.g. if you’re willing to give up the Aleutian Chain and Vladivostok Oblast markets you can skip about 25% of the longitudes, less a thin sliver for Hawaii.
I’m not saying this is necessarily better or easier than an all-fixed point solution using trig. But it might be a lot easier than you think, and it may well provide better error bars. Trig functions get pretty pathological at the boundaries too.
I’m curious if you have access to a list of say 500 or 1000 cities and their latitudes and longitudes for the US and Canada. For this project, they should be major cities that are evenly spaced geographically but not too close together - Minneapolis and St. Paul don’t both need to be listed for example. It’d be ideal if they are about 100 miles apart so a user could select the closest city. It doesn’t need to be an exact match, but for sunset they could use whatever’s closest.
It’s all matter of size - I want to do whatever is smallest.
The floating point library is around 18K. My current Rise/Set calculator is another 2K or so. If I could find a reasonable solution that was smaller than that, I’d use it. If not, I’ll go with what I’ve got. I’m pretty sure I’ve got the Flash space to do it with floating point - it’s just annoying, since this is the only place in the program that uses it.
Oh, and my idea for the table - you only need it for one hemisphere :smack: .
For the southern hemisphere, just take the answer for the opposite latitude 6 months from now (average what the sunrise/snset will be 182 and 183 days from now).
But yes, fixed point SINE is a much better answer anyway.
The “Collected Algorithms of the ACM” has some very nice sets of algorithms for fixed point trig functions, which I used back in the late 70’s. They took far less memory than lookup tables, and there were various implementations for different time/precision tradeoffs and other factors. Remarkably simple implementations, and not at all intuitive except perhaps to a really serious math whiz. Best of all, IIRC, there were algorithms where you could choose the radix, and from that and the word length, calculate the corresponding maximum error terms.
I’m no longer an ACM member and don’t know how to access that text, but you might just try googling.
Is there a reason why you’re not just using a photosensor to determine when it’s dark enough that the lights should go on? If it’s dark due to a storm, people might find the lights to be useful then as well.
Yes.
Photosensors are inherently unreliable - they get dirt, bird poop, and debris on them.
They also require being mounted where they can see the sky - out systems are often mounted in Electrical rooms or switch gear cabinets.