Monday 2 November 2015

Arduino Sidereal Clock with an RTC Module

Continuing on from the previous post - Arduino Sidereal Clock

Let's discuss how we can use just a real time clock (RTC)  like the DS1307 modules which are available all over the internet to calculate and display local Sidereal Time.

I had real trouble calculating sidereal time on paper and by using excel and other methods and I spent a great deal of time trying to get the arduino to calculate it and failed miserably.  I managed to get close by reverse engineering a javascript applet which applied the formula below:

Current Julian Day (JN) = floor(365.25*(y+4716)) + floor(30.6001*(m+1)) + d - 13 -1524.5 + UTC/24.0;

Where floor is a mathematical function which essentially means use the largest integer...

y = year
m = month
d = day
ut = universal co-ordinated time or UTC


The Javascript function I forked for the arduino is below:

function JulDay (d, m, y, u){
 if (y<1900) y=y+1900
 if (m<=2) {m=m+12; y=y-1}
 A = Math.floor(y/100);
 JD =  Math.floor(365.25*(y+4716)) + Math.floor(30.6001*(m+1)) + d - 13 -1524.5 + u/24.0;
 return JD 
} 

Essentially what it does is:

Given the current date made up of YYYY/MM/DD and the current time in UTC calculate the current Julian Day Number with respect to the current time.

So the arduino function needs to perform as follows:

Get the current date and time.
Process the year - if the year is less than 1900 then add 1900 to the value for year
Process the month and year - if the month is January or February add 12 months to the value for month and subtract one off the value for year.
Calculate A - Divide the value for year by 100 and use the largest integer value found as the result.
Calculate the Julian Date using all of the above values found and print the result.

I then connected a tiny RTC real time clock module to the I2C pins on the arduino R3.
Here are the circuit connections for those that are interested.

RTC                Arduino Uno R3

SCL    ------>   Pin A4
SDA   ------>   Pin A5
VCC   ------>   Pin 5V
GND  ------>    Pin 0V

RTC Module connected to Arduino Clone

I then set the date and time on the RTC module using the time.H example included in the arduino example files and used the processing sketch to set the DS1307 using the serial connection and my PC's time.

I then wrote some arduino 'C' code which matches the functionality of the javascript function.

That wasn't too hard to implement:

void calcJulian()
{

 int d1 = day();
 int m1 = month();
 int y1 = year(); 

 int Hr1 = hour();
 int Mn1 = minute();
 int Sc1 = second();
 float A1;
 double JDN4;
 if ( y1 <= 1900 ) 
  {
    y1 = y1 + 1900;
  }
 if (m1 <= 2) 
 { 
   m1 = m1 + 12; 
   y1 = y1 - 1;
 }
 A1 = floor(y1 / 100);

 JDN4 = floor( 365.25* ( y1 + 4716 )) + floor( 30.6001 * ( m1 + 1 )) + d1 - 13 - 1524.5;
 Serial.print("Julian Day Number: ");
 Serial.print(JDN4,2);
 Serial.println();  
}

The only issue is that for some reason whenever I added the code required to include the current time parameter the results were incorrect.

I also tried several other methods which I also found were incorrect.

The extra code required to calculate the current JD number and not the JD number for the current date at 00:00:00 is:

  Hr1 = day() / 24;
 Mn1 = minute / 60;
 Sc1 = second / 3600;

 UT = Hr1 + Mn1 + Sc1;

 JD = JDN4 + UT;

If people are truly interested they can grab my attempts at coding here:

Calculating Sidereal Time

But in the spirit of not giving in to mere trifles of poor programming I looked about the internet to see if anyone else had attempted to calculate sidereal time. There were lots of people who have tried this and some have managed it! Most used a look up table for the Julian Year to achieve the result required. One stood out...

The best implementation...in my opinion is here:


The function of interest is here:

// ********************************************************************************** //
//                       GREENWICH & LOCAL SIDEREAL TIME CALCULATIONS
// ********************************************************************************** //
// based on "ASTR 310 - Observational Astronomy: Formula for Greenwich Sidereal Time (GST)" 
// see http://www.astro.umd.edu/~jph/GST_eqn.pdf formulas

void doSiderealCalc() {
// UTC calculation
   DateTime now = RTC.now();                                  // current time
   DateTime utcTime (now.unixtime() - TZ*3600);               // adjust to GMT
   utc = (float)utcTime.hour() + (float)utcTime.minute()/60.0 + (float)utcTime.second()/3600.0; // decimal form
   
// calculate G (based on extrapolation)
   int g = (utcTime.year() - 2000);
   int leap = int((g+1.0)/4.0);                              // number of leap years since 2000
   int nleap = g-leap;                                       // number of non-leap years since 2000   
   double G = g2000 + (float)leap*lc + (float)nleap*nc;      // number of days
   
// calculate nd
  int nd = doNumDays(utcTime.year(), utcTime.month(), utcTime.day());
  
// calculate GST and Local Sidereal Time (LST)
  GST = G + (dc*nd) + (tc*utc) + fudge;                      // Grenwich Sidereal Time
  LST = GST + 24.0 + (float)(LONGITUDE/360*siderealday);     // adjust for longitude (longitude portion of siderail day
  while(LST>24.0) {  LST -= 24.0; }                          // adjust to bring into 0-24 hours
  dh = int( LST );                                           // translate into hours, ...
  dm = int( (LST - (float)dh)*60.0 );                        //... mins and ...
  ds = int( (LST - (float)dh - (float)dm/60.0)*3600.0 );     //... seconds
 }

// number of days of month (m) and date (d) since beginning of year (y)
int doNumDays(int y, int m, int d) {
  int v=0;
  byte leapyear = ((y-2000)%4 == 0)? 1 : 0;
  switch(m) {
      case 12:  v += 30;        // Dec
      case 11:  v += 31;        // Nov
      case 10:  v += 30;        // Oct
      case 9:   v += 31;        // Sep
      case 8:   v += 31;        // Aug
      case 7:   v += 30;        // Jul
      case 6:   v += 31;        // Jun
      case 5:   v += 30;        // May
      case 4:   v += 31;        // Apr
      case 3:   v += 28 + leapyear;   // May (if year is leapyear, add extra day after February)
      case 2:   v += 31; break; // Feb
  }
  return v+d;                   // days from Jan 1 of given year
}

The entire code did exactly what I wanted and I may well just fork this code to achieve my own plans. The author used a different timing library from the stock time.h library and I want to investigate what the difference might be.  Of curious interest is that in this implementation the Julian Date is not calculated explicitly...which means that as usual there is more than one way to solve a problem.

I may also implement the function correctly from here:

http://www.astro.umd.edu/~jph/GST_eqn.pdf

Helpfully the author has shared the entire code (arduino sketch) on their blog - Nice bloke!  I have also shared my version of it here - I have updated the code to remove the LCD display section and updated the daylight savings function.  The latitude and longitude are hard coded for Manchester, UK.  These values will have to be changed to calculate sidereal time for your location and the whole thing relies on the real time clock being set to the accurate local time.

Sidereal Code - Working

If it is to be compiled then a copy of the RTClib.h library will be required from here:


I'm actually over the moon (heh great pun!) that someone managed this as I was beginning, after hours of trying and several attempts, to wonder if the lack of floating point precision with the arduino was going to be a factor....Thankfully it was not.

The next post will discuss using a GPS module to receive the current UTC time, update the RTC module and then calculate local sidereal time using the latitude and longitude and then display it on a 20x4 Display.

Take care - Langster!

No comments :

Post a Comment