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 | + |
| M | Makefile | | | 38 | ++++++++++++++++++++++++++++++++++++-- |
| A | doc/scem.1 | | | 139 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/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;
+}