12
#include "libimage/Image.h"
13
#include "libmultiple/RayleighScattering.h"
15
// Calculate the airmass using the expressions in Smith and Smith, JGR
16
// 77(19), 3592--3597, 1972
17
// x = distance from planet center, units of atmospheric scale height
20
chapman(double x, double chi)
23
double y = sqrt(x/2) * fabs(cos(chi));
26
chapman = sqrt(x * M_PI_2) * exp(y*y) * erfc(y);
28
chapman = sqrt(2 * M_PI * x)
29
* (sqrt(sin(chi))*exp(x*(1-sin(chi)))
30
-0.5*exp(y*y)*erfc(y));
35
// Find the altitude (units of planetary radius) where the sun sets
36
// for a given zenith angle
38
shadowHeight(double sza)
44
shadowHeight = 1/sin(sza) - 1;
49
// find the height above the ground given the zenith angle at the ground
50
// and the slant path length to the ground
52
slantHeight(double chi, double slant)
56
double c = -slant * (slant + 2 * cos(chi));
58
return (-b + sqrt(b*b-4*a*c))/2*a;
61
// find the slant path length to the ground given the zenith angle at the ground
62
// and the height above the ground
64
slantLength(double chi, double height)
67
double b = 2 * cos(chi);
68
double c = -height * (height + 2);
70
return (-b + sqrt(b*b-4*a*c))/2*a;
73
// find the zenith angle along the slant path given the
74
// zenith angle at the ground and the height above the ground
76
zenithAlongSlant(double chi, double height)
78
return (asin(sin(chi) / (1+height)));
81
// find the zenith angle at a point along the tangent given the zenith
82
// angle at the tangent point. Set isNear to true if the point is
83
// closer than the tangent, false if it is on the far side. Tangent
84
// point is assumed to be at the ground.
86
zenithAlongTangent(double chi, double height, bool isNear)
88
double angle1 = asin(1/(1+height));
89
double angle2 = M_PI_2 - chi;
93
return angle1 - angle2;
97
return M_PI - angle1 - angle2;
102
RayleighScattering::createTables()
104
double deg_to_rad = M_PI/180;
106
double planck1 = 1.19104e-6;
107
double planck2 = 0.0143883;
110
double peakLambda = 510e-9;
111
double peakRadiance = planck1/(pow(peakLambda,5)
112
* (exp(planck2/(peakLambda*temp))-1));
114
double htop = tanHeight_[tanHeight_.size()-1];
115
double min_dh = scaleHeight_ / 5;
117
// assume tables for each degree between 0 and 180
118
vector<double> phase_d;
119
vector<double> phase;
120
for (int i = 0; i < 181; i++)
122
phase_d.push_back(i);
123
phase.push_back(i * deg_to_rad);
126
int area_th = incidence_.size() * tanHeight_.size();
127
int area_em = incidence_.size() * emission_.size();
129
double n2m1 = indexOfRefraction_*indexOfRefraction_-1;
131
// build the limb tables
132
for (unsigned int ip = 0; ip < phase.size(); ip++)
134
double th_array[lambda_.size() * area_th];
135
for (unsigned int i = 0; i < lambda_.size() * area_th; i++)
138
for (unsigned int il = 0; il < lambda_.size(); il++)
140
double lambda4 = pow(lambda_[il], 4);
141
double rayleighScale = 1/peakRadiance;
142
rayleighScale *= planck1/(pow(lambda_[il],5)
143
* (exp(planck2/(lambda_[il]*temp))-1));
144
rayleighScale *= (2 * pow(M_PI*n2m1, 2)) / (3*numberDensity_);
145
double crossRayleigh = 4*M_PI*rayleighScale / (numberDensity_*lambda4);
147
// fudge factor - scale the red down at low phase angles
148
if (il == 0) rayleighScale *= 0.5 * (1 + (1-cos(phase[ip]/2)));
150
for (unsigned int ii = 0; ii < incidence_.size(); ii++)
152
if (phase[ip] > (M_PI_2+incidence_[ii])+2*deg_to_rad) continue;
153
if (phase[ip] < fabs(M_PI_2-incidence_[ii])-2*deg_to_rad) continue;
155
double hmin = shadowHeight(incidence_[ii]) * radius_;
156
if (htop < hmin) continue;
158
double cos2phase = cos(phase[ip]) * cos(phase[ip]);
160
for (unsigned int it = 0; it < tanHeight_.size(); it++)
162
unsigned int ipos = il*area_th + it*incidence_.size() + ii;
165
// slant distance from the tangent point to htop
166
double xfar = slantLength(M_PI_2,
167
(htop-tanHeight_[it])
168
/(radius_+tanHeight_[it]));
169
xfar *= (radius_+tanHeight_[it]);
170
int n_h = (int) ceil(xfar/min_dh);
171
double dx = xfar / (n_h-1);
173
// the far side of the tangent point
174
for (int ix = n_h-2; ix >= 0; ix--)
176
double x = (ix + 0.5)* dx;
177
double h = slantHeight(M_PI_2,
178
x/(radius_+tanHeight_[it]));
179
double thisIncidence = zenithAlongTangent(incidence_[ii], h,
181
double thisEmission = zenithAlongTangent(M_PI_2, h, false);
182
h *= (radius_ + tanHeight_[it]);
185
hmin = shadowHeight(thisIncidence) * radius_;
186
if (h < hmin) continue;
188
double atten = exp(-h/scaleHeight_);
189
double tauScale = numberDensity_*atten*scaleHeight_*crossRayleigh;
190
double tauSun = chapman((h+radius_)/scaleHeight_, thisIncidence);
191
double tauView = chapman((h+radius_)/scaleHeight_, thisEmission);
193
rayleigh += dx * atten * exp(-(tauSun+tauView)*tauScale);
195
// the near side of the tangent point
196
for (int ix = 0; ix < n_h-1; ix++)
198
double x = (ix+0.5) * dx;
199
double h = slantHeight(M_PI_2, x/(radius_+tanHeight_[it]));
200
double thisIncidence = zenithAlongTangent(incidence_[ii], h,
202
double thisEmission = zenithAlongTangent(M_PI_2, h, true);
203
h *= (radius_ + tanHeight_[it]);
206
hmin = shadowHeight(thisIncidence) * radius_;
207
if (h < hmin) continue;
209
double atten = exp(-h/scaleHeight_);
210
double tauScale = numberDensity_*atten*scaleHeight_*crossRayleigh;
211
double tauSun = chapman((h+radius_)/scaleHeight_, thisIncidence);
212
double tauView = chapman((h+radius_)/scaleHeight_, thisEmission);
214
rayleigh += dx * atten * exp(-(tauSun+tauView)*tauScale);
216
rayleigh *= (0.75*(1+cos2phase)*rayleighScale/lambda4);
218
th_array[ipos] = rayleigh;
223
snprintf(buffer, 64, limbTemplate_.c_str(), (int) (phase_d[ip]));
225
writeTable(buffer, th_array, lambda_.size(), incidence_.size(),
226
tanHeight_.size(), limbPNG_);
229
// Now build the tables for the disk
230
for (unsigned int ip = 0; ip < phase.size(); ip++)
232
double em_array[lambda_.size() * area_em];
233
for (unsigned int i = 0; i < lambda_.size() * area_em; i++)
236
double cos2phase = cos(phase[ip]) * cos(phase[ip]);
237
for (unsigned int il = 0; il < lambda_.size(); il++)
239
double lambda4 = pow(lambda_[il], 4);
240
double rayleighScale = 1/peakRadiance;
241
rayleighScale *= planck1/(pow(lambda_[il],5)
242
* (exp(planck2/(lambda_[il]*temp))-1));
243
rayleighScale *= (2 * pow(M_PI*n2m1, 2)) / (3*numberDensity_);
244
double crossRayleigh = 4*M_PI*rayleighScale
245
/ (numberDensity_*lambda4);
247
for (unsigned int ii = 0; ii < incidence_.size(); ii++)
249
double hmin = shadowHeight(incidence_[ii]) * radius_;
250
if (hmin > htop) continue;
252
for (unsigned int ie = 0; ie < emission_.size(); ie++)
254
if (phase[ip] > (emission_[ie]+incidence_[ii])+2*deg_to_rad) continue;
255
if (phase[ip] < fabs(emission_[ie]-incidence_[ii])-2*deg_to_rad) continue;
258
unsigned int ipos = il * area_em + ie * incidence_.size() + ii;
262
// slant distance from the tangent point to htop
263
double xfar = slantLength(emission_[ie], htop/radius_);
265
int n_h = (int) ceil(xfar/min_dh);
266
double dx = xfar / (n_h-1);
268
// integrate along the slant path
269
for (int ix = 0; ix < n_h-1; ix++)
271
double x = (ix+0.5) * dx;
272
double h = slantHeight(emission_[ie], x/radius_);
273
double thisIncidence = zenithAlongSlant(incidence_[ii], h);
274
double thisEmission = zenithAlongSlant(emission_[ie], h);
277
if (h < hmin) continue;
279
double atten = exp(-h/scaleHeight_);
280
double tauSun = chapman((h+radius_)/scaleHeight_, thisIncidence);
281
double tauView = chapman((h+radius_)/scaleHeight_,thisEmission);
282
double tauScale = numberDensity_ * atten *
283
scaleHeight_ * crossRayleigh;
285
rayleigh += dx * atten * exp(-(tauSun+tauView)*tauScale);
287
rayleigh *= (0.75*(1+cos2phase)*rayleighScale/lambda4);
289
em_array[ipos] = rayleigh;
295
snprintf(buffer, 64, diskTemplate_.c_str(), (int) (phase_d[ip]));
297
writeTable(buffer, em_array, lambda_.size(), incidence_.size(),
298
emission_.size(), diskPNG_);
302
RayleighScattering::RayleighScattering(string configFile) :
305
indexOfRefraction_(0.),
312
bool foundFile = findFile(configFile, "scattering");
315
ostringstream errStr;
316
errStr << "Can't load scattering file " << configFile << "\n";
317
xpExit(errStr.str(), __FILE__, __LINE__);
321
for (int i = 0; i < 181; i++)
323
phaseDeg_.push_back((double) i);
324
PNGTable_.insert(make_pair(i, (Image *) NULL));
325
BINTable_.insert(make_pair(i, (double *) NULL));
328
for (int i = 0; i < 3; i++) scattering_[i] = 0;
330
readConfigFile(configFile);
334
RayleighScattering::readConfigFile(string configFile)
336
ifstream inFile(configFile.c_str());
337
char line[MAX_LINE_LENGTH];
339
vector<double> values;
340
if (!readBlock(inFile, "INCIDENCE %d", values))
342
ostringstream errStr;
343
errStr << "INCIDENCE block not found in " << configFile << "\n";
344
xpExit(errStr.str(), __FILE__, __LINE__);
347
for (unsigned int ii = 0; ii < values.size(); ii++)
348
incidence_.push_back(values[ii] * deg_to_rad);
350
if (!readBlock(inFile, "EMISSION %d", values))
352
ostringstream errStr;
353
errStr << "EMISSION block not found in " << configFile << "\n";
354
xpExit(errStr.str(), __FILE__, __LINE__);
357
for (unsigned int ii = 0; ii < values.size(); ii++)
358
emission_.push_back(values[ii] * deg_to_rad);
360
if (!readBlock(inFile, "TANGENT_HEIGHT %d", values))
362
ostringstream errStr;
363
errStr << "TANGENT_HEIGHT block not found in " << configFile << "\n";
364
xpExit(errStr.str(), __FILE__, __LINE__);
367
for (unsigned int ii = 0; ii < values.size(); ii++)
368
tanHeight_.push_back(values[ii] * 1e3);
370
diskTemplate_.clear();
371
limbTemplate_.clear();
372
while (inFile.getline(line, MAX_LINE_LENGTH, '\n') != NULL)
375
while (isDelimiter(line[i]))
378
if (static_cast<unsigned int> (i) > strlen(line)) break;
380
if (static_cast<unsigned int> (i) > strlen(line)) continue;
381
if (isEndOfLine(line[i])) continue;
383
char templ1[MAX_LINE_LENGTH];
384
char templ2[MAX_LINE_LENGTH];
385
sscanf(line, "TEMPLATES %s %s", templ1, templ2);
387
diskTemplate_.assign(templ1);
388
limbTemplate_.assign(templ2);
390
size_t found = diskTemplate_.find(".png");
391
if (found == string::npos) found = diskTemplate_.find(".PNG");
392
diskPNG_ = (found != string::npos);
394
found = limbTemplate_.find(".png");
395
if (found == string::npos) found = limbTemplate_.find(".PNG");
396
limbPNG_ = (found != string::npos);
400
if (diskTemplate_.length() == 0)
402
ostringstream errStr;
403
errStr << "TEMPLATE disk file not found in " << configFile << "\n";
404
xpExit(errStr.str(), __FILE__, __LINE__);
406
if (limbTemplate_.length() == 0)
408
ostringstream errStr;
409
errStr << "TEMPLATE limb file not found in " << configFile << "\n";
410
xpExit(errStr.str(), __FILE__, __LINE__);
413
// remaining parameters are only required for table generation
414
readBlock(inFile, "WAVELENGTHS %d", values);
416
for (unsigned int ii = 0; ii < values.size(); ii++)
417
lambda_.push_back(values[ii] * 1e-9);
419
readValue(inFile, "RADIUS %lf", radius_);
422
readValue(inFile, "SCALE_HEIGHT %lf", scaleHeight_);
424
readValue(inFile, "INDEX_OF_REFRACTION %lf", indexOfRefraction_);
426
readValue(inFile, "DENSITY %lf", numberDensity_);
429
RayleighScattering::~RayleighScattering()
435
RayleighScattering::readBlock(ifstream &inFile,
437
vector<double> &values)
441
char line[MAX_LINE_LENGTH];
442
while (inFile.getline(line, MAX_LINE_LENGTH, '\n') != NULL)
445
while (isDelimiter(line[i]))
448
if (static_cast<unsigned int> (i) > strlen(line)) break;
450
if (static_cast<unsigned int> (i) > strlen(line)) continue;
451
if (isEndOfLine(line[i])) continue;
454
if (sscanf(line, format, &size) == 0) break;
456
for (int ii = 0; ii < size; ii++)
460
values.push_back(thisValue);
468
RayleighScattering::readValue(ifstream &inFile,
472
char line[MAX_LINE_LENGTH];
473
while (inFile.getline(line, MAX_LINE_LENGTH, '\n') != NULL)
476
while (isDelimiter(line[i]))
479
if (static_cast<unsigned int> (i) > strlen(line)) break;
481
if (static_cast<unsigned int> (i) > strlen(line)) continue;
482
if (isEndOfLine(line[i])) continue;
484
if (sscanf(line, format, &value) == 0) break;
493
RayleighScattering::clear()
495
for (int i = 0; i < 3; i ++) scattering_[i] = 0;
499
RayleighScattering::clearTables()
501
for (map<int, Image *>::iterator it = PNGTable_.begin();
502
it != PNGTable_.end(); it++)
507
for (map<int, double *>::iterator it = BINTable_.begin();
508
it != BINTable_.end(); it++)
510
delete [] it->second;
515
// store a double (0 < x < 1) as a four byte array of unsigned char
517
// four bytes ABCD -> alpha = A, red = B, green = C, blue = D
519
RayleighScattering::doubleToARGB(double x, unsigned char argb[4])
521
static const unsigned long maxValue = 0xffffffff;
522
unsigned long ul = (unsigned long) (x * maxValue);
524
argb[0] = (ul >> 24) & 0xff;
525
argb[1] = (ul >> 16) & 0xff;
526
argb[2] = (ul >> 8) & 0xff;
530
// turn a four byte unsigned char array to a double (0 < x < 1)
531
// Note: four bytes is only good enough for a float
533
RayleighScattering::ARGBToDouble(unsigned char argb[4])
535
static const unsigned long maxValue = 0xffffffff;
536
unsigned long ul = argb[0] << 24 | argb[1] << 16 | argb[2] << 8 | argb[3];
544
RayleighScattering::writeTable(const char *buffer, double *array,
545
size_t dim0, size_t dim1, size_t dim2,
550
unsigned int area = dim1 * dim2;
552
unsigned char rgb[dim0 * 3 * area];
553
unsigned char alpha[dim0 * area];
554
memset(rgb, 0, dim0 * 3 * area);
555
memset(alpha, 0, dim0 * area);
557
unsigned char argb[4];
559
for (unsigned int il = 0; il < dim0; il++)
561
for (unsigned int it = 0; it < dim2; it++)
563
for (unsigned int ii = 0; ii < dim1; ii++)
565
unsigned int ipos = il * area + it * dim1 + ii;
566
doubleToARGB(array[ipos], argb);
567
alpha[il * area + it * dim1 + ii] = argb[0];
568
memcpy(rgb+3*(il * area + it*dim1 + ii),
573
Image pngImage(dim1, dim0*dim2, rgb, alpha);
574
if (!pngImage.Write(buffer))
576
ostringstream errStr;
577
errStr << "Can't write scattering file " << buffer << "\n";
578
xpWarn(errStr.str(), __FILE__, __LINE__);
582
string message("Wrote ");
583
message.append(buffer);
584
message.append("\n");
585
xpMsg(message, __FILE__, __LINE__);
590
FILE *outfile = fopen(buffer, "wb");
593
ostringstream errStr;
594
errStr << "Can't write scattering file " << buffer << "\n";
595
xpWarn(errStr.str(), __FILE__, __LINE__);
599
fwrite(&dim0, sizeof(size_t), 1, outfile);
600
fwrite(&dim1, sizeof(size_t), 1, outfile);
601
fwrite(&dim2, sizeof(size_t), 1, outfile);
602
fwrite(array, sizeof(double), dim0*dim1*dim2, outfile);
604
string message("Wrote ");
605
message.append(buffer);
606
message.append("\n");
607
xpMsg(message, __FILE__, __LINE__);
613
RayleighScattering::readBinaryTable(const char *filename)
615
double *dblArray = NULL;
617
FILE *inFile = fopen(filename, "rb");
621
size_t dim0, dim1, dim2;
622
fread(&dim0, sizeof(size_t), 1, inFile);
623
fread(&dim1, sizeof(size_t), 1, inFile);
624
fread(&dim2, sizeof(size_t), 1, inFile);
626
size_t size = dim0*dim1*dim2;
628
dblArray = new double[size];
629
fread(dblArray, sizeof(double), size, inFile);
638
RayleighScattering::getColor(int index)
640
return scattering_[index];
644
RayleighScattering::calcScatteringLimb(double inc, double tanht, double phase)
646
for (int ic = 0; ic < 3; ic++)
647
calcScattering(inc, tanht, phase, ic, tanHeight_, limbTemplate_,
652
RayleighScattering::calcScatteringDisk(double inc, double ems, double phase)
654
if (phase > M_PI_2) phase = M_PI - phase;
655
for (int ic = 0; ic < 3; ic++)
656
calcScattering(inc, ems, phase, ic, emission_, diskTemplate_, diskPNG_);
660
RayleighScattering::readTableValue(string thisTemplate, bool usePNG,
661
int color, int x, int y,
662
vector<double> yaxis,
663
int phase, double values[4])
667
map<int, Image *>::iterator it = PNGTable_.find(phase);
669
if (it->second == NULL)
672
snprintf(buffer, 64, thisTemplate.c_str(), phase);
673
string thisFile(buffer);
674
if (!findFile(thisFile, "scattering"))
676
ostringstream errStr;
677
errStr << "Can't load scattering file " << thisFile << "\n";
678
xpExit(errStr.str(), __FILE__, __LINE__);
681
if (!image->Read(thisFile.c_str()))
683
scattering_[color] = 0;
689
unsigned char argb[4];
691
image->getPixel(x, y, argb+1, argb);
692
values[0] = ARGBToDouble(argb);
693
image->getPixel(x+1, y, argb+1, argb);
694
values[1] = ARGBToDouble(argb);
695
image->getPixel(x, y+1, argb+1, argb);
696
values[2] = ARGBToDouble(argb);
697
image->getPixel(x+1, y+1, argb+1, argb);
698
values[3] = ARGBToDouble(argb);
702
map<int, double *>::iterator it = BINTable_.find(phase);
703
double *array = NULL;
704
if (it->second == NULL)
707
snprintf(buffer, 64, thisTemplate.c_str(), phase);
708
string thisFile(buffer);
709
if (!findFile(thisFile, "scattering"))
711
ostringstream errStr;
712
errStr << "Can't load scattering file " << thisFile << "\n";
713
xpExit(errStr.str(), __FILE__, __LINE__);
715
array = readBinaryTable(thisFile.c_str());
718
scattering_[color] = 0;
725
unsigned int ipos[4];
726
ipos[0] = y * incidence_.size() + x;
727
ipos[1] = y * incidence_.size() + x+1;
728
ipos[2] = (y+1) * incidence_.size() + x;
729
ipos[3] = (y+1) * incidence_.size() + x+1;
730
unsigned int isize = 3 * yaxis.size() * incidence_.size();
731
for (int i = 0; i < 4; i++)
733
if (isize > ipos[i]) values[i] = array[ipos[i]];
740
RayleighScattering::calcScattering(double inc, double yValue, double phase,
741
int color, vector<double> yaxis,
742
string thisTemplate, bool usePNG)
744
double deg_to_rad = M_PI / 180;
746
if (inc < incidence_.front() || inc > incidence_.back() \
747
|| yValue < yaxis.front() || yValue > yaxis.back())
749
scattering_[color] = 0;
753
double phase_deg = phase / deg_to_rad;
755
int phase_lo = floor(phase_deg);
756
if (phase_lo < 0) phase_lo = 0;
758
int phase_hi = ceil(phase_deg);
759
if (phase_hi - phase_deg < 1e-3) phase_hi = phase_lo+1;
761
vector<double>::iterator inc_hi, inc_lo;
762
// lower_bound returns first element greater than or equal to value
763
inc_lo = lower_bound(incidence_.begin(), incidence_.end(), inc);
764
if (inc_lo > incidence_.begin()) inc_lo--;
765
// upper_bound returns first element greater than value
766
inc_hi = upper_bound(incidence_.begin(), incidence_.end(), inc);
768
vector<double>::iterator y_hi, y_lo;
769
y_lo = lower_bound(yaxis.begin(), yaxis.end(), yValue);
770
if (y_lo > yaxis.begin()) y_lo--;
771
y_hi = upper_bound(yaxis.begin(), yaxis.end(), yValue);
773
int x = inc_lo - incidence_.begin();
774
double xFrac = (*inc_hi == *inc_lo ? 0 : (inc - *inc_lo) / (*inc_hi - *inc_lo));
775
int y = y_lo - yaxis.begin();
776
double yFrac = (*y_hi == *y_lo ? 0 : (yValue - *y_lo) / (*y_hi - *y_lo));
779
getWeights(xFrac, 1-yFrac, weight);
781
y += color * yaxis.size();
785
readTableValue(thisTemplate, usePNG, color, x, y, yaxis, phase_lo, values);
787
for (int i = 0; i < 4; i++)
788
loPhase += weight[i] * values[i];
790
readTableValue(thisTemplate, usePNG, color, x, y, yaxis, phase_hi, values);
792
for (int i = 0; i < 4; i++)
793
hiPhase += weight[i] * values[i];
795
scattering_[color] = loPhase;
796
if (phase_hi - phase_lo != 0)
797
scattering_[color] += (phase_deg-phase_lo)/(phase_hi-phase_lo)
798
* (hiPhase - loPhase);
800
if (scattering_[color] < 0) scattering_[color] = 0;