Get the elevation and azimuth from lat, long, year, month, day, hour, minute, second

Hi, I'm making a project that controls the position of the sun, with the lat, long, year, month, day, hour, minute, second current, using a gps and an rtc.
To do this project I am using a formula, already for Arduino, of the Institute for Earth Science Research and Education, which should give the correct values.
In the previous project, in which I did not use this formula, I tried to transform a pure formula, always from the previous entity, into a language understandable for Arduino, but I had a problem with the azimuth and elevation changing their decimals with a jump of 0.200, both positive and negative, every 30 seconds. (normally they change by 0.010 every 1-2 sec)
I'm pretty sure the lat and long didn't change in value, they were always the same, so the only thing that could cause this jump in values ​​was the changing time, but it shouldn't.

Now I'm trying with this formula already written for the microcontroller, but I don't know why it gives me azimuth and elevation values ​​that do not correspond in any way with what I see on the suncal.org site. That is, I would like values ​​in degrees, but unfortunately I don't know in which "format" the azimuth and elevation values ​​are calculated.

In this formula I put the values ​​that my gps and my watch read.

#include <DS3231.h>
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <timestamp32bits.h>
#include <Wire.h>

#define DEG_TO_RAD 0.01745329
#define PI 3.141592654
#define TWOPI 6.28318531

SoftwareSerial mySerial(4, -1); //è -1 perchè non utilizziamo quel pin del gps, non ci interess inviare istruzioni ma solo ricevere dati
TinyGPS gps;
// Init the DS3231 using the hardware interface
DS3231 rtc(SDA, SCL);
timestamp32bits stamp = timestamp32bits();

int int_year, int_month, int_day, int_hour, int_minute, int_second;
byte month, day, hour, minute, second;
int year, count, count2; int x;
float flat, flon;
bool GPS_acquired = false;

float T,JD_frac,L0,M,e,C,L_true,f,R,GrHrAngle,Obl,RA,Decl,HrAngle,elev,azimuth;
long JD_whole,JDx;

String splitString(String str, char sep, int index)
{
  int found = 0;
  int strIdx[] = { 0, -1 };
  int maxIdx = str.length() - 1;

  for (int i = 0; i <= maxIdx && found <= index; i++)
  {
    if (str.charAt(i) == sep || i == maxIdx)
    {
      found++;
      strIdx[0] = strIdx[1] + 1;
      strIdx[1] = (i == maxIdx) ? i + 1 : i;
    }
  }
  return found > index ? str.substring(strIdx[0], strIdx[1]) : "";
}

void setup() {

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  // set the data rate for the SoftwareSerial port
    mySerial.begin(9600);

//int hour=16,minute=43,second=0,month=8,day=31,year,zone=1;
//float Lon=8.457*DEG_TO_RAD, Lat=44.667*DEG_TO_RAD;

}
void loop() {
  
  rtc.begin();
  bool newdata=false;
  int year;
  byte month, day, hour, minute, second, hundredths;

  if (mySerial.available())
  {
    char c = mySerial.read();
    // (c);
    if (gps.encode(c))
    {
      newdata = true;
    }
  }

  if ((newdata) && (count <= 50)) // acquisisce i 50 dati dal gps
  {
    gps.f_get_position(&flat, &flon);
    if (isnan(flat) || isnan(flon)) {
      count = 0;
    } else {
            count = count + 1;
      Serial.println("Lat: "+(String)flat);
      Serial.println("Long: "+(String)flon);
    }
  }

  if ((newdata) && (count > 50))
  {
    if ((count > 50) && (GPS_acquired == false)) {
      gps.crack_datetime(&year, &month, &day, &hour, &minute, &second);
      rtc.setTime(hour, minute, second);
      rtc.setDate(day, month, year);
      rtc.setSQWRate(0);
      Serial.println("Dati GPS acquisiti");
      delay(250);

    }

    GPS_acquired = true;
        

    //FORMULA (NON TOCCARE) **********************************************************************************************************************************
  
    String day = splitString(rtc.getDateStr(), '.', 0);
    String month = splitString(rtc.getDateStr(), '.', 1);
    String year = splitString(rtc.getDateStr(), '.', 2);
    String hour = splitString(rtc.getTimeStr(), ':', 0);
    String minute = splitString(rtc.getTimeStr(), ':', 1);
    String second = splitString(rtc.getTimeStr(), ':', 2);
    int_day = day.toInt(); int_month = month.toInt(); int_year = year.toInt(); int_hour = hour.toInt(); int_minute = minute.toInt(); int_second = second.toInt();
    
  
  
    Serial.print("Longitude and latitude "); Serial.print(flon/DEG_TO_RAD,3);
Serial.print(" "); Serial.println(flat/DEG_TO_RAD,3);
Serial.println("year,month,day,local hour,minute,second,elevation,azimuth");
//year=2022;
// Changes may be required in for... loop to get complete
// daylight coverage in time zones farther west.
//for (hour=10; hour<=24; hour++) {
JD_whole=JulianDate(int_year,int_month,int_day);
JD_frac=(int_hour+int_minute/60.+int_second/3600.)/24.-.5;
T=JD_whole-2451545; T=(T+JD_frac)/36525.;
L0=DEG_TO_RAD*fmod(280.46645+36000.76983*T,360);
M=DEG_TO_RAD*fmod(357.5291+35999.0503*T,360);
e=0.016708617-0.000042037*T;
C=DEG_TO_RAD*((1.9146-0.004847*T)*sin(M)+(0.019993-0.000101*T)*sin(2*M)+0.00029*sin(3*M));
f=M+C;

Obl=DEG_TO_RAD*(23+26/60.+21.448/3600.-46.815/3600*T);
JDx=JD_whole-2451545;
GrHrAngle=280.46061837+(360*JDx)%360+.98564736629*JDx+360.98564736629*JD_frac;
GrHrAngle=fmod(GrHrAngle,360.);
L_true=fmod(C+L0,TWOPI);
R=1.000001018*(1-e*e)/(1+e*cos(f));
RA=atan2(sin(L_true)*cos(Obl),cos(L_true));
Decl=asin(sin(Obl)*sin(L_true));
HrAngle=DEG_TO_RAD*GrHrAngle+flon-RA;
elev=asin(sin(flat)*sin(Decl)+cos(flat)*(cos(Decl)*cos(HrAngle)));
// Azimuth measured eastward from north.
azimuth=PI+atan2(sin(HrAngle),cos(HrAngle)*sin(flat)-tan(Decl)*cos(flat));
Serial.print(int_year); Serial.print(","); Serial.print(int_month);
Serial.print(","); Serial.print(int_day); Serial.print(", ");
Serial.print(int_hour); Serial.print(",");
Serial.print(int_minute); Serial.print(","); Serial.print(int_second);





Serial.print(","); Serial.print(elev/DEG_TO_RAD,3);
Serial.print(","); Serial.print(azimuth/DEG_TO_RAD,3); Serial.println();
  }
}

long JulianDate(int year, int month, int day) {
long JD_whole;
int A,B;
if (month<=2) {
year--; month+=12;
}
A=year/100; B=2-A+A/4;
JD_whole=(long)(365.25*(year+4716))+(int)(30.6001*(month+1))+day+B-1524;
return JD_whole;
}

I will attach the official PDF of the Institute for Earth Science Research and Education with the formula, and I will also put my code with the formula only implemented gps and rtc.

If anyone knew how to give me some information I would be very grateful.
Thanks again for your availability.

sketch_aug31a.ino (5.2 KB)
ArduinoUnoSolarCalculations.pdf (228.8 KB)
Libreries.zip (587.2 KB)

OMG! I hope you have a really powerful servo, because I hear the sun is quite large and heavy!

ahahah Yeah, it is very big

So what did you mean to say?

Please post your code according to the forum guide. Many forum members browse the forum on smartphones and tablets and cannot open a .ino or .zip file, but can open .pdf files.

In the meantime, I can think of some reasons why your formula is not working as expected.

  1. The sin(), cos() etc functions in C take parameter in Radians, not degrees. Similarly, the asin(), acos() return values in Radians.

  2. In many models of Arduino, the float and double data types are the same, they are 32-bit floating-point values. They may not have enough precision for the calculations your code are performing. The website you linked to may be performing the calculations using 64-bit precision.

  3. Formulae in C language can be tricky for inexperienced programmers. Those that mix integer and float values can produce results beginners don't expect, for example because C will perform integer division with integer variables. For example 3 / 2 == 1, not 1.5 as a beginner might expect. However, 3.0 / 2 == 1.5

That "Yeah" was a joke. I want to monitor the position of the sun with this formulae and all the data that I give to it.
Anyway thanks for the various information you have given me, but I believe that some solution can be found to have the azimuth and elevation in degrees. I tried to multiply them by 180 / Pi, but nothing to do

If I run the calc from your PDF it gives plausible results that tie fairly closely to your suncalc site. I suggest that you go back to that, get it working and then add your GPS code to get the position. When that works, add the clock.

Thanks Wild Bill, I rewrote the code and after an hour I was able to solve the problem.
Thanks again for your availability

Those formulas were incorporated into the convenient SolarPosition library by a forum member, which works quite well. GitHub - KenWillmott/SolarPosition: Arduino Library to calculate the position of the sun relative to geographic coordinates

What processor do you have? The generic formulas generate truncation and rounding errors when implemented in 32 bit floating point. The SolarPosition library uses special handling to avoid those, but you have more choices if you are using an ESP or STM CPU instead of AVR.

find the Ephemeris library. remove all the other planets from the example.

Does the Ephemeris library work accurately using 32 bit floating point math?

there is a guy named github who can tell you that

Hi,
What is the application?

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

Which one? there are two libraries. This one:
https://github.com/MarScaper/ephemeris
says,

"Due to VSOP87 and ELP2000 implementation, code needs too much flash memory for classic Arduinos (Uno, etc)."

yet it does also say,

Code optimized for single-precision calculations with Arduino.

and the question about which processor the OP is using hasn't been answered yet.

This entry has no useful information OR code: GitHub - chrisspurgeon/arduino-ephemeris: Arduino library to perform planetary calculations

This entry appears to be optimized for 32 bit floats, but does not seem to include any comparisons that allow one to judge the accuracy of the calculations. GitHub - MarScaper/ephemeris: Simple C++ library allowing to compute planet coordinates (equatorial and horizontal) with an Arduino Mega.

Many accuracy tests have been performed and published for the approximations used in the OP's PDF link, and so the SolarPosition library is the obvious choice for a beginner interested in calculating solar positions.

The code in the first post contains the original AVR specific code written by David R. Brooks. That portion of the code was incorporated directly in the SolarPosition library with Dr. Brooks permission. Only, the variable names were fleshed out, code formatting tidied up, and a class wrapper written for it. Thus it remains faithful to the original code.

There is a need for an ephemeris library, so I welcome the one on Github. However there remains some incomplete information about processor limitations, and since the OP has never responded about which board they're using, it remains dubious. In reply #14, I quote two portions of the Ephemeris documentation that seem to contradict one another, it says both that the code is optimized for single precision (which you would find on an AVR) but also that it needs too much flash memory for AVR. So you can understand my confusion, I hope.

I didn't publish a library "to win". I did it because of a personal need, and to fill what seemed like a gap in the public repository. I look forward to testing Ephemeris, really a library that can handle multiple astronomical objects, vs. just Sol, is more valuable to the community. But to be honest, I came away from it with the impression that it needs some work to be really user friendly. It needs a lot better documentation and lots more examples.

Also needed, is some clarity about the provenance of the math, and about processor compatibility. Some testing would also be good. I did a lot of testing with SolarPosition against the results of online calculators, FWIW. That is a minimum.

The Ephemeris time system integration is extremely primitive. It badly needs a better method of specifying time, than setting an ad hoc collection of time unit variables.

// Choose a date and time
int day=10,month=4,year=2014,hour=19,minute=21,second=0;

This is not a practical interface. It would be awkward to integrate into an existing (or even some new) sketch because of the lack of encapsulation.

I suspect the OP is long gone.

For ephemeris calculations, there are plenty of alternatives to Arduino that run full blown linux, with its myriad of libraries, and are just as cheap.

I have a Arduino Uno R3 with ATMEGA328P

Sure, I didn't look but I'll bet there are ready to roll repository packages you could install on any significant Linux distro.

The only thing that I want is the azimuth and the elevation of the sun