DUV index incorrect
sysoppl opened this issue · comments
I'm using example from this library, and it looks like UV index is computed wrong
float eUV = uv.getUV();
float eDUV = uv.estimateDUVindex(eUV);
Serial.print(eUV, 4);
Serial.print("\t");
Serial.print(eDUV, 1);
Serial.println();
delay(1000);
and the output is:
1.0345 413.8
413 DUV is way to much. Value of ~1mW seems pretty accurate. Any advise?
been a year ago (more) I last worked on that specific code so I just looked at it again
OK lets look at the code / comments, just writing my thoughts
The code
from the ml8511.cpp file
// experimental estimate DUV index (not calibrated, USE WITH CARE !!)
// input is power in mW per cm2
// weight is pretty high
float ML8511::estimateDUVindex(float mWcm2)
{
float weight = 1.0; // this can be tuned to callibrate
// convert to mW per m2
float mWm2 = mWcm2 * 10000;
// factor to normalize to an 0..10 scale see Wikipedia.
float factor = 0.04; // 1.0/25;
return mWm2 * weight * factor;
};- the code explicitly states it is experimental,
- the weight "which is pretty high" is used for calibration.
- callibrate is spelled wrong should have one 'l'
- Conversion from cm2 to m2 is a factor 10000 so that seems OK.
- the normalization factor comes from Wikipedia, so that seems OK too.
Your measurement
If the UV = 1.0345 mW/cm2 ===> 10345 mW/m2
normalize divide by 25 gives 413.8 which is outside the range 0..15 or so, so indeed (very) wrong.
WIkipedia
From https://en.wikipedia.org/wiki/Ultraviolet_index
... because the UV of greatest concern occupies a spectrum of wavelength from 295 to 325 nm, and shorter wavelengths have already been absorbed a great deal when they arrive at the earth's surface. Skin damage from sunburn, however, is related to wavelength, the shorter wavelengths being much more damaging. The UV power spectrum (expressed as watts per square meter per nanometer of wavelength) is therefore multiplied by a weighting curve known as the erythemal action spectrum, and the result integrated over the whole spectrum. This gave Canadian scientists[who?] a weighted figure (sometimes called Diffey-weighted UV irradiance, or DUV, or erythemal dose rate) typically around 250 mW/m2 in midday summer-sunlight. So, they arbitrarily divided by 25 mW/m2 to generate a convenient index value,[8][9] essentially a scale of 0 to 11+ (though ozone depletion is now resulting in higher values, as mentioned above).
https://en.wikipedia.org/wiki/File:Erythemal_action_spectrum.svg
Analysis
The sensor does not provide any information about how much radiation there is per wavelength, only the total.
So it is hard to impossible to determine the weight on first sight.
The UV spectrum ~280-420 (part that reaches the earth, UVB 280-320 and UVA 320-420 approx.)
Looking at the Erythemal action spectrum (EAS) graph (green line) we see that it drops from 1.0 @280 to something as low as 1/500 th around 330 nm
Looking at the blue line, the effective spectrum, we know that the measurement of the sensor is the sum of the radiation per wavelength under this blue line. Given that the Y-scale is logarithmic I assume(!) that 95% of the radiation is between 305 and 315 nm ("flat top" of the blue line). In that range the EAS is between 1/8 and 1/100, so the average weight is expected to be somewhere between 1/30 and 1/60 , so lets estimate it on 1/45.
With the weight set to 1/45 = 0.022222222... (the reasoning needs to be verified and at least better understood)
What does this weight mean for your measurement
eUV = 1.0345 mW/cm2 ===> 10345 mW/m2
normalize divide by 25 gives 10345 / 25 ==> 413.8
a weight of 1/45 derived above 413.8 / 45 ==> 9.2
This value is well within the range 0..15 so it might be a better value for weight to use in the code.
Reverse engineering
If you have an external reference of the DUV index, e.g. from a weather station, you can "reverse engineer" a reasonable value of weight.
Than you should solve the formula eUV * 10000 / 25 * weight = DUV
Assume you measure eUV = 1.0345 mW/cm2
and the DUV = 7.8 according to the weather station.
1.0345 * 10000 / 25 * weight = 7.8
(shuffle)
weight = 7.8 * 25 / 1.0345 / 10000 ==> 0,018849... ==> approx 1/53
As said, weight is not calibrated and these are my thoughts (that need to be verified)
Does this help you?
Ok, thank you for the extensive explanation.
I checked measurements with station 25km away. My result was 46.59 W/m² (direct sun, no clouds or glass) and their 765 W/m². So I just grab DUV index and calculate it to my needs.
My weight is approx 0.0040240, which seems accurate with other station measurements:
DUV 7.5 [@ 4.6595 mW/cm²], no clouds, very hot, 11AM (DUV 7-8 at other stations)
DUV ~1.6 [@ 1.0345 mW/cm²], sun low, 18PM) (DUV 1-2 at other stations)
Great to hear that you found a fairly good value for the weight
0.0040240 ~ 1/249 so it seems even much less than the 1/45 from my deduction.
A possible cause for this difference is the sensitivity per wavelength of the sensor (or my poor deduction 😂)
Code could be minimized to a single factor if performance is an issue.
float ML8511::estimateDUVindex(float mWcm2)
{
return mWcm2 * 1.61; // 1.61 = 0.0040240 * 10000 * 0.04
};I add a reference to this issue in the code as other people will need to calibrate their sensor too.
Thanks for filing the issue!
Created a new 0.1.6 branch that allows for easier calibrating the sensor.
the DUV factor will now be reference / getUV() ==> in your case it will be 1.61 (which I made the default value.
Two functions are added, setDUVfactor(f) and getDUVfactor()
think that will make the DUV function easier to use and to calibrate. (although still experimental)
And added a small example sketch to help calibrating.
If you have time, have a look at the develop branch (I have to fix some unit test / code) or the 0.1.6 version.
Thanks for helping to improve the library by asking the right question !
0.1.6 released