ssol_spectrum.c (6072B)
1 /* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) 2 * Copyright (C) 2016, 2018 CNRS 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17 #include "ssol.h" 18 #include "ssol_spectrum_c.h" 19 #include "ssol_device_c.h" 20 21 #include <rsys/algorithm.h> 22 #include <rsys/hash.h> 23 #include <rsys/math.h> 24 #include <rsys/mem_allocator.h> 25 #include <rsys/ref_count.h> 26 27 /******************************************************************************* 28 * Helper functions 29 ******************************************************************************/ 30 static void 31 spectrum_release(ref_T* ref) 32 { 33 struct ssol_device* dev; 34 struct ssol_spectrum* spectrum = CONTAINER_OF(ref, struct ssol_spectrum, ref); 35 ASSERT(ref); 36 dev = spectrum->dev; 37 ASSERT(dev && dev->allocator); 38 darray_double_release(&spectrum->wavelengths); 39 darray_double_release(&spectrum->intensities); 40 MEM_RM(dev->allocator, spectrum); 41 SSOL(device_ref_put(dev)); 42 } 43 44 static int 45 eq_dbl(const void* key, const void* base) 46 { 47 const double k = *(const double*) key; 48 const double b = *(const double*) base; 49 if(k > b) return +1; 50 if(k < b) return -1; 51 return 0; 52 } 53 54 /******************************************************************************* 55 * Local ssol_spectrum functions 56 ******************************************************************************/ 57 double 58 spectrum_interpolate 59 (const struct ssol_spectrum* spectrum, const double wavelength) 60 { 61 const double* wls; 62 const double* ints; 63 const double* next; 64 double slope; 65 double intensity; 66 size_t id_next, sz; 67 ASSERT(spectrum); 68 69 sz = darray_double_size_get(&spectrum->wavelengths); 70 wls = darray_double_cdata_get(&spectrum->wavelengths); 71 ints = darray_double_cdata_get(&spectrum->intensities); 72 next = search_lower_bound(&wavelength, wls, sz, sizeof(double), &eq_dbl); 73 if(!next) { /* Clamp to upper bound */ 74 return ints[sz-1]; 75 } 76 77 id_next = (size_t)(next - wls); 78 if(!id_next) { /* Clamp to lower bound */ 79 return ints[0]; 80 } 81 82 ASSERT(id_next); /* because spectrum_includes_point */ 83 ASSERT(wls[id_next] >= wls[id_next - 1]); 84 85 slope = (ints[id_next] - ints[id_next-1]) / (wls[id_next] - wls[id_next-1]); 86 intensity = ints[id_next-1] + (wavelength - wls[id_next - 1]) * slope; 87 ASSERT(intensity >= 0); 88 return intensity; 89 } 90 91 int 92 spectrum_check_data 93 (const struct ssol_spectrum* spectrum, const double lower, const double upper) 94 { 95 size_t sz, i; 96 double current_wl = 0; 97 ASSERT(spectrum && lower <= upper); 98 sz = darray_double_size_get(&spectrum->intensities); 99 if(!sz) return 0; 100 if(sz != darray_double_size_get(&spectrum->wavelengths)) return 0; 101 FOR_EACH(i, 0, sz) { 102 const double wl = darray_double_cdata_get(&spectrum->wavelengths)[i]; 103 const double data = darray_double_cdata_get(&spectrum->intensities)[i]; 104 if(data < lower || data > upper) return 0; 105 if(wl <= 0) return 0; 106 if(wl <= current_wl) return 0; 107 current_wl = wl; 108 } 109 return 1; 110 } 111 112 /******************************************************************************* 113 * Exported ssol_spectrum functions 114 ******************************************************************************/ 115 res_T 116 ssol_spectrum_create 117 (struct ssol_device* dev, struct ssol_spectrum** out_spectrum) 118 { 119 struct ssol_spectrum* spectrum = NULL; 120 res_T res = RES_OK; 121 122 if(!dev || !out_spectrum) { 123 res = RES_BAD_ARG; 124 goto error; 125 } 126 127 spectrum = MEM_CALLOC(dev->allocator, 1, sizeof(struct ssol_spectrum)); 128 if(!spectrum) { 129 res = RES_MEM_ERR; 130 goto error; 131 } 132 133 SSOL(device_ref_get(dev)); 134 spectrum->dev = dev; 135 ref_init(&spectrum->ref); 136 darray_double_init(dev->allocator, &spectrum->wavelengths); 137 darray_double_init(dev->allocator, &spectrum->intensities); 138 139 exit: 140 if(out_spectrum) *out_spectrum = spectrum; 141 return res; 142 error: 143 if(spectrum) { 144 SSOL(spectrum_ref_put(spectrum)); 145 spectrum = NULL; 146 } 147 goto exit; 148 } 149 150 res_T 151 ssol_spectrum_ref_get(struct ssol_spectrum* spectrum) 152 { 153 if(!spectrum) return RES_BAD_ARG; 154 ref_get(&spectrum->ref); 155 return RES_OK; 156 } 157 158 res_T 159 ssol_spectrum_ref_put(struct ssol_spectrum* spectrum) 160 { 161 if(!spectrum) return RES_BAD_ARG; 162 ref_put(&spectrum->ref, spectrum_release); 163 return RES_OK; 164 } 165 166 SSOL_API res_T 167 ssol_spectrum_setup 168 (struct ssol_spectrum* spectrum, 169 void (*get)(const size_t iwlen, double* wlen, double* data, void* ctx), 170 const size_t nwlens, 171 void* ctx) 172 { 173 double* wavelengths; 174 double* intensities; 175 double current_wl = 0; 176 size_t i; 177 res_T res = RES_OK; 178 179 if(!spectrum || !nwlens || !get) { 180 res = RES_BAD_ARG; 181 goto error; 182 } 183 184 res = darray_double_resize(&spectrum->wavelengths, nwlens); 185 if(res != RES_OK) goto error; 186 res = darray_double_resize(&spectrum->intensities, nwlens); 187 if(res != RES_OK) goto error; 188 189 wavelengths = darray_double_data_get(&spectrum->wavelengths); 190 intensities = darray_double_data_get(&spectrum->intensities); 191 FOR_EACH(i, 0, nwlens) { 192 get(i, wavelengths + i, intensities + i, ctx); 193 if(wavelengths[i] <= current_wl || intensities[i] < 0) { 194 res = RES_BAD_ARG; 195 goto error; 196 } 197 current_wl = *(wavelengths + i); 198 } 199 200 spectrum->checksum[0] = hash_fnv64(wavelengths, nwlens*sizeof(double)); 201 spectrum->checksum[1] = hash_fnv64(intensities, nwlens*sizeof(double)); 202 203 exit: 204 return res; 205 error: 206 if(spectrum) { 207 darray_double_clear(&spectrum->wavelengths); 208 darray_double_clear(&spectrum->intensities); 209 spectrum->checksum[0] = 0; 210 spectrum->checksum[1] = 0; 211 } 212 goto exit; 213 } 214