|
|
View previous topic :: View next topic |
Author |
Message |
bwhiten
Joined: 26 Nov 2003 Posts: 151 Location: Grayson, GA
|
time.h Library or equivalent |
Posted: Wed Nov 26, 2003 2:08 pm |
|
|
I'm trying to include some existing C code in my project which uses the time.h include file, the tm struct and specifically the following functions:
time()
localtime()
The CCS compiler doesn't provide the time library, that I can locate. Anyone seen PIC code for these functions or know the calculations they provide so I can write my own? If so I would apreciate a push in the right direction.
Thanks. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Nov 26, 2003 2:35 pm |
|
|
PIC microcontrollers have no inherent Real Time Clock in them,
unlike PC's. If want a RTC or elapsed time function, you'll have to
add a RTC chip, such as the DS1307.
If you just want to get the elapsed time since the PIC was powered up,
you could use a clock derived from the PIC's crystal frequency to
run one of the PIC's timers, and count elapsed seconds (or minutes, etc.).
Accuracy might not be perfect over a long period of time, because
microprocessor crystals have larger tolerances than RTC crystals.
(Though you could pay more, to get higher accuracy).
What are your actual requirements ? |
|
|
bwhiten
Joined: 26 Nov 2003 Posts: 151 Location: Grayson, GA
|
|
Posted: Wed Nov 26, 2003 3:02 pm |
|
|
Thanks for the reply. Sorry, should have stated that I have a battery backed RTC, the 1307 as you stated. I am working a problem of sun location based on latitude, longitude and date. I saw some related but limited information on a previous post from Mark but didn't see a resolution to the problem. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Nov 26, 2003 3:31 pm |
|
|
Here are bits and pieces of the code that I used. It should give you a start. I used the C18 compiler for this project so you will probably have to make a few mods. If you come across a couple of functions that I didn't include, just post back. BTW, this does take up quite a bit of code space. I was using the 18F6720 so I didn't really care. However, I am running a bit short now and will probably at some point try and crunch a few things or possibly do a look up table with some offsets.
My sun.c file
Code: |
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/* *************************************************************************
A macro to compute the number of days elapsed since 2000 Jan 0.0 £
(which is equal to 1999 Dec 31, 0h UT)
*************************************************************************** */
#define days_since_2000_Jan_0(y, m, d) \
(367L * (y) - ((7 * ((y) + (((m) + 9) / 12))) / 4) + ((275 * (m)) / 9) + (d) - 730530L)
#define INV360 (1.0 / 360.0)
static UINT16 sunrise = 0;
static UINT16 sunset = 0;
UINT16 Get_Sunrise(void);
UINT16 Get_Sunset(void);
static int sun_rise_set
(
unsigned int year,
unsigned char month,
unsigned char day,
double lon,
double lat,
double *trise,
double *tset
);
/* static INT16 sun_rise_set(UINT16 year, UINT8 month, £
* UINT8 day, double lon, double lat, £
* double *rise, double *set); */
static void sunpos(double d, double *lon, double *r);
static void sun_RA_dec(double d, double *RA, double *dec, double *r);
static double revolution(double x);
static double rev180(double x);
static double GMST0(double d);
/* *************************************************************************
NAME: sun_rise_set
DESCRIPTION: This function computes the length of the day PARAMETERS:
year (IN) calendar date, 1801-2099 only month (IN) calendar
date, 1801-2099 only day (IN) calendar date, 1801-2099 only
lon (IN) Eastern longitude positive, Western longitude
negative lat (IN) Northern latitude positive, Southern
latitude negative altit (IN) the altitude which the Sun
should cross upper_limb (IN) non-zero -> upper limb, zero
-> center rise (OUT) where to store the sunrise time set
(OUT) where to store the sunset time GLOBALS: none
RETURN: 0 = sun rises/sets this day, times stored at trise and *tset. +1
= sun above the specified "horizon" 24 hours. trise set to time
when the sun is at south, minus 12 hours while *tset is set to
the south time plus 12 hours. "Day" length = 24 hours -1 = sun is
below the specified "horizon" 24 hours "Day" length 0 hours,
trise and *tset are both set to the time when the sun is at
south.
ALGORITHM: none
NOTES: The longitude value is not critical. Set it to the correct
longitude if you're picky, otherwise set to to, say, 0.0 The
latitude however IS critical - be sure to get it correct Set the
altitude to: -35/60 degrees for rise/set, -6 degrees for civil,
-12 degrees for nautical and -18 degrees for astronomical
twilight. Set the upper limb to non-zero (e.g. 1) when computing
day length and to zero when computing day+twilight length.
*************************************************************************** */
int sun_rise_set(
unsigned int year, /* FIX ME: add comment */
unsigned char month, /* FIX ME: add comment */
unsigned char day, /* FIX ME: add comment */
double lon, /* FIX ME: add comment */
double lat, /* FIX ME: add comment */
double *trise, /* FIX ME: add comment */
double *tset) /* FIX ME: add comment */
{
double altit = -0.583333333; /* the altitude which the Sun should cross */
double d = 0.0, /* Days since 2000 Jan 0.0 (negative before) */
sr = 0.0, /* Solar distance, astronomical units */
sRA = 0.0, /* Sun's Right Ascension */
sdec = 0.0, /* Sun's declination */
sradius = 0.0, /* Sun's apparent radius */
t = 0.0, /* Diurnal arc */
tsouth = 0.0, /* Time when Sun is at south */
sidtime = 0.0, /* Local sidereal time */
cost = 0.0; /* the diurnal arc */
int rc = 0; /* Return code from function - usually 0 */
/* Compute d of 12h local mean solar time */
d = (long)days_since_2000_Jan_0((long)year, (long)month, (long)day) + 0.5 - lon / 360.0;
/* Compute local sidereal time of this moment */
sidtime = revolution(GMST0(d) + 180.0 + lon);
/* Compute Sun's RA + Decl at this moment */
sun_RA_dec(d, &sRA, &sdec, &sr);
/* Compute time when Sun is at south - in hours UT */
tsouth = 12.0 - rev180(sidtime - sRA) / 15.0;
/* Compute the Sun's apparent radius, degrees */
sradius = 0.2666 / sr;
/* Adjust the altitude */
altit -= sradius;
/* Compute the diurnal arc that the Sun traverses to reach £
* the specified altitude altit: */
cost = (sind(altit) - sind(lat) * sind(sdec)) / (cosd(lat) * cosd(sdec));
if (cost >= 1.0)
{
rc = -1;
t = 0.0; /* Sun always below altit */
}
else if (cost <= -1.0)
{
rc = +1;
t = 12.0; /* Sun always above altit */
}
else
t = acosd(cost) / 15.0; /* The diurnal arc, hours */
/* Store rise and set times - in hours UT */
*trise = tsouth - t;
*tset = tsouth + t;
return (rc);
}
#if 0
/* *************************************************************************
FIX ME: add comment
*************************************************************************** */
INT16 sun_rise_set(
UINT16 year, /* FIX ME: add comment */
UINT8 month, /* FIX ME: add comment */
UINT8 day, /* FIX ME: add comment */
double lon, /* FIX ME: add comment */
double lat, /* FIX ME: add comment */
double *trise, /* FIX ME: add comment */
double *tset) /* FIX ME: add comment */
{
double altit = -0.583333333; /* the altitude which the Sun should cross */
double d = 0.0, /* Days since 2000 Jan 0.0 (negative before) */
sr = 0.0, /* Solar distance, astronomical units */
sRA = 0.0, /* Sun's Right Ascension */
sdec = 0.0, /* Sun's declination */
sradius = 0.0, /* Sun's apparent radius */
t = 0.0, /* Diurnal arc */
tsouth = 0.0, /* Time when Sun is at south */
sidtime = 0.0, /* Local sidereal time */
cost = 0.0; /* the diurnal arc */
INT16 rc = 0; /* Return code from function - usually 0 */
/* Compute d of 12h local mean solar time */
d = (long)days_since_2000_Jan_0((long)year, (long)month, (long)day) + 0.5 - lon / 360.0;
/* Compute local sidereal time of this moment */
sidtime = revolution(GMST0(d) + 180.0 + lon);
/* Compute Sun's RA + Decl at this moment */
sun_RA_dec(d, &sRA, &sdec, &sr);
/* Compute time when Sun is at south - in hours UT */
tsouth = 12.0 - rev180(sidtime - sRA) / 15.0;
/* Compute the Sun's apparent radius, degrees */
sradius = 0.2666 / sr;
/* Adjust the altitude */
altit -= sradius;
/* Compute the diurnal arc that the Sun traverses to reach £
* the specified altitude altit: */
cost = (sind(altit) - sind(lat) * sind(sdec)) / (cosd(lat) * cosd(sdec));
if (cost >= 1.0)
{
rc = -1;
t = 0.0; /* Sun always below altit */
}
else if (cost <= -1.0)
{
rc = +1;
t = 12.0; /* Sun always above altit */
}
else
t = acosd(cost) / 15.0; /* The diurnal arc, hours */
/* Store rise and set times - in hours UT */
*trise = tsouth - t;
*tset = tsouth + t;
return (rc);
}
#endif
/* *************************************************************************
NAME: sunpos
DESCRIPTION: This function computes the Sun's position at any instant
PARAMETERS: d (IN) days since 2000 Jan 0 lon (OUT) ecliptic
longitude r (OUT) distance, astronomical units GLOBALS:
none
RETURN: none
ALGORITHM: none
NOTES: Computes the Sun's ecliptic longitude and distance at an instant
given in d, number of days since 2000 Jan 0.0. The Sun's ecliptic
latitude is not computed, since it's always very near 0.
*************************************************************************** */
static void sunpos(
double d, /* FIX ME: add comment */
double *lon, /* FIX ME: add comment */
double *r) /* FIX ME: add comment */
{
double M = 0.0, /* Mean anomaly of the Sun */
w = 0.0, /* Mean longitude of perihelion */
/* Note: Sun's mean longitude = M + w */
e = 0.0, /* Eccentricity of Earth's orbit */
E = 0.0, /* Eccentric anomaly */
x = 0.0, y = 0.0, /* x, y coordinates in orbit */
v = 0.0; /* True anomaly */
/* Compute mean elements */
M = revolution(356.0470 + 0.9856002585 * d);
w = 282.9404 + 4.70935E-5 * d;
e = 0.016709 - 1.151E-9 * d;
/* Compute true longitude and radius vector */
E = M + e * RADEG * sind(M) * (1.0 + e * cosd(M));
x = cosd(E) - e;
y = sqrt(1.0 - e * e) * sind(E);
*r = sqrt(x * x + y * y); /* Solar distance */
v = atan2d(y, x); /* True anomaly */
*lon = v + w; /* True solar longitude */
if (*lon >= 360.0)
*lon -= 360.0; /* Make it 0..360 degrees */
return;
}
/* *************************************************************************
NAME: sun_RA_dec
DESCRIPTION: Returns the Sun's Right Ascension and the Sun's declination
for this moment. PARAMETERS: d (IN) days since 2000 Jan 0
RA (OUT) Right Ascension dec (OUT) declination r (OUT)
distance, astronomical units GLOBALS: none
RETURN: none
ALGORITHM: none
NOTES: none
*************************************************************************** */
static void sun_RA_dec(
double d, /* FIX ME: add comment */
double *RA, /* FIX ME: add comment */
double *dec, /* FIX ME: add comment */
double *r) /* FIX ME: add comment */
{
double lon = 0.0; /* Sun's ecliptic longitude */
double obl_ecl = 0.0; /* obliquity of ecliptic */
double x = 0.0; /* rectangular coordinate */
double y = 0.0; /* rectangular coordinate */
double z = 0.0; /* rectangular coordinate */
/* Compute Sun's ecliptical coordinates */
sunpos(d, &lon, r);
/* Compute ecliptic rectangular coordinates (z=0) */
x = *r * cosd(lon);
y = *r * sind(lon);
/* Compute obliquity of ecliptic (inclination of Earth's axis) */
obl_ecl = 23.4393 - 3.563E-7 * d;
/* Convert to equatorial rectangular coordinates - x is unchanged */
z = y * sind(obl_ecl);
y = y * cosd(obl_ecl);
/* Convert to spherical coordinates */
*RA = atan2d(y, x);
*dec = atan2d(z, sqrt(x * x + y * y));
return;
}
/* *************************************************************************
NAME: GMST0
DESCRIPTION: This function computes GMST0, the Greenwich Mean Sidereal
Time at 0h UT (i.e. the sidereal time at the Greenwhich
meridian at 0h UT). PARAMETERS: d (IN) angle GLOBALS: none
RETURN: angle to within +180..+180 degrees
ALGORITHM: Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr L M
+ w, as defined in sunpos(). Since I'm too lazy to add these
numbers, I'll let the C compiler do it for me. Any decent C
compiler will add the constants at compile time, imposing no
runtime or code overhead.
NOTES: GMST is then the sidereal time at Greenwich at any time of the
day. I've generalized GMST0 as well, and define it as: GMST0 GMST
- UT -- this allows GMST0 to be computed at other times than 0h
UT as well. While this sounds somewhat contradictory, it is very
practical: instead of computing GMST like: GMST (GMST0) UT
(366.2422/365.2422) where (GMST0) is the GMST last time UT was 0
hours, one simply computes: GMST = GMST0 + UT where GMST0 is the
GMST "at 0h UT" but at the current moment! Defined in this way,
GMST0 will increase with about 4 min a day. It also happens that
GMST0 (in degrees, 1 hr = 15 degr) is equal to the Sun's mean
longitude plus/minus 180 degrees! (if we neglect aberration,
which amounts to 20 seconds of arc or 1.33 seconds of time)
*************************************************************************** */
static double GMST0(
double d) /* FIX ME: add comment */
{
return (revolution((180.0 + 356.0470 + 282.9404) + (0.9856002585 + 4.70935E-5) * d));
}
/* *************************************************************************
NAME: revolution
DESCRIPTION: This function reduces any angle to within the first
revolution by subtracting or adding even multiples of 360.0
until the result is >= 0.0 and < 180.0 PARAMETERS: x (IN)
angle
RETURN: angle to within +180..+180 degrees
ALGORITHM: none
NOTES: none
*************************************************************************** */
static double rev180(
double x) /* FIX ME: add comment */
{
return (x - 360.0 * floor(x * INV360 + 0.5));
}
/* *************************************************************************
NAME: revolution
DESCRIPTION: This function reduces any angle to within the first
revolution by subtracting or adding even multiples of 360.0
until the result is >= 0.0 and < 360.0 PARAMETERS: x (IN)
angle
RETURN: angle to within 0..360 degrees
ALGORITHM: none
NOTES: none
*************************************************************************** */
static double revolution(
double x) /* FIX ME: add comment */
{
return (x - 360.0 * floor(x * INV360));
}
/* *************************************************************************
NAME: Calculate_Dusk_Dawn
DESCRIPTION: Returns the time for dawn and dusk in minutes after
midnight PARAMETERS: latitude (IN) latitude (in deg min)
longitude (IN) longitude (in deg min) tz (IN) time zone (in
minutes) year (IN) years after Christ for which to compute
month (IN) months (1=January...12=December) day (IN) day of
the month GLOBALS: none
RETURN: none
ALGORITHM: none
NOTES: TimeZone is to be specified according to U.S Naval Observatory
Astronimical Applications Department World Time Zone Map, with
cities located west of 0 degrees longitude (Zulu time, UTC) using
a negative value for time zone. year,month,date = calendar date,
1801-2099 only. Eastern longitude positive, Western longitude
negative Northern latitude positive, Southern latitude negative
The longitude value is not critical. Set it to the correct
longitude if you're picky, otherwise set to to, say, 0.0 The
latitude however IS critical - be sure to get it correct
*************************************************************************** */
void Calculate_Dusk_Dawn(
INT16 latitude, /* FIX ME: add comment */
INT16 longitude, /* FIX ME: add comment */
INT16 tz, /* FIX ME: add comment */
UINT16 date) /* FIX ME: add comment */
{
double rise = 0.0; /* decimal hours */
double set = 0.0; /* decimal hours */
double lat = 0.0; /* decimal degrees */
double lon = 0.0; /* decimal degrees */
UINT8 dst = 0;
UINT8 month, day, y;
UINT16 year;
Time_DateFrom2000(&month, &day, &y, date);
year = y + 2000;
/* We need to disable ints before we do the floating point math. £
* I had problems if I didn't. */
Global_Int(INT_DISABLED);
/* Adjust for daylight savings */
if (Time_IsDaylightSavings(month, day, y))
dst = 60;
/* convert minutes to decimal degrees */
lat = (double)latitude / 60.0;
lon = (double)longitude / 60.0;
if (!sun_rise_set(year, month, day, lon, lat, &rise, &set))
{
/* convert decimal hours into minutes and adjust for locality */
sunrise = (INT16) ((rise * 60.0) + dst + tz);
sunset = (INT16) ((set * 60.0) + dst + tz);
/* Validate the time */
if (sunrise >= (24 * 60L))
sunrise = 0;
if (sunset >= (24 * 60L))
sunset = 0;
}
else
{
sunrise = 0;
sunset = 0;
}
Global_Int(INT_RESTORE);
return;
}
/* *************************************************************************
NAME: Get_Sunrise
DESCRIPTION: This function allows access to our module level variable
containing the sunrise value. PARAMETERS: none
RETURN: The time when the sun will rise in minutes
ALGORITHM: none
NOTES: none
*************************************************************************** */
UINT16 Get_Sunrise(void)
{
return (sunrise);
}
/* *************************************************************************
NAME: Get_Sunset
DESCRIPTION: This function allows access to our module level variable
containing the sunset value. PARAMETERS: none
RETURN: The time when the sun will set in minutes
ALGORITHM: none
NOTES: none
*************************************************************************** */
UINT16 Get_Sunset(void)
{
return (sunset);
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
|
Other functions called from sun.c
Code: |
typedef enum { MON, TUE, WED, THU, FRI, SAT, SUN, HOL } DAYSOFWEEK;
rom const UINT8 MONTH_DAYS[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/* *************************************************************************
DESCRIPTION: Converts days to month, day, and year
RETURN: month, day, and year are returned through parameters
ALGORITHM: none
NOTES: none
*************************************************************************** */
void Time_DateFrom2000(
UINT8 *pMonth, /* holds month */
UINT8 *pDay, /* holds day */
UINT8 *pYear, /* holds year */
UINT16 days) /* days that we are converting */
{
static UINT16 date = 0; /* storage to speed up if the last date was the same */
static UINT8 month = 1; /* storage to speed up if the last date was the same */
static UINT8 day = 1; /* storage to speed up if the last date was the same */
static UINT8 year = 0; /* storage to speed up if the last date was the same */
/* See if we have already computed the month, day, and year for this
* date */
if (days != date)
{
/* Set date equal to days so that we can avoid this calculation later */
date = days;
year = 0;
month = 1;
day = 1;
while (days >= 365)
{
if ((LeapYear(year)) && days == 365)
break;
days -= 365;
if (LeapYear(year))
--days;
year++;
}
while (days >= MonthDays(month, year))
{
days -= MonthDays(month, year);
month++;
}
day += days;
}
*pYear = year;
*pMonth = month;
*pDay = day;
}
/* *************************************************************************
DESCRIPTION: Returns true if the date is during DST.
RETURN: TRUE if date falls in DST. FALSE if not.
ALGORITHM: none
NOTES: none
*************************************************************************** */
UINT8 Time_IsDaylightSavings(
UINT8 month, /* holder for month */
UINT8 day, /* holder for day */
UINT8 year) /* holder for year */
{
UINT8 i; /* day of month counter */
/* a month between April and October */
if ((month > 4) && (month < 10))
{
return (TRUE);
}
/* get the start date for DST - First Sunday in April */
if (month == 4)
{
for (i = 1; i <= MonthDays(month, year); i++)
{
if (DayOfWeek(Time_DaysSince2000(month, i, year)) == SUN)
{
if (day >= i)
return (TRUE);
}
}
}
/* get the end date for DST - Last Sunday in October */
else if (month == 10)
{
for (i = MonthDays(month, year); i > 0; i--)
{
if (DayOfWeek(Time_DaysSince2000(month, i, year)) == SUN)
{
if (day < i)
return (TRUE);
}
}
}
return (FALSE);
}
/* *************************************************************************
DESCRIPTION: Converts date to days since 1/1/2000
RETURN: number of days since Jan 1, 2000, or 0 if date is invalid
ALGORITHM:
NOTES: none
*************************************************************************** */
UINT16 Time_DaysSince2000(
UINT8 month, /* holds month */
UINT8 day, /* holds day */
UINT8 year) /* holds year */
{
UINT8 i;
UINT16 days = 0; /* return value */
/* check for a vaild date */
if (!Time_IsDateValid(month, day, year))
return (0);
for (i = 0; i < year; i++)
{
days += 365;
if (LeapYear(i))
++days;
}
for (i = 1; i < month; i++)
{
days += MonthDays(i, year);
}
days += (day - 1);
return (days);
}
/* *************************************************************************
DESCRIPTION: Returns the day of the week value
RETURN: day of the week (0 = Monday...6=Sunday)
ALGORITHM: none
NOTES: none
*************************************************************************** */
static DAYSOFWEEK DayOfWeek(
UINT16 days) /* number of days since 1/1/2000 */
{
return (DAYSOFWEEK) (((days + 5) % 7));
}
/* *************************************************************************
DESCRIPTION: This routine determines if a year is a leap year.
RETURN: 1 if a leap year, 0 if not.
ALGORITHM: none
NOTES: Note that this is not the proper way of checking for leap year
but is good enough for our life time.
*************************************************************************** */
static UINT8 LeapYear(
UINT8 year) /* year that we are checking */
{
if (year & 0x03)
return (FALSE);
else
return (TRUE);
}
/* *************************************************************************
DESCRIPTION: Returns the number of days in given month and year
RETURN: number of days in month, or -1 if month is out of range
ALGORITHM: none
NOTES: none
*************************************************************************** */
static UINT8 MonthDays(
UINT8 month, /* holds month */
UINT8 year) /* holds year */
{
if ((month == 2) && LeapYear(year))
return (29);
else if (month < 13)
return (MONTH_DAYS[month]);
else
return (0);
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
bwhiten
Joined: 26 Nov 2003 Posts: 151 Location: Grayson, GA
|
|
Posted: Wed Nov 26, 2003 4:12 pm |
|
|
Mark! I'm not worthy! I'm sure I can pull the functions I need out of your listing. Have you had a chance to verify any sunrise/sunset results with the online calculators like the US Naval Observatory? Thanks again for the support. Hopefully I can return the favor some day. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Nov 26, 2003 6:27 pm |
|
|
Yes, I have never seen it more than a minute off.
Here are some BCD functions that I use for the DS1307
Code: |
/* *************************************************************************
DESCRIPTION: This function converts an 8 bit binary value to an 8 bit
BCD value. The input range must be from 0 to 99.
RETURN: bcd representation of binary_value
ALGORITHM: none
NOTES: none
*************************************************************************** */
UINT8 bin2bcd(
UINT8 binary_value) /* value we are converting to binary */
{
UINT8 temp;
UINT8 retval;
temp = binary_value;
retval = 0;
while (1)
{
/* Get the tens digit by doing multiple subtraction of 10 from the
* binary value */
if (temp >= 10)
{
temp -= 10;
retval += 0x10;
}
else /* Get the ones digit by adding the remainder. */
{
retval += temp;
break;
}
}
return (retval);
}
/* *************************************************************************
DESCRIPTION: This function converts an 8 bit BCD value to an 8 bit
binary value. The input range must be from 0 to 99.
RETURN: none
ALGORITHM: none
NOTES: none
*************************************************************************** */
UINT8 bcd2bin(
UINT8 bcd_value) /* FIX ME: add comment */
{
UINT8 tens;
UINT8 ones;
tens = (bcd_value >> 4) * 10;
ones = bcd_value & 0x0f;
return (tens + ones);
}
|
|
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|