Wednesday, 29 January 2014

Initial Arduino Programming for a Tracking Mount

I had been thinking about buying an Arduino for a very long time, but could never really work out what to make with one! I have never done any C++ programming before but I have had experience with CNC coding in the past when I was working in manufacturing engineering (predominantly 9 axis Fanuc and Mazatrol) so I understand the concepts in programming quite well, even if the applications are million miles apart.

I will say this early. I am NOT in any shape or form an expert in C++ language or even arduino in general I first looked at the language on Boxing day 2013 (5 weeks ago), what I have learned is pretty basic and done through experimenting and trawling the Arduino Website for hours on end - there is a lot of really useful info on there.

Also, there are definitely more accurate ways of calculating the following functions and probably simpler ways too, but this is what I decided to do and how I have decided to it is based on the following points:


  • It is accurate enough to last a whole evening (does it need to be any more accurate??)
  • It doesn't need a Real Time Clock / permanent power supply.
  • It doesn't need to be connected to a PC.
  • And, most importantly, I was able to write the program with my limited skills!!
As with all control designs, I started with a control philosophy. I tried lots of things before settling for (conceding) the following:

There will be 3 no. stepper motors controlling:

  1. Focusing (possibly, not urgent though)
  2. Right Ascension / Hour Angle (2 speeds, Slew and Tracking).
  3. Declination.
Declination and the Focusing are both quite simple in concept, so I will go into them once I have explained the Right Ascension. Basically Right Ascension  "is the angular distance measured eastward along the celestial equator from the vernal equinox to the hour circle of the point in question." 


There are some good explanations of Right Ascension on-line (Here and Here). BUT, the problem with Right Ascension is that the Earth's time is not as we now it; we all know we have leap year every 4 years, it doesn't affect us any more than an extra day once in a while so no-one really appreciates it - well for calculating a celestial co-ordinate it is a bloody pain in the ass!

  • Earth Day according to GMT/UTC is 24 hrs 00 mins 00 seconds with a 4 year synchronisation (and on occasion other smaller corrections).
  • Sidereal Day (actual time it takes for a full 360 degree rotation of Earth is 23 hours, 56 minutes, 4.0916 seconds.
So the Right Ascension of any given point in the sky changes by about 4 minutes a day, this shift is compensated and is called the Hour Angle;


LHA is the Localised Hour Angle for the Object you are trying to locate.
LST is your Local Sidereal Time.
xobject is the Right Ascension of the object you are trying to locate.

So, to the coding, in order to create a usable system for object locating we need to do a few things:
  1. Convert the polar alignment "start position" (polaris) Right Ascension into an angle (so that the stepper motor position can be used to create a "live" Right Ascension figure.
  2. Convert the Right Ascension angle into an hour angle by removing the the current sidereal time from it.
  3. Convert the final decimal into a readable "time stamp" so you can slew to a given Hour Angle and view the object you are slewing too!
This was not as simple as I had initially thought it to be, there were many ways I could have achieved this, they were either expensive (not really, but more than I wanted to spend), time consuming, not suited to my personal preference or just plain out of my range of knowledge, they included:

  • Using a RTC and coding it for LST - I didn't want the hassle.
  • Using serial time from the PC and coding a scaling factor on the Epoch for LST. - very complex for me and I didn't want to have to be connected to a laptop all the time.
  • GPS timing - too pricy for this project.

I decided on something relatively simple.

Sidereal time is complex to keep for long periods dues to the disparity between itself and UTC, however as long as you not pass 23 hours and 56 minutes it is for all intents and purposes, just UTC, shifted. People will be shouting at their keyboards. I don't care. I have spent many years in engineering and I have learnt many things, the most important thing is and always will KISS - Keep It Simple, Stupid. For my clients I call it "appropriate technology" it offends them less!

  • How long am I using the telescope for at a time? - A whole night, tops - less than 23 hours and 56 minutes.
  • Will it be accurate for that time period? Yes.

Right, so the idea is that we have the Right Ascension for polaris hardcoded into the Arduino (02h 31m 49.09s) as decimal degrees. when you polar align, you then press Hour, Minute and Second buttons to set the time to Sidereal time, this will in effect give you 23 hours and 56 minutes of accurate LHA tracking without having to actually calculate LST.

LST can be entered using a LST app or from This Sidereal Clock.

How does this translate into C++

Well this was my first attempt, it is basically a calculator, and didn't work very well, but it may help people understand the basics of right ascension and it's relationship with a real angle so I have included it with explanations.

First we need to create all the variables required to make the counting system work:

int H; // output of truncated float for Hours
int M; // output of truncated float for Minutes

int Hnudge; // Nudge Hour + 1
int Mnudge; // Nudge Minute + 1

int HAH; // Hour angle from Right Ascension minus Local Sidereal time
int HAM; // Hour angle from Right Ascension minus Local Sidereal time
float HAS; // Hour angle from Right Ascension minus Local Sidereal time

float ihr;  // create float for output of initial conversion
float imin; // create float for output of initial conversion
float S; // create float for output of Seconds, no conversion needed, decimals wanted.
float stepdegrees; // output from stepper motor in degrees

  1. int H will be used to create something called a truncated output, in this instance it gives us only the integer from a decimal time i.e. 2.51 hours would give only an hour integer of 2, which is correct - it will not round up, this is very important.
  2. int M is the same but for minutes.
  3. int Hnudge and Mnudge are variables for buttons whereby if the corresponding button is pressed the Xnudge values increases by 1 - not currently programmed.
  4. int HAH and HAM are final compensated time integers.
  5. float HAS is a float, this means it can handle decimals and will output a true reading rather than a truncated one like "int" this is used against the truncated figure to get the time in seconds.
  6. float ihr, imin and S are the initial float variables in the right ascension calculation, relating to the idea in point 5.
  7. float stepdegrees is the output from the stepper motor count in degrees i.e. where the telescope is pointing in a raw decimal degree figure.

Here is the code for the actual calculation, I'll try to explain it below with a worked example;


int Hnudge; // Nudge Hour + 1
int Mnudge; // Nudge Minute + 1
float ihr;  // create float for output of initial conversion
float imin; // create float for output of initial conversion
float RAS; // create float for output of Seconds, no conversion needed, decimals wanted.
float stepdegrees; // output from stepper motor in degrees

int RAH; // output of truncated float for Hours
int RAM; // output of truncated float for Minutes

stepdegrees = (Stepcount/5.68); // 5.68 degrees per step

Hnudge = 0;                  // Sidereal clock set Hour nudge
Mnudge = 0;                  // Sidereal clock set Minute nudge
ihr = ((stepdegrees/15));   // divides total degrees into number of hours in an 
sidereal day (15 degrees per hour)
RAH = ihr - 0.5;            // ensures that the output in hours is always rounded down to show correct value in "time form"

imin = ((ihr - RAH)*60);   // multiplies the remainder of stepdegrees minus rounded hours and multiplies by 60 to convert to minutes.
RAM = imin - 0.5;            // ensures that the output in minutes is always rounded down to show correct value in "time form"

RAS = (((imin - RAM)*60));   // multiplies the remainder of stepdegrees minutes minus rounded minutes and multiplies by 60 to convert to seconds, this is float so also includes 10ths.



This may look like complete gobbledegook, but it is basically fairly simple algebra whereby all the variables we started earlier are being used in a fixed way to create an output. I am really rubbish at explaining myself, I am told this daily, but I will try:

At the beginning of the code we have added a couple of additional variables RAH, RAM and RAS, as I mentioned earlier we are using an integer variable to create our readable output, in order to create the "truncated output" mentioned before we have to create an integer which will always give us the correct Hour, Minute or Second for a given decimal:

if, ihr = 2.516 hours then RAH = 2.516 - 0.5 = 2.016, which as an int = 2
if, ihr = 2.016 hours then RAH = 2.016 - 0.5 = 1.516, which as an int = 2

Edit: Thanks to Tom (TCWORLD) from the Stargazer's Lounge, this was creating an error and most likely the cause of my other problems further down; polaris is more than 30 minutes into an hour (>0.5) so, this code actually worked by accident while I was initially testing. Tom correctly points out that as the float to int cast is only truncating the results would be as follows for some given floats:


2.016 -> 2
2.980 -> 2
1.999 -> 1
1.516 -> 1

This would give anything with a right ascension of less than 30 minutes past the hour the incorrect hour.

This code was already redundant following my updates, but for anyone thinking they could use this elsewhere; it doe not work. Will - 31-01-2014, 13:21.

This is what is called truncating the output, it allows us to show any decimal pertaining to an hour as a single integer and the correct one regardless of rounding, to boot.

We then need to do the same for the Minutes to get a sensible reading:

the code states that; imin = (ihr - RAH)*60 this means that imin is equal to the remainder of the hour decimal minus the truncated result:

imin = 2.516 - 2 = 0.516 * 60 (to turn the figure into minutes) = 30.96 minutes

this needs to be truncated to give a minute integer

RAM = (imin - 0.5)   =  30.96 - 0.5   = 30.46 = int = 30.

RAS = (imin - RAM) * 60  =  30.96-30  = 0.96 * 60 = float = 57.60 seconds.

So that process has taken a decimal hour figure and created 2 sets of integers and a float, printed like so:

2.516 hours is equal to 2h : 30m : 57.60 s. 

I couldn't get this code any further than creating a fixed result it didn't integrate well once a clock counter (millis) was introduced. Learning curves, aye! - See edit above for why!!!




All was not lost though, I actually ended up with something much more simple using the unsigned long function and adding the decimal angle of the Right Ascension of polaris + the stepper position to the millis() count before the  unsigned long function to create the Hour Angle offset as follows:


 int Hnudge; // Nudge Hour + 1
 int Mnudge; // Nudge Minute + 1
 float ihr;  // create float for output of initial convertion
 float stepdegrees; // output from stepper motor in degrees
 

 stepdegrees = ((Stepcount/5.68)/10)+37.954; // temporary fixed angle for testing will be a "stepcount * no. degrees/step" where stepcount will have a button starting the count at 37.954541 degrees *location of polaris*

 Hnudge = 0;                  // Sidereal clock set Hour nudge
 Mnudge = 0;                  // Sidereal clock set Minute nudge
 ihr = ((stepdegrees/15));   // divides total degrees into number of hours in an sidereal day (15 degrees per hour)
 
 float D,LSTH,LSTM,LSTS,ms;   // Local Sidereal time calculator, with nudge control for setting the clock.
 unsigned long over;
 elapsed=(((ihr*3600000)+(Hnudge*3600000)+(Mnudge*60000))+start); // elapsed is the ajustable hour angle in milliseconds = Right Ascension + Hour set + Minute Set + Elapsed Time 
 D=int(elapsed/86164091);                                         // = 23 hrs 56 mins 4 seconds = Sidereal day
 over=elapsed%86164091;                                           // +1 Sidereal day
 LSTH=int(elapsed/3600000);                                       // = 60 minutes
 over=elapsed%3600000;                                            // + 1 hour
 LSTM=int(over/60000);                                            // = 60 seconds
 over=over%60000;                                                 // + 1 Minute
 LSTS=int(over/1000);                                             // = 1000 ms
 ms=over%1000;  

I'll try to explain how this is working on the back of the previous explanation of the right ascension calc above.

So in the background we have the microprocessor counting continuously in milliseconds, this count is being used to calculate all the timings in this program so everything interacting with it must be scaled to the same units:

Firstly we start with a step count from the stepper motor for argument sake 500. Each step has an angular value of 5.68 degrees and it is attached to a 10:1 drive so 500 steps is equal to:

(500/5.68)/10 =  8.803 degrees.

We then need to add the value of polaris to this, so that during polar alignment the system is "zeroed" to the celestial pole;

8.803 + 37.954. this has been designated as "stepdegrees" it will always equal the value of the step count in degrees plus polaris.

To convert this to a decimal hour we need to divide by how many degrees the earth rotates in 1 hour. this is 15 degrees;

ihr = stepdegrees/15   ==  46.757 / 15 = 3.1171 hours.

Then in order to add this value to the millisecond count we need to convert it to milliseconds

3.1171 * 3600000.

Then, we need to account for the Local Sidereal Time adjustment, as explained this is just an offset to keep the system simple. Each time the nudge buttons are pressed they increase the value by an increment of one so we need to scale each increment to the value they are supposed to represent in milliseconds, also:

+ (Hnudge * 3600000) + (Mnudge * 6000)

Plus the millisecond count;

+start

So, as the motor position changes so does the time relative to both the celestial co-ordinate AND Local Sidereal Time. Success.

Now to make the the time readable by a human and equal to the Hour Angle within something like stellarium:

unsigned long, over = a counting function which will roll over and count sets of numbers based on the value, this can be used quite simply to turn the complex mess above into a simple clock time:



 elapsed=(((ihr*3600000)+(Hnudge*3600000)+(Mnudge*60000))+start); // elapsed is the ajustable hour angle in milliseconds = Right Ascension + Hour set + Minute Set + Elapsed Time 
 D=int(elapsed/86164091);                                         // = 23 hrs 56 mins 4 seconds = Sidereal day
 over=elapsed%86164091;                                           // +1 Sidereal day


D is equal to how many times the "ihr" has gone over 86164091ms (a sidereal day)


 LSTH=int(elapsed/3600000);                                       // = 60 minutes
 over=elapsed%3600000;                                            // + 1 hour



LSTH is equal to how many times the "ihr" has gone over 3600000ms within 86164091ms (hours)


 LSTM=int(over/60000); // = 60 seconds  over=over%60000;                                                 // + 1 Minute


LSTM is equal to how many times the "ihr" has gone over 60000ms within 36000000ms (minutes)


 LSTS=int(over/1000);                                             // = 1000 ms
 ms=over%1000;  

LSTS is equal to how many times the "ihr" has gone over 1000ms within 60000ms (seconds)


I hope that has made some sense, I have since done some work on compiling this into a program with all the buttons and stepper motor controls. I will upload this in full once completed so anyone can hopefully replicate the whole build for cheap tracking telescope mount with joypad controlled driven "point to" capabilities!

If you have any questions, please feel free to email me.

1 comment: