C library for Sun/Moon effects

Do you have a question on how to do something.
Ask in here.
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

I am using yoir code & sawing lightning effect from last 30 mins. How to make it short though?

Secondly, i live +37800 from GMT. Wat changes shud i need to make in my code to set this on my local time?
Image
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Set GMT offset as a negative number.

I am not sure, I came home to an extended storm as well. I am going to look at the code. I must have changed something and forgot to catch the other end of the loop for time. This should be a trivial fix. I will post the change when I get it figured out. At least its running!! Congrats!!
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

Yeah. Its running but i think still long way to go. Any chances for solar or lunar eclipse in it?
Image
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

But seriously... an eclipse? How would you even code that.. a numerical model of the solar system?
Seems esoteric... and what would it look like? a dark tank?

I have no plans it will not be included unless you or someone else writes it in.

I think I know what was causing the problem

find this line

byte CloudsMax=11;//DONT INCREASE BEYOND 10 or it will DIE, or increase array size to handle it

Change the 11 to a 10 as below...

byte CloudsMax=10;//etc

I was overwriting the array... just missed that I usually add one to the max value on random calls but had allready done that on the CloudsMax declaration so it occasionally, only rarely on days when the random call hit 11.. would write past the end of the array into random memory... or just generally be wrong.

I also note that I found a few things that no one would probably notice but that I didn't like and I rewrote a fairly significant part of the weather function as well as added two additional functions. Its kinda esoteric stuff like making sure a cloud starts by ramping intensity to 50% of set point before the random walk starts and then changed how the StormStart is initiated in terms of limiting the durations as that was also looking a bit fishy and possibly could have also been causing this problem.

If fixing the above line still does not fix the error, tomorrow I will be posting my new code after I test it (its just copy and paste and only 1 segment of the program so easy). But until then, if your worried about it


Just set
byte CloudChance=100;//% Chance of a Cloud every day
to
byte CloudChance=0;

that will precent clouds and storms until I get this figured out but lights and moon lights and sunrise and sunset calc and insolation modeling will go on normally.
User avatar
JNieuwenhuizen
Posts: 96
Joined: Thu Feb 16, 2012 12:39 am
Location: South Africa

Re: C library for Sun/Moon effects

Post by JNieuwenhuizen »

Lol, ok, will change the GMT to -

I saw clouds, lightning, storm etc. All I can say is Well Done Rufessor, Well Done!

It is some of the best I have ever seen
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

BE SURE TO ALSO modify CloudsMax

EVERYONE RUNNING THIS SHOULD MAKE THIS CHANGE

byte CloudsMax=11;//DONT INCREASE BEYOND 10 or it will DIE, or increase array size to handle it

Change the 11 to a 10 as below...

byte CloudsMax=10;//etc

-

Thanks! Glad your liking it. Once you get updated to the newest version (see below) if you don't mind and have the ability- could you post a video? It would be fun to see this running a whole bunch of different setups.



And... there are.... more improvements coming.

I made a decent number of changes throughout the Weather function and re-wrote ClearSky() as well as added two new functions that should create a less abrupt start and end to the clouds. Its totally minor and you would have to be randomly watching the tank at exactly the millisecond that the cloud started... but as it is now when the cloud starts the light intensity simply gets set to 50% of prior... and at cloud end I had a function ClearSky() that was supposed to bring the lights up to max gradually... but I never quite finished it (apparently) and now that I know more about how things look visually I re-wrote it so that the 10 seconds after the programmed completion of a cloud are spent bringing lights back to full so it transitions cleanly to the eye. I also added a function that should ENSURE that there is no funny business with length of storms... given the above correction to CLoudMax I think we would have been fine without this new function... but it is slightly more sophisticated and should allow people to easily set their MAX cloud length which is nice. Its running on my tank- when I get home if I can see a storm start and end normally I will post the new code- its just a big copy and paste so very easy to change without affecting a single setup variable.

After this, unless someone reports a bug I will be setting up comm....
User avatar
JNieuwenhuizen
Posts: 96
Joined: Thu Feb 16, 2012 12:39 am
Location: South Africa

Re: C library for Sun/Moon effects

Post by JNieuwenhuizen »

So I saw a storm today, it looks so good

http://www.youtube.com/watch?feature=pl ... QwJo3AhRAQ
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

WOW-

That works really really well with some white lights on... I am almost wondering if its better than the straight blue that I run for a storm! Might try changing a few things to allow me to use the white strings as well but constrain their intensity to a very low level during the storm... Super cool! Your camera does a much better job of allowing us to see the cloud intensity random walk.

Nice tank! I like all the fish- nice variety!

Thanks!

One more thing, looks like a really nice job on the build of that fixture!


Ok--- added an edit.

I like your tank so much, I am writing in new code that will leave whites on (constraining their intensity) during storms that are programmed to have less frequent lightning strikes.... then the "severe" storms with frequent lightning effects will turn off whites and use them only for lightning. Yours looks to good not to implement this change!
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

So here is the changes i made in DST of code which i am using. now the problem is that local time here is 22:00 but lights are still on whereas we should suppose to have sunset now.

//***********************************ARRAYS YOU MUST MODIFY TO MAKE YOUR TANK SET UP WORK*********************
//CHANGE this to the number of seconds your time zone is offset from GMT (WITHOUT REGARD TO DAYLIGHT SAVINGS TIME)
int GMToffset=-19800;//INDIA IST time zone is -19,800 if that helps you
//GLOBAL Variables YOU NEED TO CHANGE
boolean ApplyDST=false;//CHANGE THIS IF YOUR NOT USING DST
byte ChMax[]={200,210,200,210,225,0,0,0};//Incremental value (Max-flicker) above flicker you want as max intensity-
byte flicker[]={30,28,30,28,25,0,0,0};//need to input actual values here for flicker point on all channels in PWM expansion box
boolean Wchannel[]={1,0,1,0,0,0,0,0}; //use 1 to designate white channel (i.e. off during storm and used for lightning). Array corresponds to PWM channel 0-5 in order
//Array to give direction to dimming.  e.g. DimOrder[]={0,0,1,1,0,0} (cloud chase effects, just group channels you want to dim together during a cloud or storm 
//as either a 0 or a 1, i.e. all left side channels are 0 all right are 1 or all front are 0 all back are 1 or whatever
//(which is zero or 1 will change who dims first).  set them all to 0 if your tank has no left/right or front/back lights.
byte DimOrder[]={0,0,1,1,1,0,0,0};
//**********************************DONE CHANGING THINGS HERE BUT YOU MUST CHANGE ChOffset array IN CalcSUN function******

Image
User avatar
JNieuwenhuizen
Posts: 96
Joined: Thu Feb 16, 2012 12:39 am
Location: South Africa

Re: C library for Sun/Moon effects

Post by JNieuwenhuizen »

rufessor wrote:WOW-

That works really really well with some white lights on... I am almost wondering if its better than the straight blue that I run for a storm! Might try changing a few things to allow me to use the white strings as well but constrain their intensity to a very low level during the storm... Super cool! Your camera does a much better job of allowing us to see the cloud intensity random walk.

Nice tank! I like all the fish- nice variety!

Thanks!

One more thing, looks like a really nice job on the build of that fixture!


Ok--- added an edit.

I like your tank so much, I am writing in new code that will leave whites on (constraining their intensity) during storms that are programmed to have less frequent lightning strikes.... then the "severe" storms with frequent lightning effects will turn off whites and use them only for lightning. Yours looks to good not to implement this change!

It's a good thing I posted the video then!

How long should a storm last? I have one now for about 3 hours. I did change the 11 to a 10.
And the gmt offset, I think I am making a mistake or something, the -7200 gives me moonlights during day

Edit --
So to give you an idea, when I posted this - it as 6:40PM in South Africa, am i not calculating the time wrong?
Ideally I would like my sun to set roughly 10pm SA time, and rise about 10am
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: C library for Sun/Moon effects

Post by rimai »

Try using 0 offset.
Just a guess
Roberto.
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

I am using -19800 as my country timing is +5:30 GMT. The light are on just like a bright sunny day looks like there is something fishy with the DST btw i m notan expert in coding its just my wild guess.
Image
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Two thoughts.

I have a bug I don't know about, or your latitude and longitude settings are off. Which I am guessing might be true.

The program requires that it know if your east or west of the prime meridian or north or south of the equator.
Longitudes EAST of the prime meridian are given in POSITIVE values
Latitudes South of the equator are given in NEGATIVE values.

The program does not use an E/W or N/S tag, it requires the sign to indicate the same thing.

Thus, since I live in the USA- I am WEST of the prime meridian and my longitude is a negative number and I am NORTH of the equator so my latitude is positive and my settings look like this

latitude=dmsToSeconds(40,44,32); //Set to about the latitude of Salt Lake City
longitude=dmsToSeconds(-111,48,11); //Set to Salt lake City,

For instance, according to google... SouthAfric is 28 South (negative) and 25 East (positive)
So a very rough set up would be

latitude=dmsToSeconds(-28,0,0);
longitude=dmsToSeconds(25,0,0);

which is exactly opposite in sign to my coordinates which makes perfect sense as South Africa is on the other side of both the prime meridian and the equator... Let me know if this provides a eureka moment... Else I will run mine with serial debugging and see what I get for rise set but I doubt the library functions are wrong.

In terms of sun rise and sunset... this program is designed specifically to run the lights EXACTLY according to the seasonal variation of the sun. So.... it will rise and set according to what you see out your window within a few minutes. I personally like to look at the tank later at night and extend my lights using the Offsets to achieve the color and duration I want, you could also delay the rise etc etc... the day length will however change with seasons.

You could chop out the entire segment that does the calculation and simply hard code a sunrise and sunset as the number of seconds past midnight in both instances (this would be easy but I have no plans to release an option for this right now).

I am still looking at code to see if there is something I am missing with the storms but I don't see it... they should be limited to 500 seconds. I will post my rejigged code tonight for people to try and hope this fixes it but I am going through line by line to see what the heck is up.


Let me know if you had an error in the lat lon settings... almost have to be this unless your ChOffset array has really big number is in (like 3,600=1 hour if your off by 2-4 hours its got to be a really big number)


Can you tell me the local time your tank lights rise and set and the local time of the actual sunrise and sunset. An error in the sign of the latitude could reasonably give you an hour or two difference with the day being longer than anticipated as your in winter now and were in summer. An error in sign in the longitude would basically invert your day and the moon lights would be on when its dark in the USA .... but thats not quite right in South Africa.

PLEASE reply with precise information about the sun rise sunset errors and if it was a simple sign error.

If its not that. Please record approximate rise and sets as hours from your local midnight.
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

You should not be using DST algorithm unless you LIVE in the USA or a country that EXACTLY follows the DST rules of the USA.

Please SET this (its in the header of things you need to change)

boolean ApplyDST=true;//CHANGE THIS IF YOUR NOT USING DST

to
boolean ApplyDST=false;//etc

I am reasonably certain the DST algorithm is accurate but its structured based upon calendar math using the USA rules for DST which actually changed a few years ago (I am using current rules). Its working for me... I will not know for sure if its working until the fall here when it will stop subtracting an hour.

EVEN if it was enabled inappropriately (which is the case if your in another country and have it set to true) it only at its worst case subtracts an hour from the time... so it would not shift light by more than that- EVER.

I don't know whats up with the long storms. I am hoping that my rewrite fixes something I am not seeing. NONE of the settings you should be playing with (i.e. things I have posted as things to change) could possibly influence this. So its me.. unfortunately. SORRY!
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

do we need to Apply DST false here also
    // Every day at midnight, we calculate rise, set, time of highest sun, moon phase, or we do this if system has reset
        if (ApplyDST==true){
            CalcDST(day(),month(),weekday());
        }
This is what i have done with the longitudes & latitudes
        //Calculate Latitude and Longitude converting from Degree, Minute, Seconds to decimal
        latitude=dmsToSeconds(26,27,81); //Set to about the latitude of Kanpur City
        longitude=dmsToSeconds(80,20,68); //Set to Kanpur City, or IST zone, INDIA
        
will be uploading this once you sort out.
Image
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

No... thats part of the code that actually looks at what you set that variable to and then decides if it should run the function that tells it if it is in DST or not so that it can change the time ahead one hour if necessary.... If you mess with this part you might actually hard code DST in or possibly out... I would have to think about it but its not worth it... don't play with that part.

Good to ask... most of this code is not something you really probably want to mess with changing unless you have some background and completely understand what its doing, there are a lot of interdependent things that are not necessarily obvious.... but its also not perfect so it could use the help you and others are providing to debug this. I appreciate your putting up with strange events. I think we can figure this out.

I just killed arduino somehow else I would be posting my last code. All I did was alter the update interval for the cloud effect to run it every 150 msec (I was seeing flashes of intensity changes so I lowered the max change per interval and then reduced the interval of lighting intensity update to 150 msec from 300 msec... I swear I can see the flashes at 300 msec... but its kinda subliminal almost... it was bugging me.

And then suddenly....

Everytime I compile I get...

at java.util.regex.Pattern$Loop.match(Pattern.java:4295)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)

I am pretty sure its a pre compile error (cause i kan type google) and the only other precompile error I think I have seen was solved by deleting the Arduino temp directory because Arduino loves to precompile a whole bunch of files and the directory gets stacked up.... but since I switched from PC to MAC I don't know where it is...

Any help..? Thinking that deleting it will probably solve issue, it not I will search code again... but I all ready did a little big..

yech... reloaded arduino updated and still java errors... I even tried deleting the reef angels preferences file by moving it to the desk top but it seemed not to help. UGH. I think I go to sleep and figure it out in the morning... I would post code but now I am not sure if I made all the necessary changes to compensate for the faster "frame rate" of the cloud display...

Let me know if lat lon sign errors were present and causing rise set issues... I am assuming that until I hear differently.
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

Thanks for the response. i have loaded the code which change in longitude & Latitude values. will update if i find any effect.
Image
User avatar
JNieuwenhuizen
Posts: 96
Joined: Thu Feb 16, 2012 12:39 am
Location: South Africa

Re: C library for Sun/Moon effects

Post by JNieuwenhuizen »

ok, So I made the changes on the code, I will upload it now

To give you a idea, this is the code EXACTLY as I will upload it to the PWM module now

Code: Select all

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

//You MUST have installed the SWFLTEK Ephmerma library- this library was written by Michael Rice (swfltek.com) 
//its source code and distribution rights are stated in the .h file -  Thanks to Michael for doing such a great job 
#include <Time.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <avr/wdt.h>
//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>


//***********************************ARRAYS YOU MUST MODIFY TO MAKE YOUR TANK SET UP WORK*********************
//CHANGE this to the number of seconds your time zone is offset from GMT (WITHOUT REGARD TO DAYLIGHT SAVINGS TIME)
int GMToffset=0;//USA MST time zone is 25,200 if that helps you
//GLOBAL Variables YOU NEED TO CHANGE
boolean ApplyDST=false;//CHANGE THIS IF YOUR NOT USING DST
byte ChMax[]={225,225,225,225,225,0,0,0};//Incremental value (Max-flicker) above flicker you want as max intensity- 
byte flicker[]={30,30,30,30,30,30,0,0};//need to input actual values here for flicker point on all channels in PWM expansion box
boolean Wchannel[]={1,1,1,1,0,0,0,0}; //use 1 to designate white channel (i.e. off during storm and used for lightning).  Array corresponds to PWM channel 0-5 in order
//Array to give direction to dimming.  e.g. DimOrder[]={0,0,1,1,0,0} (cloud chase effects, just group channels you want to dim together during a cloud or storm 
//as either a 0 or a 1, i.e. all left side channels are 0 all right are 1 or all front are 0 all back are 1 or whatever
//(which is zero or 1 will change who dims first).  set them all to 0 if your tank has no left/right or front/back lights.
byte DimOrder[]={0,1,0,1,0,0,1,1};
//**********************************DONE CHANGING THINGS HERE BUT YOU MUST CHANGE ChOffset array IN CalcSUN function******


//defines for SWFLTEK functions
// 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

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
//Unless your planning on editing the program DO NOT CHANGE ANYTHING HERE
long latitude, longitude;
byte TrueIntensity[8];//array used to place hold values calculated by insolation
long elapsedTime;//used multiple places as elapsed since new day.
//Globals Vavriables for ReefAngel PWM Cloud, PWM Settings, Epherma Lat/Long Sunrise/Sunset
unsigned long rise;//time in seconds from midnight for sunrise today
unsigned long set;//time in seconds from midnithgt for sunset today
unsigned long newDay;// time in seconds since 2000 adjusted for DST (if used) at a new day (12:00:00am)
long ChRiseSet[16];//times of rise and set for all 8 channels based upon offsets from calc rise and set values
float ChSlope[16];//slopes for 1/2 day calculations based upon time from offset to midday for channel 1-8
long CloudMaster[20];// Set up array to hold start and end times for clouds for the day- dont size it dynamically make it big enough for any day
long midDay;// exactly 1/2 way between rise and set, i.e. my take on solar noon- which its not... 
byte PWMports[] ={
    3,5,6,9,10,11};
byte ChannelValue[8];// Array to store current setpoint for PMW control of output to ch (0,1,2,3,4,5)
unsigned long StrikeStart;//timer to keep track of strike sequence
int StrikeMaster[20];//Array to hold random strike pattern generated by weather array is sized to MAX needed given strike patter generator (8 strikes=16 positions)
byte StrikeNumber;//place to hold total number of strikes this sequence
boolean StrikeNow;//starts lightning strike sequence in loop state change made in weather/storm loop
byte StrikeCount;//Used to properly sequence strike sequence for delay between strikes
byte cmdnum=255;
byte datanum=255;
byte dow=0;//day of week
boolean trigger; //used a few places as a switch to ensure things only run 1x when needed trigger 2 is for daily reset of sunrise sunset
byte strikePattern, strikeTime;//used in Lightning() for timing of particular instance of strike 
boolean isDST;//are we in DST 
boolean Cloud;// are we in a cloud interval on days that have clouds
boolean CloudToday;//set in CalcSun if randomization yields a day with clouds.
boolean IsStorm;// are we in a storm
byte CloudsTotal;// how many clouds today
long lastmillis;// variable to track millis to enable cloud and insolation loop restriction by time
boolean StormAdvance;//storm timer for light effect advance
boolean InsolationAdvance;//when true we recalculate light intensity during clear sky
byte counter;//used to track millis advance for insolation,cloud trigger
//****************************************
//END HEADER/Global Variable declaration//
//****************************************

//Setup
void setup(){
    Serial.begin(57600);
    Wire.begin(8);
    //Wire.onReceive(receiveEvent);
    //Wire.onRequest(requestEvent);

    pinMode(3,OUTPUT);
    pinMode(5,OUTPUT);
    pinMode(6,OUTPUT);
    pinMode(9,OUTPUT);
    pinMode(10,OUTPUT);
    pinMode(11,OUTPUT);
    wdt_enable(WDTO_1S);
    randomSeed(analogRead(0));//start random generator at a different point each time (not perfect but whatever its gonna be pretty damn random)
    setSyncProvider(RTC.get);   // the function to get the time from the RTC
    setSyncInterval(SECS_PER_HOUR);  // Changed to sync every hour.
    
    isDST=false;//set day light savings time correction to false, is overridden by DST function if you choose to use it 
    dow=0;//set Day Of Week (dow) to a 0 value which is impossible (day()=1-7)... so we trigger calcSun on restart 
    StrikeNow=false;//no lightning strike yet
    CloudToday=false;//set to no clouds so CalcSun can set correctly if should be true
    Cloud=false;//set cloud to false
    IsStorm=false;//set storm to false
    lastmillis=millis();//start our millis timer now
    counter=0;//used in weather for triggering a storm, triggering lightning in a storm.
    StrikeCount=0;//Number of lightning strikes in the sequence.. set to zero until initialized in sequence
}
//End Setup
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Loop
void loop(){ 
    wdt_reset();
    if (cmdnum!=255){
        ProcessCMD(cmdnum,datanum);    
        cmdnum=255;
        datanum=255;
    }
    
    if (dow!=day()){ //be aware that during DST times the new day calculation will occur 1 hour early....but its corrected out
      trigger=true;//on reset this will also be true as numbering is 1-31 max
      //Serial.println("We set trigger to true");
      dow=day();//now set dow to actual day numbering so that at midnight/new day we again calc sunrise sunset
    }
  
    //Use millis to enable tracking of time interval
    //this will ensure at least minimally 1 sec intervals but may be longer if program is slow during a long calculation but at least never shorter.
    if ((millis()-lastmillis)>=300){
        lastmillis=millis();
        counter+=1;
        StormAdvance=true;
        //now a bunch of stuff that may or may not be true at the same time but that all needs to happen when its true
        if (counter==0) InsolationAdvance=true;//so that it runs on start up to provide light immediately 
        if (counter%10==0){
          InsolationAdvance=true;
          //Serial.println (elapsedTime);
        }
        if (counter==230) counter=0; 
    }     
    
    // now run through rise/set calculation and then intensity set and finally weather overlay when required

   if (trigger==true) CalSun();
   if (InsolationAdvance==true) Insolation();
   Weather();//due to variable storm vs cloud update timing this must be run from loop without condition
  //check to see if were need to have a lightning strike
    
    if (StrikeNow==true){
       if ((millis()-StrikeStart)>=StrikeMaster[(StrikeCount*2)]){//check if time has passed the delay (position 0,2,4,6,8 etc in StrikeMaster)-StrikeCount is indexed up by 1 after each strike so we see positions 0,2,4,6,etc in sequence
          byte intensity;
          intensity=random(150,256);// this little bit should generate a randomly bright flash variation between the series of flashes in StrikeMaster
              for (byte b=0; b<6; b++){
                  if (Wchannel[b]==1) analogWrite(PWMports[b],intensity);// set all whites to whatever random intensity was generated
              }
          delay(StrikeMaster[((StrikeCount*2)+1)]);//index to +1 position in array from 0,2,4, etc to 1,3,5 etc
          StrikeCount++;//so that the next time we look at elapsed time were looking at the right array position
          if (StrikeCount==(StrikeNumber-1)) StrikeNow=false;
        }
    }

    //track time in seconds-dont move this part up in the loop it really should stay below the rest as DST is not calculated until CalSun is run 1 time.
    elapsedTime=((now()-946684800)-newDay);//this is uncorrected for DST, the ONLY thing that is, since its all relative... is the actual sunrise and set seconds
    //Now that we have generated our sun pattern, Weather as clouds, and possibly a storm,  and possibly lightning each of which override the prior channel setting
    //lets actually make some light.
    for (byte a=0;a<6;a++){
      analogWrite(PWMports[a],TrueIntensity[a]);//dont change this to 8 to refelct array for channels.. we only have 6 here!
    }
}
//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(); 
}

//End Standard Functions
//*********************************************************************************************************************************

void CalSun(){
   //Serial.println("CalSun Run Now");
   trigger=false;
    // Every day at midnight, we calculate rise, set, time of highest sun, moon phase, or we do this if system has reset
        if (ApplyDST==true){
            CalcDST(day(),month(),weekday());
        }
        
        //Calculate Latitude and Longitude converting from Degree, Minute, Seconds to decimal
        latitude=dmsToSeconds(-25,87,13); //Set to about the latitude of Salt Lake City
        longitude=dmsToSeconds(29,23,32); //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.
        
       unsigned long SecInput;//intermediate variable to set rise and set input times
       unsigned long hours;//avoid casting and use UL
       unsigned long minutes;//avoid casting and use UL
          time_t t=now();
          hours=hour(t);
          minutes=minute(t);
        
        //Serial.println("Hours:Mins");
        //Serial.println(hours);
        //Serial.println(minutes);
        //Serial.print("CalSun now()=");
        //Serial.println(now());
        
        //Using time library from Arduino.time_t now(); returns seconds since 1970 in local time with NO DST correction
        //Arduino Time.h Library states #seconds to subtract to bring to Jan 1 2000 as origin for time is 
        //#define SECS_YR_2000  (946684800) the time at the start of y2k
        
         //Seems weird, but actually DO NOT correct new Day for DST 
        hours=(hours*3600);//seconds conversion saving a variable
        minutes=(minutes*60);//seconds conversion saving a variable
        newDay=(now()-(946684800+hours+minutes));//local time uncorrected for DST- hours and minutes were just converted to seconds in prior step
        SecInput=(newDay+GMToffset);
        rise=SecInput;// Dont screw with DST in sunrise sunset input its NOT part of GMT  
        set=SecInput;//were not converted yet- library uses POINTERS.. to modify these values  
        
        //Calculate rise time and set time using Epherma Library   
        SunRise(&rise);//call to Epherma Library using reference- memory is modified by Epherma
        SunSet(&set);//Call to Epherma library using reference- memory position is modified by Epherma
        
        if (isDST==true){//must correct DST NOW by subtracting 1 hour from the offset (i.e. clocks are 1 hr ahead and thus closer to GMT)
          rise=(rise-(GMToffset-3600));//during DST rise is 1 hour earlier than calculated i.e. clocks ahead 1 hour 6 am occurs "at 5am" if that makes sense
          set=(set-(GMToffset-3600)); 
        }
        else if (isDST==false){
          rise=(rise-GMToffset);
          set=(set-GMToffset);
        }
        
        //Serial.print("newDay=");
        //Serial.println(newDay);
        
        rise=(rise-newDay);//set to elapsed seconds today
        set=(set-newDay);
        Serial.println("Local Elapsed Seconds from NewDay--rise=");
        Serial.println(rise);
        Serial.println("Local Elapsed Seconds from NewDay--set=");
        Serial.println(set);
        
        //DONT MOVE THIS LINE ABOVE FINAL rise and set decleration it will SCREW up your rise and set during DST by 1 hour
        if (isDST==true) newDay-=3600;//subtract an hour from the time that will be  subtracted from GMT i.e. were one hour closer during DST
        
        
        
        //*********************UNLESS YOUR TANK LIGHTING IS IDENTICAL IN EVERY WAY TO MINE----YOU NEED TO CHANGE THESE VALUES***************************
        //channels 0-5 are PWM expansion board lights 6,7 are ReefAngel Controller PWM outputs
        //offsets for rise/set all values in seconds offset from calculated rise or set value (-) am offset=longer day****** (-)pm offset=shorter day)
        //array order is ch0 am offset, pm offset, ch1 am offset, pm offset etc..
        //THESE values are the number of seconds that a particular channel will be offset from the rise/set time, i.e. negative to rise earlier/set earlier
        int Choffset[]={
            900,-3600,1800,-2700,2700,-1800,3600,-900,0,0,0,0,0,0,0,0,0};
        //**********************ok now were done changing things here********************************************   
        
        
        
        //Calculate rise and set times for all channels in equivlants to elapsed seconds from midnight today
        //populate array for chRise and Set as well as chSlope for 0.5pi/ half day lenght for each channel from midday (asymmetric days are allowed)
        float deltaY=1.570796327;//1/2 * pi as integer by scaling* 10^9 to fill UL
        midDay=(((set-rise)/2)+rise);
        long HalfDayLength=((set-rise)/2);
        //Serial.print("MidDay");
        //Serial.println(midDay);
        
        for (byte b=0;b<16;b++){//working as of April 5 2012 serial tested
            if (b%2==0){
                ChRiseSet[b]=rise+(Choffset[b]);
                ChSlope[b]=(deltaY/(float)(HalfDayLength-(Choffset[b])));
                //Serial.print("Rise/Set a=");
                //Serial.println(b);
                //Serial.print("Value=");
                //Serial.print(ChRiseSet[b]);
                //Serial.print("Slope");
                //Serial.println(ChSlope[b], 12);
            }
            else if (b%2==1){
                ChRiseSet[b]=set+(Choffset[b]);
                ChSlope[b]=(deltaY/(float)(HalfDayLength+(Choffset[b])));
                //Serial.print("Rise/Set a=");
                //Serial.println(b);
                //Serial.print("Value=");          
                //Serial.print(ChRiseSet[b]);
                //Serial.print("Slope");
                //Serial.println(ChSlope[b], 12);
            }
        }  
        
        //***************** to CHANGE THE chance of Clouds actually occuring on a given day************************
        byte CloudChance=0;//% Chance of a Cloud every day
        //****************************now were done- did you use a value from 0-100 without a decimal?****************
        
        //once a day, after rise set has been calculated and populated we need to trigger a reset to the Cloud time calculation loop
        byte RainMaker=random(1,101); 
        if (RainMaker<=CloudChance){
            CloudToday=true;//used to trigger weather function, can also be used to send flag to controller
        }
        else if (RainMaker>CloudChance){
            CloudToday=false;//see above comment on CloudToday
            //Serial.print("no cloud today");
            return;
        }
        // to "simplify" things and avoid redundant calculation loops all cloud times are in fraction of day lengths 
        
        //*********** this is only executed 1x /day unless power outage and ONLY if last statement is true****************
        ///ALl cloud set up is done here... weather subroutine just implements it
        /*The general strategy for this algorithim is as follows.  Calculate random cloud cover as percent of day,
         then randomly generate the # of discreet cloud instances for the day,
         then determine cloud length by (daylight seconds * percent overcast)/#clouds
         then randomize cloud lengths by splitting into groups of twos correcting for if we have odd # of clouds today*/
        
        long dayLength=(set-rise);
        //Serial.println("DayLength");
        //Serial.println(dayLength);
        
        // number of clouds possible for the day, max and min
        byte CloudsMax=10;//DONT INCREASE BEYOND 11 or it will DIE, or increase array size to handle it
        byte CloudsMin=4;//dont use 1 it may or may not work in the next loops and the cloud will likely be very long, i.e. daylength*overcast fraction
        CloudsTotal=random(CloudsMin,(CloudsMax+1));
        //Serial.println("CloudsTotal");
        //Serial.println(CloudsTotal);
        
        
        // Average day is 50,000 secs so if 4 clouds and 10% that gets you 5,000 seconds of clouds (about 1800 seconds length for each of the 4 clouds in independent segments (if 4 is # clouds)
        byte OvercastMin=((CloudsTotal*10)/5);//Min cloud length will be about 1000 seconds (15 mins)- 1 hour min of clouds if you have 4, 2 hours if you have 8
        byte OvercastMax=((CloudsTotal*10)/2);//max cloud length will be about 2500 seconds (45 mins)- 6 hours max of clouds if you have 8, 3 hours max if you have 4
        float Overcast=random(OvercastMin,OvercastMax);
        Overcast=(Overcast/100);
        //Serial.println("Overcast");
        //Serial.println(Overcast);
 
        // set up start time for clouds
        //The way this is done is to split the total lenght of time for clouds into equal segments and then to randomly chop or add time to the 
        //segments such that cloud length is variable.  Then distribute into random parts of the day and fill array with start,duration pairs for clouds
        int CloudLength;
        CloudLength=((dayLength*Overcast)/CloudsTotal);//average cloud length
        long SunSegment=((dayLength-(dayLength*Overcast))/(CloudsTotal+1));//average sun length between clouds
        float CloudFraction=0;
        float SunFraction=0;
        /*Serial.print("CloudLength=");
        Serial.println(CloudLength);
        Serial.print("SunSegment=");
        Serial.println(SunSegment);*/
        
        //start by zero filling CloudMaster array
        for (byte a=0; a<20; a++){
          CloudMaster[a]=0;
        }
        byte b=0;//used to get pairs of fraction for SunFraction in for loop
        byte c=0;//used to get pairs of fraction for CloudFraction in for loop
        //now randomize cloud length and sunsegment length as pairs to get different looking days- 
        
        for (byte a=0; a<(CloudsTotal*2); a++){
          if ((CloudsTotal%2!=0) && (a==((CloudsTotal*2)-1))){//if were at the last cloud in an odd cloud day make it full length
               CloudMaster[a]=CloudLength;
             }
          else if (a%2==0){
            if (b==0){
              SunFraction=random(20,181);//vary each pair of SunSegments from 20%-180% of possible length such that every pair =2*SunSegment in length
              SunFraction=(SunFraction/100);
              CloudMaster[a]=(SunFraction*SunSegment);
              b++;
            }
            else if (b==1){
              SunFraction=(2-SunFraction);//were on the second part of a pair
              CloudMaster[a]=(SunFraction*SunSegment);
              b=0;//reset so next time we start a new fraction
            }
          }
          else if (a%2==1){//if were in odd positions we need to determine cloud lengths in random pairs such that each pair =2*CloudLength in length
            if (c==0){
              CloudFraction=random(20,181);//vary each pair of SunSegments from 20%-180% of possible length such that every pair =2*SunSegment in length
              CloudFraction=(CloudFraction/100);
              CloudMaster[a]=(CloudFraction*CloudLength);
              c++;       
            }
            else if (c==1){
              CloudFraction=(2-CloudFraction);
              CloudMaster[a]=(CloudFraction*CloudLength);
              c=0;//reset so next loop finds a new fraction
            }
         }
      }
      //serial debugging to examine array
        /*Serial.println("here is cloud master in is entirety prior to forming start and end pairs");
        for (byte a=0;a<20;a++){
          Serial.println(CloudMaster[a]);
        }*/
      
         // now we have CloudMaster constructed as SunSegment,CloudLength,SunSegment,CloudLength.... 
         //reframe it to generate cloud start, cloud end, cloud start, cloud end
      for (byte a=0; a<(CloudsTotal*2); a++){
        if (a==0){// if were starting our first cloud we need to add to rise value to first sun segment
          CloudMaster[a]=rise+CloudMaster[a];
        }
        else {
          CloudMaster[a]=(CloudMaster[a-1]+CloudMaster[a]);//just add prior values together e.g. (second position is cloud end so to find end add rise corrected start time with duration)
                                                           // subsequent start would be end of 1st cloud + next sunsegment fraction
        }
      }
        //serial debugging to examine array
        /*Serial.println("here is cloud master in is entirety as start and end pairs");
        for (byte a=0;a<20;a++){
          Serial.println(CloudMaster[a]);
        }*/
}//END SunCalc FUNCTION

void Insolation()
{
  InsolationAdvance=false;//reset this flag now that we have entered function
        /*Serial.println("Insolation 3 sec elapsed");
        Serial.print("Elapsed Time is=");
        Serial.println(elapsedTime);*/
        
        //define Pi as delta Y for slope since cos 0.5-1.5 Pi goes 0-1-0 in 0.5 pI increments slope of 1/2 day (0-1 intensity) delta Y is 1/2 Pi 
        float Pi=3.1415926;//scale to 10^8
        float PiHalf=1.5707963;//scale to 10^8
        
        float secSoFar;//variable to account for seconds elapsed for each channel 1/2 day period from rise-->midDay and midDay-->set
        
        /* using -cos(pi/2+elapsedTime/slope) calculate fractional intensity of each channel throughout the day
         use flicker points to adjust minimum intensity to stable light.  Turn off lights after set or before rise etc.
         by splitting into half days centered on midday (1/2 ofset-rise) we center exactly the cos function for every channel so color blends are maintained 
         throughout intensity ramp... more or less ... change intensity every 120 seconds throughout the day*/
        
        if (elapsedTime<=midDay){
          //Serial.println("in first half of day");
            for (byte b=0;b<16;b=(b+2)){
                if (elapsedTime>=ChRiseSet[b]){
                    secSoFar=(elapsedTime-ChRiseSet[b]);//just account for length of every channel 1/2 day and switch at midDay
                    switch (b){
                        case 0:
                            ChannelValue[0]=flicker[b]+ChMax[b]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
                            //Serial.println((float)ChMax[b],4);
                            //Serial.println(b);
                            //Serial.println(ChannelValue[0]);
                            //Serial.println((float)ChMax[b],4);
                            break;
                        case 2:
                            ChannelValue[1]=flicker[(b-1)]+ChMax[(b-1)]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));   
                            //Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(b);
                            //Serial.println(ChannelValue[1]);
                            break;
                        case 4:
                            ChannelValue[2]=flicker[(b-2)]+ChMax[(b-2)]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(b);
                            //Serial.println(ChannelValue[2]);
                            break;
                        case 6:
                            ChannelValue[3]=flicker[(b-3)]+ChMax[(b-3)]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(b);
                            //Serial.println(ChannelValue[3]);
                            break;
                        case 8:
                            ChannelValue[4]=flicker[(b-4)]+ChMax[(b-4)]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(b);
                            //Serial.println(ChannelValue[4]);
                            break;
                        case 10:
                            ChannelValue[5]=flicker[(b-5)]+ChMax[(b-5)]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(b);
                            //Serial.println(ChannelValue[5]);
                            break;
                       case 12:
                            ChannelValue[6]=flicker[(b-6)]+ChMax[(b-6)]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(b);
                            //Serial.println(ChannelValue[6]);
                            break;
                        case 14:
                            ChannelValue[7]=flicker[(b-7)]+ChMax[(b-7)]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(b);
                            //Serial.println(ChannelValue[7]);
                            break;
                    }   
                }
                else if (elapsedTime<ChRiseSet[b]){
                    switch (b){
                        case 0:
                            ChannelValue[0]=0;                //to enable moon phase lighting for any channel comment out this part and uncomment the next 2 lines
                            //ChannelValue[0]=MoonPhase();    //this is the after sunset part I told you to look for when you set the before sunrise moon phase channels
                            //if (ChannelValue[0]<flicker[0]) ChannelValue[0]=0;
                            break;
                        case 2:
                            ChannelValue[1]=0;
                            //ChannelValue[1]=MoonPhase();    
                            //if (ChannelValue[1]<flicker[1]) ChannelValue[1]=0;
                            break;
                        case 4:
                            ChannelValue[2]=0;
                            //ChannelValue[2]=MoonPhase();    
                            //if (ChannelValue[2]<flicker[2]) ChannelValue[2]=0;
                            break;
                        case 6:
                            ChannelValue[3]=0;
                            //ChannelValue[3]=MoonPhase();    
                            //if (ChannelValue[3]<flicker[3]) ChannelValue[3]=0;
                            break;
                        case 8:
                            ChannelValue[4]=0;
                            //ChannelValue[4]=(MoonPhase()/2);
                            //if (ChannelValue[4]<flicker[4]) ChannelValue[4]=0;
                            break;
                        case 10:
                            //ChannelValue[5]=0;
                            ChannelValue[5]=MoonPhase()/2;    
                            if (ChannelValue[5]<flicker[5]) ChannelValue[5]=0;
                            break;
                       case 12:
                            ChannelValue[6]=0;
                            //ChannelValue[6]=MoonPhase();    
                            //if (ChannelValue[6]<flicker[6]) ChannelValue[6]=0;
                            break;
                        case 14:
                            ChannelValue[7]=0;
                            //ChannelValue[7]=MoonPhase();    
                            //if (ChannelValue[7]<flicker[7]) ChannelValue[7]=0;
                            break;
                    }
                } 
            }
        }  
        else if (elapsedTime>midDay){
          //Serial.println("were in the second half of the day");
            for (byte b=1;b<12;b=b+2){
                
                if (elapsedTime<=ChRiseSet[b]){
                    secSoFar=(elapsedTime-midDay);
                    switch (b){
                        case 1:
                            ChannelValue[0]=flicker[(b-1)]+ChMax[(b-1)]*(-cos(Pi+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(Pi+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(ChannelValue[0]);
                            break;
                        case 3:
                            ChannelValue[1]=flicker[(b-2)]+ChMax[(b-2)]*(-cos(Pi+(ChSlope[b]*secSoFar)));    
                            //Serial.println(-cos(Pi+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(ChannelValue[1]);
                            break;
                        case 5:
                            ChannelValue[2]=flicker[(b-3)]+ChMax[(b-3)]*(-cos(Pi+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(Pi+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(ChannelValue[2]);
                            break;
                        case 7:
                            ChannelValue[3]=flicker[(b-4)]+ChMax[(b-4)]*(-cos(Pi+(ChSlope[b]*secSoFar)));
                           //Serial.println(-cos(Pi+(ChSlope[b]*secSoFar)),4); 
                           //Serial.println(ChannelValue[3]);
                            break;
                        case 9:
                            ChannelValue[4]=flicker[(b-5)]+ChMax[(b-5)]*(-cos(Pi+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(Pi+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(ChannelValue[4]);
                            break;
                        case 11:
                            ChannelValue[5]=flicker[(b-6)]+ChMax[(b-6)]*(-cos(Pi+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(Pi+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(ChannelValue[5]);
                            break;
                       case 13:
                            ChannelValue[6]=flicker[(b-7)]+ChMax[(b-7)]*(-cos(Pi+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(Pi+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(ChannelValue[6]);
                            break;
                        case 15:
                            ChannelValue[7]=flicker[(b-8)]+ChMax[(b-8)]*(-cos(Pi+(ChSlope[b]*secSoFar)));
                            //Serial.println(-cos(Pi+(ChSlope[b]*secSoFar)),4);
                            //Serial.println(ChannelValue[7]);
                            break;
                    }
                }
                else if (elapsedTime>ChRiseSet[b]){
                    switch (b){
                        case 1:
                            ChannelValue[0]=0;
                            //ChannelValue[0]=MoonPhase();    
                            //if (ChannelValue[0]<flicker[0]) ChannelValue[0]=0;
                            break;
                        case 3:
                            ChannelValue[1]=0;
                            //ChannelValue[1]=MoonPhase();    
                            //if (ChannelValue[1]<flicker[1]) ChannelValue[1]=0;
                            break;
                        case 5:
                            ChannelValue[2]=0;
                            //ChannelValue[2]=MoonPhase();    
                            //if (ChannelValue[2]<flicker[2]) ChannelValue[2]=0;
                            break;
                        case 7:
                            ChannelValue[3]=0;
                            //ChannelValue[3]=MoonPhase();    
                            //if (ChannelValue[3]<flicker[3]) ChannelValue[3]=0;
                            break;
                        case 9:
                            ChannelValue[4]=0;
                            //ChannelValue[4]=(MoonPhase()/2);
                            //if (ChannelValue[4]<flicker[4]) ChannelValue[4]=0;
                            break;
                        case 11:
                            //ChannelValue[5]=0;
                            ChannelValue[5]=MoonPhase()/2;    
                            if (ChannelValue[5]<flicker[5]) ChannelValue[5]=0;
                            break;
                       case 13:
                            ChannelValue[6]=0;
                            //ChannelValue[6]=MoonPhase();    
                            //if (ChannelValue[6]<flicker[6]) ChannelValue[6]=0;
                            break;
                        case 15:
                            ChannelValue[7]=0;
                            //ChannelValue[7]=MoonPhase();    
                            //if (ChannelValue[7]<flicker[7]) ChannelValue[7]=0;
                            break;
                    }
                }
            }
        }
        /*Serial.println("Insolation settings=");
        Serial.println(ChannelValue[0]);
        Serial.println(ChannelValue[1]);
        Serial.println(ChannelValue[2]);
        Serial.println(ChannelValue[3]);
        Serial.println(ChannelValue[4]);
        Serial.println(ChannelValue[5]);
        Serial.println(ChannelValue[6]);
        Serial.println(ChannelValue[7]);
        Serial.println("ChMax Settings");
        Serial.println(ChMax[0]);
        Serial.println(ChMax[1]);
        Serial.println(ChMax[2]);
        Serial.println(ChMax[3]);
        Serial.println(ChMax[4]);
        Serial.println(ChMax[5]);
        Serial.println(ChMax[6]);
        Serial.println(ChMax[7]);*/
        
}//END function


//This is where modification of insolation by clouds or a storm occurs
//Its worth noting that the clouds simply modify (depending on how much cloud cover there is) the existing light set by insolation as a fractional value
void Weather ()  
{
  
    static float CloudCover; // variable to store value in random walk - declared static to accumulate Cloud effect
    static float PriorCloudCover;  //used to "delay" one side of the tank from the other in cloud passing effects
    static long StormStart;
    static long StormEnd;
    static long CloudEnd;
    static boolean wtrigger;//use to track the first run of functions to calculate random times that become fixed, see change from cloud to storm for useage
    static byte Counter;//used to trigger storms from cloud you can change its if loop comparison to decrease or increase storm rate see below in cloud==true if loop
    int LongestStorm;//used to pass max possible time to storm if loop from cloud loop within weather function
    static byte Severity;
    static byte StormCount;// used to limit X storms per cloud and to choose which cloud can have a storm
    
    //check to see if were having a scheduled cloud
    if (Cloud==false){
        for (byte a=0; a<(CloudsTotal*2); a=(a+2)){//if its time for a cloud, run it
            if ((elapsedTime>=CloudMaster[a]) && (elapsedTime<=CloudMaster[(a+1)])) {
                CloudEnd=CloudMaster[(a+1)];//to avoid this loop running true during the compute cycles at the end of the cloud and before elapsedTime advances a second, actual cloud does not
                CloudEnd-=10;//remove 10 sec from cloud end to clear sky when done...
                Cloud=true;//Clear sky function resets to false at time end of cloud
                CloudCover=50;
                Counter=0;
                StormCount=random(0,3);//the number of storms MAX that may occur in this cloud (remember Random is range= -1 on high end)
                break;//were starting this cloud so stop looking for another one
            }
        } 
     //just write ChannelVales to TrueIntensity without modification so that cloud/storm can later modify them
        for (byte a=0; a<8; a++){
            TrueIntensity[a]=ChannelValue[a];//this is where intensity is set for the PWM channel analog write in the loop... don't mess with this.
        }   
     }
         
    else if ((Cloud==true) && (IsStorm==false)){
        if (StormAdvance==false){//use millis tracker to run this loop every 2 seconds
            return;
        }
        StormAdvance=false;//reset to false when true so we run this once, until time advance is true again
        //Serial.println("in a cloud doing stuff");
        if (elapsedTime>=CloudEnd){
            ClearSky(CloudCover, CloudEnd); 
            return;
        }
        
        /*Use fractional intensity to set minimum value for any channel.  Dimming is proportional to actual intensity output 
         and constrained by flicker point.  Random walk uses static variable "CloudCover" constrained to 0-100 to represent fractional intensity (i.e. (1-(CloudCover/100))*Insolation SetPoint 
         is how the current cloud intensity is set, i.e. cloud cover of 90 gives 10% insolation setpoint unless below flicker in which case = flicker*/
        
        int stepsize;
        stepsize=random(-15,16);// in Percent% (0-100) This is how much light intensity can change every 1 or 2 seconds (Whatever CloudAdvance is in this case);         
        //Serial.print("Stepsize=");
        //Serial.println(stepsize);
        PriorCloudCover=CloudCover;//this way its going to change every 250 msec  
        //Serial.print("PriorCloudCover=");
        //Serial.println(PriorCloudCover);
        CloudCover=(CloudCover+stepsize);//Now add the % increment or decremement to intensity
        //Serial.print("CloudCover=");
        //Serial.println(CloudCover);
        
        if (CloudCover>=100){ //arbitrary "STORM" cutoff... i.e. if the sky gets dark enough we must be having a storm- if you dont get enough storms increase stepsize boundry or change this value
            CloudCover=100;//since we see dark clouds better let it just sit here until it walks away
            Counter++;
            //Serial.print("CLoudCover was over 100 and got corrected to=");
            //Serial.println(CloudCover);
        }
        else if (CloudCover<=0){//i.e if were bright sky in a cloud.. uhh.. we need a cloud
            //Serial.print("Cloud cover was negative so I fixed it and its now=");
            CloudCover-=(stepsize*1.5);// since we are supposed to be cloudy if we get too bright correct it down by amplifying the change that got us to a clear sky and inverting it
              if (Counter>=2) Counter-=random(0,2);                           //reflect to less positive value, i.e. we had to be adding a negative so subtract it instead (i.e. add a positive)
            //Serial.println(CloudCover);
        }
        
        if ((Counter>=115) && ((CloudEnd-elapsedTime)>=500)) {//this is where a storm is triggered.  Counter indexes when cloud cover reaches 100 on the random walk
        //to change the frequency of storms increase or decrease the number comparison for counter in the if statement above (larger #== less storms).
        //if you change counter comparison here change it in the next loop as well
           if (StormCount>0){
             byte RandomStorm;
             RandomStorm=random(0,11);//counter at 250 msec timing yeilds a storm in about 7 minutes.. this randomizes for longer clouds without storm, avg cloud is much longer
               if (RandomStorm>=6){
                 IsStorm=true;
                 StormCount-=1;//count down by 1 the number of storms in this cloud
                 wtrigger=true;
                 Counter=0; 
                 LongestStorm=(CloudEnd-elapsedTime);
                 // Serial.print("we started a storm"); 
               }
             // Serial.print("Random call was missed- not a storm");  
           }
        }
        else if ((Counter>=115) && ((CloudEnd-elapsedTime)<500)){
           Counter=0;
        }
        
        for (int a=0;a<8;a++){
            if (DimOrder[a]==0){
                //Serial.print("Initial Channel setting is= ");
                //Serial.println(a);
                //Serial.println(FullSun);
                TrueIntensity[a]=(flicker[a]+(((float)(ChannelValue[a]-flicker[a]))*(1-((CloudCover/100)))));//max intensity is 65% of initial for better visual dimming performance
                //Serial.print("DimOrder 0 setting is"); 
                //Serial.println(TrueIntensity[a]);
            }
            else if (DimOrder[a]==1){
                //Serial.print("Initial Channel setting is= ");
                //Serial.println(a);
                //Serial.println(FullSun);
                TrueIntensity[a]=(flicker[a]+(((float)(ChannelValue[a]-flicker[a]))*(1-((PriorCloudCover/100)))));//max intensity is 65% of initial for better visual dimming performance
                //Serial.print("DimOrder 1 setting is"); 
                //Serial.println(TrueIntensity[a]);
            }     
         }
    }  
    //enable a flag sent from controller to triger a storm, i.e. IsStorm=true
    // set channel intensities for all but white with random walk continuing from above using static variable so should be seamless
    else if (((Cloud==true) && (IsStorm==true)) || ((Cloud==false) && (IsStorm==true))){
        //current else statement covers possibility of triggering storm from controller (i.e. not coming out of a cloud) but remember you need to flag wtrigger as TRUE when you do this
        //do this next part exactly once per storm then loop past
        if (wtrigger==true){
            StormStart=elapsedTime;
            LongestStorm-=120;//remove 2 mins from longest storm so that we end up with 2 minutes of cloud after the storm before the sky clears to daylight
            //*****************To CHANGE THE LENGTH OF THE LONGEST POSSIBLE STORM DURING A CLOUD read next line comments****************************
            if (LongestStorm>500) LongestStorm=500;//YOU CAN CHANGE THE value in the comparison and the define to be whatever you want your max lenght to be
            ////*************************EDIT ABOVE***************************************************************************************************
            int StormDuration=random((LongestStorm/4),(LongestStorm+1));//set (min,max) seconds any single Storm can exist currently 1/4 length to full length remaining cloudtime
            StormEnd=(StormStart+StormDuration);
            Severity=random(2,9);//changes lightning strike frequency in a storm from very frequent  (every 10-15 sec or less) to about once in a minute or maybe less
            Counter=0;
        }
        
        if (StormAdvance==false){//Every 1 second duing a storm change intensity, clouds are movin fast baby
           return;
        }
        
        StormAdvance=false;//reset so we run again in 1 second.
        //Serial.print("in a storm now");
        
       if (elapsedTime>=StormEnd){ //if were done with the storm we need to stop this loop, but were probably still cloudy so dont mess with that here
            IsStorm=false;
            Counter=0;
            return;
        }
        
        int stepsize;
        stepsize=random(-19,20);
        PriorCloudCover=CloudCover;
        CloudCover=(CloudCover+stepsize);
        
        if (CloudCover>=100){ //arbitrary "STORM" cutoff... i.e. if the sky gets dark enough we must be having a storm- if you dont get enough storms increase stepsize boundry or change this value
            CloudCover=100;//since we see dark clouds better let it just sit here until it walks away
            Counter++;
              if (Counter>(Severity+1)) Counter=0;//allow if to accumulate on ocassion to train strike sequences 2-3 in a row but then dump it
            //Serial.print("CLoudCover was over 100 and got corrected to=");
            //Serial.println(CloudCover);
        }
        else if (CloudCover<=0){//i.e if were bright sky in a cloud.. uhh.. we need a cloud
            //Serial.print("Cloud cover was negative so I fixed it and its now=");
            CloudCover-=(1.5*stepsize);//stay within bounds
            Counter-=random(0,2);         
            //Serial.println(CloudCover);
        }
        
        if ((Counter>=(Severity+random(-2,4))) && (StrikeNow==false)) {//this is where a storm is triggered.  Counter indexes when cloud cover reaches 100 on the random walk
        //to change the frequency of lightning strikes increase or decrease the number comparison for counter in the if statement above (larger #== less storms).
          byte RandomStriker;
          RandomStriker=random(0,11);
          if (RandomStriker>3){
            StrikeNumber=(random(2,11)); //random high =x-1 so max strike =12 each strike requires a duration and a delay thus StrikeMaster is 18 positions
            //ensure the array is zeroed out past the last position required for this strike pattern so each pattern is only as long as generated
            //Array is in pairs, position in arry of (0,1) (2,3) etc as strike (delay,duration)
            for (byte a=0;a<20;a++){
                if (a>=(StrikeNumber*2)){
                    StrikeMaster[a]=0;
                }
                if (a%2==0){
                   if (a==0){
                      StrikeMaster[a]=random(300,1601);//no need for random here but I am leaving it since I wrote it that way.  This must be independent from a=2,4,6 etc...
                    }
                    else {
                       StrikeMaster[a]=(StrikeMaster[(a-2)]+random(160,1201));//position 0,2,4,6,8.. is strike delay
                    } 
                 }
                 else if(a%2!=0){
                    StrikeMaster[a]=random(45,100);//position 1,3,5,7,9... is strike duration (I tried real lightning strike durations and its too short this is adjusted for visual effect
                 }
            }
            StrikeNow=true; //Trigger to start strike sequence in loop
            StrikeStart=millis();//set timer to "zero" now- sequence will start in loop after this function
            StrikeCount=0;
          }
        }
        
        for (int a=0;a<8;a++) {
            if (Wchannel[a]==1){//if were white we need to be off in a storm
              TrueIntensity[a]=0;
            }
            else if (Wchannel[a]==0){//if were blue, we chase as for a cloud
                if (DimOrder[a]==0){
                    //Serial.print("In a storm and DimOrder0 channel values were");
                    //Serial.println(ChannelValue[a]);
                    //Serial.println(a);
                    TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])))*(1-((CloudCover/100))))); 
                    //Serial.print("and now the channel is set to");
                    //Serial.println(TrueIntensity[a]); 
                }
                else if (DimOrder[a]==1){
                    //Serial.print("In a storm and DimOrder1 channel values were");
                    //Serial.println(ChannelValue[a]);
                    //Serial.println(a);
                    TrueIntensity[a]=(flicker[a]+((((float)(ChannelValue[a]-flicker[a])))*(1-((PriorCloudCover/100)))));
                    //Serial.print("and now the channel is set to");
                    //Serial.println(TrueIntensity[a]); 
                }
            }    
        }
    }//end of storm if loop
}//End Weather function

//THE LAST 2 mins of ANY Storm, or cloud, are used to ramp linearlly to full daylight setting, whatever that is at the given time of day
void ClearSky(float CloudCover, int CloudEnd)
{
    
    // no need to restrict time to avoid resource hogging, its only called every x seconds from weather.
    //basically, add 120 seconds to every cloud and use it to bring light up to full
    int elapsed=(elapsedTime-CloudEnd);//counts up from zero forever... but we stop it by 120 to end cloud
    
    //Since CloudCover is amount taken away as decimal fraction of InsolationSetting (i.e. 70 produces light at 30% of insolation setpoint... we need to come up 70 to get back.
    
    float slope=(CloudCover/10);//cloud cover goes from 0 (Clear) to 90 (storm) in a cloud, since storms are forced to end before the cloud we know this is the limit
    if (elapsed<=120){//at this point lights are back full on so cancel the cloud and start waiting for the next one
        Cloud=false;
        return;
    }
    byte LightAdvance;
    LightAdvance=(CloudCover-(slope*elapsed));//were reducing CloudCover from start to zero over 120 seconds seconds...
    for (byte a=0; a<8; a++){ 
    TrueIntensity[a]=(byte)(((ChannelValue[a]-flicker[a])*(float)(1-((float)(LightAdvance/100))))+flicker[a]); 
    }
}//End Clear Sky function

byte MoonPhase()
{
   int m,d,y;
   int yy,mm;
   long K1,K2,K3,J,V;
   byte PWMvalue;
   m = month();
   d = day();
   y = year();
   yy = y-((12-m)/10);
   mm = m+9;
   if (mm>=12) mm -= 12;
   K1 = 365.25*(yy+4712);
   K2 = 30.6*mm+.5;
   K3 = int(int((yy/100)+49)*.75)-38;
   J = K1+K2+d+59-K3;
   V = (J-2451550.1)/0.29530588853;
   V -= int(V/100)*100;
   V = abs(V-50);
   PWMvalue = 4*abs(50-V);  // 5.12=100%    4=~80%
   //pinMode(lowATOPin,OUTPUT);
   //return (PWMvalue*100)/255; //output is 0-100
   return PWMvalue;
}

void CalcDST(byte D, byte M, byte dow)
{ 
    //January, february, and december are out.
    if (M < 3 || M > 11){
        isDST=false; 
    }
    //April to October are in
    else if (M > 4 && M < 11){
        isDST=true; 
    }
    else{  
        int previousSunday=(D-dow); // Sunday=1 Sat=7
        if (M==3 && previousSunday>7){ 
            isDST=true;
        }
        else if (M==11 && previousSunday<=0){
            isDST=false;
        }
    }
}

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


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

So on this exact code aove the sun is now setting in my tank 12:51 pm South Africa time.. LoL, I am confused about the GMT Offset
Last edited by JNieuwenhuizen on Sat Jun 09, 2012 8:08 am, edited 1 time in total.
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

updates. i have removed all the lighting codes from my head unit code but still i m not able to see any lighting effect similar to sunrise/sunset.
Image
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Jnieuwenhuizen.... I see this

int GMToffset=0;//

Are you really in GMT... looking on a map at rough lat lon cords your using (-25,29) I see what looks to be you running 2-3 hours ahead off GMT. So if the sun was supposed to rise at 9 am GMT... and your offset was 0 but your actually 12 pm when GMT is 9 am... your sun would rise at 12 pm....

its 3600 sec/hour so what I would is put in the exact offset required using a negative number (i.e. you need to subtract xxxx seconds to get Grenwich Mean Time). This will go a long way to fixing your problem. IF your actually on GMT (it looks like thats not possible unless your lat lon are for somewhere other than your house (which would not be expected to produce sunrise and sunset times equivalent to your house). Then tell me if there is any type of time correction in South Africa throughout the year. I.e. if in, for instance, April of every year you set your clock ahead or back an hour (or whatever) and then in October (or whatever) you set it back to what it was prior to the spring change.... this would further separate your actual time as GMT offset from the 0 value you have now.

Simple way to get it to work right, just look up the actual sunrise time today for your location, then add up the hours your tank was late (it should be pretty close to exactly xx hours not x.4 hours. Then put that number of seconds into your GMT offset as a negative value. This will get it perfect. I am going to say that the program is working as intended... I don't see how it gets the right number for my lat lon pair which is different from the location of the person who wrote these particular functions (and who has also distributed this and others find it to work) and gets it wrong for you. You just have the wrong GMToffset value. Its almost 100% certain to be that. I now am curious if able_123 gets it working if they use the correct sign on Lat/Lon (yours is correct) and get their GMT offset negative, if they report back a working sun time that pretty much would confirm your GMToffset as being the issue.
User avatar
JNieuwenhuizen
Posts: 96
Joined: Thu Feb 16, 2012 12:39 am
Location: South Africa

Re: C library for Sun/Moon effects

Post by JNieuwenhuizen »

ok

I used the 0 as Roberto suggested, It was only a test. The figure i get is 18000 when i look at the sunset, and time it set in tank, I cant look at sunrise as it is during late night/early morning hours somewhere.

I will try the -18000 offset and see what it does
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

I was assuming Roberto was referring to the ChOffset values in the array that sets times for each channel with zeros being calculated rise/set values.

That seems like a big number. Remember to use the time difference between when the sun centers on the horizon. Not a dark sky. This should be calculating more or less the time at which 1/2 of the sun is visible. Also remember to calculate the difference using a ChOffset value of Zero. If you had a ChOffset of 3600 (and you do) abd calculated based on that you will be An hour wrong. I would try -10800 based on what I am seeing on a map.
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

I am sorry to say but things havent working out as expected. from last 12 hours i m keeping a close eye on lights to see any effects without any success. looks like there is some issue or bug with this offset or lat/long part of code.
Image
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

I Have made following changes in my code to test the efficiency. I set the latitude & longitude as per G.M.T & Offset to zero.

will examine & find how its behaving.
Image
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

I will look more closely at that. I have however been working on the cloud and storm part and have a fairly significant update that should fix the long storm problem.

You need to delete the entire weather() and ClearSky() functions.

If you just search the code for
void Weather(){

and the select every line including that one up to and including

//End Clear Sky function

then simply hit the delete key and paste the large block of code at the end of this post into that spot.


You will also want to change this line in the loop part of the program.

search for
void loop(){

then just make your code look like this, you will have to edit a few numbers in a few lines but nothing big. You don't need to copy and paste, just look and compare this to your code and change yours to match mine.
here is mine. I am fairly certain you will need to change the first line by putting in 100 instead of your setting. Then you will need to change the if (counter%XX==0) line to use 30 and finally I think the last change is in the last line of this code. IF I forgot and there are more differences... just USE this code. ITs important- without these changes the cloud and storm visuals will be screwed big time.

Code: Select all

if ((millis()-lastmillis)>=100){
        lastmillis=millis();
        counter+=1;
        StormAdvance=true;
        //now a bunch of stuff that may or may not be true at the same time but that all needs to happen when its true
        if (counter==0){
          InsolationAdvance=true;//so that it runs on start up to provide light immediately 
        }  
        if (counter%30==0){
          InsolationAdvance=true;
          /*Serial.print("elapsed time in seconds from midnight is =");
          Serial.println(elapsedTime);
          Serial.print("cloud=");
          Serial.println(Cloud);
          Serial.print("IsStorm=");
          Serial.println(IsStorm);*/
        }
        if (counter==210) counter=0; 
    }     
This is the major segment your going to want to copy and paste. I am trying to NOT post an entirely new code for those helping trouble shoot this because you would have to at least find all the changes to the arrays you made which are probably in two places and then be sure you had it. This way, your settings do NOT have the possibility of changing... I will post a complete code when I figure out whats going on with lat long.

This should fix storms... It also implements severe storms with blue lights only, and normal storms with white lights on but only at 20% max intensity (and still ramping from 0-20 as cloud effect). As you get into the later part of the day the frequency of severe storms will go up markedly. The MAX length ANY storm can exist now is 10 minutes. NO CLOUD can have more than 2 storms and some will have NONE. This should be MUCH better.


Code: Select all

void Weather ()  
{
    static byte loopCount;
    static float CloudCover; // variable to store value in random walk - declared static to accumulate Cloud effect
    static float PriorCloudCover;  //used to "delay" one side of the tank from the other in cloud passing effects
    static long StormStart;
    static long StormEnd;
    static long CloudEnd;
    static boolean wtrigger;//use to track the first run of functions to calculate random times that become fixed, see change from cloud to storm for useage
    static byte Counter;//used to trigger storms from cloud you can change its if loop comparison to decrease or increase storm rate see below in cloud==true if loop
    static byte Severity;
    static byte StormCount;// used to limit X storms per cloud and to choose which cloud can have a storm
    static int StepSize;
    static int LastStepSize;
    //check to see if were having a scheduled cloud
    if (Cloud==false){
      //Write Insolation values to TrueIntensity so the loop will pick them up and the cloud/storm will get the right data (since intensity changes during the day)
      for (byte a=0; a<8; a++){//this must be above the next loop
            TrueIntensity[a]=ChannelValue[a];//this is where intensity is set for the PWM channel analog write in the loop... don't mess with this.
        }   
      for (byte a=0; a<(CloudsTotal*2); a=(a+2)){//if its time for a cloud, run it
         if ((elapsedTime>=CloudMaster[a]) && (elapsedTime<=CloudMaster[(a+1)])) {
             CloudEnd=CloudMaster[(a+1)];//to avoid this loop running true during the compute cycles at the end of the cloud and before elapsedTime advances a second, actual cloud does not
             //Serial.print("We started a cloud and its end is=");
             //Serial.println(CloudEnd);
             CloudCover=CloudStart(CloudMaster[a]);//CloudStart modifies TrueIntensity to get us to 50% intensity at the start of the cloud effect and also sets cloud=true to bypass this
             Counter=0;
             StormCount=random(0,3);//the number of storms MAX that may occur in this cloud (remember Random is range= -1 on high end)
             loopCount=1;
             LastStepSize=0;//zero out cloud random walk variables
             StepSize=0;//zero out cloud ranodm walk variables
             return;//exit having started a cloud in CLoudStart routine called above
         }
       } 
     //just write ChannelVales to TrueIntensity without modification so that cloud/storm can later modify them
     
     }
         
    else if ((Cloud==true) && (IsStorm==false)){
        if (StormAdvance==false){//use millis tracker to run this loop every 2 seconds
            return;
        }
        StormAdvance=false;//reset to false when true so we run this once, until time advance is true again
        //Serial.println("in a cloud doing stuff");
        if (elapsedTime>=CloudEnd){
            ClearSky(CloudCover, CloudEnd); 
            return;
        }
        
        /*Use fractional intensity to set minimum value for any channel.  Dimming is proportional to actual intensity output 
         and constrained by flicker point.  Random walk uses static variable "CloudCover" constrained to 0-100 to represent fractional intensity (i.e. (1-(CloudCover/100))*Insolation SetPoint 
         is how the current cloud intensity is set, i.e. cloud cover of 90 gives 10% insolation setpoint unless below flicker in which case = flicker*/
        if (loopCount==1){
          PriorCloudCover=CloudCover; //e.g. PriorCloudCover=CloudCover with no float math to screw things up
          LastStepSize=StepSize;
          StepSize=(random(5,21));// in Percent% (0-100) This is how much light intensity will change over the loop count interval (this actual time is dependent upon the call frequency of StromAdvance as set in the loop)
          if ((random(0,2)!=1)) StepSize=-(StepSize);
          if ((CloudCover+StepSize)>=100){//cannot shut off lights more than 100% so limit here
            StepSize=(100-CloudCover);//
            Counter++;
            //Serial.print("Counter for cloud to storm transition just hit 100 cloud cover and went up by 1=");
            //Serial.println(Counter);  
          }
          else if ((CloudCover+StepSize)<=0){//cannot be brighter than 100% so since were in a cloud dont "limit" it but reflect it back down
            StepSize=-(StepSize);
            if (Counter>=35) Counter-=random(-1,2);//since we got bright... lets further delay and randomize storm occurence 
            //Serial.print("Counter for cloud to stormtransition just hit 0% cloud and it now is =");
            //Serial.println(Counter);  
          }
          CloudCover=CloudCover+StepSize;
        }
       // float math for light intensity setting is PriorCloudCover (dim order 1)=(PriorCLoudCover+(LastStepSize/4)*loopCount);//this way its going to change every 250 msec  
       //float math for light intensity setting is CloudCover (dim order 0)=(CloudCover+(StepSize/4)*loopCount);//Now add the % increment or decremement to intensity
        
        /*Serial.print("PriorCloudCover=");
          Serial.println(PriorCloudCover);
          Serial.print("Stepsize=");
          Serial.println(StepSize);
          Serial.print("CloudCover=");
          Serial.println(CloudCover);*/
        
        if ((Counter>=60) && ((CloudEnd-elapsedTime)>=300)) {//if Counter (indexes when cloud cover reaches 100) has accumulated and we still have time lets make a storm
        //to change the frequency of storms increase or decrease the number comparison for counter in the if statement above (larger #== less storms).
        //if you change counter comparison here change it in the next loop as well
           if (StormCount>=1){//if we can have storms in this cloud (random- statisticly 1/3 clouds = no storm, 1/3 = 1 possible storm, 1/3 = 2 possible storms)
             byte RandomStorm;
             RandomStorm=random(0,11);//this randomizes for longer clouds without storm, avg cloud is much longer prior to storm occuring- thus short clouds will not generally have a storm
               if (RandomStorm>=4){
                 StormCount-=1;//count down by 1 the number of storms in this cloud- this will not roll the byte since the loop requires it to be at least 1 to ever subtract here. 
                 Counter=0;//reset this variable since Storm loop uses it as well.
                 int LongestStorm;//used to pass max possible time to storm if loop from cloud loop within weather function
                 LongestStorm=(CloudEnd-elapsedTime);
                 Severity=StartStorm(LongestStorm, IsStorm, StormEnd);
                 loopCount=1;//reset counting loop for the storm
                 // Serial.print("we started a storm"); 
               }
             // Serial.print("Random call was missed- not a storm");  
           }
        }
        else if ((Counter>=60) && ((CloudEnd-elapsedTime)<300)){
           Counter=0;//just reset the counter (does not really matter in this case but its clean)
        }
        
        for (int a=0;a<8;a++){
            if (DimOrder[a]==0){
                //Serial.print("Initial Channel setting is= ");
                //Serial.println(a);
                //Serial.println(FullSun);
                TrueIntensity[a]=(flicker[a]+(((float)(ChannelValue[a]-flicker[a]))*(1-((CloudCover-(StepSize-(StepSize/4)*loopCount))/100))));//max intensity is 65% of initial for better visual dimming performance
                //Serial.print("DimOrder 0 setting is"); 
                //Serial.println(TrueIntensity[a]);
            }
            else if (DimOrder[a]==1){
                //Serial.print("Initial Channel setting is= ");
                //Serial.println(a);
                //Serial.println(FullSun);
                TrueIntensity[a]=(flicker[a]+(((float)(ChannelValue[a]-flicker[a]))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/4)*loopCount))/100))));//max intensity is 65% of initial for better visual dimming performance
                //Serial.print("DimOrder 1 setting is"); 
                //Serial.println(TrueIntensity[a]);
            }     
         }
         loopCount++;
         if (loopCount>4) loopCount=1;
    }  
    //enable a flag sent from controller to triger a storm, i.e. IsStorm=true
    // set channel intensities for all but white with random walk continuing from above using static variable so should be seamless
    else if (((Cloud==true) && (IsStorm==true)) || ((Cloud==false) && (IsStorm==true))){
        //current else statement covers possibility of triggering storm from controller (i.e. not coming out of a cloud) but remember you need to flag wtrigger as TRUE when you do this
        if (StormAdvance==false){//Every 1 second duing a storm change intensity, clouds are movin fast baby
           return;
        }
        
        StormAdvance=false;//reset so we run again in 1 second.
        //Serial.print("in a storm now");
        
       if (elapsedTime>=StormEnd){ //if were done with the storm we need to stop this loop, but were probably still cloudy so dont mess with that here
            IsStorm=false;
            Counter=0;
            return;
        }
        if (loopCount==1){
          PriorCloudCover=CloudCover; //e.g. PriorCloudCover=CloudCover with no float math to screw things up
          LastStepSize=StepSize;
          StepSize=(random(5,21));// in Percent% (0-100) This is how much light intensity will change over the loop count interval (this actual time is dependent upon the call frequency of StromAdvance as set in the loop)
          if ((random(0,2)!=1)) StepSize=-(StepSize);
          if ((CloudCover+StepSize)>=100){//cannot shut off lights more than 100% so limit here
            StepSize=(100-CloudCover);
            Counter++;
            if (Counter>(Severity+2)) Counter=0;//allow if to accumulate on ocassion to train strike sequences 2-3 in a row but then dump it 
          }
          else if ((CloudCover+StepSize)<=0){//cannot be brighter than 100% so since were in a cloud dont "limit" it but reflect it back down
            StepSize=-(StepSize);
            if (Counter>=2) Counter-=random(0,2);//since we got bright... lets further delay and randomize strike occurence    
          }
          CloudCover=CloudCover+StepSize;
        }
   
       // float math to get intensity changes in 3 steps  PriorCloudCover=(PriorCloudCover+(LastStepSize/3)*loopCount);//this way its going to change every 250 msec  
       //float math to get intensity changes in 3 steps in intensity modeling CloudCover=(CloudCover+(StepSize/3)*loopCount);//Now add the % increment or decremement to intensity
        
        if ((Counter>=(Severity+random(-1,6))) && (StrikeNow==false)) {//this is where a storm is triggered.  Counter indexes when cloud cover reaches 100 on the random walk
        //to change the frequency of lightning strikes increase or decrease the number comparison for counter in the if statement above (larger #== less storms).
          byte RandomStriker;
          RandomStriker=random(1,11);
          if (RandomStriker>4){
            StrikeNumber=(random(2,11)); //random high =x-1 so max strike =12 each strike requires a duration and a delay thus StrikeMaster is 18 positions
            //ensure the array is zeroed out past the last position required for this strike pattern so each pattern is only as long as generated
            //Array is in pairs, position in arry of (0,1) (2,3) etc as strike (delay,duration)
            for (byte a=0;a<20;a++){
                if (a>=(StrikeNumber*2)){
                    StrikeMaster[a]=0;
                }
                if (a%2==0){
                   if (a==0){
                      StrikeMaster[a]=random(300,1601);//no need for random here but I am leaving it since I wrote it that way.  This must be independent from a=2,4,6 etc...
                    }
                    else {
                       StrikeMaster[a]=(StrikeMaster[(a-2)]+random(160,1201));//position 0,2,4,6,8.. is strike delay
                    } 
                 }
                 else if(a%2!=0){
                    StrikeMaster[a]=random(45,100);//position 1,3,5,7,9... is strike duration (I tried real lightning strike durations and its too short this is adjusted for visual effect
                 }
            }
            StrikeNow=true; //Trigger to start strike sequence in loop
            StrikeStart=millis();//set timer to "zero" now- sequence will start in loop after this function
            StrikeCount=0;
          }
          Counter=0;
        }
        if (Severity>=5){
          for (int a=0;a<8;a++) {
              if (Wchannel[a]==1){//if were in a storm but not a severe storm constrain whites to 50% of Insolation intensity
                if (DimOrder[a]==0){
                      //Serial.print("In a storm and DimOrder0 channel values were");
                      //Serial.println(ChannelValue[a]);
                      //Serial.println(a);
                      TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])*0.2))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100)))); 
                      //Serial.print("and now the channel is set to");
                      //Serial.println(TrueIntensity[a]); 
                  }
                  else if (DimOrder[a]==1){
                      //Serial.print("In a storm and DimOrder1 channel values were");
                      //Serial.println(ChannelValue[a]);
                      //Serial.println(a);
                      TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])*0.2))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
                      //Serial.print("and now the channel is set to");
                      //Serial.println(TrueIntensity[a]); 
                  }
              }
              else if (Wchannel[a]==0){//if were blue, we chase as for a cloud
                  if (DimOrder[a]==0){
                      //Serial.print("In a storm and DimOrder0 channel values were");
                      //Serial.println(ChannelValue[a]);
                      //Serial.println(a);
                      TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100)))); 
                      //Serial.print("and now the channel is set to");
                      //Serial.println(TrueIntensity[a]); 
                  }
                  else if (DimOrder[a]==1){
                      //Serial.print("In a storm and DimOrder1 channel values were");
                      //Serial.println(ChannelValue[a]);
                      //Serial.println(a);
                      TrueIntensity[a]=(flicker[a]+((((float)(ChannelValue[a]-flicker[a])))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
                      //Serial.print("and now the channel is set to");
                      //Serial.println(TrueIntensity[a]); 
                  }
              }    
          }
        }
        else if (Severity<5){
          for (int a=0;a<8;a++) {
              if (Wchannel[a]==1){//if were white we need to be off in a storm
                TrueIntensity[a]=0;
              }
              else if (Wchannel[a]==0){//if were blue, we chase as for a cloud
                  if (DimOrder[a]==0){
                      //Serial.print("In a storm and DimOrder0 channel values were");
                      //Serial.println(ChannelValue[a]);
                      //Serial.println(a);
                      TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100)))); 
                      //Serial.print("and now the channel is set to");
                      //Serial.println(TrueIntensity[a]); 
                  }
                  else if (DimOrder[a]==1){
                      //Serial.print("In a storm and DimOrder1 channel values were");
                      //Serial.println(ChannelValue[a]);
                      //Serial.println(a);
                      TrueIntensity[a]=(flicker[a]+((((float)(ChannelValue[a]-flicker[a])))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
                      //Serial.print("and now the channel is set to");
                      //Serial.println(TrueIntensity[a]); 
                  }
              }    
          }
        }
        loopCount++;
        if (loopCount>3) loopCount=1;
    }//end of storm if loop
}//End Weather function

//CloudStart drops light intensity to 50% of whatever daylight setting is to start the cloud at 50
int CloudStart(long StartTime){
    byte elapsed;
    elapsed=(elapsedTime-StartTime);//counts up since we start this at elapsedTime=StartTime and StartTime is fixed
    for (byte a=0; a<8; a++){ 
      TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])))*(1-(elapsed*2.5)/100))); 
    if (elapsed>=20);
      Cloud=true;//start the cloud
    }
    return 50;//set CloudCover to 50 
}//end CloudStart function

//StartStorm sets up duration and severity of storm. Its currently limited to 90-600 sec in length- it will rarely be lower than 3 minutes
byte StartStorm(int MaxLength, boolean& trigger, long& EndTime){

      int StormDuration;
      MaxLength-=120;//remove 2 mins from longest storm so that we end up with 2 minutes of cloud after the storm before the sky clears to daylight
      if (MaxLength>600){
         MaxLength=600;//modify local variable
         StormDuration=random((MaxLength/3),(MaxLength+1));
         EndTime=(elapsedTime+StormDuration);//Set by reference StormEnd static variable in weather
         //Serial.print("We started a max length storm it should last=");
         //Serial.println(StormDuration);
      }
      else {
         StormDuration=random((MaxLength/2),(MaxLength+1));
         EndTime=(elapsedTime+StormDuration);//Set by reference StormEnd static variable in weather
         //Serial.print("We staarted a cloud time limited length storm it should last=");
         //Serial.println(StormDuration);
      }
      if (elapsedTime<midDay){
        byte LightningIntensity=random(3,7);//afternoon storms are more likely to be severe (every 10-15 sec or less) to about once in a minute or maybe less
      }
      else if (elapsedTime>midDay){
        byte LightningIntensity=random(3,11);//morning storms are generally less severe.
      }
      trigger=true;
      return (LightningIntensity);
}
//End Storm Start Function
        
//Similar to Cloud start but in reverse... now ramp intensity from wherever we were at the end of the cloud to the value set by Insolation
void ClearSky(int CloudPercent, long TerminationTime)
{
    byte elapsed=(elapsedTime-TerminationTime);//Counts up from the scheduled end of the cloud in seconds
    float slope=(CloudPercent/30);//Just calculate how much to increment every second to go from CloudCover to clear sky (CloudCover of zero)
    float LightAdvance;
    LightAdvance=(CloudPercent-(slope*elapsed));//were reducing CloudCover from start to zero over 10 seconds.
    for (byte a=0; a<8; a++){ 
    TrueIntensity[a]=(flicker[a]+(((float)(ChannelValue[a]-flicker[a]))*(1-(LightAdvance/100)))); 
    }
    if (elapsed>=30){//at this point lights are back to full Insolation setting so cancel the cloud and start waiting for the next one
        Cloud=false;//stop the cloud we are now outside of a true condition in the if loop so it will now stay false and lights are back on
        IsStorm=false;//just to be redundant this is not called from a storm... 
    }
}//End Clear Sky function
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

hi just edited the code.

here is the error i am getting

return (LightningIntensity) ;

Lightning intensity was not declared in this scope
Image
abhi_123
Posts: 216
Joined: Tue Mar 20, 2012 8:34 am

Re: C library for Sun/Moon effects

Post by abhi_123 »

an update related to lights. My lights were @100% at noon following 90% @3:00 p.m. which shows lights decreasing as day is progressing but with just 4 hours left in a day don't think they will match the sunset.

Encountering a lightning @ 3:50 p.m.
Image
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Sorry--

:? I have NO idea how that error came up :mrgreen: Actually I do... its the code that creates the severity of the storms... I thought of changing their severity likelihood during the day (so that afternoon storms are likely to be more severe) and changed the code but I must not have compiled it and when I stuck the variable inside that if loop... its scope got limited and the return value got "lost"... this is trivial to fix.


in the new code just find this small bit of code (look for StormStart (){)

and replace it with this...
byte StartStorm(int MaxLength, boolean& trigger, long& EndTime){
byte LightningIntensity;
int StormDuration;
MaxLength-=120;//remove 2 mins from longest storm so that we end up with 2 minutes of cloud after the storm before the sky clears to daylight
if (MaxLength>600){
MaxLength=600;//modify local variable
StormDuration=random((MaxLength/3),(MaxLength+1));
EndTime=(elapsedTime+StormDuration);//Set by reference StormEnd static variable in weather
//Serial.print("We started a max length storm it should last=");
//Serial.println(StormDuration);
}
else {
StormDuration=random((MaxLength/2),(MaxLength+1));
EndTime=(elapsedTime+StormDuration);//Set by reference StormEnd static variable in weather
//Serial.print("We staarted a cloud time limited length storm it should last=");
//Serial.println(StormDuration);
}
if (elapsedTime<midDay){
LightningIntensity=random(3,7);//afternoon storms are more likely to be severe (every 10-15 sec or less) to about once in a minute or maybe less
}
else if (elapsedTime>midDay){
LightningIntensity=random(3,11);//morning storms are generally less severe.
}
trigger=true;
return (LightningIntensity);
}
//End Storm Start Function


in terms of the light intensity... it can be hard to say, the cosine function is severely non-linear. I would have to actually complete the entire calculation on Microsoft Excel to even guess what it should be at xx time from noon. Let me know how things to
rufessor
Posts: 291
Joined: Tue Oct 25, 2011 7:39 am

Re: C library for Sun/Moon effects

Post by rufessor »

Ok... I am now focused on solving the time issue... I STILL think its a wrong setting I looked at the code and it just looks right (which means nothing yet) but I want to SEE your set up.

Can both of you post the following lines from your files... (copy and paste) here is what I want (copied from my file)

//CHANGE this to the number of seconds your time zone is offset from GMT (WITHOUT REGARD TO DAYLIGHT SAVINGS TIME)
int GMToffset=25200;//USA MST time zone is 25,200 if that helps you
//GLOBAL Variables YOU NEED TO CHANGE
boolean ApplyDST=true;//CHANGE THIS IF YOUR NOT USING DST

Then... also grab this and post back

//Calculate Latitude and Longitude converting from Degree, Minute, Seconds to decimal
latitude=dmsToSeconds(40,44,32); //Set to about the latitude of Salt Lake City
longitude=dmsToSeconds(-111,48,11); //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.


and finally grab and post back this

int Choffset[]={
-600,0,-4200,5400,0,600,-3600,6000,-4500,7200,0,0,0,0,0,0};

Finally, can you tell me (spell it correctly) what city you live in (if your willing to provide this) so I can basically recreate all your data and be sure I think you have the right set up. I just don't get whats up. Until I am absolutely certain that your set up is exactly what I think it should be I am not going to try to trouble shoot the code since its working for me and a first glance through it revealed no oddities (i.e. its checking for the sign on the lat/lon pairs) etc etc....

If you know roughly how far off your tank is from the sun with the program running the parameters you post please let me know... like... my tank sun sets 3 hours late or whatever.

THANKs... I will be able to figure out what is the likely error with this information and will ask for more information if I need it or post suggested changes or code updates... I cannot set up my tank to try to recreate your problem without your information. Please include the city you live in if your willing (or somewhere close enough that the sunrise and sunset times are more or less the same).
User avatar
JNieuwenhuizen
Posts: 96
Joined: Thu Feb 16, 2012 12:39 am
Location: South Africa

Re: C library for Sun/Moon effects

Post by JNieuwenhuizen »

//CHANGE this to the number of seconds your time zone is offset from GMT (WITHOUT REGARD TO DAYLIGHT SAVINGS TIME)
int GMToffset=-18000;//USA MST time zone is 25,200 if that helps you
//GLOBAL Variables YOU NEED TO CHANGE
boolean ApplyDST=false;//CHANGE THIS IF YOUR NOT USING DST

//Calculate Latitude and Longitude converting from Degree, Minute, Seconds to decimal
latitude=dmsToSeconds(-25,87,13); //Set to Witbank, or GMT+2 zone, SA
longitude=dmsToSeconds(29,23,32); //Set to Witbank, or GMT+2 zone, SA

int Choffset[]={
900,-3600,1800,-2700,2700,-1800,3600,-900,0,0,0,0,0,0,0,0,0};

I live in South Africe - State is Mpumalanga, City is Witbank (or new name eMalahleni)
Post Reply