star-meteo

Time varying meteorological data
git clone git://git.meso-star.fr/star-meteo.git
Log | Files | Refs | README | LICENSE

commit e12efe45ddbafa16f099c3a7492ec7623897ebef
parent 1706926b082abc50e5785b7b37e2215ad377e1cc
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 14 May 2025 16:40:58 +0200

Add loading functions

Loading can be done from a path to a file on disk or from a stream.

Diffstat:
MMakefile | 7+++----
Msrc/smeteo.c | 14++++----------
Msrc/smeteo.h | 11+++++++++++
Asrc/smeteo_c.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/smeteo_load.c | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 316 insertions(+), 14 deletions(-)

diff --git a/Makefile b/Makefile @@ -28,11 +28,11 @@ all: default tests ################################################################################ # Library ################################################################################ -SRC = src/smeteo.c +SRC = src/smeteo.c src/smeteo_load.c OBJ = $(SRC:.c=.o) DEP = $(SRC:.c=.d) -CFLAGS_LIB = $(CFLAGS_SO) $(INCS) -DSMETEO_SHARED_BUILD +CFLAGS_LIB = -std=c99 $(CFLAGS_SO) $(INCS) -DSMETEO_SHARED_BUILD LDFLAGS_LIB = $(LDFLAGS_SO) $(LIBS) library: .config $(DEP) @@ -67,7 +67,6 @@ libsmsh.o: $(OBJ) .c.o: $(CC) $(CFLAGS_LIB) -c $< -o $@ - ################################################################################ # Miscellaneous ################################################################################ @@ -122,7 +121,7 @@ PKG_CONFIG_LOCAL = PKG_CONFIG_PATH="./:$${PKG_CONFIG_PATH}" $(PKG_CONFIG) INCS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags rsys smeteo-local.pc) LIBS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs rsys smeteo-local.pc) -CFLAGS_TEST = $(CFLAGS_EXE) $(INCS_TEST) +CFLAGS_TEST = -std=c89 $(CFLAGS_EXE) $(INCS_TEST) LDFLAGS_TEST = $(LDFLAGS_EXE) $(LIBS_TEST) tests: library $(TEST_DEP) $(TEST_BIN) diff --git a/src/smeteo.c b/src/smeteo.c @@ -14,18 +14,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "smeteo.h" +#include "smeteo_c.h" -#include <rsys/logger.h> #include <rsys/mem_allocator.h> -#include <rsys/ref_count.h> -#include <rsys/str.h> - -struct smeteo { - struct str filename; - struct mem_allocator* allocator; - struct logger* logger; - ref_T ref; -}; /******************************************************************************* * Helper functions @@ -43,6 +34,7 @@ release_smeteo(ref_T* ref) struct smeteo* smeteo = CONTAINER_OF(ref, struct smeteo, ref); ASSERT(smeteo); str_release(&smeteo->filename); + darray_entry_release(&smeteo->entries); MEM_RM(smeteo->allocator, smeteo); } @@ -69,7 +61,9 @@ smeteo_create ref_init(&smeteo->ref); smeteo->allocator = allocator; smeteo->logger = args->logger ? args->logger : LOGGER_DEFAULT; + smeteo->verbose = args->verbose; str_init(smeteo->allocator, &smeteo->filename); + darray_entry_init(smeteo->allocator, &smeteo->entries); exit: if(out_smeteo) *out_smeteo = smeteo; diff --git a/src/smeteo.h b/src/smeteo.h @@ -64,6 +64,17 @@ SMETEO_API res_T smeteo_ref_put (struct smeteo* smeteo); +SMETEO_API res_T +smeteo_load + (struct smeteo* smeteo, + const char* filename); + +SMETEO_API res_T +smeteo_load_stream + (struct smeteo* smeteo, + FILE* fp, + const char* name); + END_DECLS #endif /* SMETEO_H */ diff --git a/src/smeteo_c.h b/src/smeteo_c.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2025 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redismshbute 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 dismshbuted 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/>. */ + +#ifndef SMETEO_C_H +#define SMETEO_C_H + +#include <rsys/dynamic_array.h> +#include <rsys/logger.h> +#include <rsys/ref_count.h> +#include <rsys/str.h> + +#include <time.h> /* struct tm */ + +/* Helper macros for logging */ +#define LOG__(Dev, Lvl, Type, ...) { \ + if ((Dev)->verbose >= (Lvl)) \ + logger_print((Dev)->logger, Type, __VA_ARGS__); \ +} (void)0 +#define ERROR(Dev, ...) LOG__(Dev, 1, LOG_ERROR, "error: "__VA_ARGS__) +#define WARN(Dev, ...) LOG__(Dev, 2, LOG_WARNING, "warning: "__VA_ARGS__) +#define INFO(Dev, ...) LOG__(Dev, 3, LOG_OUTPUT, __VA_ARGS__) + +struct entry { + struct tm time; + double Tsrf; /* Surface temperature [K] */ + double Tatm; /* Atmosphere temperature [K] */ + double SWdn; /* Shortwave downward flux [W/m^2] */ + double SWup; /* Shortwave downward flux [W/m^2] */ + double Trad; /* Radiative temperature [K] */ + double H; /* Convection coefficient [W/K/m^2] */ + double LE; /* ??? */ + double day_1850; /* ??? */ +}; +#define ENTRY_NULL__ {0} +static const struct entry ENTRY_NULL = ENTRY_NULL__; + +/* Generate the darray_entry data type and its API */ +#define DARRAY_NAME entry +#define DARRAY_DATA struct entry +#include <rsys/dynamic_array.h> + +struct smeteo { + struct str filename; + struct darray_entry entries; + + int verbose; /* Verbosity level */ + + struct mem_allocator* allocator; + struct logger* logger; + ref_T ref; +}; + +#endif /* SMETEO_C_H */ diff --git a/src/smeteo_load.c b/src/smeteo_load.c @@ -0,0 +1,233 @@ +/* Copyright (C) 2025 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redismshbute 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 dismshbuted 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 */ + +#include "smeteo.h" +#include "smeteo_c.h" + +#include <rsys/cstr.h> +#include <rsys/text_reader.h> + +#include <errno.h> +#include <string.h> +#include <time.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE void +reset(struct smeteo* smeteo) +{ + ASSERT(smeteo); + str_clear(&smeteo->filename); + darray_entry_clear(&smeteo->entries); +} + +/* TODO pay attention to time zone and locale (?)*/ +static res_T +parse_time + (struct smeteo* smeteo, + struct txtrdr* txtrdr, + const char* date, + const char* hour, + struct tm* time) +{ + char buf[64] = {0}; + size_t ldate=0, lhour=0; /* Length of corresponding string */ + res_T res = RES_OK; + + ASSERT(smeteo && time); + + if(!date || !time) { res = RES_BAD_ARG; goto error; } + + /* Check memory space to build a string concatenating date and time */ + ldate = strlen(date); + lhour = strlen(hour); + if(ldate + lhour > sizeof(buf)+1/*NUL byte*/+1/*date/hour separator*/) { + res = RES_MEM_ERR; + goto error; + } + + /* Concatenate the data and time strings */ + strcpy(buf, date); + buf[ldate] = 'T'; /* Separator between date and time */ + strcpy(buf+ldate+1, hour); + + /* Convert time */ + if(!strptime(buf, "%d-%b-%YT%H:%M:%S", time)) { + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + ERROR(smeteo, "%s:%zu: error parsing time '%s' '%s' -- %s\n", + txtrdr_get_name(txtrdr), + txtrdr_get_line_num(txtrdr), + date ? date : "(null)", + hour ? hour : "(null)", + res_to_cstr(res)); + goto exit; +} + +static res_T +parse_double + (struct smeteo* smeteo, + struct txtrdr* txtrdr, + const char* name, + const char* str, + double range[2], /* Inclusive boundaries */ + double* out_dbl) +{ + double dbl = 0; + res_T res = RES_OK; + ASSERT(smeteo && txtrdr && name && out_dbl); + + if(!str) { res = RES_BAD_ARG; goto error; } + + res = cstr_to_double(str, &dbl); + if(res == RES_OK && (dbl < range[0] || range[1] < dbl)) res = RES_BAD_ARG; + if(res != RES_OK) goto error; + + *out_dbl = dbl; + +exit: + return res; +error: + ERROR(smeteo, "%s:%zu: error parsing %s '%s' -- %s\n", + txtrdr_get_name(txtrdr), + txtrdr_get_line_num(txtrdr), + name, + str ? str : "(null)", + res_to_cstr(res)); + goto exit; +} + +static res_T +parse_line(struct smeteo* smeteo, struct txtrdr* txtrdr) +{ + struct entry entry = ENTRY_NULL; + + char* line = NULL; + char* date = NULL; + char* hour = NULL; + char* tk_ctx = NULL; + res_T res = RES_OK; + + ASSERT(smeteo && txtrdr && txtrdr_get_line(txtrdr)); + + line = txtrdr_get_line(txtrdr); + + date = strtok_r(line, " \t", &tk_ctx); + hour = strtok_r(NULL, " \t", &tk_ctx); + res = parse_time(smeteo, txtrdr, date, hour, &entry.time); + if(res != RES_OK) goto error; + + #define PARSE_DOUBLE(Name, Var, Min, Max) { \ + double range[2] = {Min, Max}; /* Inclusive boundaries */ \ + char* Var = strtok_r(NULL, " \t", &tk_ctx); \ + res = parse_double(smeteo, txtrdr, Name, Var, range, &entry.Var); \ + if(res != RES_OK) goto error; \ + } (void)0 + PARSE_DOUBLE("surface temperature", Tsrf, 0, INF); + PARSE_DOUBLE("atmosphere temperature", Tatm, 0, INF); + PARSE_DOUBLE("shortwave downward flux", SWdn, 0, INF); + PARSE_DOUBLE("shortwave upward flux", SWup, 0, INF); + PARSE_DOUBLE("radiative temperature", Trad, 0, INF); + PARSE_DOUBLE("convection coefficient", H, -INF, INF); + PARSE_DOUBLE("???", LE, -INF, INF); + PARSE_DOUBLE("???", day_1850, 0, INF); + #undef PARSE_DOUBLE + + res = darray_entry_push_back(&smeteo->entries, &entry); + if(res != RES_OK) { + ERROR(smeteo, "%s:%zu: error registering data -- %s\n", + txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr), res_to_cstr(res)); + goto error; + } + +exit: + return res; +error: + goto exit; +} + +static res_T +load_stream(struct smeteo* smeteo, FILE* fp, const char* name) +{ + struct txtrdr* txtrdr = NULL; + res_T res = RES_OK; + ASSERT(smeteo && name); + + reset(smeteo); + + res = txtrdr_stream(smeteo->allocator, fp, name, '#', &txtrdr); + if(res != RES_OK) goto error; + + if((res = str_set(&smeteo->filename, name)) != RES_OK) { + ERROR(smeteo, "Error copying file name '%s' -- %s\n", name, res_to_cstr(res)); + goto error; + } + + for(;;) { + if((res = txtrdr_read_line(txtrdr)) != RES_OK) { + ERROR(smeteo, "%s: error reading line -- %s\n", name, res_to_cstr(res)); + goto error; + } + if(!txtrdr_get_cline(txtrdr)) break; /* No more line */ + if((res = parse_line(smeteo, txtrdr)) != RES_OK) goto error; + } + +exit: + if(txtrdr) txtrdr_ref_put(txtrdr); + return res; +error: + goto exit; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +SMETEO_API res_T +smeteo_load(struct smeteo* smeteo, const char* filename) +{ + FILE* fp = NULL; + res_T res = RES_OK; + + if(!smeteo || !filename) { res = RES_BAD_ARG; goto error; } + + if(!(fp = fopen(filename, "r"))) { + ERROR(smeteo, "Error opening file %s -- %s\n", filename, strerror(errno)); + res = RES_IO_ERR; + goto error; + } + + if((res = load_stream(smeteo, fp, filename)) != RES_OK) goto error; + +exit: + if(fp) CHK(fclose(fp) == 0); + return res; +error: + goto exit; +} + +SMETEO_API res_T +smeteo_load_stream(struct smeteo* smeteo, FILE* fp, const char* name) +{ + if(!smeteo || !fp || !name) return RES_BAD_ARG; + return load_stream(smeteo, fp, name); +}