star-cem

Compute the position of the sun
git clone git://git.meso-star.fr/star-cem.git
Log | Files | Refs | README | LICENSE

commit d6a292110c9944113e8d30ae989a8a1374c18db0
parent 5a5de33fe66af7d1421c5a1e0994c26dc3f8edc0
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  8 Oct 2025 09:02:06 +0200

Start writing the scem utility

This is a command-line tool that computes the position of the sun
relative to a date and location on Earth. Only command-line analysis is
currently implemented. However, its reference documentation has been
written.

Diffstat:
M.gitignore | 1+
MMakefile | 38++++++++++++++++++++++++++++++++++++--
Adoc/scem.1 | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scem_main.c | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 329 insertions(+), 2 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -7,3 +7,4 @@ tags test* !test*.[ch] +scem diff --git a/Makefile b/Makefile @@ -22,7 +22,7 @@ LIBNAME_SHARED = libscem.so LIBNAME = $(LIBNAME_$(LIB_TYPE)) default: library -all: default tests +all: default tests util ################################################################################ # Library @@ -67,6 +67,32 @@ libsmsh.o: $(OBJ) $(CC) $(CFLAGS_LIB) -c $< -o $@ ################################################################################ +# Utils +################################################################################ +UTIL_SRC = src/scem_main.c +UTIL_OBJ = $(UTIL_SRC:.c=.o) +UTIL_DEP = $(UTIL_SRC:.c=.d) + +PKG_CONFIG_LOCAL = PKG_CONFIG_PATH="./:$${PKG_CONFIG_PATH}" $(PKG_CONFIG) +INCS_UTIL = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags rsys scem-local) +LIBS_UTIL = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs rsys scem-local) + +CFLAGS_UTIL = $(CFLAGS_EXE) $(INCS_UTIL) +LDFLAGS_UTIL = $(LDFLAGS_EXE) $(LIBS_UTIL) + +util: library $(UTIL_DEP) + @$(MAKE) -fMakefile -f"$(UTIL_DEP)" scem + +scem: config.mk scem-local.pc $(UTIL_OBJ) + $(CC) $(CFLAGS_UTIL) -o $@ $(UTIL_OBJ) $(LDFLAGS_UTIL) + +$(UTIL_DEP): config.mk scem-local.pc + @$(CC) $(CFLAGS_UTIL) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +$(UTIL_OBJ): config.mk scem-local.pc + $(CC) $(CFLAGS_UTIL) -c $(@:.o=.c) -o $@ + +################################################################################ # Miscellaneous ################################################################################ pkg: @@ -87,22 +113,30 @@ install: library pkg install() { mode="$$1"; prefix="$$2"; shift 2; \ mkdir -p "$${prefix}"; \ cp "$$@" "$${prefix}"; \ - chmod "$${mode}" "$$@"; \ + chmod "$${mode}" "$${prefix}/$${@##*/}"; \ }; \ install 755 "$(DESTDIR)$(LIBPREFIX)" $(LIBNAME); \ + install 755 "$(DESTDIR)$(BINPREFIX)" scem; \ + install 644 "$(DESTDIR)$(MANPREFIX)/man1" doc/scem.1; \ install 644 "$(DESTDIR)$(LIBPREFIX)/pkgconfig" scem.pc; \ install 644 "$(DESTDIR)$(INCPREFIX)/star" src/scem.h; \ install 644 "$(DESTDIR)$(PREFIX)/share/doc/star-uniq" COPYING README.md uninstall: rm -f "$(DESTDIR)$(LIBPREFIX)/$(LIBNAME)" + rm -f "$(DESTDIR)$(BINPREFIX)/scem" + rm -f "$(DESTDIR)$(MANPREFIX)/man1/scem.1" rm -f "$(DESTDIR)$(LIBPREFIX)/pkgconfig/scem.pc" rm -f "$(DESTDIR)$(INCPREFIX)/star/scem.h" rm -f "$(DESTDIR)$(PREFIX)/share/doc/star-uniq/COPYING" rm -f "$(DESTDIR)$(PREFIX)/share/doc/star-uniq/README.md" +lint: + mandoc -T lint -Wbase doc/scem.1 + clean: clean_test rm -f $(DEP) $(OBJ) $(LIBNAME) + rm -f $(UTIL_DEP) $(UTIL_OBJ) scem rm -f .config libscem.o scem.pc scem-local.pc ################################################################################ diff --git a/doc/scem.1 b/doc/scem.1 @@ -0,0 +1,139 @@ +.\" Copyright (C) 2025 |Méso|Star> (contact@meso-star.com) +.\" +.\" This program is free software: you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation, either version 3 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU Lesser General Public License for more details. +.\" +.\" You should have received a copy of the GNU Lesser General Public License +.\" along with this program. If not, see <http://www.gnu.org/licenses/>. +.Dd October 8, 2025 +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Dt SCEM 1 +.Os +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh NAME +.Nm scem +.Nd compute the position of the sun +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh SYNOPSIS +.Nm +.Op Fl hr +.Op Fl a Ar algo +.Op Fl d Ar utc_date +.Ar latitude +.Ar longitude +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh DESCRIPTION +.Nm +computes the position of the sun relative to a given point on the Earth's +surface, on a given date. +.Pp +The longitude of the Earth's position is between +.Bq -180, 180 +decimal degrees relative to Greenwich. +The angle is positive towards the east. +The latitude of the Earth's position is between +.Bq -90, 90 +decimal degrees relative to the equator. +It is positive towards the north. +.Pp +On output, +.Nm +displays the position of the sun in zenith and azimuth on the standard +output. +The message is formatted as follows: +.Bd -literal -offset Ds +"%f %f\\n", zenith, azimuth +.Ed +.Pp +The zenith angle is between +.Bq -90, 90 +degrees. +It defines the elevation of the sun relative to the horizon. +The azimuth angle ranges from +.Bq 0, 360 +degrees, with 0° indicating north, +90° east, +180° south, +and 270° west. +.Pp +The options are as follows +.Bl -tag -width Ds +.\"""""""""""""""""""""""""""""""""""""" +.It Fl a Ar algo +Defines the algorithm to be used to calculate the position of the sun. +By default, use the +.Cm meeus +algorithm. +.Pp +The solar position algorithms are as follows: +.Bl -tag -width Ds +.It Cm meeus +The algorithm described in the Jean Meeus' book, +.Dq Astronomical Algorithm . +.It Cm psa +The algorithm developed by the +.Dq Palaforma Solar de Almería . +.El +.\"""""""""""""""""""""""""""""""""""""" +.It Fl d Ar utc_date +Date on which the position of the sun is calculated at the specified +location. +Must be defined in Coordinated Universal Time +.Pq UTC+00:00 . +By default, use the local date. +.Pp +The date must be in the format +.Dq YYYY-MM-DDThh:mm:ss , +i.e., as printed for the current date by the following date command: +.Bd -literal -offset Ds +date -u +"%Y-%m-%dT%H:%M:%S" +.Ed +.\"""""""""""""""""""""""""""""""""""""" +.It Fl h +Output short help and exit. +.\"""""""""""""""""""""""""""""""""""""" +.It Fl r +The solar output coordinates are defined in radians rather than degrees. +.El +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh EXIT +.Ex -std +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh EXAMPLES +Calculate the position of the sun on the current date for a latitude of +43.559962° east and a longitude of 1.468150° north: +.Bd -literal -offset Ds +scem 43.559962 1.468150 +.Ed +.Pp +Same as above, but setting the observation date to October 21, 2015, at +7:28:00 a.m.: +.Bd -literal -offset Ds +scem -d 2015-10-21T7:28:00 43.559962 1.468150 +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh SEE ALSO +.Rs +.%A Jean Meeus +.%B Astronomical Algorithms +.%D 1991 +.Re +.Rs +.%A Manuel Blanco-Muriel +.%A Diego C. Alarcón-Padilla +.%A Teodoro López-Moratalla +.%A Martín Lara-Coira +.%T Computing the solar vector +.%J Solar Energy +.%V 70 +.%N 5 +.%D 2001 +.%P 431-441 +.Re diff --git a/src/scem_main.c b/src/scem_main.c @@ -0,0 +1,153 @@ +/* Copyright (C) 2025 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redismeteobute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is dismeteobuted in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _XOPEN_SOURCE /* strptime support */ +#define _POSIX_C_SOURCE 200112L /* getopt support */ + +#include "scem.h" + +#include <rsys/cstr.h> +#include <rsys/mem_allocator.h> + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> /* getopt */ + +struct args { + struct scem_location pos; + struct tm time; /* In UTC+00:00 */ + enum scem_sun_algo algo; + int radian; /* Sun position in radians instead of degrees */ + int quit; +}; +static const struct args ARGS_DEFAULT = { + SCEM_LOCATION_NULL__, {0}, SCEM_SUN_MEEUS, 0, 0 +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE void +usage(FILE* stream) +{ + fprintf(stream, + "usage: scem [-hr] [-a algo] [-d utc_date] latitude longitude\n"); +} + +static res_T +parse_algo(const char* str, enum scem_sun_algo* algo) +{ + ASSERT(str && algo); + + if(!strcmp(str, "meeus")) { + *algo = SCEM_SUN_MEEUS; + } else if(!strcmp(str, "psa")) { + *algo = SCEM_SUN_PSA; + } else { + return RES_BAD_ARG; + } + return RES_OK; +} + +static res_T +parse_date(const char* str, struct tm* time) +{ + ASSERT(str && date); + + if(!strptime(str, "%Y-%m-%dT%H:%M%S\n", time)) { + return RES_BAD_ARG; + } else { + return RES_OK; + } +} + +static res_T +parse_dbl + (const char* str, + const double min, + const double max, + double* out_dbl) /* In [min, max] */ +{ + double dbl = 0; + res_T res = RES_OK; + ASSERT(str && out_lat && min <= max); + + if((res = cstr_to_double(str, &dbl)) != RES_OK) return res; + if(dbl < min || dbl > max) return RES_BAD_ARG; + + *out_dbl = dbl; + return RES_OK; +} + +static res_T +args_init(struct args* args, int argc, char** argv) +{ + int opt = 0; + res_T res = RES_OK; + + *args = ARGS_DEFAULT; + + while((opt = getopt(argc, argv, "a:d:hr")) != -1) { + switch(opt) { + case 'a': res = parse_algo(optarg, &args->algo); break; + case 'd': res = parse_date(optarg, &args->time); break; + case 'h': usage(stdout); args->quit = 1; goto exit; + case 'r': args->radian = 1; break; + default: res = RES_BAD_ARG; break; + } + if(res != RES_OK) { + if(optarg) { + fprintf(stderr, "scem: invalid option argument '%s' -- '%c'\n", + optarg, opt); + } + goto error; + } + } + + /* Is location missing? */ + if(argc - optind < 2) { res = RES_BAD_ARG; goto error; } + + res = parse_dbl(argv[optind], -90, 90, &args->pos.latitude); + if(res != RES_OK) goto error; + res = parse_dbl(argv[optind], -180, 180, &args->pos.longitude); + if(res != RES_OK) goto error; +exit: + return res; +error: + usage(stderr); + goto exit; +} + +/******************************************************************************* + * The program + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct args args = ARGS_DEFAULT; + int err = 0; + res_T res = RES_OK; + + if((res = args_init(&args, argc, argv)) != RES_OK) goto error; + if(args.quit) goto exit; + +exit: + CHK(mem_allocated_size() == 0); + return err; +error: + err = 1; + goto exit; +}