rnsf

Define and load a phase function data format
git clone git://git.meso-star.fr/rnsf.git
Log | Files | Refs | README | LICENSE

commit 283118f491df57508d1b850a8752fa9a9ea72100
parent dc77a3e11bf5be054b7dd1d6f98c0c16f84b033e
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 21 Jun 2022 10:45:44 +0200

Implement the loading functions

Diffstat:
Mdoc/rnsf.5.scd | 4++--
Msrc/rnsf.c | 637++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 638 insertions(+), 3 deletions(-)

diff --git a/doc/rnsf.5.scd b/doc/rnsf.5.scd @@ -64,8 +64,8 @@ only of spaces and tabs. <wlen-band-func> ::= <wavelength-min> <wavelength-max> <phase-func> <bands-count> ::= INTEGER -<wavelength-min> ::= REAL # In nanometers -<wavelength-max> ::= REAL # In nanometers +<wavelength-min> ::= REAL # Inclusive bound in nanometers +<wavelength-max> ::= REAL # Inlcusive bound in nanometers --- diff --git a/src/rnsf.c b/src/rnsf.c @@ -1,7 +1,7 @@ /* Copyright (C) 2022 GSMA - Université de Reims Champgne-Ardenne, CNRS * Copyright (C) 2022 IPGP, Université Paris Cité, CNRS * Copyright (C) 2022 LAPLACE - Université de Toulouse, CNRS, INPT, UPS - * Copyright (C) 2022 LATMOS/IPSL - UVSQ, Université Paris-Saclay, + * Copyright (C) 2022 LATMOS/IPSL - UVSQ, Université Paris-Saclay, * Sorbonne Université, CNRS * Copyright (C) 2022 LESIA - Observatoire de Paris, Université PSL, * Sorbonne Université, Université Paris Cité @@ -23,10 +23,18 @@ * 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 _POSIX_C_SOURCE 200112L /* strtok_r */ + #include "rnsf.h" #include "rnsf_c.h" #include "rnsf_log.h" +#include <string.h> + +#include <rsys/cstr.h> +#include <rsys/math.h> +#include <rsys/text_reader.h> + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -37,6 +45,582 @@ check_rnsf_create_args(const struct rnsf_create_args* args) return args ? RES_OK : RES_BAD_ARG; } +static res_T +parse_phase_fn_hg + (struct rnsf* rnsf, + struct txtrdr* txtrdr, + char** tk_ctx, + struct phase_fn_hg* phase) +{ + char* tk = NULL; + res_T res = RES_OK; + ASSERT(rnsf && txtrdr && tk_ctx && phase); + + tk = strtok_r(NULL, " \t", tk_ctx); + if(!tk) { + log_err(rnsf, + "%s:%lu: missing the Henyey & Greenstein asymmetric parameter.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + res = cstr_to_double(tk, &phase->g); + if(res == RES_OK && (phase->g < -1 || phase->g > 1)) res = RES_BAD_ARG; + if(res != RES_OK) { + log_err(rnsf, + "%s:%lu: invalid Henyey & Greenstein asymmetric parameter `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + goto error; + } + + tk = strtok_r(NULL, " \t", tk_ctx); + if(tk) { + log_warn(rnsf, "%s:%lu: unexpected text `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + } + +exit: + return res; +error: + goto exit; +} + +static res_T +parse_discrete_item + (struct rnsf* rnsf, + struct txtrdr* txtrdr, + struct discrete_item* item) +{ + char* tk = NULL; + char* tk_ctx = NULL; + res_T res = RES_OK; + ASSERT(rnsf && txtrdr && item); + + res = txtrdr_read_line(txtrdr); + if(res != RES_OK) { + log_err(rnsf, "%s: could not read the line `%lu' -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + res_to_cstr(res)); + goto error; + } + + if(!txtrdr_get_cline(txtrdr)) { + log_err(rnsf, "%s:%lu: missing an item of a discretized phase function.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); + ASSERT(tk); + + if(!strcmp(tk, "PI")) { + item->theta = PI; + } else { + res = cstr_to_double(tk, &item->theta); + if(res == RES_OK && (item->theta < 0 || item->theta > PI)) res = RES_BAD_ARG; + if(res != RES_OK) { + log_err(rnsf, "%s:%lu: invalid phase function angle `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + goto error; + } + } + + tk = strtok_r(NULL, " \t", &tk_ctx); + if(!tk) { + log_err(rnsf, + "%s:%lu: the value of the discretized phase function is missing.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + res = cstr_to_double(tk, &item->value); + if(res == RES_OK && item->value < 0) res = RES_BAD_ARG; + if(res != RES_OK) { + log_err(rnsf, "%s:%lu: invalid value `%s' of the discretized phase function.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + goto error; + } + + tk = strtok_r(NULL, " \t", &tk_ctx); + if(tk) { + log_warn(rnsf, "%s:%lu: unexpected text `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + } + +exit: + return res; +error: + goto exit; +} + +static res_T +parse_phase_fn_discrete + (struct rnsf* rnsf, + struct txtrdr* txtrdr, + char** tk_ctx, + struct phase_fn_discrete* phase) +{ + char* tk = NULL; + unsigned long nangles = 0; + unsigned long i; + res_T res = RES_OK; + ASSERT(rnsf && txtrdr && tk_ctx && phase); + + tk = strtok_r(NULL, " \t", tk_ctx); + if(!tk) { + log_err(rnsf, + "%s:%lu: missing the angle number of the discretized phase function.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + res = cstr_to_ulong(tk, &nangles); + if(res == RES_OK && nangles < 2) res = RES_BAD_ARG; + if(res != RES_OK) { + log_err(rnsf, + "%s:%lu: invalid angle number of the discretized phase function `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + goto error; + } + + res = darray_discrete_item_resize(&phase->items, nangles); + if(res != RES_OK) { + log_err(rnsf, + "%s:%lu: could not allocate the list of values of the discretized " + "phase function -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + res_to_cstr(res)); + goto error; + } + + tk = strtok_r(NULL, " \t", tk_ctx); + if(tk) { + log_warn(rnsf, "%s:%lu: unexpected text `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + } + + FOR_EACH(i, 0, nangles) { + struct discrete_item* item = NULL; + item = darray_discrete_item_data_get(&phase->items) + i; + + res = parse_discrete_item(rnsf, txtrdr, item); + if(res != RES_OK) goto error; + + if(i == 0 && item->theta != 0) { + log_err(rnsf, + "%s:%lu: invalid angle value `%g'. The first angle must be 0.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + item->theta); + res = RES_BAD_ARG; + goto error; + } + + if(i == nangles - 1 && item->theta != PI) { + log_err(rnsf, + "%s:%lu: invalid angle value `%g'. The last angle must be PI.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + item->theta); + res = RES_BAD_ARG; + goto error; + } + + if(i > 0 && item[0].theta <= item[-1].theta) { + log_err(rnsf, + "%s:%lu: the discretized phase function must be sorted in ascending " + "order of angle.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + } + +exit: + return res; +error: + goto exit; +} + +static res_T +parse_phase_fn + (struct rnsf* rnsf, + struct txtrdr* txtrdr, + char** tk_ctx, + struct rnsf_phase_fn* phase) +{ + char* tk = NULL; + res_T res = RES_OK; + ASSERT(rnsf && txtrdr && tk_ctx && phase); + + tk = strtok_r(NULL, " \t", tk_ctx); + if(!tk) { + log_err(rnsf, "%s:%lu: missing phase function type.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + if(!strcmp(tk, "hg")) { + phase->type = RNSF_PHASE_FN_HG; + res = parse_phase_fn_hg(rnsf, txtrdr, tk_ctx, &phase->param.hg); + if(res != RES_OK) goto error; + + } else if(!strcmp(tk, "discrete")) { + phase->type = RNSF_PHASE_FN_DISCRETE; + phase_fn_discrete_init(rnsf->allocator, &phase->param.discrete); + res = parse_phase_fn_discrete(rnsf, txtrdr, tk_ctx, &phase->param.discrete); + if(res != RES_OK) goto error; + + } else { + log_err(rnsf, "%s:%lu: invalid phase function type `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + + +static res_T +parse_wlen_phase_fn + (struct rnsf* rnsf, + struct txtrdr* txtrdr, + struct rnsf_phase_fn* phase) +{ + char* tk = NULL; + char* tk_ctx = NULL; + double wlen = 0; + res_T res = RES_OK; + ASSERT(rnsf && txtrdr && phase); + + res = txtrdr_read_line(txtrdr); + if(res != RES_OK) { + log_err(rnsf, "%s: could not read the line `%lu' -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + res_to_cstr(res)); + goto error; + } + + if(!txtrdr_get_cline(txtrdr)) { + const size_t nexpect = darray_phase_fn_size_get(&rnsf->phases); + const size_t nparsed = (size_t) + (phase - darray_phase_fn_cdata_get(&rnsf->phases)); + log_err(rnsf, + "%s:%lu: missing phase function. " + "Expecting %lu function%swhile %lu %s parsed.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + (unsigned long)nexpect, nexpect == 1 ? " " : "s ", + (unsigned long)nparsed, nparsed > 1 ? "were" : "was"); + res = RES_BAD_ARG; + goto error; + } + + tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); + ASSERT(tk); + + res = cstr_to_double(tk, &wlen); + if(res == RES_OK && wlen < 0) res = RES_BAD_ARG; + if(res != RES_OK) { + log_err(rnsf, "%s:%lu: invalid wavelength `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + goto error; + } + phase->wlen[0] = wlen; + phase->wlen[1] = wlen; + + res = parse_phase_fn(rnsf, txtrdr, &tk_ctx, phase); + if(res != RES_OK) goto error; + + tk = strtok_r(NULL, " \t", &tk_ctx); + if(tk) { + log_warn(rnsf, "%s:%lu: unexpected text `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + } + +exit: + return res; +error: + goto exit; +} + +static res_T +parse_per_wlen_phase_fn + (struct rnsf* rnsf, + struct txtrdr* txtrdr, + char** tk_ctx) +{ + char* tk = NULL; + unsigned long count = 0; + unsigned long i; + res_T res = RES_OK; + ASSERT(rnsf && txtrdr && tk_ctx); + + tk = strtok_r(NULL, " \t", tk_ctx); + if(!tk) { + log_err(rnsf, "%s:%lu: the number of wavelengths is missing.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + res = cstr_to_ulong(tk, &count); + if(res != RES_OK) { + log_err(rnsf, "%s:%lu: invalid number of wavelengths `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + res = RES_BAD_ARG; + goto error; + } + + tk = strtok_r(NULL, " \t", tk_ctx); + if(tk) { + log_warn(rnsf, "%s:%lu: unexpected text `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + } + + res = darray_phase_fn_resize(&rnsf->phases, count); + if(res != RES_OK) { + log_err(rnsf, + "%s:%lu: could not allocate the list of phase functions -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + res_to_cstr(res)); + goto error; + } + + FOR_EACH(i, 0, count) { + struct rnsf_phase_fn* phase = NULL; + + phase = darray_phase_fn_data_get(&rnsf->phases) + i; + + res = parse_wlen_phase_fn(rnsf, txtrdr, phase); + if(res != RES_OK) goto error; + ASSERT(phase->wlen[0] == phase->wlen[1]); + + if(i > 0 && phase[0].wlen[0] <= phase[-1].wlen[0]) { + log_err(rnsf, + "%s:%lu: the phase function must be sorted in ascending order of " + "wavelength.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + } + +exit: + return res; +error: + darray_phase_fn_clear(&rnsf->phases); + goto exit; +} + +static res_T +parse_band_brdf + (struct rnsf* rnsf, + struct txtrdr* txtrdr, + struct rnsf_phase_fn* phase) +{ + char* tk = NULL; + char* tk_ctx = NULL; + res_T res = RES_OK; + ASSERT(rnsf && txtrdr && phase); + + res = txtrdr_read_line(txtrdr); + if(res != RES_OK) { + log_err(rnsf, "%s: could not read the line `%lu' -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + res_to_cstr(res)); + goto error; + } + + if(!txtrdr_get_cline(txtrdr)) { + const size_t nexpect = darray_phase_fn_size_get(&rnsf->phases); + const size_t nparsed = (size_t) + (phase - darray_phase_fn_cdata_get(&rnsf->phases)); + log_err(rnsf, + "%s:%lu: missing phase function. " + "Expecting %lu functions%swhile %lu %s parsed.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + (unsigned long)nexpect, nexpect == 1 ? " " : "s ", + (unsigned long)nparsed, nparsed > 1 ? "were" : "was"); + res = RES_BAD_ARG; + goto error; + } + + tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); + ASSERT(tk); + + res = cstr_to_double(tk, &phase->wlen[0]); + if(res == RES_OK && phase->wlen[0] < 0) res = RES_BAD_ARG; + if(res != RES_OK) { + log_err(rnsf, "%s:%lu: invalid band min boundary `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + goto error; + } + + tk = strtok_r(NULL, " \t", &tk_ctx); + if(!tk) { + log_err(rnsf, "%s:%lu: missing band max boundary.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + res = cstr_to_double(tk, &phase->wlen[1]); + if(res == RES_OK && phase->wlen[1] < 0) res = RES_BAD_ARG; + if(res != RES_OK) { + log_err(rnsf, "%s:%lu: invalid band max boundary `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + goto error; + } + + if(phase->wlen[0] > phase->wlen[1]) { + log_err(rnsf, "%s:%lu: band boundaries are degenerated -- [%g, %g].\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + phase->wlen[0], phase->wlen[1]); + res = RES_BAD_ARG; + goto error; + } + + res = parse_phase_fn(rnsf, txtrdr, &tk_ctx, phase); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +static res_T +parse_per_band_phase_fn + (struct rnsf* rnsf, + struct txtrdr* txtrdr, + char** tk_ctx) +{ + char* tk = NULL; + unsigned long count = 0; + unsigned long i = 0; + res_T res = RES_OK; + ASSERT(rnsf && txtrdr && tk_ctx); + + tk = strtok_r(NULL, " \t", tk_ctx); + if(!tk) { + log_err(rnsf, "%s:%lu: the number of bands is missing.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + res = cstr_to_ulong(tk, &count); + if(res != RES_OK) { + log_err(rnsf, "%s:%lu: invalid number of bands `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + res = RES_BAD_ARG; + goto error; + } + + tk = strtok_r(NULL, " \t", tk_ctx); + if(tk) { + log_warn(rnsf, "%s:%lu: unexpected text `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + } + + res = darray_phase_fn_resize(&rnsf->phases, count); + if(res != RES_OK) { + log_err(rnsf, + "%s:%lu: could not allocate the list of phase functions -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + res_to_cstr(res)); + goto error; + } + + FOR_EACH(i, 0, count) { + struct rnsf_phase_fn* phase = NULL; + + phase = darray_phase_fn_data_get(&rnsf->phases) + i; + + res = parse_band_brdf(rnsf, txtrdr, phase); + if(res != RES_OK) goto error; + + if(i > 0) { + if(phase[0].wlen[0] < phase[-1].wlen[1] + || phase[0].wlen[0] == phase[-1].wlen[1]) { + log_err(rnsf, + "%s:%lu: the phase function must be sorted in ascending order of " + "wavelength.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + } + } + +exit: + return res; +error: + darray_phase_fn_clear(&rnsf->phases); + goto exit; +} + +static res_T +load_stream + (struct rnsf* rnsf, + FILE* stream, + const char* stream_name) +{ + char* tk = NULL; + char* tk_ctx = NULL; + struct txtrdr* txtrdr = NULL; + res_T res = RES_OK; + ASSERT(rnsf && stream && stream_name); + + darray_phase_fn_clear(&rnsf->phases); /* Clean-up */ + + res = txtrdr_stream(rnsf->allocator, stream, stream_name, '#', &txtrdr); + if(res != RES_OK) { + log_err(rnsf, "Could not create the text reader to parse `%s' -- %s.\n", + stream_name, res_to_cstr(res)); + goto error; + } + + res = txtrdr_read_line(txtrdr); + if(res != RES_OK) { + log_err(rnsf, "%s: could not read the line `%lu' -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + res_to_cstr(res)); + goto error; + } + + if(!txtrdr_get_cline(txtrdr)) goto exit; /* No line to parse */ + + tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); + ASSERT(tk); + + if(!strcmp(tk, "wavelengths")) { + res = parse_per_wlen_phase_fn(rnsf, txtrdr, &tk_ctx); + if(res != RES_BAD_ARG) goto error; + } else if(!strcmp(tk, "bands")) { + res = parse_per_band_phase_fn(rnsf, txtrdr, &tk_ctx); + if(res != RES_BAD_ARG) goto error; + } else { + log_err(rnsf, "%s:%lu: invalid directive `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + res = RES_BAD_ARG; + goto error; + } + +exit: + if(txtrdr) txtrdr_ref_put(txtrdr); + return res; +error: + goto exit; +} + static void release_rnsf(ref_T* ref) { @@ -114,3 +698,54 @@ rnsf_ref_put(struct rnsf* rnsf) ref_put(&rnsf->ref, release_rnsf); return RES_OK; } + +res_T +rnsf_load(struct rnsf* rnsf, const char* filename) +{ + FILE* fp = NULL; + res_T res = RES_OK; + + if(!rnsf || !filename) { + res = RES_BAD_ARG; + goto error; + } + + fp = fopen(filename, "r"); + if(!fp) { + log_err(rnsf, "%s: error opening file `%s' -- %s.\n", + FUNC_NAME, filename, strerror(errno)); + res = RES_IO_ERR; + goto error; + } + + res = load_stream(rnsf, fp, filename); + if(res != RES_OK) goto error; + +exit: + if(fp) fclose(fp); + return res; +error: + goto exit; +} + +res_T +rnsf_load_stream + (struct rnsf* rnsf, + FILE* stream, + const char* stream_name) +{ + res_T res = RES_OK; + + if(!rnsf || !stream) { + res = RES_BAD_ARG; + goto error; + } + + res = load_stream(rnsf, stream, stream_name ? stream_name : "<stream>"); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +}