JavaScript: Al-Biruni Calculator with Refraction

This is the Javascript source code of the online calculator in the article Measuring Earths Radius like Al-Biruni taking Refraction into account.

#INCLUDE ControlPanel.inc

<jscript>

function ComputeMeanAndTolerance( func, means, tolerances ) {
// uses the means[n] and tolerances[n] to generate all possible
// variation[i] = means[i] +/- tolerances[i] and calls func(variation[i])
// for each variation. From the results of func() the min and max
// values are determined.
//
// Returns { exact:, mean:, tolerance: } value from all variantions,
// where mean = (max + min) / 2 and tolerance = (max - min) / 2.

return Math.pow( 3, nParam );
}

function makeVariation( mask, means, tolerances ) {
// to get all possible variations[n]. Increasing the mask
// and call this function repeatedly to get all possible variations.
// mask = 0 returns means[] without tolerances[] applied.
//
// mask = 0 -> [ m[0]     , m[1]     , ..., m[n] ]
// mask = 1 -> [ m[0]-t[0], m[1]     , ..., m[n] ]
// mask = 2 -> [ m[0]+t[0], m[1]     , ..., m[n] ]
// mask = 3 -> [ m[0]     , m[1]-t[1], ..., m[n] ]
// mask = 4 -> [ m[0]-t[0], m[1]-t[1], ..., m[n] ]
// mask = 5 -> [ m[0]+t[0], m[1]-t[1], ..., m[n] ]
// mask = 6 -> [ m[0]     , m[1]+t[1], ..., m[n] ]
// mask = 7 -> [ m[0]-t[0], m[1]+t[1], ..., m[n] ]
// mask = 8 -> [ m[0]+t[0], m[1]+t[1], ..., m[n] ]
// and so on

var variations = [];
var nValues = means.length;
for (var i = 0; i < nValues; i++) {
variations[i] = means[i];
}
for (var i = 0; i < nValues; i++) {
variations[i] -= tolerances[i];
} else if (maskBit === 2) {
variations[i] += tolerances[i];
}
}
return variations;
}

var exactValue = func( ...means );

var minValue = exactValue;
var maxValue = exactValue;
var pars = makeVariation( mask, means, tolerances );
var currentValue = func( ...pars );
if (currentValue < minValue) minValue = currentValue;
if (currentValue > maxValue) maxValue = currentValue;
}
var mean      = (minValue + maxValue) / 2;
var tolerance = (maxValue - minValue) / 2;
return { exact: exactValue, mean: mean, tolerance: tolerance };
}

var AlBiruniModel = {

Pressure:       1013.25, // mbar
PressureAbsTol:       1, // mbar
PressureRelTol:       0, // AbsTol/Mean in %

TempC:             15.5,
TempCAbsTol:       0.25,
TempCRelTol:          0,

RefrCoeff:         0.17,
RefrCoeffAbsTol:   0.04,
RefrCoeffRelTol:      0,

LightCurve:           0,
LightCurveAbsTol:     0,
LightCurveRelTol:     0,

ObsHeight:          358, // m
ObsHeightAbsTol:     12, // m
ObsHeightRelTol:      0,

DipAngle:          0.56667, // deg
DipAngleAbsTol:    0.016667,
DipAngleRelTol:       0,

R:                    0, // m
RMean:                0, // m
RAbsTol:              0, // m
RRelTol:              0,

DistAB:               1000,
DistABAbsTol:            5,
DistABRelTol:            0,

Alpha:                  45, // degrees
AlphaAbsTol:          0.25,
AlphaRelTol:             0,

Beta:                14.75,
BetaAbsTol:           0.25,
BetaRelTol:              0,

FeMntHeight:             0,
FeMntHeightMean:         0,
FeMntHeightAbsTol:       0,
FeMntHeightRelTol:       0,

GlobeMntHeight:          0,
GlobeMntHeightMean:      0,
GlobeMntHeightAbsTol:    0,
GlobeMntHeightRelTol:    0,

MntRefrCoeff:         0.13,
MntRefrCoeffAbsTol:  0.026,
MntRefrCoeffRelTol:      0,

GlobeFlatDiff:           0,
GlobeFlatDiffMean:       0,
GlobeFlatDiffAbsTol:     0,
GlobeFlatDiffrelTol:     0,

UnitsType: 0,  // 0 -> m, 1 -> mi/ft, 2 -> ft
LengthUnits: {
Selection: '.UnitsType',
Units: [ 'm', 'mi', 'ft' ],
Mults: [ 1, 1609.344, 0.3048 ]
},
BigLengthUnits: {
Selection: '.UnitsType',
Units: [ 'km', 'mi', 'ft' ],
Mults: [ 1000, 1609.344, 0.3048 ]
},
InvLengthUnits: {
Selection: '.UnitsType',
Units: [ 'm<sup>-1</sup>', 'mi<sup>-1</sup>', 'ft<sup>-1</sup>' ],
Mults: [ 1, 1/1609.344, 1/0.3048 ],
Digits:[ 6, 6, 6 ],
},
HeightUnits: {
Selection: '.UnitsType',
Units: [ 'm', 'ft', 'ft' ],
Mults: [ 1, 0.3048, 0.3048 ]
},
Selection: '.UnitsType',
Units: [ '°C/m', '°C/ft', '°C/ft' ],
Mults: [ 1, 1/0.3048, 1/0.3048 ]
},

AngleFormat: 1, // 0 -> deg, 1 -> DM, 2 -> DMS
AngleFormats: {
Selection: '.AngleFormat',
Units: [ '°', '', '' ],
Formats: [ 'prec', 'dm', 'dms' ],
},

Comp_RefrCoeff_from_LightCurve: function( lightCurve ) {
return lightCurve * 6371000;
},

Comp_LightCurve_from_RefrCoeff: function( refrCoeff ) {
return refrCoeff / 6371000;
},

return 7.895e-5 * pressure / Math.pow( tempC + 273.15, 2 ) * (0.0343 + tempGrad);
},

Comp_TempGrad_from_LightCurve_Pressure_TempC: function( lightCurve, pressure, tempC ) {
var tempAbs = tempC + 273.15;
return (lightCurve * tempAbs * tempAbs) / (7.895e-5 * pressure) - 0.0343;
},

return 1 / lightCurve;
},

},

Comp_Pressure_from_ObsHeight: function( obsHeight ) {
var g = 9.80665;
var Rs = 287.058;
var alpha = -0.0065;
var Tref = 288.15;
var Pref = 1013.25;
var beta = g / Rs / alpha;
var p = Pref * Math.pow( (Tref + alpha * obsHeight) / Tref, -beta );
return p;
},

Comp_R_from_ObsHeight_DipAngle_LightCurve: function( obsHeight, dipAngleDeg, lightCurve ) {
var cosDip = Math.cos( dipAngleDeg * Math.PI / 180 );
return (obsHeight* cosDip - 0.5 * lightCurve * obsHeight * obsHeight) / (lightCurve * obsHeight + 1 - cosDip);
},

Comp_FeMntHeight_from_Alpha_Beta_DistAB: function( alpha, beta, distAB ) {
var tanAlpha = Math.tan( alpha * Math.PI / 180 );
var tanBeta  = Math.tan( beta  * Math.PI / 180 );
var h = distAB * tanAlpha * tanBeta / (tanAlpha - tanBeta);
return h;
},

Comp_GlobeMntHeight_from_Alpha_Beta_DistAB_MntRefrCoeff: function( alpha, beta, distAB, refrCoeff ) {
// note: using the refracted curvature of earth CRrefr rather than the refracted radius of curvature R'
// prevents divisions by zero for k = 1
var R = 6371000;
var CRrefr = (1 - refrCoeff) / R;
var tanAlpha = Math.tan( alpha * Math.PI / 180 );
var tanBeta  = Math.tan( beta  * Math.PI / 180 );
var x = distAB * (tanBeta + 0.5 * distAB * CRrefr) / (tanAlpha - tanBeta - distAB * CRrefr);
var h = x * tanAlpha + x*x * CRrefr / 2;
return h;
},

Comp: function( resultName, argNames ) {
var funcName = 'Comp_' + resultName + '_from';
var means = [];
var tols = [];
var nArgs = argNames.length;
for (var i = 0; i < nArgs; i++) {
funcName += '_' + argNames[i];
means[i] = this[argNames[i]];
tols[i]  = this[argNames[i]+'AbsTol'];
}
var result = ComputeMeanAndTolerance( this[funcName], means, tols );
this[resultName] = result.exact;
this[resultName+'AbsTol'] = result.tolerance;
this[resultName+'RelTol'] = 100 * Math.abs( result.tolerance / this[resultName] );
return result.mean;
},

Update: function( field ) {

function allNames( namesStr ) {
// returns expanded namesStr to include all names + their tolerances
// e.g. namesStr = 'DipAngle' -> 'DipAngle,DipAngleAbsTol,DipAngleRelTol'
var names = namesStr.split( ',' );
var all = [];
for (var i = 0; i < names.length; i++) {
all.push( names[i] );
all.push( names[i]+'AbsTol' );
all.push( names[i]+'RelTol' );
}
return all.join( ',' );
}

var fieldNames = [
'Pressure'  , 'TempC'   , 'TempGrad' ,
'ObsHeight' , 'DipAngle', 'MntRefrCoeff',
'Alpha'     , 'Beta'    , 'DistAB'
];

// convert relative tolerance entries to absolute tolerances

if (xDef(field)) {
for (var i = 0; i < fieldNames.length; i++) {
if (ControlPanels.MatchesField( field, fieldNames[i]+'RelTol' )) {
this[fieldNames[i]+'AbsTol'] = this[fieldNames[i]+'RelTol'] * Math.abs(this[fieldNames[i]]) / 100;
}
}
}

// if observer height is changed, calculate and set the pressure according the international standard atmosphere
// the tolerances are kept untouched

if (ControlPanels.MatchesField( field, 'ObsHeight' )) {
this.Pressure = this.Comp_Pressure_from_ObsHeight( this.ObsHeight );
}

// when refraction coeff, light curvature or light radius is changes, compute temperature gradient from it
// keeping pressure and absolute temperature constant

if (xDef(field) && ControlPanels.MatchesField( field, allNames('RefrCoeff,LightCurve,LightRad') )) {

if (ControlPanels.MatchesField( field, allNames('LightRad') )) {
this.Comp( 'LightCurve', [ 'LightRad' ] );
this.Comp( 'RefrCoeff', [ 'LightCurve' ] );
}

if (ControlPanels.MatchesField( field, allNames('LightCurve') )) {
this.Comp( 'RefrCoeff', [ 'LightCurve' ] );
}

if (ControlPanels.MatchesField( field, allNames('RefrCoeff') )) {
this.Comp( 'LightCurve', [ 'RefrCoeff' ] );
}

this.Comp( 'TempGrad', [ 'LightCurve', 'Pressure', 'TempC' ] );

} else {

this.Comp( 'LightCurve', [ 'Pressure', 'TempC', 'TempGrad' ] );
this.Comp( 'RefrCoeff', [ 'LightCurve' ] );

}

this.Comp( 'LightRad', [ 'LightCurve' ] );
this.RMean = this.Comp( 'R', [ 'ObsHeight', 'DipAngle', 'LightCurve' ] );

this.FeMntHeightMean    = this.Comp( 'FeMntHeight', [ 'Alpha', 'Beta', 'DistAB' ] );
this.GlobeMntHeightMean = this.Comp( 'GlobeMntHeight', [ 'Alpha', 'Beta', 'DistAB', 'MntRefrCoeff' ] );

this.GlobeFlatDiff       = this.GlobeMntHeight       - this.FeMntHeight;
this.GlobeFlatDiffMean   = this.GlobeMntHeightMean   - this.FeMntHeightMean;
this.GlobeFlatDiffAbsTol = this.GlobeMntHeightAbsTol - this.FeMntHeightAbsTol;
this.GlobeFlatDiffRelTol = this.GlobeMntHeightRelTol - this.FeMntHeightRelTol;

// compute relative tolerances for all values

for (var i = 0; i < fieldNames.length; i++) {
this[fieldNames[i]+'RelTol'] = 100 * Math.abs( this[fieldNames[i]+'AbsTol'] / this[fieldNames[i]] );
}

ControlPanels.Update();

},

};

ControlPanels.NewPanel( {
Name: 'UnitsPanel',
ModelRef: 'AlBiruniModel',
NCols: 2,
OnModelChange: function(field){ AlBiruniModel.Update(field); },

Text: 'Choose the Units and Angle Formats for the Calculators',
ColSpan: 4,

Name: 'UnitsType',
Label: 'Lengths',
ValueType: 'int',
Items: [
{
Name: 'km/m (Metric)',
Value: 0
}, {
Name: 'mi/ft (Imp)',
Value: 1
}, {
Name: 'ft/ft (Imp)',
Value: 2
}
]

Name: 'AngleFormat',
Label: 'Angles',
ValueType: 'int',
Items: [
{
Name: 'deg.',
Value: 0
}, {
Name: 'DM',
Value: 1
}, {
Name: 'DMS',
Value: 2
}
]

} ).Render();

ControlPanels.NewPanel( {
Name: 'InputAlBiruniParametersPanel',
ModelRef: 'AlBiruniModel',
NCols: 3,
OnModelChange: function(field){ AlBiruniModel.Update(field) },
Format: 'fix0',
Digits: 2,
FormatTab: true,
PanelFormat: 'InputNormalWidth'

Text: 'Measured Parameters',
ColSpan: 3,

Text: 'Tolerances Absolute',
ColSpan: 2,

Text: 'Tolerances Relative',
ColSpan: 1,

Name: 'ObsHeight',
Label: 'Observer Height h',
UnitsData: 'HeightUnits',
Inc: 10,
LowerLimit: 0,
UpperLimit: 11000,

Name: 'ObsHeightAbsTol',
Label: '+/-',
UnitsData: 'HeightUnits',
Inc: 0.1,
LowerLimit: 0,
UpperLimit: 11000,

Name: 'ObsHeightRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'DipAngle',
Label: 'Dip Angle &alpha;',
InputFormat: 'dms',
Format: 'dms',
UnitsData: 'AngleFormats',
Digits: 4,
Inc: 0.05,
LowerLimit: -45,
UpperLimit: 45,

Name: 'DipAngleAbsTol',
Label: '+/-',
InputFormat: 'dms',
Format: 'dms',
UnitsData: 'AngleFormats',
Digits: 4,
Inc: 0.005,
LowerLimit: 0,
UpperLimit: 45,

Name: 'DipAngleRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'Pressure',
Label: 'Pressure P',
Units: 'mbar',
Inc: 1,
LowerLimit: 200,
UpperLimit: 1200,

Name: 'PressureAbsTol',
Label: '+/-',
Units: 'mbar',
Inc: 0.1,
LowerLimit: 0,
UpperLimit: 1200,

Name: 'PressureRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'TempC',
Label: 'Temperature T',
Units: '°C',
Inc: 1,
LowerLimit: -100,
UpperLimit: 100,

Name: 'TempCAbsTol',
Label: '+/-',
Units: '°C',
Inc: 0.1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'TempCRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Digits: 5,
Inc: 0.0005,
LowerLimit: -1,
UpperLimit: 1,

Label: '+/-',
Digits: 5,
Inc: 0.00005,
LowerLimit: 0,
UpperLimit: 1,

Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'RefrCoeff',
Label: 'Refraction k',
Units: '',
Digits: 3,
Inc: 0.01,
LowerLimit: -10,
UpperLimit: 10,

Name: 'RefrCoeffAbsTol',
Label: '+/-',
Units: '',
Digits: 3,
Inc: 0.001,
LowerLimit: 0,
UpperLimit: 10,

Name: 'RefrCoeffRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

UnitsData: 'BigLengthUnits',
Inc: 1000,

Label: '+/-',
UnitsData: 'BigLengthUnits',
Inc: 100,
LowerLimit: 0,

Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'LightCurve',
Label: 'Light Curve c',
UnitsData: 'InvLengthUnits',
Format: 'sci',
Inc: 1e-9,
LowerLimit: -1e-6,
UpperLimit: 1e-6,

Name: 'LightCurveAbsTol',
Label: '+/-',
UnitsData: 'InvLengthUnits',
Format: 'sci',
Inc: 1e-10,
LowerLimit: 0,
UpperLimit: 1e-6,

Name: 'LightCurveRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

} ).Render();

ControlPanels.NewPanel( {
ModelRef: 'AlBiruniModel',
NCols: 4,
OnModelChange: function(field){ AlBiruniModel.Update(field) },
FormatTab: true,
Format: 'fix0',
Digits: 2,
PanelFormat: 'InputSmallerWidth'

Text: 'Result',
ColSpan: 1,

Text: 'Exact',
ColSpan: 2,

Text: 'Mean',
ColSpan: 2,

Text: 'Accuracy Absolute',
ColSpan: 2,

Text: 'Accuracy Relative',
ColSpan: 1,

Name: 'R',
UnitsData: 'BigLengthUnits',

Name: 'RMean',
Label: '',
UnitsData: 'BigLengthUnits',

Name: 'RAbsTol',
Label: '+/-',
UnitsData: 'BigLengthUnits',

Name: 'RRelTol',
Label: '+/-',
Units: '%',

} ).Render();

//// Mountain Calculator Panel ////

ControlPanels.NewPanel( {
Name: 'InputMountainHeightParametersPanel',
ModelRef: 'AlBiruniModel',
NCols: 3,
OnModelChange: function(field){ AlBiruniModel.Update(field) },
Format: 'fix0',
Digits: 2,
FormatTab: true,
PanelFormat: 'InputNormalWidth'

Text: 'Mountain Measurements',
ColSpan: 3,

Text: 'Tolerances Absolute',
ColSpan: 2,

Text: 'Tolerances Relative',
ColSpan: 1,

Name: 'DistAB',
Label: 'Distance AB',
UnitsData: 'LengthUnits',
Digits: 3,
Inc: 10,
LowerLimit: 0,
UpperLimit: 11000,

Name: 'DistABAbsTol',
Label: '+/-',
UnitsData: 'LengthUnits',
Digits: 3,
Inc: 0.1,
LowerLimit: 0,
UpperLimit: 11000,

Name: 'DistABRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'Alpha',
Label: 'Angle &alpha; at A',
InputFormat: 'dms',
Format: 'dms',
UnitsData: 'AngleFormats',
Digits: 4,
Inc: 1,
LowerLimit: 0,
UpperLimit: 90,

Name: 'AlphaAbsTol',
Label: '+/-',
InputFormat: 'dms',
Format: 'dms',
UnitsData: 'AngleFormats',
Digits: 4,
Inc: 0.005,
LowerLimit: 0,
UpperLimit: 45,

Name: 'AlphaRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'Beta',
Label: 'Angle &beta; at B',
InputFormat: 'dms',
Format: 'dms',
UnitsData: 'AngleFormats',
Digits: 4,
Inc: 1,
LowerLimit: 0,
UpperLimit: 90,

Name: 'BetaAbsTol',
Label: '+/-',
InputFormat: 'dms',
Format: 'dms',
UnitsData: 'AngleFormats',
Digits: 4,
Inc: 0.005,
LowerLimit: 0,
UpperLimit: 45,

Name: 'BetaRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

Name: 'MntRefrCoeff',
Label: 'Refraction k',
Units: '',
Digits: 3,
Inc: 0.01,
LowerLimit: -10,
UpperLimit: 10,

Name: 'MntRefrCoeffAbsTol',
Label: '+/-',
Units: '',
Digits: 3,
Inc: 0.001,
LowerLimit: 0,
UpperLimit: 10,

Name: 'MntRefrCoeffRelTol',
Label: '+/-',
Units: '%',
Inc: 1,
LowerLimit: 0,
UpperLimit: 100,

} ).Render();

ControlPanels.NewPanel( {
Name: 'OutputMountainHeightPanel',
ModelRef: 'AlBiruniModel',
NCols: 4,
OnModelChange: function(field){ AlBiruniModel.Update(field) },
FormatTab: true,
Format: 'fix0',
Digits: 3,
PanelFormat: 'InputSmallerWidth'

Text: 'Mount. H',
ColSpan: 1,

Text: 'Exact',
ColSpan: 2,

Text: 'Mean',
ColSpan: 2,

Text: 'Accuracy Absolute',
ColSpan: 2,

Text: 'Accuracy Relative',
ColSpan: 1,

Name: 'FeMntHeight',
Label: 'Flat Earth',
UnitsData: 'HeightUnits',

Name: 'FeMntHeightMean',
Label: '',
UnitsData: 'HeightUnits',

Name: 'FeMntHeightAbsTol',
Label: '+/-',
UnitsData: 'HeightUnits',

Name: 'FeMntHeightRelTol',
Label: '+/-',
Units: '%',

Name: 'GlobeMntHeight',
Label: 'Globe Earth',
UnitsData: 'HeightUnits',

Name: 'GlobeMntHeightMean',
Label: '',
UnitsData: 'HeightUnits',

Name: 'GlobeMntHeightAbsTol',
Label: '+/-',
UnitsData: 'HeightUnits',

Name: 'GlobeMntHeightRelTol',
Label: '+/-',
Units: '%',

Name: 'GlobeFlatDiff',
Label: 'Globe - Flat',
UnitsData: 'HeightUnits',

Name: 'GlobeFlatDiffMean',
Label: '',
UnitsData: 'HeightUnits',

Name: 'GlobeFlatDiffAbsTol',
Label: '+/-',
UnitsData: 'HeightUnits',

Name: 'GlobeFlatDiffRelTol',
Label: '+/-',
Units: '%',

} ).Render();

</jscript>



Blog-Functions

 About Walter Bislin (wabis) Rights Public Domain
 More Page Infos / Sitemap Created Tuesday, August 1, 2023
 Scroll to Top of Page Changed Friday, September 1, 2023