C library for Sun/Moon effects

Do you have a question on how to do something.
Ask in here.
Post Reply
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

C library for Sun/Moon effects

Post by rufessor »

Hi All-
So I can see that many people are becoming interested in the calculation of sunrise/sunset and moon phase for automating lighting in our systems. In particular I think with respect to PWM control over LED dimming. I have adopted a very nice piece of code and loaded it onto my PWM expansion controller and its working great, but the existing code (Deckoz2302 Progressive Sunphase thread) is locked into a particular lat/lon coordinate. I have no earthly idea why I care, but I was really curious about enabling the simple input of Lat/Lon coords and getting back correct sunrise/sunset/moon phase for that coordinate set. In the end you probably want to select your own longitude and then the latitude of the area of the world you want to emulate (i.e. great barrier reef etc etc).

So, I did a bunch of reading and working in Excel and figuring out the math (not overly complex but not trivial) when I found that Michael Rice of www.swfltek.com had basically allready done the entire thing, optimized the calculations to run on Arduino processors (floating point can be an issue) and posted the entire thing as a third generation of working code. So I have been corresponding with him and its available and he asks that this be posted regarding the source code... so here goes his adviso... we can do anything we want with it no charge... but if were going to use it we should acknowledge his work as its significant to get something like this working well and on arduino.

The code provided on this web site is provided in a free and open manner, and may be used in open or proprietary applications
under the terms of the license below.



© 2010 Michael Rice, SWFLTEK

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.

* Neither the name of the copyright holders nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.


Here are the .h and .cpp files I generated from his code.

you can call it whatever you want but just remember to reference the correct file name and move the files into the library location in your set up.

.cpp file

Code: Select all

//==================================================================================================

#include <stdlib.h>
#include <math.h>

// arc-seconds per radian
#define _sec_rad 206264.806247096370813

// axial tilt of earth at epoch, in radians
#define _tilt 0.409092804222329

// tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _tropical_year 31556925

// 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)
#define _zenith 3.11250383272322

//#include "SWFLTEK_Ephemera.h"
/*------------------------------------------------------------------------------------------------
convert degrees to seconds of arc
*/

// 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 for' 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 Noon' adjusts the passed time stamp to the time (GMT) of local solar noon.
	The accuracy is about 40 seconds (set by the equation of time).
*/
void SolarNoon(unsigned long * dt){
long r;

	// Set stamp to noon GMT
	*dt /= 86400UL;
	*dt *= 86400UL;
	*dt += 43200UL;

	// adjust for equation of time, at noon GMT
	*dt -= equation_of_time(*dt);

	// rotate to our longitude
	r = longitude / 15L;
	*dt -= r;
}

/* -----------------------------------------------------------------------------------------------
	'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);
}

/* ------------------------------------------------------------------------------------------------
	Mean Sidereal Time.
	Accurate to within 1 sidereal second.
*/
unsigned long GMSidereal(unsigned long gmt){
unsigned long long sidereal;

	sidereal = gmt * 10027379094LL; // multiply by 10 Billion times the ratio
	sidereal /= 10000000000LL; // and divide by 10 billion
	sidereal += 23992LL; // add sidereal time at the epoch
	return sidereal;
}

unsigned long LMSidereal(unsigned long gmt){
	return  GMSidereal(gmt ) + longitude / 15L;
}

/* ------------------------------------------------------------------------------------------------
 	An approximation of the moons phase.
	Magnitude of the result approximates the percentage of the moons illuminated surface.
	Sign of the result indicates a Waxing (+) or Waning (-) moon.
	It uses the mean lunar cycle, which may differ from the actual by many hours.
	As such, it may vary from the correct value by 20%.
*/

char MoonPhase(unsigned long d){
long r;

	// the first full moon of the epoch was Jan 21 at 04:40
	// but the 'mean' full moon was 4 hours later
	d -= 1759365UL; // subtract time of first full moon
	d %= 2551443L; // mod by the mean lunar cycle
	r = d - 1275721L;
	r /= 12757L;
	return r;
}

// Season of the year
// 0 = winter, 1 = spring, 2 = summer, 3 = fall
unsigned char season(unsigned long dt){

	dt += 838800UL;// refer to prior winter solstice
	dt %= _tropical_year;

	dt /= 7889400UL; // 91.3125 days

	if(latitude<0) dt += 2UL;
	return dt % 4;
}
.h file

Code: Select all

/*
	SWFLTEK_Ephemera copyright 2010 by Michael Rice

	Swfltek Ephemera is a set of astronomical functions often of interest.
	Though written for avr-gcc, it should convert easily to other compilers.

	Most functions require an unsigned long time stamp as an argument. This 32 bit value represents
	the number of seconds elapsed since midnight Jan 1 2000 GMT.
*/
#ifndef SWFLTEK_EPHEMERA_h
#define SWFLTEK_EPHEMERA_h



// geographic coordinates, in seconds of arc North and East
long latitude, longitude;

// conveniences to convert two typical representations into seconds of arc
long ddToSeconds(float);
long dmsToSeconds(int, unsigned char, unsigned char);

// return equation of time in seconds
int equation_of_time(unsigned long);

// adjust stamp to Solar noon
void SolarNoon(unsigned long * );

// return solar declination in radians
double SolarDeclination(unsigned long dt);

// return seconds between sunrise and sunset
unsigned long daylightseconds(unsigned long);

// compute time of sun rise or sunset
char SunRiseSet(unsigned long*, char);

// shorthand form
char SunRise(unsigned long*);
char SunSet(unsigned long*);

// convert from GMT to Mean Sidereal Time
unsigned long GMSidereal(unsigned long);
unsigned long LMSidereal(unsigned long);

// approximate phase of the moon
char MoonPhase(unsigned long);

// season
unsigned char season(unsigned long);


#endif // SWFLTEK_EPHEMERA_h
In corresponding with Michael I have the following useful information which I will just simply cut and paste below for those that want to try this out. I have not yet finished and so cannot tell you if it works for us or not but I strongly suspect that it would.

The 'set' parameter is required for the 'worker' SunRiseSet() function. The 'helper' functions SunRise() and SunSet will pass the correct value to the worker function.
Since your code is using the helper functions, you need not worry about it.

I have noted several things I must document more clearly however.

The Ephemera Library is straight 'C', not a C++ class. All the functions are global, so there is no need to instantiate any SWFLTEK_EPHEMERA object. Just #include the library and call the functions.

The timestamp is an unsigned 32 bit value, using an 'int' will not work.

Coordinates are in seconds, requiring a 'long' to represent them. For your application you will want to set the latitude to the same latitude as the reef you are simulating. You can also set the longitude same as the reef, but you'll probably want to set it to your local longitude to minimize disturbances from natural light and human activities.

The Ephemera functions use Greenwich Mean Time. Passing the local time will return a value which may have twice the 'daily' error, which probably isn't significant in your application but you might want to consider it.

The sun functions arguments are passed by reference. The returned value indicates if the call was successful. Since the reef in question is (probably ?) not inside either polar circle, you can ignore the returned value in your application.

Here is a framework for what you want to do...

============================================================================

#include SWFLTEK_EPHEMERA_h

unsigned long sunrise, sunset; // 32 bit unsigned integer representing seconds since year 2000

char moonphase; // 8 bit signed value, -100 ... 0 ... 100 %, sign indicates waning or waxing

void setup(){
// we are mimicking a coral reef at the Bahama Islands but 'rotated' to our longitude
latitude = dmsToSeconds(22,0,0);
longitude = dmsToSeconds(20,0,0);
...
...
}

void CalSun{
unsigned long SecInput;

// compute the current timestamp as SecInput
...
...

// copy to sunrise and sunset
sunrise = sunset = SecInput;

// compute actual times
// we know we are not in the Arctic or Antarctic, so we can ignore the returned value
SunRise( & sunrise);
SunSet( & sunset);

// compute moon phase
moonphase = MoonPhase(SecInput);


}

============================================================================

Hope this helps.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

I have mostly finished a routine to cal the number of seconds from 2000 to use as input but its a tiny bit painful with leap year etc, I am thinking someone has this up... anyone? I would like to use one that works... mine is really my first serious attempt at writing out a c++ code block and although I suspect it might work... it may be awfull... for reference (and its not complete) I was going along this line..

Code: Select all

void CalSun(){
   // Every day at midnight, we calculate sunrise, sunset, time of highest sun, moon phase.
  if (hour()==0 && minute()==0 && second()==0) 
  {
      int YearsSince=year()-2001;
      int DaysPerYear, DaysTotal;
        for (int a=0;YearsSince;a++){
          if (year()%4 == 0 && !(year()%100 == 0 && year()%400 != 0)) {
           DaysPerYear=366;
           DaysTotal+=DaysPerYear;
            }
        else {
            DaysPerYear = 365;
            DaysTotal+=DaysPerYear;
              }
        }
    Int CurrentYearDays=day();
      for (int b=0;month()-1;B++){
        if b=1 CurrentYearDays+=31;
        if b=2 {
          if (year()%4 == 0 && !(year()%100 == 0 && year()%400 != 0))CurrentYearDays+=29;
          else CurrentYearDays+=28;
        }
        if b=3 CurrentYearDays+= // is there a better way else... and so on and on to november, december is taken care of by the month()-1 and using CUrrentYearDays=day() at the start
    long unsigned int SecInput=DaysTotal*86400;
I am pretty sure I will embarass myself and someone who codes will tell me just to get #days in current year using a simple command or else a similar workaround to adding up all the days in the current years calender up to today while accounting for leap years. Oh... Its also probably got terrible structure, might be wrong i.e. I didn't look up if its += or =+ while writing this, still trying to get all the short cuts under my fingers...
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

Seems to be really nice work :shock:
I tried to get something similar and got stuck in too many floating point calculations, but this seems to be lightweight enough for us to use.
Roberto.
binder
Posts: 2865
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: C library for Sun/Moon effects

Post by binder »

rimai wrote:Seems to be really nice work :shock:
I tried to get something similar and got stuck in too many floating point calculations, but this seems to be lightweight enough for us to use.
Yeah, he's been in contact with me about it (via PM). I was going to look to add it to the libraries in the future once we got things "calmed" down with the upgrade process to the new version of the libraries.
binder
Posts: 2865
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: C library for Sun/Moon effects

Post by binder »

rimai wrote:Seems to be really nice work :shock:
I tried to get something similar and got stuck in too many floating point calculations, but this seems to be lightweight enough for us to use.
Yeah, he's been in contact with me about it (via PM). I was going to look to add it to the libraries in the future once we got things "calmed" down with the upgrade process to the new version of the libraries.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Its easy to make your own library for now, so no rush...
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Ok... So I have been working on a bit of code to use this library. I am "done" with figuring out time, DST, fixing Unix time to the library requirement of Jan 2000 as 0 seconds, passing corrected DST/TimeZone time to GMT and then passed that time to the library Epherma.

What I am not sure I know, and I could be doing a LOT wrong above... is what exactly the Epherma library spits back, looking into it now. It may be documented as this is just where I stopped... it may not. But anyhow, this is by far the most complex bit of code I have tried in C++ and I wrote a lot of it.

I found the idea for the DST correction on line, but charted it for myself and used a different numerical value than they had.... because I think I am right, and it was a different language and it all depends on which day is #1 in the language it was written in originally which I don't know, I am not even sure I have Arduino right as Day1=Sunday...

So anyhow, I guess I am just looking for general it will work/its gonna bail because comments. I am not ready to upload this as I still need to figure out the Library return and format that to fit my desired rise/set/left/right parameters... so if you can program and have a moment i would love some helpers/fixes/won't works/great/whatever...

This is the function that is called within the loop... so I want to calculate all this exactly 1/day, i.e. when clock turns over... then no big deal computationally....

I kinda hope this is not a total disaster but it might be so if its totally useless just stop when this occurs to you and let me know the error of my ways.

Code: Select all

/*********************************************************************************************************************************
// Function to run Epherma Calc for sunrise/sunset/noon/moon phase 
void CalSun(){
   // Every day at midnight, we calculate sunrise, sunset, time of highest sun, moon phase.
  if (hour()==0 && minute()==0 && second()==0) 
  {
      // Arduino uses Unix time- seconds since Jan 1 12:00 am UTC 1970
      //Must convert to MST by subtracting 7 hrs =7*3600=25,200 seconds
      //Must get daylight savings rules into effect or sunrise set will change by 1 hour
     bool IsDST(int D=day(), int M==month(), int dow=weekday())
    {
        //January, february, and december are out.
        if (M < 3 || M > 11)
          {
            return false; 
          }
        //April to October are in
        else if (M > 4 && M < 11)
          {
            return true; 
          }
        else 
          {  
            int previousSunday = D - dow; // Sunday=1 Sat=7
            if M==3 && previousSunday>7 return true
            else if M==11 && previousSunday<=0 return false
          }
    }
      //Using time library from Arduino.time_t now(); returns seconds since 1970
      //Library states #seconds to subtrace to bring to Jan 1 2000 as origin for time is 
      //#define SECS_YR_2000  (946684800) the time at the start of y2k
      //DST rules are march second sunday until november 1st sunday + 1 hr starting at 2 am which I dont care about so that part is neglected 
     unsigned long sunrise,sunset, SecInput;
     char moontoday;
     if IsDST=true SecInput=now()-946684800+25,200+3,600//time in seconds since 2000 as GMT + 1 hr DST adjustment=3600
     else SecInput=now()-946684800+25,200; //time in seconds since 2000 as GMT
 
     latitude=dmsToSeconds(25,0,0); //Set to about the latitude of the great barrier reef rotated into the morthern hemisphere
     longitude=dmsToSeconds(111,51,17); //Set to Salt lake City, or MST zone, USA or there abouts- actually the AT921 remote weather station in Salt Lake City UT.  Random web grab.
     SunRise(SecInput)=SunUP;
     SunSet(SecInput)=SunDN
     //Not ready to do anything with MoonPhase yet and its replicated (unsure which is "better" in terms of accuracy but I suspect it 
     //may be ReefAngel libraries so I am not using this value further, just testing the library call for now
     MoonPhase(SecInput)=MoonToday;
       
   //Lets try setting up some offsets for sunrise/sunset with colors
   //Whites come on at true "Sunrise"  
   //Blues come on at SunUP-Blue_am and off at SunDN+Blue_pm
   //Violets come on at SunUP-(Blue_am+Violet_am) and off at SunDN+Blue_pm+Violet_am)
     Int long Blue_am=36,000 //Value is in seconds for ALL offsets (60 min)
     Int long Blue_pm=54,000 //90 min for evening viewing
     Int long Violet_am=54,000 // 90 min
     int long Violet_pm=72,000 // 2 hour for evening viewing
  // Lets set up left right offset for sunrise/sunset.  
  //This number is the right side delay (morning) or right side advance (evening) (you can make this negative to switch sides)
  //Use seconds again
  //Caution, the total offset delay for the evening or advance for the morning should be less than a vlaue that would 
  //push you into a new day, because it indexes hour by +1 intervals and is not smart enough (as I have coded it) to start a new day
  Int long offset  12000 //20 Min
  }
//Obviously I need to work on a conversion... I need to figure out what the Epherma LIbrary is actually returning... so will
//need to set up serial monitor and put this in loop or contact Michael Rice... but general idea is outlined
//Convert or parse SunRise into Hour Min SunRiseH, SunRiseM
//Add/subtract relevant offsets and away you go...
ChannelValue[LEDPWM0]=PWMSlope(SunRiseH,SunRiseM,SunSetH,SunSetM,10*2.55,2.55*70,30,0);
/*
ChannelValue[LEDPWM1]=PWMSlope(rhour,rmin,shour+1,smin,10*2.55,2.55*70,45,0);
ChannelValue[LEDPWM2]=PWMSlope(wrhour,wrmin+15,wshour+1,wsmin+15,10*2.55,2.55*70,30,0);
ChannelValue[LEDPWM3]=PWMSlope(rhour,rmin+15,shour+1,smin+15,10*2.55,2.55*70,45,0);
ChannelValue[LEDPWM4]=PWMSlope(vrhour,vrmin,vshour+1,vsmin,7*2.55,2.55*70,45,20);*/
}
//*********************************************************************************************************************************
Deckoz2302
Posts: 149
Joined: Tue Nov 01, 2011 11:05 am

Re: C library for Sun/Moon effects

Post by Deckoz2302 »

this is the code you need to parse your seconds to hours/minutes

Code: Select all

rhour=rtime/3600; 
      rtime=rtime%3600;
      rmin=rtime/60; 
      rtime=rtime%60; 
      rsec=rtime;
      if(rsec > 30) rmin++; 
      shour=stime/3600; 
      stime=stime%3600;
      smin=stime/60; 
      stime=stime%60; 
      ssec=stime;
      if(ssec > 30) smin++;
I wish I could say I had a better solution for you such as a external function because you will use the same general code for the offset values but you need to return 2 values and we cannot pass arrays in c++. I would say set global bytes, but then you would need a separate function for each which would negate the purpose of a separate function for the main & offset seconds parser....also you wrote 36000 seconds for an hour offset which an hour is 3600 seconds

only way I can think of an external parser is - set the total seconds variable as a global and create a function with 3 pointers

global variable

Code: Select all

byte hour = 0, byte min = 0;
voidParsSec(long,byte,byte,byte);
function

Code: Select all

void ParsSec(long int totalsec,byte hour, byte min, byte offset)
{
      totalsec+=offset
      hour=totalsec/3600; 
      totalsec=totalsec%3600;
      min=totalsec/60; 
      totalsec=totalsec%60; 
      sec=totalsec;
      if(sec > 30) min++; 
}
and you would call it like

Code: Select all

ParsSec(SunRise, hour, min, 0) SunRiseH=hour, SunRiseM = min;
ParsSec(SunRise, hour, min, 3600) WSunRiseH=hour, WSunRiseM=min;
i beleive this make work for you...let me know
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

That looks like it would work, I am fairly certain that SunRise just returns seconds since midnight for the event and SunSet etc so the parsing is correct if thats true. I am going to just sit down and try to code this and forget I saw what you have here... and see how I end up doing it. If I get stuck I will just use the famous copy and paste programming feature of open source coding!

I love that DST algorithim, I didn't write the original but like I said, I blocked it all out in a chart and it looks like it works and is super lightweight. Mod was to modify for Arduino/C++ and recode it as I could not follow some of the logic in the original but got how they were doing it.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Ok... here goes.

I have two basic compile errors that I am not totally getting, but have been looking at this too much so probably stupid but if you could look at it and help me out that would be great, I would love to test this.


Here is some code that I wrote to try to use the epherma libraries I posted above.

This is intended to load onto the arduino processor on the PWMExpansion board internal to the expansion hub for PWM control of LED lights.

The basics are, I have a call to a functions called CalSun()

CalSun() calls CalcDST() within it, which determines if we need to adjust the time locally for DST rules and returns simple boolean true false

Then CalSun runs through the GMT correction and DST corrections in elapsed seconds since 2000 to find out what that # is today... and passes it to Epherma sunrise and sunset which should return seconds back as sunrise and sunset.

Then these are converted to actual rise and set times for the variety of colors I have, i.e. toilets, blues, whites using left right off sets to get the sun to rise and set right side rise, left side set. I use the sun rise and sunset gals from epherma to represent when the WHITE lights turn on, I scale earlier (sunrise) for blues and earlier still for violets and add time to sunset similarly for blues and violets.

If someone can comment on the whole thing and help me figure out whats puking the compiler I would very much appreciate it. I post the ENTIRE PDE with the cloud function so it can be uploaded and compiled if anyone is willing to try to help me finish this.

Code: Select all

// Reef Angel Includes
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <Globals.h>
#include <ReefAngel_Features.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <avr/wdt.h>
#include <SWFLTEK_EPHERMA.h>


//Defines for ReefAngel PWM module

#define LEDPWM0 0
#define LEDPWM1 1
#define LEDPWM2 2
#define LEDPWM3 3
#define LEDPWM4 4
#define LEDPWM5 5


//*********************************************************************************************************************************
//Globals Vavriables for ReefAngel PWM Cloud, PWM Settings, Epherma Lat/Long Sunrise/Sunset
long SunUP;
long SunDN;
long MoonToday;
static byte lightningchance=0;
byte cloudduration=0;
int cloudstart=0;
int rhour = 0, rmin = 0, shour = 0, smin = 0;
byte PWMports[] ={
  3,5,6,9,10,11};
int ChannelValue[] = {
  0,0,0,0,0,0};
byte cmdnum=255;
byte datanum=255;
boolean ForceCloud=false;
byte toggle=false
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Setup
void setup()
{
  Serial.begin(57600);
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  randomSeed(analogRead(0));
  pinMode(3,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  //wdt_enable(WDTO_1S);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  setSyncInterval(SECS_PER_HOUR);  // Changed to sync every hour.
  now();
}
//End Setup
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Loop
void loop()
{
  wdt_reset();
  //Serial.println(ChannelValue[0],DEC);
  if (cmdnum!=255)
  {
    ProcessCMD(cmdnum,datanum);    
    cmdnum=255;
    datanum=255;
  }
 // functions called from loop
  CheckCloud();
  CalSun();
  //Serial.println("Start");
  //Serial.println(ChannelValue[0],DEC);
  //Serial.println(rhour,DEC);
  //Serial.println(rmin,DEC);
  //Serial.println(shour,DEC);
  //Serial.println(smin,DEC);
  //Serial.println(cloudstart/60,DEC);
  //Serial.println(cloudstart%60,DEC);
  //Serial.println(cloudduration,DEC);
  for (int a=0;a<6;a++)
  {
    analogWrite(PWMports[a],ChannelValue[a]);

  }
}
//End Loop
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Standard PWM Functions Receive/Process
void receiveEvent(int howMany) {
  wdt_reset();
  if (howMany==5)
  {
    byte cmd1, cmd2, cmd3, cmd4, cmd5;
    cmd1=Wire.read();
    cmd2=Wire.read();
    cmd3=Wire.read();
    cmd4=Wire.read();
    cmd5=Wire.read();
    if (cmd1=='$' && cmd2=='$' && cmd3=='$')
    {
      cmdnum=cmd4;
      datanum=cmd5;
      //Serial.println(cmd4,DEC);
      //Serial.println(cmd5,DEC);
    }
  }
  else
  {
    for (int a=0;a<howMany;a++)
    {
      Wire.read();
    }
  }  
}

void ProcessCMD(byte cmd, byte data)
{
  wdt_reset();
  // Individual Channel
  if (cmd>=0 && cmd<=5)
  {
    ChannelValue[cmd]=data;
    analogWrite(PWMports[cmd],data);
  }
  if (cmd==6) ForceCloud=true;
}
//End Standard Functions
//*********************************************************************************************************************************

//*********************************************************************************************************************************
/*Send Data Function
void requestEvent() {
  SeasonsVar[0]=ChannelValue[0];
  SeasonsVar[1]=rhour;
  SeasonsVar[2]=rmin;
  SeasonsVar[3]=shour;
  SeasonsVar[4]=smin;
  SeasonsVar[5]=cloudstart/60;
  SeasonsVar[6]=cloudstart%60;
  SeasonsVar[7]=cloudduration;
  SeasonsVar[8]=lightningchance;
  SeasonsVar[9]=ChannelValue[1];
  SeasonsVar[10]=ChannelValue[2];
  SeasonsVar[11]=ChannelValue[3];
  Wire.write(SeasonsVar,12);
}
End Send Data*/
//*********************************************************************************************************************************
// Function to run Epherma Calc for sunrise/sunset/noon/moon phase 
bool CalcDST(int D, int M, int dow)
      {
        byte dst;  
        //January, february, and december are out.
        if (M < 3 || M > 11)
          {
            dst=false; 
          }
        //April to October are in
        else if (M > 4 && M < 11)
          {
            dst=true; 
          }
        else if
          {  
            int previousSunday = D - dow; // Sunday=1 Sat=7
            if M==3 && previousSunday > 7
             { 
               dst=true
             }
            else if M==11 && previousSunday<=0 
              {
                dst=false;
              }
          }
          return (dst);
    }
void CalSun(){
   // Every day at midnight, we calculate sunrise, sunset, time of highest sun, moon phase.
  if (hour()==0 && minute()==0 && second()==0) 
  {
      // Arduino uses Unix time- seconds since Jan 1 12:00 am UTC 1970
      //Must convert to MST by subtracting 7 hrs =7*3600=25,200 seconds
      //Must get daylight savings rules into effect or sunrise set will change by 1 hour
     // In D, M, dow;
    byte isDST=CalcDST(day(),month(),weekday());
    
      //Using time library from Arduino.time_t now(); returns seconds since 1970
      //Library states #seconds to subtrace to bring to Jan 1 2000 as origin for time is 
      //#define SECS_YR_2000  (946684800) the time at the start of y2k
      //DST rules are march second sunday until november 1st sunday + 1 hr starting at 2 am which I dont care about so that part is neglected 
     unsigned long SecInput;
     char moontoday;
     if isDST=true SecInput=now()-946684800+25,200+3,600 //time in seconds since 2000 as GMT + 1 hr DST adjustment=3600
     else SecInput=now()-946684800+25,200; //time in seconds since 2000 as GMT
 
     latitude=dmsToSeconds(25,0,0); //Set to about the latitude of the great barrier reef rotated into the morthern hemisphere
     longitude=dmsToSeconds(111,51,17); //Set to Salt lake City, or MST zone, USA or there abouts- actually the AT921 remote weather station in Salt Lake City UT.  Random web grab.
     SunRise(SecInput)=SunUP;
     SunSet(SecInput)=SunDN;
     // Correct back to MST time
     SunUP=SunUP-25,200;
     SunDN-SunDN-25,200;
     //just testing the library call for now 
     MoonPhase(SecInput)=MoonToday;
     //Eventually add conversion of MoonToday to brightness for Channel 4 PWMEnd value intensity as "default" for overnight moon lighting scale 5-50% for no moon-full moon
  
         
   //Lets try setting up some offsets for sunrise/sunset with colors
   //Whites come on at true "Sunrise"  
   //Blues come on at SunUP-Blue_am and off at SunDN+Blue_pm
   //Violets come on at SunUP-(Blue_am+Violet_am) and off at SunDN+Blue_pm+Violet_am)
     int long Blue_am=3,600;//Value is in seconds for ALL offsets (60 min=3,600)
     int long Blue_pm=5,400;//90 min for evening viewing
     int long Violet_am=4,500; // 75 min
     int long Violet_pm=5,4000; // 90 min for evening viewing
     /* Lets set up left right offset for sunrise/sunset.  
     This number is the right (east) side early in seconds (morning) or left (west) side advance (evening) 
     Beware of setting an offset that pushes you to 25 hour days.... */
     int long offset  900;
  
      //Use offsets to fix all times for rise/set/blue/violet affects work in seconds 
     //lets initialize and calculate all seed sunrise variables to set PWM Slope- LBrH= LeftBlue rise Hour Etc... white violet also
     int LWrH, LBrH, VrH, LWrM, LBrM, VrM, RWrH, RBrH, RWrM, RBrM;
  
    RWrH=(SunUP-offset)/3600;
    RWrM=((SunUP-offset)%3600)/60;
    BrH=(SunUP-(offset+Blue_am))/3600;
    RBrM=((SunUP-(offset+Blue_am))%3600)/60;
    VrH=(SunUP-(offest+Violet_am))/3600;
    VrM=((SunUP-(offset+Violet_am))%3600)/60;
    LWrH=SunUP/3600;
    LWrM=(SunUP%3600)/60;
    LBrH=(SunUP-Blue_am)/3600;
    LBrM=((SunUP-Blue_am)%3600)/60;
    
    //Ok now that I think I have this lets use same basic idea for sunset
    
    int LWsH, LBsH, VsH, LWsM, LBsH, VsM, RWsH, RBsH, RWsM. RBsM;
    
    RWsH=(SunDN+offset)/3600;
    RWsM=((SunDN+offset)%3600)/60;
    RBsH=(SunDN+(offset+Blue_pm))/3600;
    RBsM=((SunDN+(offset+Blue_pm))%3600)/60;
    VsH=(SunDN+(offest+Violet_pm))/3600;
    VsM=((SunDN+(offset+Violet_pm))%3600)/60;
    LWsH=SunDN/3600;
    LWsM=(SunDN%3600)/60;
    LBsH=(SunDN+Blue_pm)/3600;
    LBsM=((SunDN+Blue_pm)%3600)/60;
     // Channels for me are 0=RW 1=RB 2=LW 3=LB 4=Whole tank V     

ChannelValue[LEDPWM0]=PWMSlope(RWrH,RWrM,RWsH,RWsM,10*2.55,2.55*70,30,0);
ChannelValue[LEDPWM1]=PWMSlope(RBrH,RBrM,RBsH,RBsM,10*2.55,2.55*80,45,0);
ChannelValue[LEDPWM2]=PWMSlope(LWrH,LWrM,LWsH,LWsM,10*2.55,2.55*70,30,0);
ChannelValue[LEDPWM3]=PWMSlope(LBrH,LBrM,LBsH,LBsM,10*2.55,2.55*80,45,0);
ChannelValue[LEDPWM4]=PWMSlope(VRh,VRm,VSh,VSm,7*2.55,2.55*80,45,20);
}
//*********************************************************************************************************************************
// Random Cloud/Thunderstorm effects function
void CheckCloud()
{

  // ------------------------------------------------------------
  // Change the values below to customize your cloud/storm effect

  // Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
  // For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 1

  // Percentage chance of a cloud happening today
  // For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 65

  // Minimum number of minutes for cloud duration.  Don't use max duration of less than 6
#define Min_Cloud_Duration 6

  // Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 20

  // Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 2

  // Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 4

  // Only start the cloud effect after this setting
  // In this example, start could after 11:30am
#define Start_Cloud_After NumMins(9,00)

  // Always end the cloud effect before this setting
  // In this example, end could before 8:00pm
#define End_Cloud_Before NumMins(19,00)

  // Percentage chance of a lightning happen for every cloud
  // For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 60

  // Channels used by the actinic LEDs on the PWM Expansion module
  // These channels will not be dimmed when the cloud effect is triggered
  // Number is a binary form. B001100 means channel 2 and 3 are used for actinics
#define Actinic_Channels B011010

  // Channels used by the daylight LEDs on the PWM Expansion module
  // These channels will be used for the spike when lightning effect is triggered
  // Number is a binary form. B000011 means channel 0 and 1 are used for daylights
#define Daylight_Channels B000101

  // Note: Make sure to choose correct values that will work within your PWMSLope settings.
  // For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
  // Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes of effects or unforseen 
  // results could happen.
  // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
  // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can
  //fit in that 510 minutes window.
  // It's a tight fit, but it did.

  //#define printdebug // Uncomment this for debug print on Serial Monitor window
#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process. 


  // Change the values above to customize your cloud/storm effect
  // ------------------------------------------------------------
  // Do not change anything below here

  static byte cloudchance=255;
  static byte numclouds=0;
  static byte cloudindex=0;
  static byte lightningstatus=0;
  static int LastNumMins=0;
  // Every day at midnight, we check for chance of cloud happening today
  if (hour()==0 && minute()==0 && second()==0) cloudchance=255;

#ifdef forcecloudcalculation
  if (cloudchance==255)
#else
   c && cloudchance==255) 
#endif
    {
      //Pick a random number between 0 and 99
      cloudchance=random(100); 
      // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
      if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
      // Check if today is day for clouds. 
      if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0; 
      // If we have cloud today
      if (cloudchance)
      {
        // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
        numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
        // pick the time that the first cloud will start
        // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day. 
        cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));

        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  // Now that we have all the parameters for the cloud, let's create the effect
  
  if (ForceCloud)
  {
    ForceCloud=false;
    cloudchance=1;
    cloudduration=10;
    lightningchance=1;
    cloudstart=NumMins(hour(),minute())+1;
  }

  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 15 for the duration of the cycle
          // and finally slope up from 15 to PWM value within 3 minutes
          // it is basically an inversed slope
          ChannelValue[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ChannelValue[a],15,180);
        }
      }
     if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration* 0.5))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration * 0.25))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration * 0.75))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
    }



    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
      cloudindex++;
      if (cloudindex < numclouds)
      {
        cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  }
}

      
//End Cloud Function
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Reverse PWM slope from cloud function
byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
  long n=elapsedSecsToday(now());
  cstart*=60;
  cend*=60;
  if (n<cstart) return PWMStart;
  if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
  if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
  if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
  if (n>cend) return PWMStart;
}
//End Reverse PWM function
//*********************************************************************************************************************************

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

Re: C library for Sun/Moon effects

Post by rufessor »

here are the compile errors for above ino


_021712_LatLonSeasons_PWMboard:15: error: expected unqualified-id before numeric constant
_021712_LatLonSeasons_PWMboard:54: error: expected ',' or ';' before 'void'
_021712_LatLonSeasons_PWMboard.cpp: In function 'bool CalcDST(int, int, int)':
_021712_LatLonSeasons_PWMboard:185: error: expected `(' before '{' token
_021712_LatLonSeasons_PWMboard:505: error: expected `}' at end of input
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

Mostly syntax errors. Nothing major.
Very good start though!!! :)
I can't wait to see what you come up with.
I assume you are used to VB and not C, right?
I can see the syntax errors are typical of a VB programmer :)
I code VB too.

Needs ; at the end

Code: Select all

byte toggle=false
You need a requestEvent() function

Code: Select all

void requestEvent(){
}
You also need to either put a comparison or remove the if

Code: Select all

else if
() in here too:

Code: Select all

     if isDST=true SecInput=now()-946684800+25,200+3,600 //time in seconds since 2000 as GMT + 1 hr DST adjustment=3600
You also need to use () for any if statement:

Code: Select all

if M==3 && previousSunday > 7

Code: Select all

else if M==11 && previousSunday<=0
And another ; at the end here:

Code: Select all

dst=true
These 2 lines seem wrong to me. I think you wanted the other way around?

Code: Select all

     SunRise(SecInput)=SunUP;
     SunSet(SecInput)=SunDN;
Then, I stopped here.
It started getting into details on how to calculate what you wanted.
It was giving me errors about converting variables.
Roberto.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Thanks!

Yes I am a VB programmer... still have to go back and add ; to every line when I am done writing it! And obviously other stuff.


I will see if I understand all your corrections, play more, try to get further in compile and post back if I still have issues with updated code.

:oops: if (statement) seems a common error! I read it and read it and never noticed that I started using it correctly then fell back in to VB!

I have a interesting idea about modeling solar insolation instead of using PWM slope, its pretty light duty math and I have it all worked out in Excel (sorta) just have not decided how difficult it will be to get into Arduino with the 5 channels of lighting in I have.

THANK YOU!
Last edited by rufessor on Wed Feb 22, 2012 3:07 pm, edited 1 time in total.
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

Do you mean something like this?
Image
Roberto.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Absolutely, its super easy to calc, and If I just take and add or subtract my offsets for the left right and blue or violet for each channel that will get me total light on off time for all channels.

Then its a simple matter to find the center (define solar noon as 1/2 way between sun rise sunset)
and then just set the intensity by scaling from -cos( 1/2 pi) to -cos (1.5xpi) from sunrise to sunset which would get you a value that varies from 0-1-0 as a decimal from sunrise to solar noon (defined in this case as 1/2 way between the two) and sunset and that looks exactly like the plot you posted.

Then its SUPER easy to scale your intensity by just -cos(pi val of sun phase)*255 = PWMChannel output.

I am wondering how far you are on this...opps I see its working on someones system... given that you produced the plot from a reef angel monitor!

What I am trying to figure out, is WHERE should the function live and how to call if from the calSun function so that the PWM channels are set every minute or something (no need for more often than that) so its really only 5 floating point -cos(xx) calculations evert minute, so the processor should be ok.

I then want to redo cloud... but don't want to worry about that just now.

Any ideas about where this function should live? Maybe called from the loop?

And then, I guess I would make global variables for the sunrise/sunset vals and use them in the function to set PWM Channel intensity.
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

Already done for you :)
libraries v0.9.0 already have that.
Use this, instead of the PWMSlope() function.

Code: Select all

PWMParabola(9,0,21,0,15,75,0);
Parabola starts at 9:00am, ends at 9:00pm.
Instensity grows from 15 to 75 and back down to 15.
Returns 0 when not within the time schedule.
Roberto.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

YEAH HOOOOOO-

It compiles cleanly but for an error in the library code that I am working on resolving now with Michael Rice... but since it may be something even less complex (I.E. I did it... but not on purpose) I post the error here..


The files for the library are at the head of this thread...

I am not getting this error as I have included SWFLTEK_EPHERMA.h which declares latitude and londitude as

long latitude, longitude;

so the error saying that its not declared is weird? Is it a move to arduino 1.0??


\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp: In function 'void SolarNoon(long unsigned int*)':
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp:71: error: 'longitude' was not declared in this scope
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp: In function 'long unsigned int daylightseconds(long unsigned int)':
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp:101: error: 'latitude' was not declared in this scope
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp: In function 'char SunRiseSet(long unsigned int*, char)':
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp:141: error: 'longitude' was not declared in this scope
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp: In function 'long unsigned int LMSidereal(long unsigned int)':
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp:167: error: 'longitude' was not declared in this scope
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp: In function 'unsigned char season(long unsigned int)':
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp:199: error: 'latitude' was not declared in this scope
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Ok... so I am still stuck but I have managed to at least change the error message.

I did this
Within the SWFLTEK_EPHERMA.cpp file I uncommented the line
#include "SWFLTEK_EPHERMA.h" and tried it with the quotes or <> neither change any result

Now I no longer get the errors above, but now it says that


SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp.o: In function `ddToSeconds(float)':
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA/SWFLTEK_EPHERMA.cpp:24: multiple definition of `longitude'
_021712_LatLonSeasons_PWMboard.cpp.o:C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\build6082588081182502864.tmp/_021712_LatLonSeasons_PWMboard.cpp:178: first defined here
SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp.o: In function `ddToSeconds(float)':
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA/SWFLTEK_EPHERMA.cpp:24: multiple definition of `latitude'
_021712_LatLonSeasons_PWMboard.cpp.o:C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\build6082588081182502864.tmp/_021712_LatLonSeasons_PWMboard.cpp:178: first defined here


But the REALLY strange thing is...
if I comment out any mention of latitude/longitude in my .INO file I still get the same error.

So, I think its something about how I have the library formatted as my .INO file does call sunrise and sunset and these functions then call helper functions with the SWFLTEK_EPHERMA library that use latitude and longitude, what I cannot figure out it how its getting to redundant declrations and especially why its telling me that its in my temp build .cpp file....

I know that no one here has anything to do with the library, and its my *idea* to make it work... but I am hoping someone can inform me where to look to fix this, as I do NOT see anything other than a simple
long latitude, longitude;
in the .h file
and within .cpp
its always X=latitude never latitude=X or same for longitude so its not declaring it just initializing it... and the only place I declare it is

Latitude-dmsToSeconds(25,0,0) (might have numbers wrong but thats irrelevant)
and if I comment this line out in my .INO I still get the same error.

WTF?
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

Can you post your latest h, cpp and ino files?
Roberto.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Here is the SWFLTEK_EPHERMA.cpp

followed by .h

and finally by my .ino


THanks for any help/// very confuzed

Code: Select all

#include <SWFLTEK_EPHERMA.h>
#include <stdlib.h>
#include <math.h>

// arc-seconds per radian
#define _sec_rad 206264.806247096370813

// axial tilt of earth at epoch, in radians
#define _tilt 0.409092804222329

// tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _tropical_year 31556925

// 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)
#define _zenith 3.11250383272322

/*------------------------------------------------------------------------------------------------
convert degrees to seconds of arc
*/

// 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 for' 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 Noon' adjusts the passed time stamp to the time (GMT) of local solar noon.
	The accuracy is about 40 seconds (set by the equation of time).
*/
void SolarNoon(unsigned long * dt){
long r;
		// Set stamp to noon GMT
	*dt /= 86400UL;
	*dt *= 86400UL;
	*dt += 43200UL;
		// adjust for equation of time, at noon GMT
	*dt -= equation_of_time(*dt);
		// rotate to our longitude
	r = longitude / 15L;
	*dt -= r;
}

/* -----------------------------------------------------------------------------------------------
	'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);
}

/* ------------------------------------------------------------------------------------------------
	Mean Sidereal Time.
	Accurate to within 1 sidereal second.
*/
unsigned long GMSidereal(unsigned long gmt){
unsigned long long sidereal;

	sidereal = gmt * 10027379094LL; // multiply by 10 Billion times the ratio
	sidereal /= 10000000000LL; // and divide by 10 billion
	sidereal += 23992LL; // add sidereal time at the epoch
	return sidereal;
}

unsigned long LMSidereal(unsigned long gmt){
	return  GMSidereal(gmt ) + longitude / 15L;
}

/* ------------------------------------------------------------------------------------------------
 	An approximation of the moons phase.
	Magnitude of the result approximates the percentage of the moons illuminated surface.
	Sign of the result indicates a Waxing (+) or Waning (-) moon.
	It uses the mean lunar cycle, which may differ from the actual by many hours.
	As such, it may vary from the correct value by 20%.
*/

char MoonPhase(unsigned long d){
long r;

	// the first full moon of the epoch was Jan 21 at 04:40
	// but the 'mean' full moon was 4 hours later
	d -= 1759365UL; // subtract time of first full moon
	d %= 2551443L; // mod by the mean lunar cycle
	r = d - 1275721L;
	r /= 12757L;
	return r;
}

// Season of the year
// 0 = winter, 1 = spring, 2 = summer, 3 = fall
unsigned char season(unsigned long dt){
		dt += 838800UL;// refer to prior winter solstice
	dt %= _tropical_year;
		dt /= 7889400UL; // 91.3125 days
		if(latitude<0) dt += 2UL;
	return dt % 4;
}
and the .h file

Code: Select all

/*
	SWFLTEK_Ephemera copyright 2010 by Michael Rice
		Swfltek Ephemera is a set of astronomical functions often of interest.
	Though written for avr-gcc, it should convert easily to other compilers.
		Most functions require an unsigned long time stamp as an argument. This 32 bit value represents
	the number of seconds elapsed since midnight Jan 1 2000 GMT.
*/
#ifndef SWFLTEK_EPHEMERA_h
#define SWFLTEK_EPHEMERA_h



// geographic coordinates, in seconds of arc North and East
long latitude, longitude;

// conveniences to convert two typical representations into seconds of arc
long ddToSeconds(float);
long dmsToSeconds(int, unsigned char, unsigned char);

// return equation of time in seconds
int equation_of_time(unsigned long);

// adjust stamp to Solar noon
void SolarNoon(unsigned long * );

// return solar declination in radians
double SolarDeclination(unsigned long dt);

// return seconds between sunrise and sunset
unsigned long daylightseconds(unsigned long);

// compute time of sun rise or sunset
char SunRiseSet(unsigned long*, char);

// shorthand form
char SunRise(unsigned long*);
char SunSet(unsigned long*);

// convert from GMT to Mean Sidereal Time
unsigned long GMSidereal(unsigned long);
unsigned long LMSidereal(unsigned long);

// approximate phase of the moon
char MoonPhase(unsigned long);

// season
unsigned char season(unsigned long);



#endif
and the .ino file

Code: Select all

// Reef Angel Includes
#include <SWFLTEK_EPHERMA.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <Globals.h>
#include <ReefAngel_Features.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <avr/wdt.h>




//Defines for ReefAngel PWM module

#define LEDPWM0 0
#define LEDPWM1 1
#define LEDPWM2 2
#define LEDPWM3 3
#define LEDPWM4 4
#define LEDPWM5 5


//*********************************************************************************************************************************
//Globals Vavriables for ReefAngel PWM Cloud, PWM Settings, Epherma Lat/Long Sunrise/Sunset
unsigned long sunrise;
unsigned long sunset;
char MoonToday;
static byte lightningchance=0;
byte cloudduration=0;
int cloudstart=0;
byte PWMports[] ={
  3,5,6,9,10,11};
int ChannelValue[] = {
  0,0,0,0,0,0};
byte SeasonsVar[]={
  0,0,0,0,0,0,0,0,0,0,0,0}; // PWM0, sunrisehour, sunriseminute, sunsethour, sunsetminute, cloudstarthout, cloudstartminute, cloudduration,lightningchance, PWM1,PWM2,PWM3
byte cmdnum=255;
byte datanum=255;
boolean ForceCloud=false;
byte toggle=false;
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Setup
void setup()
{
  Serial.begin(57600);
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  randomSeed(analogRead(0));
  pinMode(3,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  //wdt_enable(WDTO_1S);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  setSyncInterval(SECS_PER_HOUR);  // Changed to sync every hour.
  now();
  latitude=dmsToSeconds(25,0,0); //Set to about the latitude of the great barrier reef rotated into the morthern hemisphere
  longitude=dmsToSeconds(111,51,17); //Set to Salt lake City, or MST zone, USA or there abouts- actually the AT921 remote weather station in Salt Lake City UT.  Random web grab.
}
//End Setup
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Loop
void loop()
{
  wdt_reset();
  //Serial.println(ChannelValue[0],DEC);
  if (cmdnum!=255)
  {
    ProcessCMD(cmdnum,datanum);    
    cmdnum=255;
    datanum=255;
  }
 // functions called from loop
  CheckCloud();
  CalSun();
  //Serial.println("Start");
  //Serial.println(ChannelValue[0],DEC);
  //Serial.println(rhour,DEC);
  //Serial.println(rmin,DEC);
  //Serial.println(shour,DEC);
  //Serial.println(smin,DEC);
  //Serial.println(cloudstart/60,DEC);
  //Serial.println(cloudstart%60,DEC);
  //Serial.println(cloudduration,DEC);
  for (int a=0;a<6;a++)
  {
    analogWrite(PWMports[a],ChannelValue[a]);

  }
}
//End Loop
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Standard PWM Functions Receive/Process
void receiveEvent(int howMany) {
  wdt_reset();
  if (howMany==5)
  {
    byte cmd1, cmd2, cmd3, cmd4, cmd5;
    cmd1=Wire.read();
    cmd2=Wire.read();
    cmd3=Wire.read();
    cmd4=Wire.read();
    cmd5=Wire.read();
    if (cmd1=='$' && cmd2=='$' && cmd3=='$')
    {
      cmdnum=cmd4;
      datanum=cmd5;
      //Serial.println(cmd4,DEC);
      //Serial.println(cmd5,DEC);
    }
  }
  else
  {
    for (int a=0;a<howMany;a++)
    {
      Wire.read();
    }
  }  
}

void ProcessCMD(byte cmd, byte data)
{
  wdt_reset();
  // Individual Channel
  if (cmd>=0 && cmd<=5)
  {
    ChannelValue[cmd]=data;
    analogWrite(PWMports[cmd],data);
  }
  if (cmd==6) ForceCloud=true;
}
//End Standard Functions
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Send Data Function
void requestEvent() {
  SeasonsVar[0]=ChannelValue[0];
  SeasonsVar[1];
  SeasonsVar[2];
  SeasonsVar[3];
  SeasonsVar[4];
  SeasonsVar[5]=cloudstart/60;
  SeasonsVar[6]=cloudstart%60;
  SeasonsVar[7]=cloudduration;
  SeasonsVar[8]=lightningchance;
  SeasonsVar[9]=ChannelValue[1];
  SeasonsVar[10]=ChannelValue[2];
  SeasonsVar[11]=ChannelValue[3];
  Wire.write(SeasonsVar,12);
}
//End Send Data
//*********************************************************************************************************************************
// Function to run Epherma Calc for sunrise/sunset/noon/moon phase 
bool CalcDST(int D, int M, int dow)
      {
        byte dst;  
        //January, february, and december are out.
        if (M < 3 || M > 11)
          {
            dst=false; 
          }
        //April to October are in
        else if (M > 4 && M < 11)
          {
            dst=true; 
          }
        else
          {  
            int previousSunday = D - dow; // Sunday=1 Sat=7
            if (M==3 && previousSunday > 7)
             { 
               dst=true;
             }
            else if (M==11 && previousSunday<=0)
              {
                dst=false;
              }
          }
          return (dst);
    }
void CalSun(){
   // Every day at midnight, we calculate sunrise, sunset, time of highest sun, moon phase.
  if (hour()==0 && minute()==0 && second()==0) 
  {
      // Arduino uses Unix time- seconds since Jan 1 12:00 am UTC 1970
      //Must convert to MST by subtracting 7 hrs =7*3600=25,200 seconds
      //Must get daylight savings rules into effect or sunrise set will change by 1 hour
     // In D, M, dow;
    byte isDST=CalcDST(day(),month(),weekday());
    
      //Using time library from Arduino.time_t now(); returns seconds since 1970
      //Library states #seconds to subtrace to bring to Jan 1 2000 as origin for time is 
      //#define SECS_YR_2000  (946684800) the time at the start of y2k
      //DST rules are march second sunday until november 1st sunday + 1 hr starting at 2 am which I dont care about so that part is neglected 
     unsigned long SecInput;
     char moontoday;
     if (isDST=true) 
       {
         SecInput=now()-946684800+25,200+3,600; //time in seconds since 2000 as GMT + 1 hr DST adjustment=3600
       }
     else 
       {
         SecInput=now()-946684800+25,200; //time in seconds since 2000 as GMT
       }
     
     sunrise=sunset=SecInput;
     SunRise(& sunrise);
     SunSet(& sunset);
     // Correct back to MST time
     sunrise=sunrise-25,200;
     sunset=sunset-25,200;
     //just testing the library call for now 
 //   MoonToday=MoonPhase(SecInput);
     //Eventually add conversion of MoonToday to brightness for Channel 4 PWMEnd value intensity as "default" for overnight moon lighting scale 5-50% for no moon-full moon
  
         
   //Lets try setting up some offsets for sunrise/sunset with colors
   //Whites come on at true "Sunrise"  
   //Blues come on at sunrise-Blue_am and off at sunset+Blue_pm
   //Violets come on at sunrise-(Blue_am+Violet_am) and off at sunset+Blue_pm+Violet_am)
     short int Bam,Bpm,Vam,Vpm;
     Bam=3,600;//Blue AM early rise by Value in seconds (60 min=3,600)
     Bpm=5,400;// Blue PM late sunset by 90 min for evening viewing
     Vam=4,500; // Violet AM early rise by 75 min
     Vpm=5,4000; // Violet PM Late set by 90 min for evening viewing
     /* Lets set up left right offset for sunrise/sunset.  
     This number is the right (east) side early in seconds (morning) or left (west) side advance (evening) 
     Beware of setting an offset that pushes you to 25 hour days.... */
     short int offset(900);
  
      //Use offsets to fix all times for rise/set/blue/violet affects work in seconds 
     //lets initialize and calculate all seed sunrise variables to set PWM Slope- LBrH= LeftBlue rise Hour Etc... white violet also
     long int LWrH,LBrH,VrH,LWrM,LBrM,VrM,RWrH,RBrH,RWrM,RBrM;
  
    RWrH=(sunrise-offset)/3600;
    RWrM=((sunrise-offset)%3600)/60;
    RBrH=(sunrise-(offset+Bam))/3600;
    RBrM=((sunrise-(offset+Bam))%3600)/60;
    VrH=(sunrise-(offset+Vam))/3600;
    VrM=((sunrise-(offset+Vam))%3600)/60;
    LWrH=sunrise/3600;
    LWrM=(sunrise%3600)/60;
    LBrH=(sunrise-Bam)/3600;
    LBrM=((sunrise-Bam)%3600)/60;
    
    //Ok now that I think I have this lets use same basic idea for sunset
    
    long int RWsH,RWsM,RBsH,RBsM,VsH,VsM,LWsH,LWsM,LBsH,LBsM;
    
    RWsH=(sunset+offset)/3600;
    RWsM=((sunset+offset)%3600)/60;
    RBsH=(sunset+(offset+Bpm))/3600;
    RBsM=((sunset+(offset+Bpm))%3600)/60;
    VsH=(sunset+(offset+Vpm))/3600;
    VsM=((sunset+(offset+Vpm))%3600)/60;
    LWsH=sunset/3600;
    LWsM=(sunset%3600)/60;
    LBsH=(sunset+Bpm)/3600;
    LBsM=((sunset+Bpm)%3600)/60;
     // Channels for me are 0=RW 1=RB 2=LW 3=LB 4=Whole tank V     

ChannelValue[LEDPWM0]=PWMParabola(RWrH,RWrM,RWsH,RWsM,10*2.55,2.55*70,0);
ChannelValue[LEDPWM1]=PWMParabola(RBrH,RBrM,RBsH,RBsM,10*2.55,2.55*80,0);
ChannelValue[LEDPWM2]=PWMParabola(LWrH,LWrM,LWsH,LWsM,10*2.55,2.55*70,0);
ChannelValue[LEDPWM3]=PWMParabola(LBrH,LBrM,LBsH,LBsM,10*2.55,2.55*80,0);
ChannelValue[LEDPWM4]=PWMParabola(VrH,VrM,VsH,VsM,7*2.55,2.55*80,20);
}
}
//*********************************************************************************************************************************
// Random Cloud/Thunderstorm effects function
void CheckCloud()
{

  // ------------------------------------------------------------
  // Change the values below to customize your cloud/storm effect

  // Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
  // For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 1

  // Percentage chance of a cloud happening today
  // For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 65

  // Minimum number of minutes for cloud duration.  Don't use max duration of less than 6
#define Min_Cloud_Duration 6

  // Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 20

  // Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 2

  // Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 4

  // Only start the cloud effect after this setting
  // In this example, start could after 11:30am
#define Start_Cloud_After NumMins(9,00)

  // Always end the cloud effect before this setting
  // In this example, end could before 8:00pm
#define End_Cloud_Before NumMins(19,00)

  // Percentage chance of a lightning happen for every cloud
  // For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 60

  // Channels used by the actinic LEDs on the PWM Expansion module
  // These channels will not be dimmed when the cloud effect is triggered
  // Number is a binary form. B001100 means channel 2 and 3 are used for actinics
#define Actinic_Channels B011010

  // Channels used by the daylight LEDs on the PWM Expansion module
  // These channels will be used for the spike when lightning effect is triggered
  // Number is a binary form. B000011 means channel 0 and 1 are used for daylights
#define Daylight_Channels B000101

  // Note: Make sure to choose correct values that will work within your PWMSLope settings.
  // For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
  // Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes of effects or unforseen 
  // results could happen.
  // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
  // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can
  //fit in that 510 minutes window.
  // It's a tight fit, but it did.

  //#define printdebug // Uncomment this for debug print on Serial Monitor window
#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process. 


  // Change the values above to customize your cloud/storm effect
  // ------------------------------------------------------------
  // Do not change anything below here

  static byte cloudchance=255;
  static byte numclouds=0;
  static byte cloudindex=0;
  static byte lightningstatus=0;
  static int LastNumMins=0;
  // Every day at midnight, we check for chance of cloud happening today
  if (hour()==0 && minute()==0 && second()==0) cloudchance=255;

#ifdef forcecloudcalculation
  if (cloudchance==255)
#else
   c && cloudchance==255) 
#endif
    {
      //Pick a random number between 0 and 99
      cloudchance=random(100); 
      // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
      if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
      // Check if today is day for clouds. 
      if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0; 
      // If we have cloud today
      if (cloudchance)
      {
        // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
        numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
        // pick the time that the first cloud will start
        // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day. 
        cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));

        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  // Now that we have all the parameters for the cloud, let's create the effect
  
  if (ForceCloud)
  {
    ForceCloud=false;
    cloudchance=1;
    cloudduration=10;
    lightningchance=1;
    cloudstart=NumMins(hour(),minute())+1;
  }

  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 15 for the duration of the cycle
          // and finally slope up from 15 to PWM value within 3 minutes
          // it is basically an inversed slope
          ChannelValue[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ChannelValue[a],15,180);
        }
      }
     if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration* 0.5))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration * 0.25))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration * 0.75))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
    }



    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
      cloudindex++;
      if (cloudindex < numclouds)
      {
        cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  }
}

      
//End Cloud Function
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Reverse PWM slope from cloud function
byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
  long n=elapsedSecsToday(now());
  cstart*=60;
  cend*=60;
  if (n<cstart) return PWMStart;
  if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
  if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
  if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
  if (n>cend) return PWMStart;
}
//End Reverse PWM function
//*********************************************************************************************************************************

	
 
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

It's because you are trying to use latitude and longitude on your ino file, that was never declared within the ino.
It was declared on the library file.
If you intent to use those variables the way you coded in your ino, they have to have the word static in front of it.
http://arduino.cc/en/Reference/Static
So, all you need to do is:

Code: Select all

static long latitude, longitude;
Roberto.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

I tired placing
static long latitude, longitude;
right above the call
latitude=dmsToSeconds(x,y,x) and got the same error
I also tried moving that function call to the CalSun() function and still got the same error.

I had tried declaring it in the .INO previously... but didn't mention that in my post about the error.

Maybe you have it working and provide me with a more specific place to place that bit of intialization code.... but I thought you were saying to put it in the .INO

Its weird, because its not giving an unitiilized error and the error persists when I remove entirely the latitude=dmsToSeconds(x,y,z) and longitude=dmsToSeconds(x,y,z) so I dont see how its related to that being either declared or not. I had assumed that with a declaration in the library that got bundled into the code and I did not need to declare it in the .ino? Way over my head...
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

On h, replace this:

Code: Select all

long latitude, longitude;
With this

Code: Select all

static long latitude, longitude;
Roberto.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Sorry... deleted my first response to this last bit.

It compiles.

I am unsure it will do what its supposed to do.

Testing tonight, will work further and post what happens....
Thanks.

Starting to figure out the library a tiny bit as some of it is by reference using pointers whereas other functions are direct returns of a value...

Still need to figure out what the static declaration might do (asside from fixing a compile error) as other functions in the library need access to latitude and longitude which are declared as static to that part wherease the calculation of what they actually are in terms of a a value is executed within my code as a call to the library so I think this is going to be a problem. But I will try it and try fixing it before posing a bunch of questions... thanks
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Ok...

So I have done a LOT of coding since the last post.

If I want to finish this I am clearly going to need a bit of help and thus I need to explain what it is I am trying to do, and why. So... its a little bit of a read but I think this will be versatile somewhat portable code once its finished and its nearly complete *I think*... I think it might run if I could get past a few errors I cannot see but report in compile.

Basically, using CalSun to work with time to get number of seconds since 2000 for midnight of the new day, then pass this value to sunrise and sunset by reference to library EPHERMA and then library modifes memory location (pointer by reference) to reflect either sunrise or sunset (lowercase variables) then I convert these values to the elapsed time in seconds for the new day for sunrise and sunset accounting for daylight savings time everywhere (I think).

Then because I am so anal I cannot live without an analytical solution..

since I allow offsets for all 5 channels in both the rise and set time with respect to the true sunrise and sunset and since the offsets are accepted as an array, I can get a longer "evening" than a morning, if the blue channel sets say 2 hours after "sunset" as calculated but rises only 1 hour prior, so the morning with respect to the middle of the day by sunrise and sunset is "shorter" than the evening, so if you call parabola your colors will mix differently during the day as the centroid of the parabola will change based upon how unsummetrical your day lengths are for each channel (or color if you think about it that way). Since having different maxes in real time will change color blends, I calculated the half day length for each channel and then implemented a cosine function to model intensity for each 1/2 day- so that elapsed seonds of each day 1/2 will vary light intensity according to the function that is used to model solar insolation which is the cosine of tha angle of the sun incident to the plane of the planet as fractions of the number pi. Doing it this way each channel peaks at EXACTLY midday and if the evening is longer or shorter than the morning the intensity ramp is perfectly corrected to reach zero at the set time and 100% or whatever your channel setpoint is for midday- no color blend alterations but for SUBTLE changes in ratios unlikely to be observable by me.... and I may not even have been able to observe it if I had used parabola... but I had done all the math and wanted it this way...

Then the Insolation function uses the slope and the channel max intensity, the flicker point (so we dim from continuous light only to continuous light) to basically turn on and off the channels and set their intensity according to the -cos function which yields decimal 0-1 and thus fractional intensity. I calculate this onlyevery minute so I dont kill the CPU..
here is the code...

The compile errors are posted below that... the line numbers always weird me out, never exactly right it seems... sorry for the book... but this is not going to be obvious and I really would like help... so at least you know what I am doing.
Oh... the cloud function and some comm to and fro from PWM board to controller are still hanging on here.. totally dont know what cloud will do... but I think it should work. I will eventually up date the rest so my controller can display much of what Deckoz2302 uses... i.e. PWM channel intensities, cloud, storm, moon, etc etc... but thats for another day when this works.

Code: Select all


// Reef Angel Includes
#include "SWFLTEK_EPHERMA.h"
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <Globals.h>
#include <ReefAngel_Features.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <avr/wdt.h>




//Defines for ReefAngel PWM module

#define LEDPWM0 0
#define LEDPWM1 1
#define LEDPWM2 2
#define LEDPWM3 3
#define LEDPWM4 4
#define LEDPWM5 5


//*********************************************************************************************************************************
//Globals Vavriables for ReefAngel PWM Cloud, PWM Settings, Epherma Lat/Long Sunrise/Sunset
unsigned long sunrise;
unsigned long sunset;
unsigned long newDay;
int midDay;
unsigned int ChRiseSet[]={
    0,0,0,0,0,0,0,0,0,0};
int ChSlope[]={
        0,0,0,0,0,0,0,0,0,0};
byte dst;
//char MoonToday;
static byte lightningchance=0;
byte cloudduration=0;
int cloudstart=0;
byte PWMports[] ={
  3,5,6,9,10,11};
int ChannelValue[] = {
  0,0,0,0,0,0};
byte SeasonsVar[]={
  0,0,0,0,0,0,0,0,0,0,0,0}; // PWM0, sunrisehour, sunriseminute, sunsethour, sunsetminute, cloudstarthout, cloudstartminute, cloudduration,lightningchance, PWM1,PWM2,PWM3
byte cmdnum=255;
byte datanum=255;
boolean ForceCloud=false;
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Setup
void setup()
{
  Serial.begin(57600);
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  randomSeed(analogRead(0));
  pinMode(3,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  //wdt_enable(WDTO_1S);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  setSyncInterval(SECS_PER_HOUR);  // Changed to sync every hour.
  now();
       
}
//End Setup
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Loop
void loop()
{
  wdt_reset();
  //Serial.println(ChannelValue[0],DEC);
  if (cmdnum!=255)
  {
    ProcessCMD(cmdnum,datanum);    
    cmdnum=255;
    datanum=255;
  }
 // functions called from loop
  CheckCloud();
  CalSun();
  //Serial.println("Start");
  //Serial.println(ChannelValue[0],DEC);
  //Serial.println(rhour,DEC);
  //Serial.println(rmin,DEC);
  //Serial.println(shour,DEC);
  //Serial.println(smin,DEC);
  //Serial.println(cloudstart/60,DEC);
  //Serial.println(cloudstart%60,DEC);
  //Serial.println(cloudduration,DEC);
  for (byte a=0;a<6;a++)
  {
    analogWrite(PWMports[a],ChannelValue[a]);

  }
}
//End Loop
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Standard PWM Functions Receive/Process
void receiveEvent(int howMany) {
  wdt_reset();
  if (howMany==5)
  {
    byte cmd1, cmd2, cmd3, cmd4, cmd5;
    cmd1=Wire.read();
    cmd2=Wire.read();
    cmd3=Wire.read();
    cmd4=Wire.read();
    cmd5=Wire.read();
    if (cmd1=='$' && cmd2=='$' && cmd3=='$')
    {
      cmdnum=cmd4;
      datanum=cmd5;
      //Serial.println(cmd4,DEC);
      //Serial.println(cmd5,DEC);
    }
  }
  else
  {
    for (byte a=0;a<howMany;a++)
    {
      Wire.read();
    }
  }  
}

void ProcessCMD(byte cmd, byte data)
{
  wdt_reset();
  // Individual Channel
  if (cmd>=0 && cmd<=5)
  {
    ChannelValue[cmd]=data;
    analogWrite(PWMports[cmd],data);
  }
  if (cmd==6) ForceCloud=true;
}
//End Standard Functions
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Send Data Function
void requestEvent() {
  SeasonsVar[0]=ChannelValue[0];
  SeasonsVar[1];
  SeasonsVar[2];
  SeasonsVar[3];
  SeasonsVar[4];
  SeasonsVar[5]=cloudstart/60;
  SeasonsVar[6]=cloudstart%60;
  SeasonsVar[7]=cloudduration;
  SeasonsVar[8]=lightningchance;
  SeasonsVar[9]=ChannelValue[1];
  SeasonsVar[10]=ChannelValue[2];
  SeasonsVar[11]=ChannelValue[3];
  Wire.write(SeasonsVar,12);
}
//End Send Data
//*********************************************************************************************************************************
// Function to run Epherma Calc for sunrise/sunset/noon/moon phase 
boolean CalcDST(byte D, byte M, byte dow)
      { 
        //January, february, and december are out.
        if (M < 3 || M > 11)
          {
            dst=false; 
          }
        //April to October are in
        else if (M > 4 && M < 11)
          {
            dst=true; 
          }
        else
          {  
            int previousSunday = D - dow; // Sunday=1 Sat=7
            if (M==3 && previousSunday > 7)
             { 
               dst=true;
             }
            else if (M==11 && previousSunday<=0)
              {
                dst=false;
              }
          }
          return (dst);
    }
void CalSun(){
   // Every day at midnight, we calculate sunrise, sunset, time of highest sun, moon phase.
  if (hour()==0 && minute()==0 && second()==0) 
  {
      // Arduino uses Unix time- seconds since Jan 1 12:00 am UTC 1970
      //Must convert to MST by subtracting 7 hrs =7*3600=25,200 seconds
      //Must get daylight savings rules into effect or sunrise set will change by 1 hour
     // In D, M, dow;
    boolean isDST=CalcDST(day(),month(),weekday());
      //Using time library from Arduino.time_t now(); returns seconds since 1970
      //Library states #seconds to subtrace to bring to Jan 1 2000 as origin for time is 
      //#define SECS_YR_2000  (946684800) the time at the start of y2k
      //DST rules are march second sunday until november 1st sunday + 1 hr starting at 2 am which I dont care about so that part is neglected 
     unsigned long SecInput;
 //    char moontoday;
     if (isDST==true) 
       {
         newDay=now()+3600;
         SecInput=now()-946684800+25200+3600; //time in seconds since 2000 as GMT + 1 hr DST adjustment=3600
       }
     else 
       {
         newDay=now();
         SecInput=now()-946684800+25200; //time in seconds since 2000 as GMT
       }
     
      latitude=dmsToSeconds(25,0,0); //Set to about the latitude of the great barrier reef rotated into the morthern hemisphere
      longitude=dmsToSeconds(111,51,17); //Set to Salt lake City, or MST zone, USA or there abouts- actually the AT921 remote weather station in Salt Lake City UT.  Random web grab.
    
     sunrise=sunset=SecInput;
     SunRise(&sunrise);
     SunSet(&sunset);
     // Correct sunrise data to reflect elapsed time from midnight (newDay)
     // DST correction is allready in both variables so no need to deal with it
    sunrise=sunrise-newDay;
    sunset=sunset-newDay;
    
    //correct to elapsed time of d
    /*offsets for sunrise/sunset with colors all values in seconds offset from calculated sunrise value
    set up array with order ch0 am offset (negative to rise earlier), ch0 pm offset, ch1 am offset...ch5
    use this to set up all channel rise set values for your tank modify array size to reflect # channels*/
     int Choffset[]={
       -600,0,-3600,5400,0,600,-3600,5400,-4500,7200,0,0};

    
   //set up array to hold rise and set time for all channels
   //ch0r, ch0s, ch1r, ch1s, ch2r, chrs, ch3r, ch3s, ch4r, ch4s, ch5r, ch5s)
  
  
    //Calculate rise and set times for all channels in equivlants to elapsed seconds from midnight today
      unsigned int deltaY=3.141592653589793238462643383279502884197169399;
      midDay=(sunset-sunrise)/2;
      for (byte b=0;b<12;b++)
        {
        if (b%2==0)
          {
          ChRiseSet[b]=sunrise-newDay+Choffset[b];
          ChSlope[b]=deltaY/(midDay+abs(Choffset[b]));
           }
        else if (b%2==1)
          {
          ChRiseSet[b]=sunset-newDay+Choffset[b];
          ChSlope[b]=deltaY/(midDay+abs(Choffset[b]));
           }
      }   
    }
}
// end of function to get sunrise and sunset val 
//subsequent call to setInsolation will set lighting paramaters

void Insolation(){
    // Calculate time since day started correcting for DST
   int elapsedtime;
  if (dst==true)
   {
     elapsedtime=(now()+3600-newDay);
   }
  else
   {
     elapsedtime=(now()-newDay);
   }
   
  /*Now working on setting up each channel for correct insolation pattern
  This uses a simple cos function based upon day light length of each channel
  since channel day light lenghts vary there will be imperfect correlation 
  in time for max intensity value of all channels if your am and pm offsets are not equal 
  due to cos funstionality its a small difference and would bias the tank to blues every so slightly as you
  get further from the midpoint of the day if your running blue/violets early and late */
  
  
   // Channels for me are 0=RW 1=RB 2=LW 3=LB 4=Whole tank V  use in order to assign flicker points 
  int flicker[]={
    10,10,10,10,5,10};
  //What are the max values for ch intensity we want to reach use 0-255 scale for PWM board
  int ChInt[]={
    255,255,255,255,255,0};
  unsigned int deltaY=3.141592653589793238462643383279502884197169399;
    

     //
     //formula for light intensity in order to center the max intensity of all wavelengths and so as to keep color blend consistent
    //devide the white sun up time into two equal halves then determine the actual lenght of each 1/2 for each channel given the rise set times
   //included in the channel offsets so the Halves may not be equal- this works regardless of if you extend whites etc etc...
   // Isolation intensity is modeled as varying across the day with decimal fraction of intensity 
   //starting at -cos(0.5pi) (equals 0) to -cos(pi) at midday (equals 1) to -cos(1.5pi) at sunset (equals zero)
   // if you calculate the length of the half day as the delta X for slope and use 0.5pi as the delta Y for slope 
   //  The intensity is scaled from 0.000000001-1.0 of max set as a cos function from sunrise channel set to midday channel set 
   //  This is the formula -cos(pi/2+elapsedtime/slope)*((ChMaxVal-flicker)+flicker) in this way it starts at the flicker point and ends at max setpoint
  // Finally turn off lights if sun is not up.... moon useage of a PWM channel will require a slight mod to this code
  
  
  //and dont do this every cycle... worried it would kill the processor... update every 30 seconds
    if ((elapsedtime%30)==0)
      {
      for (byte b=0;b<12;b++)
        {
          if ((b%2==0) && (elapsedtime>==ChRiseSet[b]) && (elapsedtime<midDay))
            {
              if (b==0)ChannelValue[0]=(-cos((deltaY/2)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b]-flicker[b])+flicker[b]);
              else if (b==2) ChannelValue[1]=(-cos((deltaY/2)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b-1]-flicker[b-1])+flicker[b-1]));    
              else if (b==4) ChannelValue[2]=(-cos((deltaY/2)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b-2]-flicker[b-2])+flicker[b-2]));
              else if (b==6) ChannelValue[3]=(-cos((deltaY/2)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b-3]-flicker[b-3])+flicker[b-3])); 
              else if (b==8) ChannelValue[4]=(-cos((deltaY/2)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b-4]-flicker[b-4])+flicker[b-4]));
              else if (b==10) ChannelValue[5]=(-cos((deltaY/2)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b-5]-flicker[b-5])+flicker[b-5]));
             }
          else if ((b%2==1) && (elapsedtime<==ChRiseSet[b]) && (elapsedtime>==midDay))
            {
              if (b==1) ChannelValue[0]=(-cos((deltaY)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b]-flicker[b-1])+flicker[b-1]));
              else if (b==3) ChannelValue[1]=(-cos((deltaY)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b]-flicker[b-2])+flicker[b-2]));    
              else if (b==5) ChannelValue[2]=(-cos((deltaY)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b]-flicker[b-3])+flicker[b-3]));
              else if (b==7) ChannelValue[3]=(-cos((deltaY)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b]-flicker[b-4])+flicker[b-4])); 
              else if (b==9) ChannelValue[4]=(-cos((deltaY)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b]-flicker[b-5])+flicker[b-5]));
              else if (b==11) ChannelValue[5]=(-cos((deltaY)+(elapsedtime-sunrise/ChSlope[b]))*((ChInt[b]-flicker[b-6])+flicker[b-6]));
             }
          else if (((b%2==0) && (elapsedtime<ChRiseSet[b])) || ((b%2==1) && (elapsedtime>ChRiseSet[b])))
            {
              if (b==0) ChannelValue[0]=0;
              else if (b==2) ChannelValue[1]=0;     
              else if (b==4) ChannelValue[2]=0;
              else if (b==6) ChannelValue[3]=0; 
              else if (b==8) ChannelValue[4]=0;
              else if (b==10) ChannelValue[5]=0;//if using for moon lights ChannelValue[5]=MoonPhase or something like that using global variable for MoonPhase
            }  
          }
        } 
}
//*********************************************************************************************************************************
// Random Cloud/Thunderstorm effects function
void CheckCloud()
{

  // ------------------------------------------------------------
  // Change the values below to customize your cloud/storm effect

  // Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
  // For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 1

  // Percentage chance of a cloud happening today
  // For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 65

  // Minimum number of minutes for cloud duration.  Don't use max duration of less than 6
#define Min_Cloud_Duration 6

  // Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 20

  // Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 2

  // Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 4

  // Only start the cloud effect after this setting
  // In this example, start could after 11:30am
#define Start_Cloud_After NumMins(9,00)

  // Always end the cloud effect before this setting
  // In this example, end could before 8:00pm
#define End_Cloud_Before NumMins(19,00)

  // Percentage chance of a lightning happen for every cloud
  // For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 60

  // Channels used by the actinic LEDs on the PWM Expansion module
  // These channels will not be dimmed when the cloud effect is triggered
  // Number is a binary form. B001100 means channel 2 and 3 are used for actinics
#define Actinic_Channels B011010

  // Channels used by the daylight LEDs on the PWM Expansion module
  // These channels will be used for the spike when lightning effect is triggered
  // Number is a binary form. B000011 means channel 0 and 1 are used for daylights
#define Daylight_Channels B000101

  // Note: Make sure to choose correct values that will work within your PWMSLope settings.
  // For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
  // Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes of effects or unforseen 
  // results could happen.
  // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
  // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can
  //fit in that 510 minutes window.
  // It's a tight fit, but it did.

  //#define printdebug // Uncomment this for debug print on Serial Monitor window
#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process. 


  // Change the values above to customize your cloud/storm effect
  // ------------------------------------------------------------
  // Do not change anything below here

  static byte cloudchance=255;
  static byte numclouds=0;
  static byte cloudindex=0;
  static byte lightningstatus=0;
  static int LastNumMins=0;
  // Every day at midnight, we check for chance of cloud happening today
  if (hour()==0 && minute()==0 && second()==0) cloudchance=255;

#ifdef forcecloudcalculation
  if (cloudchance==255)
#else
   c && cloudchance==255) 
#endif
    {
      //Pick a random number between 0 and 99
      cloudchance=random(100); 
      // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
      if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
      // Check if today is day for clouds. 
      if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0; 
      // If we have cloud today
      if (cloudchance)
      {
        // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
        numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
        // pick the time that the first cloud will start
        // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day. 
        cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));

        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  // Now that we have all the parameters for the cloud, let's create the effect
  
  if (ForceCloud)
  {
    ForceCloud=false;
    cloudchance=1;
    cloudduration=10;
    lightningchance=1;
    cloudstart=NumMins(hour(),minute())+1;
  }

  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 15 for the duration of the cycle
          // and finally slope up from 15 to PWM value within 3 minutes
          // it is basically an inversed slope
          ChannelValue[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ChannelValue[a],15,180);
        }
      }
     if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration* 0.5))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration * 0.25))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration * 0.75))) && second()<1.5) 
      {
        int strikes = random(6);
        for (int b=0;b<strikes;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            if (b==random(strikes)) delay(2);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
    }



    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
      cloudindex++;
      if (cloudindex < numclouds)
      {
        cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  }
}

      
//End Cloud Function
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Reverse PWM slope from cloud function
byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
  long n=elapsedSecsToday(now());
  cstart*=60;
  cend*=60;
  if (n<cstart) return PWMStart;
  if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
  if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
  if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
  if (n>cend) return PWMStart;
}
//End Reverse PWM function
//*********************************************************************************************************************************

	
  
Then the errors... warnings I can live with I think (advice) errors kill it...

Code: Select all

In file included from _021712_LatLonSeasons_PWMboard.cpp:8:
\\psf\Home\Documents\Arduino\libraries\Globals/Globals.h:35: warning: only initialized variables can be placed into program memory area
\\psf\Home\Documents\Arduino\libraries\Globals/Globals.h:36: warning: only initialized variables can be placed into program memory area
_021712_LatLonSeasons_PWMboard.cpp: In function 'void requestEvent()':
_021712_LatLonSeasons_PWMboard.cpp:168: warning: statement has no effect
_021712_LatLonSeasons_PWMboard.cpp:169: warning: statement has no effect
_021712_LatLonSeasons_PWMboard.cpp:170: warning: statement has no effect
_021712_LatLonSeasons_PWMboard.cpp:171: warning: statement has no effect
_021712_LatLonSeasons_PWMboard.cpp: In function 'void Insolation()':
_021712_LatLonSeasons_PWMboard:314: error: expected primary-expression before '=' token
_021712_LatLonSeasons_PWMboard:316: error: expected `)' before ';' token
_021712_LatLonSeasons_PWMboard:323: error: expected primary-expression before '=' token
_021712_LatLonSeasons_PWMboard:323: error: expected primary-expression before '=' token
_021712_LatLonSeasons_PWMboard.cpp:344: warning: comparison between signed and unsigned integer expressions
_021712_LatLonSeasons_PWMboard.cpp:344: warning: comparison between signed and unsigned integer expressions
_021712_LatLonSeasons_PWMboard.cpp: In function 'void CheckCloud()':
_021712_LatLonSeasons_PWMboard.cpp:427: warning: unused variable 'LastNumMins'
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Ok... So I managed to figure out all the errors, it was all c++ formatting issues which i am going to blame on my prior VB work... but in reality it needed to happen so hopefully I am past some of this garbage.

I am as confident as I can be, having not run the .INO that the code will probably do mostly what I want it to do... which is to say not super confident but I would hope it would run more or less.

However, I am still having issues with the library call.

This summarizes it.

Using this .INO (could it be any easier)

Code: Select all

#include <SWFLTEK_EPHERMA.h>
void setup()
{
 
}

void loop()
{
latitude=dmsToSeconds(100,0,0);
}
I get this error on a compile...

Code: Select all

\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA/SWFLTEK_EPHERMA.cpp:24: multiple definition of `longitude'
sketch_feb26a.cpp.o:C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\build7597137692051508666.tmp/sketch_feb26a.cpp:8: first defined here
SWFLTEK_EPHERMA\SWFLTEK_EPHERMA.cpp.o: In function `ddToSeconds(float)':
\\psf\Home\Documents\Arduino\libraries\SWFLTEK_EPHERMA/SWFLTEK_EPHERMA.cpp:24: multiple definition of `latitude'
sketch_feb26a.cpp.o:C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\build7597137692051508666.tmp/sketch_feb26a.cpp:8: first defined here
which is the same error I get compiling this which is my "complete" (until I can run it and find out if it works) .INO that does I hope exactly what I posted in detail above...

It sounded like Roberto was trying to tell me I cannot do this, but its so far as I can tell, what I am supposed to do to use the library....

I have corresponded with the author of the Library and Michael is unsure whats going on... I may not have been totally clear so I am writing him again...with the above example which I hope will greatly clarify things...

However, if someone here knows what is going on or how to fix it given the details below, I would really appreciate it.


The issues is, that when I call SunRise or SunSet which are library calls, they (or helper functions in the library in actuality) REQUIRE that latitude and longitude be specified and are not passed these values but rather simply say something like

void helper(){
//do stuff and return a value
return value*latitude/60
}
or something like that, so the library is requiring that latitude and longitude be initialized variables of long type

so since a good number of the functions in the .cpp file require latitude or longitude, its declared without a value in the .h file so if I initialize again in the .ino thats an error... and If I don't, I get a the inexplicable error I posted above, that somehow, by not defining it twice, it thinks it has been????????
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

You can only declare latitude and longitude once.
If you have it somewhere else, it will cause the error you are seeing about multiple definitions.
Roberto.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Ok... so I think I am starting to see that I have no relationship to the error... just doing this as a stand alone .ino file and attempting to compile yields the same error...


#include "SWFLTEK_EPHERMA.h"
void setup(){
}

void loop() {

}

Its something with the library... I am lost now and hope that Michael can clarify....
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

*REVISION OF PRIOR COMMENT*... I have no relationship to error :lol:

Ok so I looked back again and I have always assumed (perhaps wrongly :mrgreen: ) that the .h file I download from Michaels site which is this

Code: Select all

/*
	SWFLTEK_Ephemera copyright 2010 by Michael Rice

	Swfltek Ephemera is a set of astronomical functions often of interest.
	Though written for avr-gcc, it should convert easily to other compilers.

	Most functions require an unsigned long time stamp as an argument. This 32 bit value represents
	the number of seconds elapsed since midnight Jan 1 2000 GMT.
*/
#ifndef SWFLTEK_EPHEMERA_h
#define SWFLTEK_EPHEMERA_h



// geographic coordinates, in seconds of arc North and East
long latitude, longitude;

// conveniences to convert two typical representations into seconds of arc
long ddToSeconds(float);
long dmsToSeconds(int, unsigned char, unsigned char);

// return equation of time in seconds
int equation_of_time(unsigned long);

// adjust stamp to Solar noon
void SolarNoon(unsigned long * );

// return solar declination in radians
double SolarDeclination(unsigned long dt);

// return seconds between sunrise and sunset
unsigned long daylightseconds(unsigned long);

// compute time of sun rise or sunset
char SunRiseSet(unsigned long*, char);

// shorthand form
char SunRise(unsigned long*);
char SunSet(unsigned long*);

// convert from GMT to Mean Sidereal Time
unsigned long GMSidereal(unsigned long);
unsigned long LMSidereal(unsigned long);

// approximate phase of the moon
char MoonPhase(unsigned long);

// season
unsigned char season(unsigned long);



/*
==================================================================================================
SNIP HERE			SNIP HERE			SNIP HERE			SNIP HERE			SNIP HERE
==================================================================================================
*/

#include <stdlib.h>
#include <math.h>

// arc-seconds per radian
#define _sec_rad 206264.806247096370813

// axial tilt of earth at epoch, in radians
#define _tilt 0.409092804222329

// tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _tropical_year 31556925

// 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)
#define _zenith 3.11250383272322

//#include "SWFLTEK_Ephemera.h"
/*------------------------------------------------------------------------------------------------
convert degrees to seconds of arc
*/

// 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 for' 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 Noon' adjusts the passed time stamp to the time (GMT) of local solar noon.
	The accuracy is about 40 seconds (set by the equation of time).
*/
void SolarNoon(unsigned long * dt){
long r;

	// Set stamp to noon GMT
	*dt /= 86400UL;
	*dt *= 86400UL;
	*dt += 43200UL;

	// adjust for equation of time, at noon GMT
	*dt -= equation_of_time(*dt);

	// rotate to our longitude
	r = longitude / 15L;
	*dt -= r;
}

/* -----------------------------------------------------------------------------------------------
	'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);
}

/* ------------------------------------------------------------------------------------------------
	Mean Sidereal Time.
	Accurate to within 1 sidereal second.
*/
unsigned long GMSidereal(unsigned long gmt){
unsigned long long sidereal;

	sidereal = gmt * 10027379094LL; // multiply by 10 Billion times the ratio
	sidereal /= 10000000000LL; // and divide by 10 billion
	sidereal += 23992LL; // add sidereal time at the epoch
	return sidereal;
}

unsigned long LMSidereal(unsigned long gmt){
	return  GMSidereal(gmt ) + longitude / 15L;
}

/* ------------------------------------------------------------------------------------------------
 	An approximation of the moons phase.
	Magnitude of the result approximates the percentage of the moons illuminated surface.
	Sign of the result indicates a Waxing (+) or Waning (-) moon.
	It uses the mean lunar cycle, which may differ from the actual by many hours.
	As such, it may vary from the correct value by 20%.
*/

char MoonPhase(unsigned long d){
long r;

	// the first full moon of the epoch was Jan 21 at 04:40
	// but the 'mean' full moon was 4 hours later
	d -= 1759365UL; // subtract time of first full moon
	d %= 2551443L; // mod by the mean lunar cycle
	r = d - 1275721L;
	r /= 12757L;
	return r;
}

// Season of the year
// 0 = winter, 1 = spring, 2 = summer, 3 = fall
unsigned char season(unsigned long dt){

	dt += 838800UL;// refer to prior winter solstice
	dt %= _tropical_year;

	dt /= 7889400UL; // 91.3125 days

	if(latitude<0) dt += 2UL;
	return dt % 4;
}

/*
==================================================================================================
SNIP HERE			SNIP HERE			SNIP HERE			SNIP HERE			SNIP HERE
==================================================================================================
*/

#endif // SWFLTEK_EPHEMERA_h

was supposed to be split into a .h file and a .cpp file. :oops:

But when I go back to his site now, I see he has added that they distribute a single .h which is easier to maintain compatibility. Since I had previously split his file into a .h and a .cpp - with the stuff between the **snip here**** parts of the file above and it was not working... I tried emptying my .cpp file (its got no content) and replaced my .h with the entire code above...

and it now compiles... :shock:


EVEN better- it is outputting a day length of just about 11.6 hours or something close to that. I have some math issues I need to clean up related to calculating when sunset is in terms of todays minutes and I still could have things totally wrong, but it actually looks like it might work!! Wish I could stay up and work but real work calls early tomorrow... more perhaps by the weekend... maybe working tested code!

Still curious WHY it was bombing when I split the files... which apparently I should not have done...
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Quick question.


Trying to figure out how to best handle a power off/on cycle and ensure that the lights get put back on and on the right cycle.

As it is now I calculate sunrise/sunset 1 time per day at midnight (as its then easy to calculate how far into the day the lights go on or off). But... if the power cycles, or the WDT resets, the lights would NOT come back on. I can set a PWMChannel value to non-zero in the void setup() which I am doing, and they would come on at that level and then be shut off at midnight, but there must be a more elegant solution.

I read WDT threads via search, but not seeing an answer. Is there an easy way to have the WDT or really any reboot trigger a value change that can be monitored to then trigger a recalculation and then reset.

maybe I could even just look at the sunrise and sunset variables and if they are non-zero leave it alone...

like pseudo code..
void setup(){
sunrise and sunset=0 }

void loop(){

if sunrise or sunset ==0{
trigger variable=true;]
elseif sunrise or sunset =>0{
trigger variable =false;}

call calculate sun()// really does not matter where this call goes since its execution is dependent upon it being midnight or the trigger being true.
}

calculate sun(){
if (its midnight || trigger==true)
{ run calculation
}
else do nothing as we assume we already did this once today
}

then I would have to have some way of fixing times for when its calculated midday... I would have to re-write that section to be less reliant on midnight being the time of calculation but its not too hard...

would this work

or better ideas?
Post Reply