Sunrise / sunset code based on location

Do you have a question on how to do something.
Ask in here.

Posts: 12
Joined: Mon Jul 30, 2012 3:53 am
PostPosted: Sun Aug 12, 2012 3:37 am
Hi guys,

I was just wondering if there is a possibility to code the sunrise/ sunset lights based on location and time?
Please see an example of a website.
http://suncalc.net/#/-37.8136,144.9631,13/2012.08.12/20:34
It will be so cool if it's possible. :lol:

thanks,
mliew
User avatar
Posts: 5362
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Sun Aug 12, 2012 5:22 am
I am actually working on this. I'm looking to simplify one of these two code bases:

viewtopic.php?f=11&t=535
or
viewtopic.php?f=11&t=1450

The second one is the one I'll be most likely using as it seems to be based on latitude/longitude, but the first one is much is much simpler and simply sets the high/low during the seasons and adjusts each day accordingly..

Would love some help if you're feeling ambitious to strip the code down :)

Posts: 12
Joined: Mon Jul 30, 2012 3:53 am
PostPosted: Sun Aug 12, 2012 12:39 pm
wow. great inevo. Please update and let me know your progress. I am a newbie with coding unfortunately and I am still in the process of building my setup at this stage. I will be looking to order the RA+ kit soon. But I will be studying the coding process as soon my setup is ready and when i order the RA kit.
User avatar
Posts: 5362
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Mon Aug 13, 2012 8:17 am
I posted my code in another thread, but the stripped down calculation code is in there. I don't know if it's working right and could use some help with someone more familiar with debug on this device....

So, bump if you can help on this.. I've got my "portable" setup working now, so hopefully should be able to do more testing soon..

viewtopic.php?p=12881#p12881
User avatar
Posts: 5362
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Mon Aug 13, 2012 9:49 pm
Ok,the code is here and it seems to be working. Except for the fact that the time appears to be off by 1:28 minutes too early for both rise and set. when set to Salt Lake City as the code was written. And when I changed it to the Great Barrier Reef it was off by +56 minutes for sunrise and -19 minutes for sunset. I've hard coded some offsets into the calculation and added a user offset to match my schedule. So theoretically my lights will now on/off and have the same daytime/nightime as the GBR. :)

I'd like rufessor to take a look at the code that I've scavanged and see if I'm missing something critical... Anyway here's the 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, longitude;
long elapsedTime;//used multiple places as elapsed since midnight
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
byte dow=0;//day of week
//END HEADER FOR MHOCKIN Weather package //

// Additional Code added for Sunrise/Sunsets
int SunriseHour, SunriseMin, SunsetHour, SunsetMin;
int UserOffset=5*3600;

#define NUMBERS_8x16
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8

byte vtechmode;
byte vtmode = 0;
boolean bFeeding=false;
boolean isNight=false;
double MoonAge(int,int,int);
int JulianDate(int,int,int);
byte MoonState();
byte ThisPhase;
byte DayAge;

#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";
// Group the menu entries together
PROGMEM const char *menu_items[] = {
menu1_label, menu2_label, menu3_label,
menu4_label, menu5_label, menu6_label,
menu7_label, menu8_label
};

//*********************************************************************************************************************************
//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define Vortech            5
#define Heater             6
#define Refugium           7
#define UPS                8
//*********************************************************************************************************************************

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

void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}

void MenuEntry3()
{
   if(ReefAngel.RF.UseMemory) {
     vtmode++;
     if ( vtmode == 7 ) vtmode = 9;
     if ( vtmode > 9 ) vtmode = 0;
     InternalMemory.RFMode_write(vtmode);
     vtechmode = vtmode;
   }
   ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry5()
{
ReefAngel.OverheatClear();
ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
ReefAngel.DisplayVersion();
}

void DrawCustomMain()
{
  char text[7];
  byte x = 5;
  byte y = 2;
  byte t;

  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);
 
  // Param Header
  y=y+20;
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y=y+10;
  // ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(128-x),y+16);
  ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, text, Num8x16);
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, text, Num8x16);
  pingSerial();
 
  y=y+20;
 
  /// Display Sunrise / Sunset (to be calculated later...)
  char temp[]="  ";
  int xTemp=x;
 
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,xTemp,y,"Rise:"); xTemp+=31;
  itoa(SunriseHour,temp,10);
  if (SunriseHour<10) {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,"0"); xTemp+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,temp); xTemp+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,temp); xTemp+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,":"); xTemp+=6;
  if (SunriseMin<10) {
      ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,"0"); xTemp+=6;
  }
  itoa(SunriseMin,temp,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,temp); xTemp+=2;
 
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,xTemp,y,"Set:"); xTemp+=24;
  itoa(SunsetHour,temp,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,temp); xTemp+=10;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,":"); xTemp+=6;
  if (SunsetMin<10) ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,"0"); xTemp+=6;
  itoa(SunsetMin,temp,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,xTemp,y,temp);

  // MoonPhase
  y=y+10;
  DayAge = MoonAge(day(), month(), year());
  MoonState(DayAge);
  char* ThisPhaseLabel[]={ "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent" };
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,ThisPhaseLabel[ThisPhase]);
  pingSerial();
 
  // MoonLight %
  y=y+10; 
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Vortech Mode
  y=y+10;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+20,y,x+(128-x),y+8);
  if (vtechmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN,255,x+20,y,"Constant");
  else if(vtechmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,x+20,y,"Lagoon");
  else if (vtechmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,x+20,y,"Reef Crest");
  else if (vtechmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,x+20,y,"Short Pulse");
  else if (vtechmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,x+20,y,"Long Pulse");
  else if (vtechmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,x+20,y,"Smart NTM");
  else if (vtechmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,x+20,y,"Tidal Swell");
  else if (vtechmode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,x+20,y,"Night");
  pingSerial();
 
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
 
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
//  ReefAngel.LCD. DrawGraph(5, 15);
}


void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = 0;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
   
    ////// Place additional initialization code below here
    InternalMemory.RFMode_write(2);
    InternalMemory.RFSpeed_write(50);
    InternalMemory.RFDuration_write(10);
   
    ReefAngel.RF.UseMemory=true;
    vtechmode = InternalMemory.RFMode_read();
    ////// Place additional initialization code above here
}

void loop()
{
    // Set Sunrise and Sunset from MHOCKIN Weather package functions.
    elapsedTime=(now()-newDay);  //Elapsed time is seconds from midnight of today- local processor time.
    if (dow!=day()){ //used to see that were in a new day and need to recalculate sunrise and sunset
      CalSun();
      dow=day();
    }

    ReefAngel.Relay.DelayedOn( Skimmer,5 );
    ReefAngel.StandardLights( WhiteLEDs,12,0,20,0 );
    ReefAngel.StandardLights( BlueLEDs,11,0,23,0 );
    ReefAngel.StandardHeater( Heater,788,792 );
   
    ////// Place your custom code below here
   
    // if (( hour() <= 11 ) || ( hour() >= 23 )) isNight=true; else isNight=false;
    int currTime = hour()*100+minute();
    int riseTime=SunriseHour*100+SunriseMin;
    int setTime=SunsetHour*100+SunsetMin;
   
    // if (( hour() <=11 ) || ( hour() >= 23 )) isNight=true; else isNight=false;
    if (( currTime <= riseTime ) || ( currTime >= setTime )) isNight=true; else isNight=false;
   
    if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
    {
      bFeeding=false;
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Smart_NTM,100,5);
      ReefAngel.Timer[4].SetInterval(600); // Timer for 10min
      ReefAngel.Timer[4].Start();
      vtechmode = 5;
    }
   
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered())
    {
      if (isNight)
      {
        ReefAngel.RF.UseMemory=false;
        ReefAngel.RF.SetMode(Night,15,0);
        vtechmode = 9;
      } else {
        ReefAngel.RF.UseMemory=true;
        vtechmode = InternalMemory.RFMode_read();
      }
    }
   
    if (isNight)
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
    } else {
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
      ReefAngel.Relay.Off( Refugium );
    }   
   
    ////// Place your custom code above here

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

int JulianDate(int d, int m, int y)
{
  int mm, yy;
  int k1, k2, k3;
  int j;

  yy = y - (int)((12 - m) / 10);
  mm = m + 9;
  if (mm >= 12)
  {
    mm = mm - 12;
  }
  k1 = (int)(365.25 * (yy + 4712));
  k2 = (int)(30.6001 * mm + 0.5);
  k3 = (int)((int)((yy / 100) + 49) * 0.75) - 38;
  // 'j' for dates in Julian calendar:
  j = k1 + k2 + d + 59;
  if (j > 2299160)
  {
    // For Gregorian calendar:
    j = j - k3; // 'j' is the Julian date at 12h UT (Universal Time)
  }
  return j;
}

double MoonAge(int d, int m, int y)
{
  int j = JulianDate(d, m, y);
  //Calculate the approximate phase of the moon
  int ip = (j + 4.867) / 29.53059;
  ip = ip - abs(ip);
  //After several trials I've seen to add the following lines,
  //which gave the result was not bad
  if(ip < 0.5)
    int ag = ip * 29.53059 + 29.53059 / 2;
  else
    int ag = ip * 29.53059 - 29.53059 / 2;
  // Moon's age in days
  byte ag = abs(ag) + 1;
  return ag;
}

byte MoonState(byte D)
{
  switch(D){
  case 1:
    0, 29;
    ThisPhase = 0;
  case 2:
    1, 2, 3, 4, 5, 6;
    ThisPhase = 1;
  case 3:
    7;
    ThisPhase = 2;
  case 4:
    8, 9, 10, 11, 12, 13;
    ThisPhase = 3;
  case 5:
    14;
    ThisPhase = 4;
  case 6:
    15, 16, 17, 18, 19, 20, 21;
    ThisPhase = 5;
  case 7:
    22;
    ThisPhase = 6;
  case 8:
    23, 24, 25, 26, 27, 28;
    ThisPhase = 7;
  default:
    return 0;
  }
}

//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
   //Serial.println("CalSun Run Now");

       // latitude=dmsToSeconds(40,44,00);//United States of America- Salt Lake City, local time is -7 hours GMT
       // longitude=dmsToSeconds(-111,47,00);
       latitude=dmsToSeconds(18,34,4);// Great Barrier Reef, Australia
       longitude=dmsToSeconds(148,33,19); // Great Barrier Reef, Australia
       //**********************ok now were done changing things IF YOU CHANGED the Top part of the GLOBAL variable decleration AND this... your FULLY configured and ready to load********************************************
   
    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);*/
   
   SunriseHour=hour(rise+(56*60)+UserOffset);
   SunriseMin=minute(rise+(56*60)+UserOffset);
   SunsetHour=hour(set-(19*60)+UserOffset);
   SunsetMin=minute(set-(19*60)+UserOffset);
   
   rise=(rise-newDay);// set to elapsed seconds of day
   set=(set-newDay);
   /*Serial.print("rise and set as elapsed seconds of day=  ");
   Serial.println(rise);
   Serial.println(set);*/
   newDay+=946684800;//Convert newDay back to unix epoch GMT midnight today (used in loop to determine how far we are into the day)
   /*Serial.print("newDay as seconds since since 1970 to todays midnight=  ");
   Serial.println(newDay);
   Serial.print("elapsed is");
   long elapsed=now()-newDay;
   Serial.println(elapsed);*/
   
 }//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);
}

Posts: 293
Joined: Tue Oct 25, 2011 7:39 am
PostPosted: Tue Aug 14, 2012 7:23 pm
Hi-

A couple comments... I have only briefly looked at your code mostly looking for pieces I recognize and its looking like you have the basics from my code in and they look pretty much ok. In terms of the offsets you added I need a few minutes to look so I have no comment just now.

But- with respect to rise and set times, being off by less than 2 minutes is basically incredibly good accuracy for this algorithim. So that pretty much confirms you have the stuff I implemented (some of which I took from Michael Rice) working. As for why the great barrier reef is giving you about 1 hour error... I will be looking at your code to be sure, but it could either be lat lon error or possibly just a 1 hour error in your time calculation (did not see it yet) which is affecting rise but not set and the error in the calculations is accounting for the rest. Will look more and post back. You could just run some serial debugging and see if you can test some other locations on the globe....
User avatar
Posts: 5362
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Tue Aug 14, 2012 7:50 pm
Just to clarify the offsets were +1:28 in SLC and +56/-19 at GBR. This was hours/minutes not minutes/seconds..

Thanks for taking a look!!!

I haven't learned how to do serial debugging yet :)
User avatar
Posts: 5362
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Tue Aug 14, 2012 9:50 pm
Ok, I looked again, and my coordinates were off. I fixed that and my time looks much better however still 20 minutes or so off. It actually fits my viewing schedule a bit better so I'm going to leave it. I changed the time to a few dates within the calendar and it does seem to be following the light cycle but the variation of offset does vary. I guess it's as close as we're going to get.

For those that want to do similar here is my final Sunrise/Sunset code changes. Thanks to rufessor for the pre-work that was involved. I could not have done it without his efforts! Thanks!

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
#define NUMBERS_8x16
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8

//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, longitude;
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
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=5*3600; // 5 hours

byte vtechmode=Random2;
byte vtechDefault=Random2;
int vtechSpeed=50;
int vtechDuration=10;
boolean bFeeding=false;
boolean isNight=false;

double MoonAge(int,int,int);
int JulianDate(int,int,int);
byte MoonState();
byte ThisPhase;
byte DayAge;

#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";
// Group the menu entries together
PROGMEM const char *menu_items[] = {
menu1_label, menu2_label, menu3_label,
menu4_label, menu5_label, menu6_label,
menu7_label, menu8_label
};

//*********************************************************************************************************************************
//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define Vortech            5
#define Heater             6
#define Refugium           7
#define UPS                8
//*********************************************************************************************************************************

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

void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}

void MenuEntry3()
{
  byte vtmode=vtechmode;
 
  vtmode++;
  if ( vtmode == 7 ) vtmode = 9;
  if ( vtmode > 9 ) vtmode = 0;
  vtechmode=vtmode;
 
  ReefAngel.RF.UseMemory=false;
  ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
  ReefAngel.RF.SetMode(vtmode,ReefAngel.RF.Speed,ReefAngel.RF.Duration);
  ReefAngel.Timer[4].SetInterval(10); // Timer for 30min
  ReefAngel.Timer[4].Start();
 
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry5()
{
  ReefAngel.OverheatClear();
  ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
  CalSun();
}
void MenuEntry8()
{
  ReefAngel.DisplayVersion();
}

void DrawCustomMain()
{
  char text[7];
  byte x = 5;
  byte y = 2;
  byte t;

  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);

  // Param Header
  y=y+20;
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y=y+10;
  ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, text, Num8x16);
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, text, Num8x16);
  pingSerial();
 
  /// Display Sunrise / Sunset (to be calculated later...)
  y=y+20; t=x;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  itoa(SunriseHour,text,10);
  if (SunriseHour<10) {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunriseMin<10) {
      ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
  }
  itoa(SunriseMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=18;
 
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
  itoa(SunsetHour,text,10);
  if (SunsetHour<10) {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunsetMin<10) { ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6; }
  itoa(SunsetMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text);

  // MoonPhase
  y=y+10;
  DayAge = MoonAge(day(), month(), year());
  MoonState(DayAge);
  char* ThisPhaseLabel[]={ "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent" };
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,ThisPhaseLabel[ThisPhase]);
  pingSerial();
 
  // MoonLight %
  y=y+10; 
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Vortech Mode
  y=y+10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtechmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN,255,t,y,"Constant");
  else if(vtechmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtechmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtechmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtechmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,t,y,"Long Pulse");
  else if (vtechmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtechmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtechmode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  pingSerial();
 
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
 
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
}


void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = 0;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
   
    ////// Place additional initialization code below here
   
    // Reset Vortech to preferred defaults
    InternalMemory.RFMode_write(vtechDefault);
    InternalMemory.RFSpeed_write(vtechSpeed);
    InternalMemory.RFDuration_write(vtechDuration);
    ReefAngel.RF.UseMemory=true;
   
    ////// Place additional initialization code above here
}

void loop()
{
    ReefAngel.Relay.DelayedOn( Skimmer,5 );
    ReefAngel.StandardHeater( Heater,788,792 );
   
    ////// 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
    }

    ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3,SunsetMin );
    ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );
   
    int currTime = hour()*100+minute();
    int riseTime=SunriseHour*100+SunriseMin;
    int setTime=SunsetHour*100+SunsetMin;
   
    if (( currTime <= riseTime ) || ( currTime >= setTime )) isNight=true; else isNight=false;
   
    if (isNight)
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
     
      if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode!=Night) {
        ReefAngel.RF.SetMode(Night,15,0);
      }
    } else {
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
      ReefAngel.Relay.Off( Refugium );

      // It's Daytime!
      if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode==Night) {
          ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
          ReefAngel.RF.SetMode(vtechDefault,15,0);
      }
    }   

    if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
    {
      bFeeding=false;
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Smart_NTM,100,5);
      ReefAngel.Timer[4].SetInterval(1800); // Timer for 30min
      ReefAngel.Timer[4].Start();
      vtechmode = Smart_NTM;
    }
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered())
    {
      ReefAngel.RF.UseMemory=true;
    }

    if (ReefAngel.RF.UseMemory) {
      vtechmode=ReefAngel.RF.Mode;
    }
   
    ////// Place your custom code above here

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

int JulianDate(int d, int m, int y)
{
  int mm, yy;
  int k1, k2, k3;
  int j;

  yy = y - (int)((12 - m) / 10);
  mm = m + 9;
  if (mm >= 12)
  {
    mm = mm - 12;
  }
  k1 = (int)(365.25 * (yy + 4712));
  k2 = (int)(30.6001 * mm + 0.5);
  k3 = (int)((int)((yy / 100) + 49) * 0.75) - 38;
  // 'j' for dates in Julian calendar:
  j = k1 + k2 + d + 59;
  if (j > 2299160)
  {
    // For Gregorian calendar:
    j = j - k3; // 'j' is the Julian date at 12h UT (Universal Time)
  }
  return j;
}

double MoonAge(int d, int m, int y)
{
  int j = JulianDate(d, m, y);
  //Calculate the approximate phase of the moon
  int ip = (j + 4.867) / 29.53059;
  ip = ip - abs(ip);
  //After several trials I've seen to add the following lines,
  //which gave the result was not bad
  if(ip < 0.5)
    int ag = ip * 29.53059 + 29.53059 / 2;
  else
    int ag = ip * 29.53059 - 29.53059 / 2;
  // Moon's age in days
  byte ag = abs(ag) + 1;
  return ag;
}

byte MoonState(byte D)
{
  switch(D){
  case 1:
    0, 29;
    ThisPhase = 0;
  case 2:
    1, 2, 3, 4, 5, 6;
    ThisPhase = 1;
  case 3:
    7;
    ThisPhase = 2;
  case 4:
    8, 9, 10, 11, 12, 13;
    ThisPhase = 3;
  case 5:
    14;
    ThisPhase = 4;
  case 6:
    15, 16, 17, 18, 19, 20, 21;
    ThisPhase = 5;
  case 7:
    22;
    ThisPhase = 6;
  case 8:
    23, 24, 25, 26, 27, 28;
    ThisPhase = 7;
  default:
    return 0;
  }
}

//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
   //Serial.println("CalSun Run Now");

       // latitude=dmsToSeconds(40,44,00);//United States of America- Salt Lake City, local time is -7 hours GMT
       // longitude=dmsToSeconds(-111,47,00);
       latitude=dmsToSeconds(-18,17,9);// Great Barrier Reef, Australia
       longitude=dmsToSeconds(147,41,59); // Great Barrier Reef, Australia
       //**********************ok now were done changing things IF YOU CHANGED the Top part of the GLOBAL variable decleration AND this... your FULLY configured and ready to load********************************************
   
    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;
 
   // 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);

   /*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=(rise-newDay);// set to elapsed seconds of day
   set=(set-newDay);
   /*Serial.print("rise and set as elapsed seconds of day=  ");
   Serial.println(rise);
   Serial.println(set);*/
   newDay+=946684800;//Convert newDay back to unix epoch GMT midnight today (used in loop to determine how far we are into the day)
   /*Serial.print("newDay as seconds since since 1970 to todays midnight=  ");
   Serial.println(newDay);
   Serial.print("elapsed is");
   long elapsed=now()-newDay;
   Serial.println(elapsed);*/
   
}//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);
}

Posts: 12327
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Tue Aug 14, 2012 10:08 pm
So now, how can we get that all into a single function?? :)
Roberto.
User avatar
Posts: 5362
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Wed Aug 15, 2012 4:24 am
I don't think it can be one function, but the only external function you need in your code is CalSun().

You could rearrange some code so that the variables that I'm using is just a time_t that you pass to CalSun() and drop all the required functions/defines into an #include then you'd just need to config your latitude and longitude...

You still need to make sure to call CalSun() once a day and handle your own offset, unless you add that to the function as well.

Shouldn't be too tough. Let me see what i can do later..

Edit: scratch some of that..we meed two times... Let me think about it..
Next

Return to How do I code ...

Who is online

Users browsing this forum: No registered users and 3 guests

cron