sbb.c (5323B)
1 /* Copyright (C) 2018-2023 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #include "sbb.h" 17 18 #include <rsys/math.h> 19 #include <math.h> 20 21 /******************************************************************************* 22 * Helper functions 23 ******************************************************************************/ 24 static double 25 wiebelt(const double v) 26 { 27 int m; 28 double w, v2, v4; 29 /*.153989717364e+00;*/ 30 const double fifteen_over_pi_power_4 = 15.0/(PI*PI*PI*PI); 31 const double z0 = 1.0/3.0; 32 const double z1 = 1.0/8.0; 33 const double z2 = 1.0/60.0; 34 const double z4 = 1.0/5040.0; 35 const double z6 = 1.0/272160.0; 36 const double z8 = 1.0/13305600.0; 37 38 if(v >= 2.) { 39 w = 0; 40 for(m=1; m<6; ++m) { 41 w += exp(-m*v)/(m*m*m*m) * (((m*v+3)*m*v+6)*m*v+6); 42 } 43 w = w * fifteen_over_pi_power_4; 44 } else { 45 v2 = v*v; 46 v4 = v2*v2; 47 w = z0 - z1*v + z2*v2 - z4*v2*v2 + z6*v4*v2 - z8*v4*v4; 48 w = 1. - fifteen_over_pi_power_4*v2*v*w; 49 } 50 ASSERT(w >= 0.0 && w <= 1.0); 51 return w; 52 } 53 54 /******************************************************************************* 55 * Exported functions 56 ******************************************************************************/ 57 double 58 sbb_blackbody_fraction 59 (const double lambda0, /* [m] */ 60 const double lambda1, /* [m] */ 61 const double temperature) /* [K] */ 62 { 63 const double C2 = 1.43877735e-2; /* [m.K] */ 64 const double x0 = C2 / lambda0; 65 const double x1 = C2 / lambda1; 66 const double v0 = x0 / temperature; 67 const double v1 = x1 / temperature; 68 const double w0 = wiebelt(v0); 69 const double w1 = wiebelt(v1); 70 return w1 - w0; 71 } 72 73 double /* [W/m^2/sr/m ] */ 74 sbb_planck_monochromatic 75 (const double lambda, /* [m] */ 76 const double temperature) /* [K] */ 77 { 78 const double c = 299792458; /* [m/s] */ 79 const double h = 6.62607015e-34; /* [J.s] */ 80 const double k = 1.380649e-23; /* [J/K] */ 81 const double lambda2 = lambda*lambda; 82 const double lambda5 = lambda2*lambda2*lambda; 83 const double B = /* [W/m²/sr/m] */ 84 ((2.0 * h * c*c) / lambda5) 85 / (exp(h*c/(lambda*k*temperature))-1.0); 86 ASSERT(temperature > 0); 87 return B; 88 } 89 90 double /* [W/m^2/sr/m ] */ 91 sbb_planck_interval 92 (const double lambda_min, /* [m] */ 93 const double lambda_max, /* [m] */ 94 const double temperature) /* [K] */ 95 { 96 const double T2 = temperature*temperature; 97 const double T4 = T2*T2; 98 const double BOLTZMANN_CONSTANT = 5.6696e-8; /* [W/m^2/K^4] */ 99 ASSERT(lambda_min < lambda_max && temperature > 0); 100 return sbb_blackbody_fraction(lambda_min, lambda_max, temperature) 101 * BOLTZMANN_CONSTANT * T4 / (PI * (lambda_max-lambda_min)); 102 } 103 104 res_T 105 sbb_brightness_temperature 106 (const double lambda_min, /* [m] */ 107 const double lambda_max, /* [m] */ 108 const double radiance, /* [W/m^2/sr/m] */ 109 double* temperature) /* [K] */ 110 { 111 const size_t MAX_ITER = 100; 112 const double epsilon_T = 1e-4; /* [K] */ 113 const double epsilon_B = radiance * 1e-8; 114 double T, T0, T1, T2; 115 double B, B0; 116 size_t i; 117 res_T res = RES_OK; 118 119 if(lambda_min < 0 120 || lambda_max < 0 121 || lambda_min > lambda_max 122 || radiance < 0 123 || temperature == NULL) { 124 res = RES_BAD_ARG; 125 goto error; 126 } 127 128 /* Search for a brightness temperature whose radiance is greater than or 129 * equal to the estimated radiance */ 130 T2 = 200; 131 FOR_EACH(i, 0, MAX_ITER) { 132 const double B2 = sbb_planck(lambda_min, lambda_max, T2); 133 if(B2 >= radiance) break; 134 T2 *= 2; 135 } 136 if(i >= MAX_ITER) { res = RES_BAD_OP; goto error; } 137 138 B0 = T0 = T1 = 0; 139 FOR_EACH(i, 0, MAX_ITER) { 140 T = (T1+T2)*0.5; 141 B = sbb_planck(lambda_min, lambda_max, T); 142 143 if(B < radiance) { 144 T1 = T; 145 } else { 146 T2 = T; 147 } 148 149 if(fabs(T-T0) < epsilon_T || fabs(B-B0) < epsilon_B) 150 break; 151 152 T0 = T; 153 B0 = B; 154 } 155 if(i >= MAX_ITER) { res = RES_BAD_OP; goto error; } 156 157 *temperature = T; 158 159 exit: 160 return res; 161 error: 162 goto exit; 163 } 164 165 res_T 166 sbb_radiance_temperature 167 (const double lambda_min, /* [m] */ 168 const double lambda_max, /* [m] */ 169 const double radiance, /* [W/m^2/sr] */ 170 double* temperature) 171 { 172 double radiance_avg = 0; 173 double T = 0; 174 res_T res = RES_OK; 175 176 if(lambda_min < 0 177 || lambda_max < 0 178 || lambda_min > lambda_max 179 || radiance < 0 180 || temperature == NULL) { 181 res = RES_BAD_ARG; 182 goto error; 183 } 184 185 /* From integrated radiance to average radiance in W/m^2/sr/m */ 186 radiance_avg = radiance; 187 if(lambda_min != lambda_max) { /* !monochromatic */ 188 radiance_avg /= (lambda_max - lambda_min); 189 } 190 191 res = sbb_brightness_temperature(lambda_min, lambda_max, radiance_avg, &T); 192 if(res != RES_OK) goto error; 193 194 exit: 195 if(temperature) *temperature = T; 196 return res; 197 error: 198 T = 0; 199 goto exit; 200 }