Import sun position algorithm to arduino

Hello,
i am using an arduino uno and i want to run this Sun Position Algorithm Plataforma Solar de Almería - PSA Algorithm Files
This code is in C. I added the void setup() and the void loop() to run to the Arduino.
The question is if the void sunpos should be inside the loop void.

#include "sunpos.h"
#include <math.h>

void sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates)
{
 // Main variables
 double dElapsedJulianDays;
 double dDecimalHours;
 double dEclipticLongitude;
 double dEclipticObliquity;
 double dRightAscension;
 double dDeclination;

 // Auxiliary variables
 double dY;
 double dX;

 // Calculate difference in days between the current Julian Day 
 // and JD 2451545.0, which is noon 1 January 2000 Universal Time
 {
 double dJulianDate;
 long int liAux1;
 long int liAux2;
 // Calculate time of the day in UT decimal hours
 dDecimalHours = udtTime.dHours + (udtTime.dMinutes 
 + udtTime.dSeconds / 60.0 ) / 60.0;
 // Calculate current Julian Day
 liAux1 =(udtTime.iMonth-14)/12;
 liAux2=(1461*(udtTime.iYear + 4800 + liAux1))/4 + (367*(udtTime.iMonth 
 - 2-12*liAux1))/12- (3*((udtTime.iYear + 4900 
 + liAux1)/100))/4+udtTime.iDay-32075;
 dJulianDate=(double)(liAux2)-0.5+dDecimalHours/24.0;
 // Calculate difference between current Julian Day and JD 2451545.0 
 dElapsedJulianDays = dJulianDate-2451545.0;
 }

 // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the 
 // ecliptic in radians but without limiting the angle to be less than 2*Pi 
 // (i.e., the result may be greater than 2*Pi)
 {
 double dMeanLongitude;
 double dMeanAnomaly;
 double dOmega;
 dOmega=2.1429-0.0010394594*dElapsedJulianDays;
 dMeanLongitude = 4.8950630+ 0.017202791698*dElapsedJulianDays; // Radians
 dMeanAnomaly = 6.2400600+ 0.0172019699*dElapsedJulianDays;
 dEclipticLongitude = dMeanLongitude + 0.03341607*sin( dMeanAnomaly ) 
 + 0.00034894*sin( 2*dMeanAnomaly )-0.0001134
 -0.0000203*sin(dOmega);
 dEclipticObliquity = 0.4090928 - 6.2140e-9*dElapsedJulianDays
 +0.0000396*cos(dOmega);
 }

 // Calculate celestial coordinates ( right ascension and declination ) in radians 
 // but without limiting the angle to be less than 2*Pi (i.e., the result may be 
 // greater than 2*Pi)
 {
 double dSin_EclipticLongitude;
 dSin_EclipticLongitude= sin( dEclipticLongitude );
 dY = cos( dEclipticObliquity ) * dSin_EclipticLongitude;
 dX = cos( dEclipticLongitude );
 dRightAscension = atan2( dY,dX );
 if( dRightAscension < 0.0 ) dRightAscension = dRightAscension + twopi;
 dDeclination = asin( sin( dEclipticObliquity )*dSin_EclipticLongitude );
 }

 // Calculate local coordinates ( azimuth and zenith angle ) in degrees
 {
 double dGreenwichMeanSiderealTime;
 double dLocalMeanSiderealTime;
 double dLatitudeInRadians;
 double dHourAngle;
 double dCos_Latitude;
 double dSin_Latitude;
 double dCos_HourAngle;
 double dParallax;
 dGreenwichMeanSiderealTime = 6.6974243242 + 
 0.0657098283*dElapsedJulianDays 
 + dDecimalHours;
 dLocalMeanSiderealTime = (dGreenwichMeanSiderealTime*15 
 + udtLocation.dLongitude)*rad;
 dHourAngle = dLocalMeanSiderealTime - dRightAscension;
 dLatitudeInRadians = udtLocation.dLatitude*rad;
 dCos_Latitude = cos( dLatitudeInRadians );
 dSin_Latitude = sin( dLatitudeInRadians );
 dCos_HourAngle= cos( dHourAngle );
 udtSunCoordinates->dZenithAngle = (acos( dCos_Latitude*dCos_HourAngle
 *cos(dDeclination) + sin( dDeclination )*dSin_Latitude));
 dY = -sin( dHourAngle );
 dX = tan( dDeclination )*dCos_Latitude - dSin_Latitude*dCos_HourAngle;
 udtSunCoordinates->dAzimuth = atan2( dY, dX );
 if ( udtSunCoordinates->dAzimuth < 0.0 ) 
 udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth + twopi;
 udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth/rad;
 // Parallax Correction
 dParallax=(dEarthMeanRadius/dAstronomicalUnit)
 *sin(udtSunCoordinates->dZenithAngle);
 udtSunCoordinates->dZenithAngle=(udtSunCoordinates->dZenithAngle 
 + dParallax)/rad;
 }
}

I don't know why you have the extra brackets, but this function should be outside of setup() and loop(). You can call it from either one.

Here is a much better sun position algorithm, designed and tested with the Arduino's floating point limitations in mind.

Please use code tags, not quote tags when posting code.

Hello,
I have an Arduino uno and i want to run this solar position algorithmhttp://www.psa.es/sdg/sunpos.htm
So I have added the void setup and void loop functions but i get this error.

sunpos.ino: In function ‘void loop()’:
sunpos.ino:12:15: error: expected primary-expression before ‘,’ token

And this is my code.

// This file is available in electronic form at http://www.psa.es/sdg/sunpos.htm

#include "sunpos.h"
#include <math.h>

void setup()
{
 Serial.begin(9600);
}

void loop(){
void sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates)
{
// Main variables
double dElapsedJulianDays;
double dDecimalHours;
double dEclipticLongitude;
double dEclipticObliquity;
double dRightAscension;
double dDeclination;

// Auxiliary variables
double dY;
double dX;

// Calculate difference in days between the current Julian Day 
// and JD 2451545.0, which is noon 1 January 2000 Universal Time
{
double dJulianDate;
long int liAux1;
long int liAux2;
// Calculate time of the day in UT decimal hours
dDecimalHours = udtTime.dHours + (udtTime.dMinutes 
+ udtTime.dSeconds / 60.0 ) / 60.0;
// Calculate current Julian Day
liAux1 =(udtTime.iMonth-14)/12;
liAux2=(1461*(udtTime.iYear + 4800 + liAux1))/4 + (367*(udtTime.iMonth 
- 2-12*liAux1))/12- (3*((udtTime.iYear + 4900 
+ liAux1)/100))/4+udtTime.iDay-32075;
dJulianDate=(double)(liAux2)-0.5+dDecimalHours/24.0;
// Calculate difference between current Julian Day and JD 2451545.0 
dElapsedJulianDays = dJulianDate-2451545.0;
}

// Calculate ecliptic coordinates (ecliptic longitude and obliquity of the 
// ecliptic in radians but without limiting the angle to be less than 2*Pi 
// (i.e., the result may be greater than 2*Pi)
{
double dMeanLongitude;
double dMeanAnomaly;
double dOmega;
dOmega=2.1429-0.0010394594*dElapsedJulianDays;
dMeanLongitude = 4.8950630+ 0.017202791698*dElapsedJulianDays; // Radians
dMeanAnomaly = 6.2400600+ 0.0172019699*dElapsedJulianDays;
dEclipticLongitude = dMeanLongitude + 0.03341607*sin( dMeanAnomaly ) 
+ 0.00034894*sin( 2*dMeanAnomaly )-0.0001134
-0.0000203*sin(dOmega);
dEclipticObliquity = 0.4090928 - 6.2140e-9*dElapsedJulianDays
+0.0000396*cos(dOmega);
}

// Calculate celestial coordinates ( right ascension and declination ) in radians 
// but without limiting the angle to be less than 2*Pi (i.e., the result may be 
// greater than 2*Pi)
{
double dSin_EclipticLongitude;
dSin_EclipticLongitude= sin( dEclipticLongitude );
dY = cos( dEclipticObliquity ) * dSin_EclipticLongitude;
dX = cos( dEclipticLongitude );
dRightAscension = atan2( dY,dX );
if( dRightAscension < 0.0 ) dRightAscension = dRightAscension + twopi;
dDeclination = asin( sin( dEclipticObliquity )*dSin_EclipticLongitude );
}

// Calculate local coordinates ( azimuth and zenith angle ) in degrees
{
double dGreenwichMeanSiderealTime;
double dLocalMeanSiderealTime;
double dLatitudeInRadians;
double dHourAngle;
double dCos_Latitude;
double dSin_Latitude;
double dCos_HourAngle;
double dParallax;
dGreenwichMeanSiderealTime = 6.6974243242 + 
0.0657098283*dElapsedJulianDays 
+ dDecimalHours;
dLocalMeanSiderealTime = (dGreenwichMeanSiderealTime*15 
+ udtLocation.dLongitude)*rad;
dHourAngle = dLocalMeanSiderealTime - dRightAscension;
dLatitudeInRadians = udtLocation.dLatitude*rad;
dCos_Latitude = cos( dLatitudeInRadians );
dSin_Latitude = sin( dLatitudeInRadians );
dCos_HourAngle= cos( dHourAngle );
udtSunCoordinates->dZenithAngle = (acos( dCos_Latitude*dCos_HourAngle
*cos(dDeclination) + sin( dDeclination )*dSin_Latitude));
dY = -sin( dHourAngle );
dX = tan( dDeclination )*dCos_Latitude - dSin_Latitude*dCos_HourAngle;
udtSunCoordinates->dAzimuth = atan2( dY, dX );
if ( udtSunCoordinates->dAzimuth < 0.0 ) 
udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth + twopi;
udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth/rad;
// Parallax Correction
dParallax=(dEarthMeanRadius/dAstronomicalUnit)
*sin(udtSunCoordinates->dZenithAngle);
udtSunCoordinates->dZenithAngle=(udtSunCoordinates->dZenithAngle 
+ dParallax)/rad;
}
}

I have try to make different call to my function but it didn't work.

Moderator edit:
</mark> <mark>[code]</mark> <mark>

</mark> <mark>[/code]</mark> <mark>
tags added.

Thank you for the answers.
jremington I test your algorithm and its working.
But i would like also to know about this algorithm also Plataforma Solar de Almería - PSA Algorithm Files
I have added the void setup and void loop functions but i get this error.

sunpos.ino: In function 'void loop()':
sunpos.ino:12:15: error: expected primary-expression before ',' token

this is my code.

// This file is available in electronic form at http://www.psa.es/sdg/sunpos.htm

#include "sunpos.h"
#include <math.h>

void setup()
{
 Serial.begin(9600);
}

void loop(){
void sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates)
{
// Main variables
double dElapsedJulianDays;
double dDecimalHours;
double dEclipticLongitude;
double dEclipticObliquity;
double dRightAscension;
double dDeclination;

// Auxiliary variables
double dY;
double dX;

// Calculate difference in days between the current Julian Day
// and JD 2451545.0, which is noon 1 January 2000 Universal Time
{
double dJulianDate;
long int liAux1;
long int liAux2;
// Calculate time of the day in UT decimal hours
dDecimalHours = udtTime.dHours + (udtTime.dMinutes
+ udtTime.dSeconds / 60.0 ) / 60.0;
// Calculate current Julian Day
liAux1 =(udtTime.iMonth-14)/12;
liAux2=(1461*(udtTime.iYear + 4800 + liAux1))/4 + (367*(udtTime.iMonth
- 2-12*liAux1))/12- (3*((udtTime.iYear + 4900
+ liAux1)/100))/4+udtTime.iDay-32075;
dJulianDate=(double)(liAux2)-0.5+dDecimalHours/24.0;
// Calculate difference between current Julian Day and JD 2451545.0
dElapsedJulianDays = dJulianDate-2451545.0;
}

// Calculate ecliptic coordinates (ecliptic longitude and obliquity of the
// ecliptic in radians but without limiting the angle to be less than 2*Pi
// (i.e., the result may be greater than 2*Pi)
{
double dMeanLongitude;
double dMeanAnomaly;
double dOmega;
dOmega=2.1429-0.0010394594*dElapsedJulianDays;
dMeanLongitude = 4.8950630+ 0.017202791698*dElapsedJulianDays; // Radians
dMeanAnomaly = 6.2400600+ 0.0172019699*dElapsedJulianDays;
dEclipticLongitude = dMeanLongitude + 0.03341607*sin( dMeanAnomaly )
+ 0.00034894*sin( 2*dMeanAnomaly )-0.0001134
-0.0000203*sin(dOmega);
dEclipticObliquity = 0.4090928 - 6.2140e-9*dElapsedJulianDays
+0.0000396*cos(dOmega);
}

// Calculate celestial coordinates ( right ascension and declination ) in radians
// but without limiting the angle to be less than 2*Pi (i.e., the result may be
// greater than 2*Pi)
{
double dSin_EclipticLongitude;
dSin_EclipticLongitude= sin( dEclipticLongitude );
dY = cos( dEclipticObliquity ) * dSin_EclipticLongitude;
dX = cos( dEclipticLongitude );
dRightAscension = atan2( dY,dX );
if( dRightAscension < 0.0 ) dRightAscension = dRightAscension + twopi;
dDeclination = asin( sin( dEclipticObliquity )*dSin_EclipticLongitude );
}

// Calculate local coordinates ( azimuth and zenith angle ) in degrees
{
double dGreenwichMeanSiderealTime;
double dLocalMeanSiderealTime;
double dLatitudeInRadians;
double dHourAngle;
double dCos_Latitude;
double dSin_Latitude;
double dCos_HourAngle;
double dParallax;
dGreenwichMeanSiderealTime = 6.6974243242 +
0.0657098283*dElapsedJulianDays
+ dDecimalHours;
dLocalMeanSiderealTime = (dGreenwichMeanSiderealTime*15
+ udtLocation.dLongitude)*rad;
dHourAngle = dLocalMeanSiderealTime - dRightAscension;
dLatitudeInRadians = udtLocation.dLatitude*rad;
dCos_Latitude = cos( dLatitudeInRadians );
dSin_Latitude = sin( dLatitudeInRadians );
dCos_HourAngle= cos( dHourAngle );
udtSunCoordinates->dZenithAngle = (acos( dCos_Latitude*dCos_HourAngle
*cos(dDeclination) + sin( dDeclination )*dSin_Latitude));
dY = -sin( dHourAngle );
dX = tan( dDeclination )*dCos_Latitude - dSin_Latitude*dCos_HourAngle;
udtSunCoordinates->dAzimuth = atan2( dY, dX );
if ( udtSunCoordinates->dAzimuth < 0.0 )
udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth + twopi;
udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth/rad;
// Parallax Correction
dParallax=(dEarthMeanRadius/dAstronomicalUnit)
*sin(udtSunCoordinates->dZenithAngle);
udtSunCoordinates->dZenithAngle=(udtSunCoordinates->dZenithAngle
+ dParallax)/rad;
}
}

I have try to make different call to my function but it didn't work also

You have included the sun position function inside loop:

void loop() {

void sunpos(...)
{
}

} //close loop()

you want:
void loop()
{
} // close loop

void sunpos(...)
{
}

Threads merged.

KeithRB with your proposal the code is ok but when i upload, in the serial monitor there is nothing.
I think it's because void loop is empty?

You have to call the function from loop().

Either make the function print some output to the serial monitor or have it modify global variables which loop() can then print.

"Global variables" means the variables are defined outside of any function so any function can use them. Local variables are defined inside a function and no other function can see them or damage them.

Arduino does not really support doubles. Sure, it pretends to support doubles, but it's really using floats. A float will only give you 7 good digits, maybe 8 if you're lucky. Maybe that's enough for stanis's algorithm to work. Maybe not. I don't know.

A genuine double will give you at least 15 good digits.

Hate to ressurect an old thread.
It doesn't seem like this was completely solved.

I am having issues too.

I was trying to figure out how to call the function.
At first I thought it was because it could not find the SunPos.h header file.
I found out in the forum how to include a header file if it's located in my sketch folder.
I hope this is correct
(Made a subfolder called SunPos in my sketch folder and it contains the header file):

#include "src/SunPos/SunPos.h"

I get the following compile errors:

Arduino: 1.8.11 Hourly Build 2019/09/30 10:33 (Windows 10), Board: "Arduino NANO 33 IoT"

C:\Users\User\Dropbox\Solar tracking\SunPos\SunPos.ino: In function 'void loop()':

SunPos:14:14: error: expected primary-expression before 'udtTime'

sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates);

^~~~~~~

SunPos:14:32: error: expected primary-expression before 'udtLocation'

sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates);

^~~~~~~~~~~

SunPos:14:61: error: expected primary-expression before '*' token

sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates);

^

SunPos:14:62: error: 'udtSunCoordinates' was not declared in this scope

sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates);

^~~~~~~~~~~~~~~~~

C:\Users\User\Dropbox\Solar tracking\SunPos\SunPos.ino:14:62: note: suggested alternative: 'cSunCoordinates'

sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates);

^~~~~~~~~~~~~~~~~

cSunCoordinates

exit status 1
expected primary-expression before 'udtTime'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

// This file is available in electronic form at http://www.psa.es/sdg/sunpos.htm

#include "src/SunPos/SunPos.h"
#include <math.h>

void setup() 
{
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

void loop() 
{
sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates);
}

void sunpos(cTime udtTime,cLocation udtLocation, cSunCoordinates *udtSunCoordinates)
{
  // Main variables
  double dElapsedJulianDays;
  double dDecimalHours;
  double dEclipticLongitude;
  double dEclipticObliquity;
  double dRightAscension;
  double dDeclination;

  // Auxiliary variables
  double dY;
  double dX;

  // Calculate difference in days between the current Julian Day 
  // and JD 2451545.0, which is noon 1 January 2000 Universal Time
  {
    double dJulianDate;
    long int liAux1;
    long int liAux2;
    // Calculate time of the day in UT decimal hours
    dDecimalHours = udtTime.dHours + (udtTime.dMinutes 
      + udtTime.dSeconds / 60.0 ) / 60.0;
    // Calculate current Julian Day
    liAux1 =(udtTime.iMonth-14)/12;
    liAux2=(1461*(udtTime.iYear + 4800 + liAux1))/4 + (367*(udtTime.iMonth 
      - 2-12*liAux1))/12- (3*((udtTime.iYear + 4900 
    + liAux1)/100))/4+udtTime.iDay-32075;
    dJulianDate=(double)(liAux2)-0.5+dDecimalHours/24.0;
    // Calculate difference between current Julian Day and JD 2451545.0 
    dElapsedJulianDays = dJulianDate-2451545.0;
  }

  // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the 
  // ecliptic in radians but without limiting the angle to be less than 2*Pi 
  // (i.e., the result may be greater than 2*Pi)
  {
    double dMeanLongitude;
    double dMeanAnomaly;
    double dOmega;
    dOmega=2.1429-0.0010394594*dElapsedJulianDays;
    dMeanLongitude = 4.8950630+ 0.017202791698*dElapsedJulianDays; // Radians
    dMeanAnomaly = 6.2400600+ 0.0172019699*dElapsedJulianDays;
    dEclipticLongitude = dMeanLongitude + 0.03341607*sin( dMeanAnomaly ) 
      + 0.00034894*sin( 2*dMeanAnomaly )-0.0001134
      -0.0000203*sin(dOmega);
    dEclipticObliquity = 0.4090928 - 6.2140e-9*dElapsedJulianDays
      +0.0000396*cos(dOmega);
  }

  // Calculate celestial coordinates ( right ascension and declination ) in radians 
  // but without limiting the angle to be less than 2*Pi (i.e., the result may be 
  // greater than 2*Pi)
  {
    double dSin_EclipticLongitude;
    dSin_EclipticLongitude= sin( dEclipticLongitude );
    dY = cos( dEclipticObliquity ) * dSin_EclipticLongitude;
    dX = cos( dEclipticLongitude );
    dRightAscension = atan2( dY,dX );
    if( dRightAscension < 0.0 ) dRightAscension = dRightAscension + twopi;
    dDeclination = asin( sin( dEclipticObliquity )*dSin_EclipticLongitude );
  }

  // Calculate local coordinates ( azimuth and zenith angle ) in degrees
  {
    double dGreenwichMeanSiderealTime;
    double dLocalMeanSiderealTime;
    double dLatitudeInRadians;
    double dHourAngle;
    double dCos_Latitude;
    double dSin_Latitude;
    double dCos_HourAngle;
    double dParallax;
    dGreenwichMeanSiderealTime = 6.6974243242 + 
      0.0657098283*dElapsedJulianDays 
      + dDecimalHours;
    dLocalMeanSiderealTime = (dGreenwichMeanSiderealTime*15 
      + udtLocation.dLongitude)*rad;
    dHourAngle = dLocalMeanSiderealTime - dRightAscension;
    dLatitudeInRadians = udtLocation.dLatitude*rad;
    dCos_Latitude = cos( dLatitudeInRadians );
    dSin_Latitude = sin( dLatitudeInRadians );
    dCos_HourAngle= cos( dHourAngle );
    udtSunCoordinates->dZenithAngle = (acos( dCos_Latitude*dCos_HourAngle
      *cos(dDeclination) + sin( dDeclination )*dSin_Latitude));
    dY = -sin( dHourAngle );
    dX = tan( dDeclination )*dCos_Latitude - dSin_Latitude*dCos_HourAngle;
    udtSunCoordinates->dAzimuth = atan2( dY, dX );
    if ( udtSunCoordinates->dAzimuth < 0.0 ) 
      udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth + twopi;
    udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth/rad;
    // Parallax Correction
    dParallax=(dEarthMeanRadius/dAstronomicalUnit)
      *sin(udtSunCoordinates->dZenithAngle);
    udtSunCoordinates->dZenithAngle=(udtSunCoordinates->dZenithAngle 
      + dParallax)/rad;
  }
}

When you call the function in loop() you have to give it an actual time and location.

odometer:
A float will only give you 7 good digits, maybe 8 if you're lucky.

6 digits, 7 if the first is small enough and as far as good floating point....

"Floating point numbers are like piles of sand; every time you move them around, you lose a little sand and pick up a little dirt." — Brian Kernighan and P.J. Plauger

Ever do a float calculation that should return 1.0 and instead returns 0.999999? I know I have.

I use integers with smaller units, like mm where the result is to be in meters.decimeters format. It gives me 2 places of roundoff before my results are affected. I could use micrometers with longs and get MMM.dcm and 3 places roundoff throwaway. BUT, if I don't lose the rounded off digits I do get error. Do it right or don't bother at all, integers don't guarantee accuracy.

@MorganS Ah... I see.

@GoForSmoke... Is the floating point issue still a problem if a 32 bit microprocessor is being used?
Sure I'd love to build a solar concentrator some day. :wink:
I'd rather just keep the tracking future proof instead of simple for something like solar panel arrays in a backyard.

https://www.pveducation.org/pvcdrom/properties-of-sunlight/suns-position-to-high-accuracy

Blanco-Muriel et al. 3 from the Plataforma Solar de Almerýa (PSA) review the accuracy of all the algorithms. Further they develop a simplified algorithm that is accurate to within 0.5 minutes of arc for the year 1999-2015 :frowning: . The PSA algorithm has been specially optimised in C++ code for microcontrollers and is available at Plataforma Solar de Almería - PSA Algorithm Files.

Took a look at the header file at:
http://www.psa.es/sdg/sunpos.htm

// This file is available in electronic form at http://www.psa.es/sdg/sunpos.htm

#ifndef __SUNPOS_H
#define __SUNPOS_H


// Declaration of some constants 
#define pi    3.14159265358979323846
#define twopi (2*pi)
#define rad   (pi/180)
#define dEarthMeanRadius     6371.01 // In km
#define dAstronomicalUnit    149597890 // In km

struct cTime
{
 int iYear;
 int iMonth;
 int iDay;
 double dHours;
 double dMinutes;
 double dSeconds;
};

struct cLocation
{
 double dLongitude;
 double dLatitude;
};

struct cSunCoordinates
{
 double dZenithAngle;
 double dAzimuth;
};

void sunpos(cTime udtTime, cLocation udtLocation, cSunCoordinates *udtSunCoordinates);

#endif

Looks like I'll have to input the latitude and longitude.
Also have to enter UDT time.
I will try to add these with a GPS module later.
For now I have to figure out how to enter them in manually.

I autoformatted the previous post code and manually formatted for ease of reading below:

// This file is available in electronic form at http://www.psa.es/sdg/sunpos.htm

#include "src/SunPos/SunPos.h"
#include <math.h>

void setup()
{
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

void loop()
{
  sunpos(cTime udtTime, cLocation udtLocation, cSunCoordinates * udtSunCoordinates);
}

void sunpos(cTime udtTime, cLocation udtLocation, cSunCoordinates *udtSunCoordinates)
{
  // Main variables
  double dElapsedJulianDays;
  double dDecimalHours;
  double dEclipticLongitude;
  double dEclipticObliquity;
  double dRightAscension;
  double dDeclination;

  // Auxiliary variables
  double dY;
  double dX;

  // Calculate difference in days between the current Julian Day
  // and JD 2451545.0, which is noon 1 January 2000 Universal Time
  {
    double dJulianDate;
    long int liAux1;
    long int liAux2;
    // Calculate time of the day in UT decimal hours
    dDecimalHours = udtTime.dHours + (udtTime.dMinutes + udtTime.dSeconds / 60.0 ) / 60.0;
    // Calculate current Julian Day
    liAux1 = (udtTime.iMonth - 14) / 12;
    liAux2 = (1461 * (udtTime.iYear + 4800 + liAux1)) / 4 + (367 * (udtTime.iMonth - 2 - 12 * liAux1)) / 12 - (3 * ((udtTime.iYear + 4900 + liAux1) / 100)) / 4 + udtTime.iDay - 32075;
    dJulianDate = (double)(liAux2) - 0.5 + dDecimalHours / 24.0;
    // Calculate difference between current Julian Day and JD 2451545.0
    dElapsedJulianDays = dJulianDate - 2451545.0;
  }

  // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the
  // ecliptic in radians but without limiting the angle to be less than 2*Pi
  // (i.e., the result may be greater than 2*Pi)
  {
    double dMeanLongitude;
    double dMeanAnomaly;
    double dOmega;
    dOmega = 2.1429 - 0.0010394594 * dElapsedJulianDays;
    dMeanLongitude = 4.8950630 + 0.017202791698 * dElapsedJulianDays; // Radians
    dMeanAnomaly = 6.2400600 + 0.0172019699 * dElapsedJulianDays;
    dEclipticLongitude = dMeanLongitude + 0.03341607 * sin( dMeanAnomaly ) + 0.00034894 * sin( 2 * dMeanAnomaly ) - 0.0001134 - 0.0000203 * sin(dOmega);
    dEclipticObliquity = 0.4090928 - 6.2140e-9 * dElapsedJulianDays + 0.0000396 * cos(dOmega);
  }

  // Calculate celestial coordinates ( right ascension and declination ) in radians
  // but without limiting the angle to be less than 2*Pi (i.e., the result may be
  // greater than 2*Pi)
  {
    double dSin_EclipticLongitude;
    dSin_EclipticLongitude = sin( dEclipticLongitude );
    dY = cos( dEclipticObliquity ) * dSin_EclipticLongitude;
    dX = cos( dEclipticLongitude );
    dRightAscension = atan2( dY, dX );
    if ( dRightAscension < 0.0 ) dRightAscension = dRightAscension + twopi;
    dDeclination = asin( sin( dEclipticObliquity ) * dSin_EclipticLongitude );
  }

  // Calculate local coordinates ( azimuth and zenith angle ) in degrees
  {
    double dGreenwichMeanSiderealTime;
    double dLocalMeanSiderealTime;
    double dLatitudeInRadians;
    double dHourAngle;
    double dCos_Latitude;
    double dSin_Latitude;
    double dCos_HourAngle;
    double dParallax;
    dGreenwichMeanSiderealTime = 6.6974243242 + 0.0657098283 * dElapsedJulianDays + dDecimalHours;
    dLocalMeanSiderealTime = (dGreenwichMeanSiderealTime * 15 + udtLocation.dLongitude) * rad;
    dHourAngle = dLocalMeanSiderealTime - dRightAscension;
    dLatitudeInRadians = udtLocation.dLatitude * rad;
    dCos_Latitude = cos( dLatitudeInRadians );
    dSin_Latitude = sin( dLatitudeInRadians );
    dCos_HourAngle = cos( dHourAngle );
    udtSunCoordinates->dZenithAngle = (acos( dCos_Latitude * dCos_HourAngle * cos(dDeclination) + sin( dDeclination ) * dSin_Latitude));
    dY = -sin( dHourAngle );
    dX = tan( dDeclination ) * dCos_Latitude - dSin_Latitude * dCos_HourAngle;
    udtSunCoordinates->dAzimuth = atan2( dY, dX );
    if ( udtSunCoordinates->dAzimuth < 0.0 )
      udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth + twopi;
    udtSunCoordinates->dAzimuth = udtSunCoordinates->dAzimuth / rad;
    // Parallax Correction
    dParallax = (dEarthMeanRadius / dAstronomicalUnit) * sin(udtSunCoordinates->dZenithAngle);
    udtSunCoordinates->dZenithAngle = (udtSunCoordinates->dZenithAngle + dParallax) / rad;
  }
}

SunPos.ino (4.22 KB)

SunPos.h (701 Bytes)

Even most ARM-duinos have no FPU. The IDE may not allow 64-bit FP, but I'd bet there's a library for it!

Even AVR's can use 64-bit type long long integer variables with 23-places. Have you ever used fixed point or very small units in calculations?

If I wanted to track the sun, I'd use sensors and something that casts a shadow. The longer the shadow the more precision I'd get.

Not to side track too much.
I was taking a look at these .
They have low power wireless transmission.
Unique Ids may be issue with multiple units around but perhaps MAC address or some type of password can take care of that.
Also they have an FPU.

In my case I was/am using sensors.
This algorithm could be used in theory as first priority along with gps shield, especially on semi cloudy days. The sensors would be secondary backup.
If someone built a large solar concentrator than the algorithm would be necessary based on what I read in the link in previous posts.

The closest application I’ve run into for really small numbers was for a high accuracy pressure transducer and using a higher resolution ADC for it.

I am lost as to where I will input latitude and UDT time values.

The arm chips do have a 64bit “double.”

Put the precision issues aside for now. It seems you need some basic C programming help.

You need to find out what a function is, how to pass values to a function and how to get multiple values back from the function.

I am sure a google search for beginner tutorials will find something useful.

westfw:
The arm chips do have a 64bit “double.”

TYKM!

knightridar:
If someone built a large solar concentrator than the algorithm would be necessary based on what I read in the link in previous posts.

When you used algorithms with trig functions, I assumed that you had some trig understanding. You can be a few degrees off and make very small difference, a solar panel pointing 8 deg away from directly flat to the sun still gets 99% of the light that one perfectly aligned gets. 2 deg off gets 99.9% of perfect.

There is a MAJOR electric generator in the Arizona desert. It uses many acres of trough reflectors to concentrate light on pipes filled with oil, and a tank or tanks of hot oil to generate steam to generate electricity at full power until almost midnight night after night. Look it up, lots to see and pick up from.

The same setup but smaller could pre-heat water for a house/building water heater and save the gas or electric spent making hot water. Be the first bill payer that likes to take long hot showers on your block! At least in my experience the one who pays that bill spends time reminding others to keep their showers short.

If you could budget for closer to perfect or add 2% more collector area, which do you think would gain more?
What can I say? I learned these things in school long ago from a teacher who used clockworks to track the sun back in the 1930's. Wind em up, line em up, let em go, not a chip in sight yet they worked.