star-blackbody

Handling the Planck function
git clone git://git.meso-star.fr/star-blackbody.git
Log | Files | Refs | README | LICENSE

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 }