Sunrise / sunset code based on location

Do you have a question on how to do something.
Ask in here.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Here's the updated stripped out code. I cleaned up a tiny bit and moved newDay to a local variable since it was only used in that function. I also moved the lat and long assignment to the global declaration and did it in decimal degrees. Then in Calsun() I used ddToSeconds instead of dmsToSeconds. Doing this brought me down to a -5/-7 error...wow! I'm using Queensland, Australia to check time, I bet if I set my coords to that exactly, I'd be even closer! Anyway, here it is.

binder, you should use this as there's less variables defined and easier to setup now :)

Code: Select all

//By Matthew Hockin 2012.  
//SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
//Epherma (SWFLTEK.com) was written by Michael Rice- thanks Michael it works great. 
//If you copy from this (its all open source) please 
//acknowledge Michael for SWFLTEK library code (obviously labeled) or Matthew Hockin (for the rest).

#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <ReefAngel.h>

////// Place global variable code below here

//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>
//defines for SWFLTEK functions
#define _sec_rad 206264.806247096370813 // arc-seconds per radian
#define _tilt 0.409092804222329 // axial tilt of earth at epoch, in radians
#define _tropical_year 31556925 // tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _zenith 3.11250383272322 // 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude=-18.285833; // Great Barrier Reef, Australia
long longitude=147.699722; // Great Barrier Reef, Australia
byte dow=0;//day of week
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************

// Additional variables added for Sunrise/Sunset calculation
int SunriseHour, SunriseMin, SunsetHour, SunsetMin;
int userOffset=-(9*3600); // -9 hours

////// Place global variable code above here


void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller

    // Ports that are always on
    
    ////// Place additional initialization code below here
       
    ////// Place additional initialization code above here
}

void loop()
{
    ////// Place your custom code below here

    // Set Sunrise and Sunset from MHOCKIN Weather package functions. 
    if (dow!=day()) // used to see that were in a new day and need to recalculate sunrise and sunset
    {
      CalSun();
      dow=day();
      // And let's reset the light schedule
    } 
    
    ////// Place your custom code above here

    // This should always be the last line
    ReefAngel.ShowInterface();
}


//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
long newDay; 
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise

  //Serial.println("CalSun Run Now");
  
  latitude=ddToSeconds(latitude);
  longitude=ddToSeconds(longitude);
  
  if (dow==0){//if the controller has resarted we need to find midnight
    long hours, minutes;//store current elapsed local hours as total seconds from midnight
    time_t t=now();//store current clock time to parse
    hours=hour(t);
    hours=(hours*3600);//current hour number 0-23 as seconds
    minutes=minute(t);
    minutes=(minutes*60);//minutes of current hour as seconds
    newDay=now();
    newDay-=(hours+minutes);//Convert current local unix epoch time to local unix epoch time of midnight
  } 
  else if (dow!=0){//if we did not restart but the day is new then it is midnight and were good to go..
    newDay=now();
  }
      
  //#define SECS_YR_2000 (946684800) the time at the start of y2k (need to subtract from unix epoch time to bring to Y2K origin
  newDay-=946684800;//convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today
    
  rise=newDay;//set value to send to SunRise as midnight GMT in seconds from Y2K
  set=newDay;//
  //Calculate rise time and set time using Epherma Library functions (see end of code) 
  SunRise(&rise);//call to Epherma function
  SunSet(&set);//Call to Epherma functionunsigned long newDay;
  /*
  Serial.print("rise and set=  ");
  Serial.println(rise);
  Serial.println(set);
  Serial.print("newDay as seconds since 2000 to todays midnight=  ");
  Serial.println(newDay);
  */
  
  // Convert rise/set to normal hours/minutes before we make it an offset from midnight. 
  // Currently correcting for a 56 minute correction to Sunrise and -19 minute correction to Sunset (8/14/2012@GBR)
  SunriseHour=hour(rise+userOffset);
  SunriseMin=minute(rise+userOffset);
  SunsetHour=hour(set+userOffset);
  SunsetMin=minute(set+userOffset);
  
}//END SunCalc FUNCTION

//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
//THE CODE BELOW THIS copied directly from the SWFLTEK Epherma library constructed by Michael Rice.  
//this code is being used freely with attribution to Micahel Rice in accord with his request
//  A big thank you for these library functions.  Its great! 

// decimal degrees
long ddToSeconds(float dd){
  return dd * 3600.0;
}

//Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s){
long ret;

  ret = labs((long)d);
  ret = ret * 3600L + 60L * m + s;
  ret = (d<0L) ? -ret : ret;
  return ret;
}
/* ------------------------------------------------------------------------------------------------
   'Equation of Time'
   We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.
   The returned value is in seconds.
*/
int equation_of_time(unsigned long dt){
double t;

  dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
  dt %= _tropical_year;
  t = dt;
  t /= _tropical_year;
  t *= 6.283185307179586;
  t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
  return t;
}

/* -----------------------------------------------------------------------------------------------
   'Solar Declination'
   Returns declination in radians
   Accurate to within 50 arc-seconds
*/
double SolarDeclination(unsigned long dt){
double y;

  dt %= _tropical_year;
  y = dt;
  y /= _tropical_year; // fractional year
  y *= 6.283185307179586;
  y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
  return y;
}

/* ------------------------------------------------------------------------------------------------
   Return the period between sunrise and sunset, in seconds.
   At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt){
float l, d, e;
long n;

  d = -SolarDeclination(dt); // will be positive in Northern winter
  l = latitude / _sec_rad; // latitude in radians

  e += 60.0 * l * tan(l + d); // latitudinal error
  d = tan(l) * tan(d); //

  if(d>1.0) return 86400UL;
  if(d < -1.0) return 0UL;

  d = acos(d);
  d /= _zenith;

  n = 86400UL * d;
  n += e;
  return n;
}


/* ------------------------------------------------------------------------------------------------
   Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
   Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
   returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
   all day.

*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;

  daylen = daylightseconds(*dt);
  if(daylen == 86400UL) return 1;   // there is no 'night' today (midnight sun)
  if(daylen == 0UL) return -1; // there is no 'day' today

  *dt /= 86400UL;
  *dt *= 86400UL;
  *dt += 43200UL; // set the time stamp to 12:00:00 GMT

  *dt -= daylen / 2; //        sunrise at the prime meridian
  if(set) *dt += daylen; //     sunset at the prime meridian

  *dt -= equation_of_time(*dt);

  *dt -= longitude / 15.0; // rotate to our own meridian

  return 0;
}

// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
  return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
  return SunRiseSet(when, 1);
}
binder
Posts: 2871
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: Sunrise / sunset code based on location

Post by binder »

lnevo wrote: binder, you should use this as there's less variables defined and easier to setup now :)
Ok. I'll save this code to use to start splitting up. Haven't started yet....been working on a few new features for the android app such as Automatic Updating the device and Automatically configuring the modules in the device based on the settings from the controller. :)
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

OK. FYI I had an odd moment last night, wife came in at 1:30am and asked why is the fishtank bright? I checked, and all the lights were on... I turned them off and did some debugging today.. can't reproduce it. Need to play around with date and see if rollover at midnight is working properly...
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Very frustrated right now.... I could really use someone else to take a look at the code and test out with different days. I've reverted my code back even to a few days back and same behavior... After last night when the lights came back on after midnight, I've been troubleshooting all day and can't find what is going on. Now when I debug every time I change the day I get wacky times and can't reproduce a consistent day/time. I'm defaulting now back to a static rise/set, but not happy with it..

Anyway, use at your own risk right now...
rimai
Posts: 12881
Joined: Fri Mar 18, 2011 6:47 pm

Re: Sunrise / sunset code based on location

Post by rimai »

Could it be the newDay check?

Code: Select all

  if (dow==0){//if the controller has resarted we need to find midnight
dow would be 0 on Sunday, right?
Why are you deducting seconds from midnight only when dow==0?
Roberto.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

This was from rufessor's code. He sets dow=0 when the controller reboots to force the check... I wonder if it was all because yesterday was Sunday though... it was working ok afaik during the week... I'll play with it later tonight then and see if it wasn't just something stupid then.. Could probably get away with setting dow=-1 initially to force the setup.

Honestly in the test versions I was working with I took out that check altogether since why should it matter that it's midnight or during the day when you do the calculation, but was still seeing issues...Was a very frustrating night of coding, especially since I got sidetracked with my portal issue...
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Thanks rufessor. All your info is extremely helpful. I can imagine it's frustrating helping to debug such a complex code, especially when it's working for you :) I have not had a chance to get back to the troubleshooting and it may be some days before I get to. I do not have any time/sync automatic setup or anything, but was using the Date/Time setup screen to update the rise/set time. It would jump around to all kinds of times and be a bit crazy, which was definitley not the case when I was debugging before which is why the Sunday bit may be something to consider. I did however try taking all the dow() code out and just having it CalcSun() every time during the loop, but i went through so many iterations debugging, I have no clue. I was mainly afraid that I stripped something out inadvertantly but even jumping back to older code versions when I know it was giving more accurate times was wierd...

And for the record when I say crazy times, I mean gong from 11:18->23:01 to 18:46->10:08 to 10:46->8:15

Past troubleshooting was not going all wacky like that... With the rotation code, the main thing I noticed was the offset seemed to match the UTC-10 hours from GBR time and as I said, the offsets from real sunrise/sunset were under 12 minutes for both, whereas without that I was +50/-20 minutes or something like that...

The main thing is me getting my head clear enought to attempt to debug again. I'd like to setup a timer in the loop so I can increment a day every 30 seconds and CalcSun() and spit out the times in serial output so I dont' have to keep grabbing and playing with the joystick... this would help trememndously...
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Sunrise / sunset code based on location

Post by rufessor »

Thats not quite correct...

I use dow=day();

which is 1-31

so it will never be 0.

I don't understand what happened to you with the lights on.

However, if your doing any kind of external time syncing I would look at that as a used on the forum for my entire code is having serious issues with this setting time to decades in the future randomly. I think they gave up... unsure why as its clearly a time sync issue for them and not my code, but everyone has a point where they move on :)

Even if day() gave a zero value, which is impossible according to the arduino time.h documentation, it should still run, what would occur is a recalculation of sunrise and sunset for the day every time the loop ran, but it would use the reset loop and correct to midnight for the current time and should thus give you correct rise and set values so the only thing that would really happen, is you would eat compute cycles.

I will look at your code when I have the time.
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Sunrise / sunset code based on location

Post by rufessor »

Sorry... I had posted a whole crap load of BUCK... forgetting how I had done this... and I think we posted simultaneously... so my response above was written to correct (I deleted three dumb posts!) my prior lunacy.

I stand with the above statement and will look at your code as I said! Please remember, I literally taught myself how to code c++/arduino to write that, so although its working for a few people (I have no idea who is using this but can confirm 2 + your experience generally validating it with above issues you documented). Its seemingly innocent (the issue were coding) but put me through some serious troubles. It does however work under the conditions I am running... I will look at yours!

Yeah, the rotation thing, I think *think* I removed that because of the way we account for time, which makes sense - but it is painful given what were doing.
I think I will work on coding in offsets- beware, this was a serious head ache but like I had said in my prior deleted posts, I think the head ache I had was self induced as that was all prior to the version your using and when I didn't understand how time is accounted on arduino.

Its seemingly SO SIMPLE... but its totally mind bending- I still cannot fully conceptualize the whole removing rotation but using local as GMT and then getting the right time... its just stupid, but if it mind bends you- your not alone!!

I am super psyched your working on this for other people who do not have dimming modules... it will be used... also happy to see people like you taking what I tried to do and spending time working it though, its the only way this is ever going to be fully fleshed out. So FAR... I am looking for errors outside of the code I wrote as its hard to believe its not going to be correct for you when its SO stable for me and at least one other person.

I wish we could get an accurate count on who, if anyone besides myself and jneuhausen (wrong spelling) in south africa are using this, it would be very nice to KNOW that its solid and not working only on two systems... but then- if thats indeed the case--- why would it not work on EVERY system?????

I am as confused as you, code will tell... I will post when I get a chance to REALLY go though yours!

OH.. as for the rotation thing... its really super easy to predict what will happen.

Just take hours*3600 + mins*60 + seconds of latitude as your input into the array- divide this by 15 and thats the seconds it will be offset (if you uncomment) so your getting pretty much exactly the correct offsets for the ACTUAL GMT rise and set given the coordinates your using (using my head and glancing at the code). BUT... we use LOCAL as GMT and the it spits back the GMT rise and set... assuming we fed it actual GMT but we did not. So... I think, if you consider that its calculating GMT rise and set and then simply correcting for the earths rotation, and since our system thinks its GMT, we don't need to correct which is why its commented out, but to be honest, its been over a month and I now forget even commenting that out, but I did... cause its not in the original code only in the distribution.

I am still unsure if you think that the times were MORE accurate when you corrected to true GMT.... I have this feeling that it will be more accurate if we do that, but like I said, I bailed out on that part due to misconceptions I had and simply MADE it work using local as GMT direct from the processor.

I still cannot quite conceptualize any error creeping in if were feeding it the true unix time for GMT midnight, it should be identical since when I put in my midnight time from my system it comes back as TRUE GMT midnight on the unix epoch time server, so there should be no difference but for requiring the /15 correction if we use true local time... so it seems like I have it correct and I dont think error should arrise... let me know.

Please PM with Q's you need help with fast.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Sunrise / sunset code based on location

Post by lnevo »

Hehe...no worries. I'm sure we will get it all worked out...it wouldn't be fun if it weren't mind bending :)

I think once I can get a debug loop going it will help male this go faster, it just takes too much time to upload, fiddle with date/time, compare, etc...

Also, I'm going to try and convert to standalone binary without arduino involved and see if I get similar results. If I can see this running at a command line, will def speed things up!
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Sunrise / sunset code based on location

Post by rufessor »

I looked at the code...

One thing thats probably causing you issues currently...

In your setup()

You do not declare
dow=0;

Nor do I see you declaring what dow actually ever is... so I think its running assuming that whatever time its currently at is midnight and never correcting for that... which will cause errors

You need to do this somewhere in the loop

dow=day();
which sets dow to 1-31 as the output of day() in arduino scales from 1-31...

Then... the loops asking if dow=0 which should check for a restart and trigger a correction of current time of day to midnight will work. Else, if you never define dow its going to always be a zero and every calculation will simply use current elapsed Unix Epoch time in CalcSun... but that could be off by 23:59:59 maximally, depending on the time of day. I am unsure what the exact magnitude of the introduced error might be but it would be but the rise and set times will NOT be the same as if you actually had used midnight.

Code seems ok otherwise... so far as I can tell. I also note that you switched to decimal latitude and longitude but enabled the correct conversion in the code so thats looking ok. As for your offset, I think its looking correct as the time library functions hour() and minute() will do what your trying to do... I think. But do you really need the library functions? You could just grab the current time in hours and minutes using time_t=now() as I did... I am not sure if there is a functional difference, but I because I have not called the hour function as you are I cannot say if its working.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Sunrise / sunset code based on location

Post by lnevo »

Im declaring byte dow=0; right after i declare lat/longitude.

Ill take a look at the rest of your feedback soon!

Thanks so much for reviewing.

Lee
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Sunrise / sunset code based on location

Post by rufessor »

Sorry... missed that. I think it looks correct (the dow=day()) command is before you call CalSun() in the loop). So... looks like it should start with a global declaration of dow=0
Then... if its still zero run cal sun and trigger a time correction to midnight.
Then set dow=day() so it does not run again until the day changes...
then dow!=0 so no time correction needed....

unsure why its not working... let us know what happens when you get a chance to look it all the way through, I easily could be missing something important. Tough to read and trouble shoot. Good luck.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Sunrise / sunset code based on location

Post by lnevo »

Thanks. Been dealing with a pinched nerve in my leg the past few days, so all activities have been on pause :(
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Sunrise / sunset code based on location

Post by rufessor »

Ouch! Sorry to hear that, I bet sitting to program would be delightful.

One thing- I am typically up fairly late so I know that I have not experienced the weird lights on late behavior that you saw but the other day I did have one weird thing at about 1 am... one side of the tank had very very dim lighting. It seems to be good now, but I wonder... more now than ever before. The library I grabbed and basically use unmodified is where the calculation results are coming from, and it has always worked when I test it. But- because were on arduino its exceedingly painful to extensively test this. As you have detailed- For me this is the only time I have ever seen lights on when they should not be... and I will be checking more regularly but I would guess I am up after midnight more often than not so its not common. I have not earthly idea whats up, to me the most likely thing is not in the code- per see... but certainly because of the code.. but I wonder if memory is being corrupted. I ran into this REPEATEDLY during my writing the code, but it was *ALL* because of my extensive use of arrays in the weather features and doing stupid stuff like writing to locations past their allocated memory. But, I NEVER looked into the library code so it may be possible that somewhere somehow the memory location for either the input midnight seconds is occasionally getting corrupted from somewhere or equally possibly, the location of the values for rise and set. I just don't see an algorithm working the vast majority of the time and then suddenly outputting garbage... its just not making sense from that perspective as I have seen that even when people feed it times that are DECADES in the future, they get the right values back so the algorithim seems solid.

Your NOT using any of my arrays for lighting effects so its not there either. Unsure how the heck to get at this, but my money is now kinda drifting to the memory side of things... which is like chasing a ghost. I would need help to look at this, its beyond my ability and understanding- very very far beyond. But- if you know anything or have ideas... I would be looking here. The other possibility is that the system time memory is getting corrupted... I think I remember that I did this accidentally once... so if there is a memory leak ANYWHERE on the code loaded- all bets are off as to what might take the hit. But seriously- this sounds like that kinda problem. No way you can write a algorithm that works when tested and then suddenly its giving garbage results, its JUST math. That is a fixed predictable process that I feel works. Memory- well, I did it- its easy to corrupt and its RANDOM what happens- possibly predictably so but this really really sounds like that kinda issue.

Any thoughts from the big guns who know this platform?
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

So, after turning the calculations into a standalone binary, I found the cause of my concerns...

The issue was that by declaring latitude and longitude as global variables... and then keeping this code in CalSun()

Code: Select all

latitude=ddToSeconds(latitude);
longitude=ddToSeconds(longitude);
I was continually re-calculating lat/long and it was completely screwing up after midnight. Reset controller and it would calc properly... only the first time... so those functions have to move out to setup().

Also, now that I had a standalone binary... I played with the following function...
rufessor wrote:

Code: Select all

   //*dt -= longitude / 15.0; // rotate to our own meridian
[/quote]

With that commented, I was able to get the best accuracy with the following offset added..

Code: Select all

 int userRiseOffset=1025;
 int userSetOffset=1058;
 // You could just call it at 1020 (17 minutes) for the average...
Keep in mind this is only ~17 minute offset for each, but one thing is that the time is assuming the local time is set to the lat/long that you are calculating for...

With that code uncommented, I get the rise/set time calculated at -14 hours from the current time which is actually what I should see as the aruduino assumes we are at GMT (and we are currently +4 and GBR, AU is at -10), so total is -14. I still had to add an offset to get the most accurate times, but now it's around ~8 minutes.

Code: Select all

 int userRiseOffset=-(14*3600)+472;
 int userSetOffset=-(14*3600)+506;
 // You could just call it at 480 for the average...
Since I really want the RA to actually offset -9 hours, I will change the 14*3600 to 9*3600 in my final code to get the hours I want. I did not compare any other GPS coordinates to the output of the function to see if the accuracy holds or is different for different parts of the world. I'm sure it wouldn't be. But this is what I came up with... Feel free to experiment.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

OK, This should be the bug free working code.

Code: Select all

//By Matthew Hockin 2012.  
//SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
//Epherma (SWFLTEK.com) was written by Michael Rice- thanks Michael it works great. 
//If you copy from this (its all open source) please 
//acknowledge Michael for SWFLTEK library code (obviously labeled) or Matthew Hockin (for the rest).

#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <ReefAngel.h>

////// Place global variable code below here

//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>
//defines for SWFLTEK functions
#define _sec_rad 206264.806247096370813 // arc-seconds per radian
#define _tilt 0.409092804222329 // axial tilt of earth at epoch, in radians
#define _tropical_year 31556925 // tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _zenith 3.11250383272322 // 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude=-18.285833; // Great Barrier Reef, Australia
long longitude=147.699722; // Great Barrier Reef, Australia
byte dom=0;//day of month
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************

// Additional variables added for Sunrise/Sunset calculation
int SunriseHour, SunriseMin, SunsetHour, SunsetMin;

// Most accurate...(for GBR at least...)
int userRiseOffset=-(14*3600)+472;
int userSetOffset=-(14*3600)+506;

////// Place global variable code above here


void setup()
{
    // Adjust lat/long to seconds here. Only need to do this once!
    latitude=ddToSeconds(latitude);
    longitude=ddToSeconds(longitude);
    
    // This must be the first line
    ReefAngel.Init();  //Initialize controller

    // Ports that are always on
    
    ////// Place additional initialization code below here
       
    ////// Place additional initialization code above here
}

void loop()
{
    ////// Place your custom code below here

    // Set Sunrise and Sunset from MHOCKIN Weather package functions. 
    if (dom!=day()) // used to see that were in a new day and need to recalculate sunrise and sunset
    {
      CalSun();
      dom=day();
      // And let's reset the light schedule
    } 
    
    ////// Place your custom code above here

    // This should always be the last line
    ReefAngel.ShowInterface();
}


//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
long newDay; 
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise

  //Serial.println("CalSun Run Now");
  
  long hours, minutes, seconds;//store current elapsed local hours as total seconds from midnight
  time_t t=now();//store current clock time to parse
  hours=hour(t);
  hours=(hours*3600);//current hour number 0-23 as seconds
  minutes=minute(t);
  minutes=(minutes*60);//minutes of current hour as seconds
  seconds=second(t);
  newDay=now();
  newDay-=(hours+minutes+seconds);//Convert current local unix epoch time to local unix epoch time of midnight
      
  //#define SECS_YR_2000 (946684800) the time at the start of y2k (need to subtract from unix epoch time to bring to Y2K origin
  newDay-=946684800;//convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today
    
  rise=newDay;
  set=newDay;

  //Calculate rise time and set time using Epherma Library functions (see end of code) 
  SunRise(&rise);//call to Epherma function
  SunSet(&set);//Call to Epherma functionunsigned long newDay;
  /*
  Serial.print("rise and set=  ");
  Serial.println(rise);
  Serial.println(set);
  Serial.print("newDay as seconds since 2000 to todays midnight=  ");
  Serial.println(newDay);
  */

  rise+= 946684800;
  set += 946684800;
  newDay+=946684800;
  
  // Convert rise/set to normal hours/minutes before we make it an offset from midnight. 
  // Currently correcting for a 56 minute correction to Sunrise and -19 minute correction to Sunset (8/14/2012@GBR)
  SunriseHour=hour(rise+userRiseOffset);
  SunriseMin=minute(rise+userRiseOffset);
  SunsetHour=hour(set+userSetOffset);
  SunsetMin=minute(set+userSetOffset);
  
}//END SunCalc FUNCTION

//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
//THE CODE BELOW THIS copied directly from the SWFLTEK Epherma library constructed by Michael Rice.  
//this code is being used freely with attribution to Micahel Rice in accord with his request
//  A big thank you for these library functions.  Its great! 

// decimal degrees
long ddToSeconds(float dd){
  return dd * 3600.0;
}

//Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s){
long ret;

  ret = labs((long)d);
  ret = ret * 3600L + 60L * m + s;
  ret = (d<0L) ? -ret : ret;
  return ret;
}
/* ------------------------------------------------------------------------------------------------
   'Equation of Time'
   We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.
   The returned value is in seconds.
*/
int equation_of_time(unsigned long dt){
double t;

  dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
  dt %= _tropical_year;
  t = dt;
  t /= _tropical_year;
  t *= 6.283185307179586;
  t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
  return t;
}

/* -----------------------------------------------------------------------------------------------
   'Solar Declination'
   Returns declination in radians
   Accurate to within 50 arc-seconds
*/
double SolarDeclination(unsigned long dt){
double y;

  dt %= _tropical_year;
  y = dt;
  y /= _tropical_year; // fractional year
  y *= 6.283185307179586;
  y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
  return y;
}

/* ------------------------------------------------------------------------------------------------
   Return the period between sunrise and sunset, in seconds.
   At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt){
float l, d, e;
long n;

  d = -SolarDeclination(dt); // will be positive in Northern winter
  l = latitude / _sec_rad; // latitude in radians

  e += 60.0 * l * tan(l + d); // latitudinal error
  d = tan(l) * tan(d); //

  if(d>1.0) return 86400UL;
  if(d < -1.0) return 0UL;

  d = acos(d);
  d /= _zenith;

  n = 86400UL * d;
  n += e;
  return n;
}


/* ------------------------------------------------------------------------------------------------
   Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
   Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
   returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
   all day.

*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;

  daylen = daylightseconds(*dt);
  if(daylen == 86400UL) return 1;   // there is no 'night' today (midnight sun)
  if(daylen == 0UL) return -1; // there is no 'day' today

  *dt /= 86400UL;
  *dt *= 86400UL;
  *dt += 43200UL; // set the time stamp to 12:00:00 GMT

  *dt -= daylen / 2; //        sunrise at the prime meridian
  if(set) *dt += daylen; //     sunset at the prime meridian

  *dt -= equation_of_time(*dt);

  *dt -= longitude / 15.0; // rotate to our own meridian

  return 0;
}

// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
  return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
  return SunRiseSet(when, 1);
}
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Here's the code I changed in order to compile it as a standalone binary... this is not in real diff format, so you'll have to patch it manually... It is based on the code listed in post http://forum.reefangel.com/viewtopic.php?p=13419#p13419

Code: Select all

> #include <test.h>
> #include <stdio.h>
> #include <unistd.h>
---
< long latitude=-18.285833; // Great Barrier Reef, Australia
< long longitude=147.699722; // Great Barrier Reef, Australia
---
> double latitude=-18.285833; // Great Barrier Reef, Australia
> double longitude=147.699722; // Great Barrier Reef, Australia
---
< int userOffset=-(9*3600); // -9 hours
>
> // Most accurate...
> int userRiseOffset=-(14*3600)+472;
> int userSetOffset=-(14*3600)+506;
>
> int os=0;
--
< Remove setup() and loop() functions
-- // Add main() function
> int main() {
>   latitude=ddToSeconds(latitude);
>   longitude=ddToSeconds(longitude);
>   int i=0;
>
>    // Set Sunrise and Sunset from MHOCKIN Weather package functions.
>    for (i=0;i<=(483+3650);i++) {
>       CalSun();
>       os+=(24*3600);
>    }
> }
--- // The next few changes go into CalSun()
>     newDay+=os;
---
<   SunriseHour=hour(rise+userOffset);
<   SunriseMin=minute(rise+userOffset);
<   SunsetHour=hour(set+userOffset);
<   SunsetMin=minute(set+userOffset);
---
>   SunriseHour=hour(rise+userRiseOffset);
>   SunriseMin=minute(rise+userRiseOffset);
>   SunsetHour=hour(set+userSetOffset);
>   SunsetMin=minute(set+userSetOffset);
>
>   printf("%02d/%02d/%d %d:%02d %d:%02d\n", month(newDay), day(newDay), year(newDay),SunriseHour, SunriseMin, SunsetHour, SunsetMin);
>
>
-- // Cast changes that had to happen to compile...
<   return dd * 3600.0;
>   return (long) (dd * 3600.0);
---
< int equation_of_time(unsigned long dt){
> double equation_of_time(int dt){
---
< double SolarDeclination(unsigned long dt){
> double SolarDeclination(long dt){
---
<   n = 86400UL * d;
<   n += e;
---
>   n = (unsigned long) (86400UL * d);
>   n += (unsigned long) e;
>
---
<   if(set) *dt += daylen; //     sunset at the prime meridian
>   if(set) *dt += (long) daylen; //     sunset at the prime meridian
---
<   *dt -= equation_of_time(*dt);
>   *dt -= (long) equation_of_time((int) *dt);
---
<   *dt -= longitude / 15.0; // rotate to our own meridian
>   *dt -= (long) (longitude / 15.0); // rotate to our own meridian
Here's the header file I had to create...

Code: Select all

void CalSun();
long ddToSeconds(float);
long dmsToSeconds(int, unsigned char, unsigned char);
double equation_of_time(long);
double SolarDeclination(long);
unsigned long daylightseconds(unsigned long);
char SunRiseSet(unsigned long*, char);
char SunRise(unsigned long*);
char SunSet(unsigned long*);
and also for Time.cpp I had to modify the now() function to return the system time...

Code: Select all

> time_t t;
> return time(&t);
The rest of the now() function should be commented out. You'll also have to copy Time.h and Time.cpp to the working folder. Here's the command I used to compile.

Code: Select all

g++ -o test test.cpp Time.cpp -I . 
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Sunrise / sunset code based on location

Post by rufessor »

So great! Thanks for going through all the trouble and verifying the code is stable and outputs correctly. I am still a little confused as to how the global declaration of Latitude and Longitude is screwing up with CalSun- my read of your post is that somehow that was calculating continuously? Was it somehow getting stuck and left as a process or was CalSun never exiting? Cause it should only run on restart or midnight?

Just trying to figure out exactly what you mean.

Very very excellent. This was much needed as there seems to be a LOT of confusion going around this piece of code. I feel a little bit responsible :oops:

Now that we have this bit of data... When I hear back from you I will change anything in the Dimming expansion module that might need changing, re post to that thread and we should have this working on the main as well as on the dimming modules. I need hear back from you before I can fully understand what's up with the lat/lon declarations. But will be testing this on my dimming module and know a few people running this I can ask them to help test as well. But- Congrats most excellent.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

So the issue with latitude and longtitude was a bug that *I* introduced...

In the original CalSun() function...

Code: Select all

      latitude=dmsToSeconds(40,44,00); //United States of America- Salt Lake City, local time is -7 hours GMT 
       longitude=dmsToSeconds(-111,47,00);
Which I modified to use a globally declared latitude and longitude as single variables (instead of hour,min,sec..)

Code: Select all

// In global declaration...
long latitude=-18.2861; // Great Barrier Reef, Australia
long longitude=147.7000; // Great Barrier Reef, Australia

// and then within CalSun() function
latitude=ddToSeconds(latitude);
longitude=ddToSeconds(longitude);
So, the problem was that every time CalSun was executed, it would re-calculate lat/long from Coordinates to seconds. So it would work the first time, but then after midnight, it would re-calc and then it would re-calculate the lat/long and get screwed up...

So, unless you copied my lat/long changes, you probably experienced something else. But running the standalone test over a few years (I ran it to 10 years and the date/times looked good, but I only verified in full 2013...), so the actual calculations look good....
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Sunrise / sunset code based on location

Post by rufessor »

Ok. Thanks

That makes more sense. I will look again at the original code, I think its ok but I dont remember for sure
alexwbush
Posts: 327
Joined: Tue Mar 22, 2011 12:45 am
Location: San Diego, CA

Re: Sunrise / sunset code based on location

Post by alexwbush »

how hard is this to make into a paste and go function? That's what I'm going to try to work on redeveloping.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Sunrise / sunset code based on location

Post by lnevo »

I pasted the minimum code needed already some posts back...theres no way to make it one function but its pretty close. Im no sure if binder is working on turning it into a class still...he's a pretty busy guy :)

But it's pretty straightforward at this point. If you have questions setting it up let me know.
binder
Posts: 2871
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: Sunrise / sunset code based on location

Post by binder »

lnevo wrote:I pasted the minimum code needed already some posts back...theres no way to make it one function but its pretty close. Im no sure if binder is working on turning it into a class still...he's a pretty busy guy :)

But it's pretty straightforward at this point. If you have questions setting it up let me know.
I haven't converted it into a class yet. You are correct...I am a pretty busy guy. I'll see what I can come up with though.
binder
Posts: 2871
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: Sunrise / sunset code based on location

Post by binder »

Ok. I've got it all merged into a class. However, I'm a little confused on some of the specifics of the functions with all the back and forth going on. :)

I have a couple questions that I need answered before I post my code for you guys to test out.
1. How / What would be the "best" way to initialize the location you want to calculate the sunrise / sunset off of? I've seen the Degrees, Minutes, Seconds route and the actual Lat and Long route. I currently have it configured to only use the Lat / Long route. Is this acceptable or do I need both options or only D,M,S?

2. User Sunrise / Sunset offset? I figured it out that it's the hours offset but I'm a little confused as to how to compute the minutes. I don't need an explanation on how it's computed or why (I figured out why it would be and it makes sense). I just don't know how you came up with the corrections. I have a function that allows for setting an offset and I allow for a sunrise offset hour and seconds along with sunset offset hour and seconds. Should that be changed to minutes? If so, what equation should I use to convert it properly?
I'm referring to this line:

Code: Select all

int userRiseOffset=-(14*3600)+472;
I figured out the hours is 14 but I don't know how you computed the minutes to be 472 for a 56 minute offset. Anyways, should we use seconds or minutes? and if minutes what formula to compute it?

3. What values are needed to be used from this class? I'm guessing it's simply Sunrise Hour, Sunrise Minute, Sunset Hour, Sunset Minute. Is this all?

Ok, that's all the questions that I have for now.
The way I have it set to be used is as follows:

Code: Select all

// standard code headers
#include <SunLocation.h>

// Globals
SunLocation sl;

void setup()
{
  ReefAngel.Init();
  // init to different location other than default
  sl.Init(-18.285833,147.699722);  // Lat, Long.  If not initialized, it's set to GBR
  // set offset if needed
  sl.SetOffset(14,472,14,506);  // If not set, defaults to the values listed
}

void loop()
{
  // handles all the additional checks for updating
  sl.CheckAndUpdate();
  // get the values and use them
  ReefAngel.StandardLights( Port1, sl.GetRiseHour(), sl.GetRiseMinute(), sl.GetSetHour(), sl.GetSetMinute());
  ReefAngel.ShowInterface();
}
This was a very crude example of how it could be used. It can be this way if it's not a part of the libraries. If it was included with the libraries, it could be simplified even more than this. In fact, you could have separate functions that handle all the "heavy lifting" and you could just have a simple StandardLightsComputed() function (or whatever) that would use the proper values for the sunrise and sunset for you.

Anyways, I just need some more feedback so I can finalize a few things with it before I post the code. :ugeek:
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Sunrise / sunset code based on location

Post by lnevo »

Here's my take,,

1) I prefer the lat/long single variables as its easier to set the variables...for a class I think it's the easier way to go...I'm not sure why you'd need both, it's easy enough to find the coords you need online in whatever format is required. I say whatever format is easiest to support...

2) the offset aside from the hours was calculated to give me the best margin of error for 2013 based on a year of calculations and comparing to GBR (I think I got the data from timeanddate.com?) it's 472 seconds in that example which is under 10 minutes, not sure where you got 56 minutes? The whole variable is seconds since 2000. So 14*3600 is 14 hours and i added an extra 472 or 506 seconds to get it more accurate. I believe this may be just margin of error in the functions used to calc...not 100% sure...I would leave it out and let user adjust...but I think best adjust would be in either seconds or minutes. Definitely not necessarily hours...for me to maintain the offset I figured out I'd need to maintain seconds...maybe make the offset take hours,min,sec as the args? Overload if too much of a pain?


3) yes...unless anyone can think of any other variables, but I think that's basically all that's calculated, unless you want the seconds...but none of the light functions need that so...


Great job..the use looks great! Will be even better if integrated into the library!

Lee
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Sunrise / sunset code based on location

Post by lnevo »

Just an Addon to #2...I'm subtracting 14 hours...so the example you had in I it would need to be -14 and again the 472 and 506 were seconds added to improve the accuracy...this took compiling a separate binary and comparing to data on the web...don't know if this would be standard or dependent on gps coords or not...Ymmv.. In the end, I think either just make the offset in seconds, or h,m,s to make it easier to calculate. Not sure the easiest for others. Would love some other input from ppl.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Sunrise / sunset code based on location

Post by lnevo »

And as far as StandardLightsComputed() type of function...you'd still want a way to modify the times like of you want to turn blues on the. Whites the. Whites off then blues off which is what I'm doing now...just saying...

Awesome job though! Can't wait to integrate it this way and simplify my RA code :)
alexwbush
Posts: 327
Joined: Tue Mar 22, 2011 12:45 am
Location: San Diego, CA

Re: Sunrise / sunset code based on location

Post by alexwbush »

well this new function will make things easier I suppose. How will the new library function be used in main ino code?
binder
Posts: 2871
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: Sunrise / sunset code based on location

Post by binder »

alexwbush wrote:well this new function will make things easier I suppose. How will the new library function be used in main ino code?
There's not a new library function right now. You would use it like I posted previously.
If it were included into the libraries, you would need to enable support for it. Then we would probably have several functions to reference it.

lnevo -
I'm going to keep it be as Lat and Long initializing. It will be simpler having 2 variables vs 6 (d,m,s to compute the lat & long).

I got the 56 minutes from comment in the file next to the offset: (I changed the offset variable names).

Code: Select all

	// Currently correcting for a 56 minute correction to Sunrise and -19 minute correction to Sunset (8/14/2012@GBR)
	m_UserRiseOffset = -(14*3600)+472;
	m_userSetOffset = -(14*3600)+506;
I was going to have the offset be in hours and seconds based on how your example was. This part may need to be adapted later...so we shall see. I'm going to default the offset to be 0 instead of what you were using.

I'm also attaching my code to this post.
Here's how you use it:
  • Download SunLocation.zip file.
  • Extract this file to Documents/Aruduino/libraries. This will create a new folder inside the libraries folder called SunLocation
  • Now the library is ready to use, see below for sample code
Here's a sample INO file on how to use the values. I just have the display showing the sunrise and sunset that is computed:

Code: Select all


#include <Salinity.h>
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <ReefAngel.h>
#include <SunLocation.h>

SunLocation sl;

void DrawCustomMain()
{
  char buf[16];
  ReefAngel.LCD.Clear(DefaultBGColor,0,0,132,132);
  sprintf(buf, "%02d:%02d", sl.GetRiseHour(), sl.GetRiseMinute());
  ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,10,10,"Sunrise: ");
  ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,58,10,buf);
  sprintf(buf, "%02d:%02d", sl.GetSetHour(), sl.GetSetHour());
  ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,10,30,"Sunset:  ");
  ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,58,30,buf);
}

void DrawCustomGraph()
{
}

void setup()
{
  ReefAngel.Init();
  // initialize the sunrise / sunset location
  // Latitude , Longitude values set
  sl.Init(-18.285833, 147.699722);
}

void loop()
{
  // handle updating sunrise and sunset values
  sl.CheckAndUpdate();
  ReefAngel.ShowInterface();
}
Please test and let me know what you think. :geek:
Attachments
SunLocation.zip
Sunrise and Sunset Location library
(3.6 KiB) Downloaded 394 times
Post Reply