I just wrote my first C program for storing the time of the sunset and sunrise for a location given in latitude and longitude in a SQLite database. I usually write C# only. Since this runs on a RaspberryPi I decided to do it in C and I don't really need all that extra stuff that C++ has and I dislike writing anything in Python that is more than just a quick mockup.
I am not familiar with C and I only now the basics about pointers and memory allocation, which is why I want to make sure if I can improve things and do them the C way
properly or at least halfway decent.
It is quite a lot of code (>400 lines) and I'm sorry for that. Writing in C was really fun and I want to do more projects in C only.
Does my code meet C99 standards?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sqlite3.h>
#include <time.h>
#include <stdbool.h>
// Mathematical constants for sunrise calculation.
#define PI 3.1415926
#define ZENITH -.83
// FILE constants for opening and reading files.
#define FILE_OK 0
#define FILE_NOT_EXIST 1
#define FILE_TOO_LARGE 2
#define FILE_READ_ERROR 3
// First and last month for date calculation.
#define FIRSTMONTH 1
#define LASTMONTH 12
/*
** Read the files contents and return them.
*/
char * read_file(const char *fname) {
char * buffer = 0;
long length;
FILE * fp = fopen(fname, "rb");
if (fp) {
fseek(fp, 0, SEEK_END);
length = ftell(fp);
fseek(fp, 0, SEEK_SET);
buffer = (char*)malloc((length+1)*sizeof(char));
if (buffer) {
fread(buffer, sizeof(char), length, fp);
}
fclose(fp);
}
buffer[length] = '\0';
return buffer;
}
/*
** Replace part of string and return the new string.
*/
char * str_replace(char *src, char *trg, char *with) {
char *retVal;
char *ins;
char *tmp;
int len_rep;
int len_with;
int len_front;
int count;
if (!src || !trg) {
return NULL;
}
len_rep = strlen(trg);
if (len_rep == 0) {
return NULL;
}
if (!with) {
with = "";
}
len_with = strlen(with);
ins = src;
for (count=0; tmp=strstr(ins, trg); ++count) {
ins = tmp + len_rep;
}
tmp = retVal = malloc(strlen(src) + (len_with - len_rep) * count + 1);
if (!retVal) {
return NULL;
}
while (count--) {
ins = strstr(src, trg);
len_front = ins-src;
tmp = strncpy(tmp, src, len_front) + len_front;
tmp = strcpy(tmp, with) + len_with;
src += len_front + len_rep;
}
strcpy(tmp, src);
return retVal;
}
/*
** Callback for sqlite3_exec() method.
** Isn't really used at all.
*/
static int callback(void *ignore, int argc, char **argv, char **azColName) {
int i;
for (i=0; i<argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[1] : "NULL");
}
printf("\n");
return 0;
}
/*
** Execute SQL command against database.
*/
int execute_command(char * command) {
sqlite3 *db;
char *zErrMsg = 0;
char *path = "./solar_db.sqlite3";
int rc;
rc = sqlite3_open(path, &db);
if (rc) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return (1);
}
rc = sqlite3_exec(db, command, callback, 0, &zErrMsg);
if (SQLITE_OK != rc) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
sqlite3_close(db);
return 0;
}
/*
** Format SQL command template and
** run formatted command against database.
*/
void insert_entry(
const char * insert,
struct tm *date,
struct tm *sunrise,
struct tm *sunset,
float lat,
float lng,
int localOffset,
int daylightSavings)
{
char *template = (char*)insert;
char datebuff[17];
strftime(datebuff, 17, "%Y-%m-%d", date);
template = str_replace(template, "{0}", datebuff);
char timebuff[7];
strftime(timebuff, 7, "%H:%M", sunrise);
template = str_replace(template, "{1}", timebuff);
strftime(timebuff, 7, "%H:%M", sunset);
template = str_replace(template, "{2}", timebuff);
int len = snprintf(NULL, 0, "%f", lat);
char * fbuff = malloc(len+1);
snprintf(fbuff, len+1, "%f", lat);
template = str_replace(template, "{3}", fbuff);
free(fbuff);
len = snprintf(NULL, 0, "%f", lng);
fbuff = malloc(len+1);
snprintf(fbuff, len+1, "%f", lng);
template = str_replace(template, "{4}", fbuff);
free(fbuff);
len = snprintf(NULL, 0, "%d", localOffset);
char * nbuff = malloc(len+1);
snprintf(nbuff, len+1, "%d", localOffset);
template = str_replace(template, "{5}", nbuff);
free(nbuff);
len = snprintf(NULL, 0, "%d", daylightSavings);
nbuff = malloc(len+1);
snprintf(nbuff, len+1, "%d", daylightSavings);
template = str_replace(template, "{6}", nbuff);
free(nbuff);
execute_command(template);
//printf(template);
return;
}
/*
** Calculate the sunrise or sunset for given date
** and location provided by latitude and longitude.
*/
float calc_sun_time(
int year, int month, int day,
float lat, float lng,
int localOffset,
int daylightSavings,
int settingTime)
{
// Calculate day of the year.
float N1 = floor(275*month/9);
float N2 = floor((month+9)/12);
float N3 = (1+floor((year-4*floor(year/4)+2)/3));
float N = N1-(N2*N3)+day-30;
// Convert longitude to hour value and approximate time.
float lngHour = lng/15.0;
float t = 0.0;
if (settingTime != -1) {
t = N+((18-lngHour)/24);
} /* calculate setting time. */
else {
t = N+((6-lngHour)/24);
} /* calculate rising time. */
// Calculate sun's mean anomaly.
float M = (0.9856*t)-3.289;
// Calculate sun's true longitude.
float L = fmod(M+(1.916*sin((PI/180)*M)) + (0.020 * sin(2*(PI/180)*M)) + 282.634, 360.0);
// Calculate sun's right ascension.
float RA = fmod(180/PI*atan(0.91764*tan((PI/180)*L)), 360.0);
// Right ascension value need to be in the quadrant as L.
float Lquadrant = floor(L/90)*90;
float RAquadrant = floor(RA/90)*90;
RA = RA + (Lquadrant-RAquadrant);
// Right ascension value needs to be converted to hours.
RA = RA / 15;
// Calculate the sun's declination.
float sinDec = 0.39782*sin((PI/180)*L);
float cosDec = cos(asin(sinDec));
// Calculate the sun's local hour angle.
float cosH = (sin((PI/180)*ZENITH)-(sinDec*sin((PI/180)*lat))) / (cosDec*cos((PI/180)*lat));
float H = .0f;
if (settingTime != -1) {
H = (180/PI)*acos(cosH);
}
else {
// Calculate H and convert to hours.
H = 360-(180/PI)*acos(cosH);
}
H = H/15;
// Calculate local mean time of rising/setting.
float T = H+RA-(0.06571*t)-6.622;
// Adjust back to UTC;
float UT = fmod(T-lngHour, 24.0);
return UT + localOffset + daylightSavings;
}
/*
** Get the amount of days for a given month.
** The year parameter is used to check for leap years
** and the amount of days in february.
*/
int days_in_month(int year, int month) {
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
return 31;
}
else if (month == 2) {
if ((year % 4 == 0)
&& (year % 100 != 0)
&& (year % 400 == 0))
{
return 29;
} /* Leap year. */
else {
return 28;
} /* Not a leap year. */
}
else if (month == 4 || month == 6 || month == 9 || month == 11) {
return 30;
}
}
/*
** Get the day of the week.
** Values range from 0 to 6;
*/
int day_of_week(int year, int month, int day) {
int dow;
dow = (day \
+ ((153 * (month+12*((14-month)/12)-3)+2)/5) \
+ (365*(year+4800-((14-month)/12))) \
+ ((year+4800-((14-month)/12))/ 4) \
- ((year+4800-((14-month)/12))/100) \
+ ((year+4800-((14-month)/12))/400) \
- 32045
) % 7;
return dow;
}
/*
** Check if provided date falls into the range
** of the central european daylight savings time.
*/
int is_central_europe_dst(int year, int month, int day) {
if (month < 3 || month > 10) {
return 0;
}
if (month > 3 && month < 10) {
return 1;
}
int ps = day - day_of_week(year, month, day);
if (month == 3) {
return (int)(ps >= 25);
}
if (month == 10) {
return (int)(ps < 25);
}
return false;
}
/*
** Gets the time of the provided day and location
** when the sun rises.
*/
struct tm get_sunrise(
int year, int month, int day,
float lat, float lng,
int offset,
int dst)
{
float localtime = fmod(24+calc_sun_time(year, month, day, lat, lng, offset, dst, -1), 24);
double hours;
float minutes = modf(localtime, &hours)*60;
struct tm sunrise = {
.tm_year=year-1900,
.tm_mday = day,
.tm_mon = month-1,
.tm_min = minutes,
.tm_hour = hours,
.tm_isdst = dst
};
return sunrise;
}
/*
** Gets the time of the provided day and location
** when the sun sets.
*/
struct tm get_sunset(
int year, int month, int day,
float lat, float lng,
int offset,
int dst)
{
float localtime = fmod(24+calc_sun_time(year, month, day, lat, lng, offset, dst, 0), 24);
double hours;
float minutes = modf(localtime, &hours)*60;
struct tm sunset = {
.tm_year=year-1900,
.tm_mday = day,
.tm_mon = month-1,
.tm_min = minutes,
.tm_hour = hours,
.tm_isdst = dst
};
return sunset;
}
/*
** Creates a tm struct for the provided date.
*/
struct tm get_date(int year, int month, int day, int dst) {
struct tm tmday = {
.tm_year=year-1900,
.tm_mday = day,
.tm_mon = month-1,
.tm_min = 0,
.tm_hour = 0,
.tm_isdst = dst
};
return tmday;
}
/*
** Get n-days and store the sunrise and sunset time for these
** days, for a specific location, within a SQLite database.
*/
int main(void) {
int from = 2101; // Start year.
int until = 2200; // End year.
int timezone = 1; // Timezone offset.
float lat = 51.63145; // Latitude of location.
float lng = 9.07999; // Longitude of location.
char *fname = "./insert_solar_time.sql";
char *template = read_file(fname);
int y;
for (y=from; y<=until; y++) {
int m;
for (m=FIRSTMONTH; m<=LASTMONTH; m++) {
int days = days_in_month(y, m);
int d;
for (d=1; d<=days; d++) {
bool ds = is_central_europe_dst(y, m, d);
struct tm current = get_date(y, m, d, (int)ds);
struct tm sunrise = get_sunrise(y, m, d, lat, lng, timezone, (int)ds);
struct tm sunset = get_sunset(y, m, d, lat, lng, timezone, (int)ds);
insert_entry(template, ¤t, &sunrise, &sunset, lat, lng, timezone, (int)ds);
}
}
}
}
```