Ignore:
Timestamp:
Jun 28, 2011, 12:31:47 PM (13 years ago)
Author:
frichard
Message:

-Version 0.8 de libini
-Formule de Marc
-Nouvelles fonctionnalités (goto nom-de l'objet etc...)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • BAORadio/libindi/libindi/drivers/telescope/astro.cpp

    r490 r504  
     1////////////////////////////////
     2// Classe Astro               //
     3// Franck RICHARD             //
     4// franckrichard033@gmail.com //
     5// BAORadio                   //
     6// juin 2011                  //
     7////////////////////////////////
     8
     9
    110#include "astro.h"
    211
    3 ////////////////////
    4 // Routines Astro //
    5 ////////////////////
     12
     13/**************************************************************************************
     14** Constructeur de la classe Astro
     15***************************************************************************************/
    616
    717Astro::Astro()
    818{
    9     Longitude=0.0;
    10     Latitude=0.0;
    11     Lst=0.0;
    12     JJ=0.0;
    13     CorP=0.0;
    14     CorEP=0.0;
    15     Annee=0.0;
    16     Mois=0.0;
    17     Jour=0.0;
    18     Heu=0.0;
    19     Min=0.0;
    20     Sec=0.0;
    21     ep=0.0;
    22     hs=0.0;
    23     UTCP=0.0;
    24     tsl=0.0;
     19    //Initialisation des variables
     20   
     21    Longitude = 0.0;                     // Cordonnées du lieu d'observation
     22    Latitude  = 0.0;
     23   
     24    Annee     = 0.0;                     // Date et heure de l'observation
     25    Mois      = 0.0;
     26    Jour      = 0.0;
     27    Heure     = 0.0;
     28    Min       = 0.0;
     29    Sec       = 0.0;
     30    UTCP      = 0.0;                     // permet de gérer le fuseau horaire
     31                                         // mais semble inutile sous Linux
     32   
     33    hs        = 0.0;                     // fraction du jour en cours hs=(heure+min/60+secs/3600)/24.
     34    ep        = 0.0;                     // obliquité de l'écliptique
     35    tsl       = 0.0;                     // temps sidéral local
     36    JJ        = 0.0;                     // Jour Julien
     37   
     38    CorP      = 0.0;                     // Nutation en longitude
     39    CorEP     = 0.0;                     // Nutation en inclinaison
     40     
     41    LongitudeSoleil = 0.0;
    2542}
    2643
     
    3047
    3148
    32 //0<=Angle<=2*PI
     49/**************************************************************************************
     50** Initialisation des variables globales de la classe Astro
     51***************************************************************************************/
     52
     53void Astro::DefinirDateHeure(double A, double M, double J, double H, double Mi, double S)
     54{
     55    Annee = A;
     56    Mois = M;
     57    Jour = J;
     58    Heure = H;
     59    Min = Mi;
     60    Sec = S;
     61}
     62
     63void Astro::DefinirPressionTemp(double P, double T)
     64{
     65    Pression = P;
     66    Temp = T;
     67}
     68
     69void Astro::DefinirLongitudeLatitude(double log, double lat)
     70{
     71    Longitude = log;
     72    Latitude = lat;
     73}
     74
     75
     76
     77/**************************************************************************************
     78** ParamÚtre : angle en radians
     79** retourne angle dans un intervalle 0 <= Angle <= 2*PI
     80***************************************************************************************/
     81
    3382double Astro::VerifAngle(double Angle)
    3483{
     
    4089
    4190
    42 //////////////////////////////////////////////////////////////////////
    43 //Nutation
     91
     92/**************************************************************************************
     93** retourne l'arrondi du nombre donné en paramÚtre
     94** Exemples: Arrondi(5.7) =  6
     95**           Arrondi(-5.7)= -6
     96***************************************************************************************/
     97
     98double Astro::Arrondi(double a)
     99{
     100    double b;
     101
     102    if (a>=0.0)
     103    {
     104        b=a-floor(a);
     105        if (b>=0.5) return ceil(a);
     106        else return floor(a);
     107    }
     108    else
     109    {
     110        b=ceil(a)-a;
     111        if (b>=0.5) return floor(a);
     112        else return ceil(a);
     113    }
     114}
     115
     116
     117
     118/**************************************************************************************
     119** converti un angle (exprimé en degrés) dans le format hh:mm:ss ou deg:mm:ss
     120** selon la valeur de HMS
     121***************************************************************************************/
     122
     123string Astro::DHMS(double mema, bool HMS)
     124{
     125    double d, m, s, a;
     126
     127    a=mema;
     128
     129    if (HMS) a=a / 15.0;
     130    a=fabs(a * 3600.0);
     131    s=Arrondi(fmod(a, 60.0) * 10.0) / 10.0;
     132    a=floor(a / 60.0);
     133    m=fmod(a, 60.0);
     134    d=floor(a / 60.0);
     135
     136    if (s == 60.0)
     137    {
     138        s=0.0;
     139        m+=1.0;
     140        if (m==60.0)
     141        {
     142            m=0.0;
     143            d+=1.0;
     144
     145            if (HMS && d>23.0) d=0.0;
     146        }
     147    }
     148
     149    stringstream strsTemp;
     150
     151    if (mema < 0) strsTemp << '-';
     152
     153    strsTemp << setfill('0') << setw(2) << d << ":" << m << ":" << s;
     154
     155    return (strsTemp.str());
     156}
     157
     158
     159/**************************************************************************************
     160** Calcul de la longitude du Soleil
     161** La quantité PS n'est pas indispensable. Elle permet d'améliorer la précision
     162***************************************************************************************/
     163
     164double Astro::CalculLongitudeSoleil()
     165{
     166    double T = (JJ - 2415020.0) / 36525.0;
     167
     168    double A = 153.23 + 22518.7541 * T;
     169    double B = 216.57 + 44037.5082 * T;
     170    double C = 312.69 + 32964.3577 * T;
     171    double D = 350.74 + ( 445267.1142 - 0.00144 * T ) * T;
     172    double E = 231.19 + 20.2 * T;
     173
     174
     175    double L = 279.69668 + ( 36000.76892 + 0.0003025 * T ) * T;
     176    double M = 358.47583 + (((( -0.0000033 * T) -0.00015 )  * T ) + 35999.04975 ) * T;
     177
     178    double CC =   (1.919460 + ((-0.000014 * T) - 0.004789 ) * T ) * sin(M * Pidiv180)
     179                  + (0.020094 - 0.0001 * T) * sin(2.0 * M * Pidiv180)
     180                  + 0.000293 * sin(3.0 * M * Pidiv180);
     181
     182    double PS =  0.00134 * cos(A * Pidiv180)
     183                +0.00154 * cos(B * Pidiv180)
     184                +0.00200 * cos(C * Pidiv180)
     185                +0.00179 * sin(D * Pidiv180)
     186                +0.00178 * sin(E * Pidiv180);
     187
     188
     189    double LS = VerifAngle( (L + CC + PS) * Pidiv180);
     190   
     191    return LS;
     192}
     193
     194
     195
     196/**************************************************************************************
     197** Calcul de l'AD et la déclinaison du Soleil
     198** indispensable pour exécuter la commande "goto sun"
     199***************************************************************************************/
     200
     201void Astro::CalculARDecSoleil(Coordonnees *Soleil)
     202{
     203    double ar;
     204    double de;
     205   
     206    ar = atan( cos(ep) * sin(LongitudeSoleil) / cos(LongitudeSoleil));
     207   
     208    if ( cos(LongitudeSoleil) < 0.0) ar += Pi;
     209   
     210    de = asin( sin(ep) * sin(LongitudeSoleil));
     211
     212    Soleil->ar = DHMS( VerifAngle(ar) * N180divPi, true );
     213   
     214    Soleil->dec = DHMS( de * N180divPi, false );
     215}
     216   
     217
     218
     219
     220/**************************************************************************************
     221** Calcul de la nutation en longitude et en inclinaison
     222** Nécessaire pour calculer la position apparente d'un objet (étoile, galaxie etc...)
     223***************************************************************************************/
    44224
    45225void Astro::Nutation()
    46226{
    47     double T,L,LP,M,MP,O,L2,LP2,O2;
    48 
    49     T=(JJ-2415020.0)/36525.0;
    50 
    51     L=(279.6967+(36000.7689+0.000303*T)*T)*Pidiv180;
    52     LP=(270.4342+(481267.8831-0.001133*T)*T)*Pidiv180;
    53     M=(358.4758+(35999.0498-0.000150*T)*T)*Pidiv180;
    54     MP=(296.1046+(477198.8491+0.009192*T)*T)*Pidiv180;
    55     O=(259.1833+(-1934.1420+0.002078*T)*T)*Pidiv180;
    56 
    57     L2=2.0*L;
    58     LP2=2.0*LP;
    59     O2=2.0*O;
    60 
    61     CorP=-(17.2327+0.01737*T)*sin(O)
    62          -(1.2729+0.00013*T)*sin(L2)
    63          +0.2088*sin(O2)
    64          -0.2037*sin(LP2)
    65          +(0.1261-0.00031*T)*sin(M)
    66          +0.0675*sin(MP)
    67          -(0.0497-0.00012*T)*sin(L2+M)
    68          -0.0342*sin(LP2-O)
    69          -0.0261*sin(LP2+MP)
    70          +0.0214*sin(L2-M)
    71          -0.0149*sin(L2-LP2+MP)
    72          +0.0124*sin(L2-O)
    73          +0.0114*sin(LP2-MP);
    74 
    75     CorEP=(9.2100+0.00091*T)*cos(O)
    76           +(0.5522-0.00029*T)*cos(L2)
    77           -0.0904*cos(O2)
    78           +0.0884*cos(LP2)
    79           +0.0216*cos(L2+M)
    80           +0.0183*cos(LP2-O)
    81           +0.0113*cos(LP2+MP)
    82           -0.0093*cos(L2-M)
    83           -0.0066*cos(L2-O);
    84 
    85     CorP=CorP/3600.0*Pidiv180;
    86     CorEP=CorEP/3600.0*Pidiv180;
    87 }
    88 
    89 
    90 //////////////////////////////////////////////////////////////////////
    91 // calcul Jour julien
     227    double T = (JJ-2415020.0) / 36525.0;
     228
     229    double L  = (279.6967 + (36000.7689 + 0.000303 * T ) * T ) * Pidiv180;
     230    double LP = (270.4342 + (481267.8831 - 0.001133 * T ) * T ) * Pidiv180;
     231    double M  = (358.4758 + (35999.0498 - 0.000150 * T ) * T ) * Pidiv180;
     232    double MP = (296.1046 + (477198.8491 + 0.009192 * T ) * T ) * Pidiv180;
     233    double O  = (259.1833 + (-1934.1420 + 0.002078 * T ) * T ) * Pidiv180;
     234
     235    double L2  = 2.0 * L;
     236    double LP2 = 2.0 * LP;
     237    double O2  = 2.0 * O;
     238   
     239    // Nutation en longitude
     240
     241    CorP = -(17.2327 + 0.01737 * T) * sin(O)
     242           -(1.2729+0.00013 * T) * sin(L2)
     243           +0.2088 * sin(O2)
     244           -0.2037 * sin(LP2)
     245           +(0.1261 - 0.00031 * T) * sin(M)
     246           +0.0675 * sin(MP)
     247           -(0.0497 - 0.00012 * T) * sin(L2 + M)
     248           -0.0342 * sin(LP2 - O)
     249           -0.0261 * sin(LP2 + MP)
     250           +0.0214 * sin(L2 - M)
     251           -0.0149 * sin(L2 - LP2 + MP)
     252           +0.0124 * sin(L2 - O)
     253           +0.0114 * sin(LP2 - MP);
     254           
     255    //Nutation en inclinaison
     256
     257    CorEP = (9.2100 + 0.00091 * T) * cos(O)
     258            +(0.5522 - 0.00029 * T) * cos(L2)
     259            -0.0904 * cos(O2)
     260            +0.0884 * cos(LP2)
     261            +0.0216 * cos(L2+M)
     262            +0.0183 * cos(LP2-O)
     263            +0.0113 * cos(LP2+MP)
     264            -0.0093 * cos(L2-M)
     265            -0.0066 * cos(L2-O);
     266
     267    CorP  = CorP / 3600.0 * Pidiv180;
     268    CorEP = CorEP / 3600.0 * Pidiv180;
     269}
     270
     271
     272
     273/**************************************************************************************
     274** Nutation des étoiles - on utilise les quantités calculées précédemment
     275** ar et de sont exprimés en radians
     276***************************************************************************************/
     277
     278void Astro::NutationEtoile(double *ar, double *de)
     279{
     280    double a = (cos(ep) + sin(ep) * sin(*ar) * tan(*de)) * CorP - (cos(*ar) * tan(*de)) * CorEP;
     281    double b = (sin(ep) * cos(*ar)) * CorP + sin(*ar) * CorEP;
     282
     283    *ar = VerifAngle(*ar+a);
     284    *de += b;
     285}
     286
     287
     288/**************************************************************************************
     289** Précession des équinoxes
     290** ar et de sont exprimés en radians
     291***************************************************************************************/
     292
     293void Astro::Precession(double *ar, double *de)
     294{
     295    double t = (JJ - 2451544.5) / 36524.2199;
     296
     297    double ze = ((((0.018 * t) + 0.302) * t + 2304.948) * t) / 3600.0 * Pidiv180;
     298    double z = ((((0.019 * t) + 1.093) * t + 2304.948) * t) / 3600.0 * Pidiv180;
     299    double delta = ((((-0.042 * t) - 0.426) * t + 2004.255) * t) / 3600.0 * Pidiv180;
     300
     301
     302    double A = cos(*de) * sin(*ar + ze);
     303    double B = cos(delta) * cos(*de) * cos (*ar + ze) - sin(delta) * sin(*de);
     304    double C = sin(delta) * cos(*de) * cos (*ar + ze) + cos(delta) * sin(*de);
     305
     306    double AMZ = atan(A / B);
     307
     308    if (B<0.0) AMZ += Pi;
     309
     310    *ar=VerifAngle(AMZ + z);
     311    *de=asin(C);
     312}
     313
     314
     315
     316/**************************************************************************************
     317** Obliquité
     318** calcul de l'inclinaison de l'axe terrestre par rapport à au plan de l'écliptique
     319** Quantité indispensable pour calculer le temps sidéral local et les coordonnées
     320** horaires du soleil
     321***************************************************************************************/
     322
     323void Astro::Obliquite(double JJ)
     324{
     325    double T;
     326
     327    T = (JJ - 2415020.0) / 36525.0;
     328
     329    ep = (23.452294+ (((0.000000503 * T ) - 0.00000164 ) * T - 0.0130125 ) * T ) * Pidiv180;
     330}
     331
     332
     333
     334
     335/**************************************************************************************
     336** Aberration annuelle des étoiles
     337** ar et de sont exprimés en radians
     338***************************************************************************************/
     339
     340void Astro::AberrationAnnuelle(double *ar, double *de)
     341{
     342    double c = -20.49 / 3600.0 * Pidiv180;
     343    double a = c * ( cos(*ar) * cos(LongitudeSoleil) * cos(ep)
     344                     + sin(*ar) * sin(LongitudeSoleil)
     345                   ) / cos(*de);
     346
     347    double b = c * ( cos(LongitudeSoleil) * cos(ep) * (tan(ep) * cos(*de) - sin(*ar) * sin(*de))
     348                     + cos(*ar) * sin(*de) * sin(LongitudeSoleil));
     349
     350    *ar = VerifAngle(*ar + a);
     351    *de += b;
     352}
     353
     354
     355/**************************************************************************************
     356** calcul Jour julien
     357***************************************************************************************/
    92358
    93359void Astro::CalculJJ(double Heure)
    94360{
    95 
     361    JJ = CalculJJ(Annee, Mois, Jour, Heure);   
     362}
     363
     364double Astro::CalculJJ(double A, double M, double J, double Heure)
     365{
    96366    long y, a, b, c, e, m;
    97367
    98     long year = (long)Annee;
    99     int month = (int)Mois;
    100     double day = Jour + Heure;
    101 
     368    long year = (long)A;
     369    int month = (int)M;
     370    double day = J + Heure;
    102371
    103372    y = year + 4800;
     
    131400
    132401gregor:
    133         b = (a/4) - a;
     402        b = (a / 4) - a;
    134403    }
    135404
    136     c = (36525L * y)/100;
    137 
    138     JJ = b + c + e + day - 32167.5;
    139 }
    140 
    141 
    142 ///////////////////////////////////////////////////////////////////////
    143 //Obliquité
    144 
    145 void Astro::Obliquite(double JJ)
    146 {
    147     double T;
    148 
    149     T = (JJ-2415020.0) / 36525.0;
    150 
    151     ep = (23.452294+ (((0.000000503 * T ) - 0.00000164 ) * T - 0.0130125 ) * T ) * Pidiv180;
    152 }
    153 
    154 ///////////////////////////////////////////////////////////////////////
    155 //Temps sidéral local
     405    c = (36525L * y) / 100;
     406   
     407    return b + c + e + day - 32167.5;
     408}
     409
     410
     411
     412/**************************************************************************************
     413** Temps sidéral local
     414***************************************************************************************/
    156415
    157416double Astro::TSL(double JJ, double HeureSiderale, double Longitude)
    158417{
    159     double rd,T;
    160 
    161     T = (JJ - 2415020.0 ) / 36525.0;
    162     rd = 0.276919398 + ( 100.0021359 + 0.000001075 * T ) * T;
     418    double T = (JJ - 2415020.0 ) / 36525.0;
     419    double rd = 0.276919398 + ( 100.0021359 + 0.000001075 * T ) * T;
    163420    rd += HeureSiderale;
    164421    rd *= Pi2;
     422    // temps sidéral apparent
    165423    rd += CorP * cos(ep);
    166424    rd -= Longitude;
     
    170428
    171429
     430/**************************************************************************************
     431** routine principale des calculs
     432***************************************************************************************/
     433
    172434void Astro::CalculTSL()
    173435{
    174     hs=(Heu+Min/60.0+Sec/3600.0)/24.0;
    175 
    176     hs-=UTCP/24.0;
     436    hs = (Heure + Min / 60.0 + Sec / 3600.0) / 24.0;
     437
     438    hs -= UTCP / 24.0;
    177439
    178440    CalculJJ(hs);
     
    182444    Nutation();
    183445
    184     tsl=TSL(JJ, hs, Longitude);
    185 }
    186 
    187 
    188 ///////////////////////////////////////////////////////////////////////
    189 //Azimut et hauteur à partir de AR et Dec
    190 
    191 void Astro::Azimut(double ts, double Latitude, double Ar, double De, double *azi, double *hau)
    192 {
    193     double ah, ht, a1, az, zc, ac;
    194 
    195     ah=ts-Ar;
    196 
    197     zc=sin(Latitude)*sin(De)+cos(Latitude)*cos(De)*cos(ah);
    198     ht=atan(zc/sqrt((-zc*zc)+1.0));
    199 
    200     a1=cos(De)*sin(ah)/cos(ht);
    201     ac=(-cos(Latitude)*sin(De)+sin(Latitude)*cos(De)*cos(ah))/cos(ht);
    202     az=atan(a1/sqrt((-a1*a1)+1.0));
     446    tsl = TSL(JJ, hs, Longitude);
     447
     448    LongitudeSoleil = CalculLongitudeSoleil();
     449}
     450
     451
     452
     453/**************************************************************************************
     454** Calcule la hauteur et l'azimut des étoiles en fct du lieu d'observation
     455***************************************************************************************/
     456
     457void Astro::Azimut(double Ar, double De, double *azi, double *hau)
     458{
     459    double ah = tsl - Ar;
     460
     461    double zc = sin(Latitude) * sin(De) + cos(Latitude) * cos(De) * cos(ah);
     462    double ht = atan(zc / sqrt((-zc * zc) + 1.0));
     463
     464    double a1 = cos(De) * sin(ah) / cos(ht);
     465    double ac = (-cos(Latitude) * sin(De) + sin(Latitude) * cos(De) * cos(ah)) / cos(ht);
     466    double az = atan(a1 / sqrt((-a1*a1)+1.0));
     467
    203468    if (ac<0.0) az=Pi-az;
    204469
    205     az=VerifAngle(Pi+az);
    206 
    207     *azi=az;
     470    *azi=VerifAngle(Pi+az);
    208471    *hau=ht;
    209472}
    210473
     474
     475/**************************************************************************************
     476** réfraction atmosphérique : formule simple de Jean Meeus
     477** la hauteur ht est exprimée en radians
     478***************************************************************************************/
     479
     480double Astro::RefractionAtmospherique(double ht)
     481{
     482    double gamma, R0, a, b, PressionTempCalc, tanHT;
     483
     484    if (Pression!=0.0)
     485    {
     486        PressionTempCalc = (Pression / 1013.25) * 273.0 / (273.0 + Temp);
     487
     488        if (ht <= 15.0 * Pidiv180)
     489        {
     490            gamma = 2.6;
     491            a = 7.5262;
     492            b = -2.2204;
     493
     494            if (ht >= 4.0 * Pidiv180)
     495            {
     496                gamma=-1.1;
     497                a=4.4010;
     498                b=-0.9603;
     499            }
     500
     501            R0 = (pow((a+b*log(ht*N180divPi+gamma)), 2.0)) / 60.0 * Pidiv180;
     502            ht += R0 * PressionTempCalc;
     503        }
     504        else
     505        {
     506            tanHT = fabs(tan(Pidiv2 - ht));
     507            R0 = (0.0161877777777777777777-0.000022888888888888888 * tanHT * tanHT)
     508                 * tanHT * Pidiv180;
     509            ht += R0 * PressionTempCalc;
     510        }
     511    }
     512
     513    return ht;
     514}
     515
     516
     517
     518
     519double Astro::slaDrange(double angle )
     520{
     521    return fmod(angle, Pi);
     522}
     523
     524float  Astro::slaRange(float angle )
     525{
     526    return fmodf(angle, Pi);
     527}
     528
     529void Astro::slaRefro ( double zobs, double hm, double tdk, double pmb,
     530                       double rh, double wl, double phi, double tlr,
     531                       double eps, double *ref )
     532/*
     533**  - - - - - - - - -
     534**   s l a R e f r o
     535**  - - - - - - - - -
     536**
     537**  Atmospheric refraction for radio and optical wavelengths.
     538**
     539**  Given:
     540**    zobs    double  observed zenith distance of the source (radian)
     541**    hm      double  height of the observer above sea level (metre)
     542**    tdk     double  ambient temperature at the observer (deg K)
     543**    pmb     double  pressure at the observer (millibar)
     544**    rh      double  relative humidity at the observer (range 0-1)
     545**    wl      double  effective wavelength of the source (micrometre)
     546**    phi     double  latitude of the observer (radian, astronomical)
     547**    tlr     double  temperature lapse rate in the troposphere (degK/met
     548**    eps     double  precision required to terminate iteration (radian)
     549**
     550**  Returned:
     551**    ref     double  refraction: in vacuo ZD minus observed ZD (radian)
     552**
     553**  Notes:
     554**
     555**  1  A suggested value for the tlr argument is 0.0065D0.  The
     556**     refraction is significantly affected by tlr, and if studies
     557**     of the local atmosphere have been carried out a better tlr
     558**     value may be available.
     559**
     560**  2  A suggested value for the eps argument is 1e-8.  The result is
     561**     usually at least two orders of magnitude more computationally
     562**     precise than the supplied eps value.
     563**
     564**  3  The routine computes the refraction for zenith distances up
     565**     to and a little beyond 90 deg using the method of Hohenkerk
     566**     and Sinclair (NAO Technical Notes 59 and 63, subsequently adopted
     567**     in the Explanatory Supplement, 1992 edition - see section 3.281).
     568**
     569**  4  The C code is an adaptation of the Fortran optical refraction
     570**     subroutine AREF of C.Hohenkerk (HMNAO, September 1984), with
     571**     extensions to support the radio case.  The following modifications
     572**     to the original HMNAO optical refraction algorithm have been made:
     573**
     574**     .  The angle arguments have been changed to radians.
     575**
     576**     .  Any value of zobs is allowed (see note 6, below).
     577**
     578**     .  Other argument values have been limited to safe values.
     579**
     580**     .  Murray's values for the gas constants have been used
     581**        (Vectorial Astrometry, Adam Hilger, 1983).
     582**
     583**     .  The numerical integration phase has been rearranged for
     584**        extra clarity.
     585**
     586**     .  A better model for Ps(T) has been adopted (taken from
     587**        Gill, Atmosphere-Ocean Dynamics, Academic Press, 1982).
     588**
     589**     .  More accurate expressions for Pwo have been adopted
     590**        (again from Gill 1982).
     591**
     592**     .  Provision for radio wavelengths has been added using
     593**        expressions devised by A.T.Sinclair, RGO (private
     594**        communication 1989), based on the Essen & Froome
     595**        refractivity formula adopted in Resolution 1 of the
     596**        13th International Geodesy Association General Assembly
     597**        (Bulletin Geodesique 1963 p390).
     598**
     599**     .  Various small changes have been made to gain speed.
     600**
     601**     None of the changes significantly affects the optical results
     602**     with respect to the algorithm given in the 1992 Explanatory
     603**     Supplement.  For example, at 70 deg zenith distance the present
     604**     routine agrees with the ES algorithm to better than 0.05 arcsec
     605**     for any reasonable combination of parameters.  However, the
     606**     improved water-vapour expressions do make a significant difference
     607**     in the radio band, at 70 deg zenith distance reaching almost
     608**     4 arcsec for a hot, humid, low-altitude site during a period of
     609**     low pressure.
     610**
     611**  5  The radio refraction is chosen by specifying wl > 100 micrometres.
     612**     Because the algorithm takes no account of the ionosphere, the
     613**     accuracy deteriorates at low frequencies, below about 30 MHz.
     614**
     615**  6  Before use, the value of zobs is expressed in the range +/- pi.
     616**     If this ranged zobs is -ve, the result ref is computed from its
     617**     absolute value before being made -ve to match.  In addition, if
     618**     it has an absolute value greater than 93 deg, a fixed ref value
     619**     equal to the result for zobs = 93 deg is returned, appropriately
     620**     signed.
     621**
     622**  7  As in the original Hohenkerk and Sinclair algorithm, fixed values
     623**     of the water vapour polytrope exponent, the height of the
     624**     tropopause, and the height at which refraction is negligible are
     625**     used.
     626**
     627**  8  The radio refraction has been tested against work done by
     628**     Iain Coulson, JACH, (private communication 1995) for the
     629**     James Clerk Maxwell Telescope, Mauna Kea.  For typical conditions,
     630**     agreement at the 0.1 arcsec level is achieved for moderate ZD,
     631**     worsening to perhaps 0.5-1.0 arcsec at ZD 80 deg.  At hot and
     632**     humid sea-level sites the accuracy will not be as good.
     633**
     634**  9  It should be noted that the relative humidity rh is formally
     635**     defined in terms of "mixing ratio" rather than pressures or
     636**     densities as is often stated.  It is the mass of water per unit
     637**     mass of dry air divided by that for saturated air at the same
     638**     temperature and pressure (see Gill 1982).
     639**
     640**  Called:  slaDrange, atmt, atms
     641**
     642**  Defined in slamac.h:  TRUE, FALSE
     643**
     644**  Last revision:   30 January 1997
     645**
     646**  Copyright P.T.Wallace.  All rights reserved.
     647*/
     648{
     649    /* Fixed parameters */
     650
     651    static double d93 = 1.623156204; /* 93 degrees in radians        */
     652    static double gcr = 8314.32;     /* Universal gas constant       */
     653    static double dmd = 28.9644;     /* Molecular weight of dry air  */
     654    static double dmw = 18.0152;     /* Molecular weight of water
     655                                                             vapour */
     656    static double s = 6378120.0;     /* Mean Earth radius (metre)    */
     657    static double delta = 18.36;     /* Exponent of temperature
     658                                         dependence of water vapour
     659                                                           pressure */
     660    static double ht = 11000.0;      /* Height of tropopause (metre) */
     661    static double hs = 80000.0;      /* Upper limit for refractive
     662                                                    effects (metre) */
     663
     664    /* Variables used when calling the internal routine atmt */
     665    double robs;   /* height of observer from centre of Earth (metre) */
     666    double tdkok;  /* temperature at the observer (deg K) */
     667    double alpha;  /* alpha          |        */
     668    double gamm2;  /* gamma minus 2  | see ES */
     669    double delm2;  /* delta minus 2  |        */
     670    double c1,c2,c3,c4,c5,c6;  /* various */
     671
     672    /* Variables used when calling the internal routine atms */
     673    double rt;     /* height of tropopause from centre of Earth (metre) */
     674    double tt;     /* temperature at the tropopause (deg k) */
     675    double dnt;    /* refractive index at the tropopause */
     676    double gamal;  /* constant of the atmospheric model = g*md/r */
     677
     678    int is, k, n, i, j, optic;
     679    double zobs1, zobs2, hmok, pmbok, rhok, wlok, tol, wlsq, gb,
     680    a, gamma, tdc, psat, pwo, w, tempo, dn0, rdndr0, sk0,
     681    f0, rdndrt, zt, ft, dnts, rdndrp, zts, fts, rs,
     682    dns, rdndrs, zs, fs, refold, z0, zrange, fb, ff, fo,
     683    fe, h, r, sz, rg, dr, tg, dn, rdndr, t, f, refp, reft;
     684
     685    /* The refraction integrand */
     686#define refi(R,DN,RDNDR) ((RDNDR)/(DN+RDNDR));
     687
     688
     689
     690    /* Transform zobs into the normal range */
     691    zobs1 = slaDrange ( zobs );
     692    zobs2 = fabs ( zobs1 );
     693    zobs2 = gmin ( zobs2, d93 );
     694
     695    /* Keep other arguments within safe bounds */
     696    hmok = gmax ( hm, -1000.0 );
     697    hmok = gmin ( hmok, 10000.0 );
     698    tdkok = gmax ( tdk, 100.0 );
     699    tdkok = gmin ( tdkok, 500.0 );
     700    pmbok = gmax ( pmb, 0.0 );
     701    pmbok = gmin ( pmbok, 10000.0 );
     702    rhok  = gmax ( rh, 0.0 );
     703    rhok  = gmin ( rhok, 1.0 );
     704    wlok  = gmax ( wl, 0.1 );
     705    alpha = fabs ( tlr );
     706    alpha = gmax ( alpha, 0.001 );
     707    alpha = gmin ( alpha, 0.01 );
     708
     709    /* Tolerance for iteration */
     710    w = fabs ( eps );
     711    tol = gmin ( w, 0.1 ) / 2.0;
     712
     713    /* Decide whether optical or radio case - switch at 100 micron */
     714    optic = ( wlok <= 100.0 );
     715
     716    /* Set up model atmosphere parameters defined at the observer */
     717    wlsq = wlok * wlok;
     718    gb = 9.784 * ( 1.0 - 0.0026 * cos ( 2.0 * phi ) - 2.8e-7 * hmok );
     719    a = ( optic ) ?
     720        ( ( 287.604 + 1.6288 / wlsq + 0.0136 / ( wlsq * wlsq ) )
     721          * 273.15 / 1013.25 ) * 1e-6
     722        :
     723        77.624e-6;
     724    gamal = gb * dmd / gcr;
     725    gamma = gamal / alpha;
     726    gamm2 = gamma - 2.0;
     727    delm2 = delta - 2.0;
     728    tdc = tdkok - 273.15;
     729    psat = pow ( 10.0, ( 0.7859 + 0.03477 * tdc ) /
     730                 ( 1.0 + 0.00412 * tdc ) ) *
     731           ( 1.0 + pmbok * ( 4.5e-6 + 6e-10 * tdc * tdc ) );
     732    pwo = ( pmbok > 0.0 ) ?
     733          rhok * psat / ( 1.0 - ( 1.0 - rhok ) * psat / pmbok ) :
     734          0.0;
     735    w = pwo * ( 1.0 - dmw / dmd ) * gamma / ( delta - gamma );
     736    c1 = a * ( pmbok + w ) / tdkok;
     737    c2 = ( a * w + ( optic ? 11.2684e-6 : 12.92e-6 ) * pwo ) / tdkok;
     738    c3 = ( gamma - 1.0 ) * alpha * c1 / tdkok;
     739    c4 = ( delta - 1.0 ) * alpha * c2 / tdkok;
     740    c5 = optic ? 0.0 : 371897e-6 * pwo / tdkok;
     741    c6 = c5 * delm2 * alpha / ( tdkok * tdkok );
     742
     743    /* Conditions at the observer */
     744    robs = s + hmok;
     745    atmt ( robs, tdkok, alpha, gamm2, delm2, c1, c2, c3, c4, c5, c6, robs,
     746           &tempo, &dn0, &rdndr0 );
     747    sk0 = dn0 * robs * sin ( zobs2 );
     748    f0 = refi ( robs, dn0, rdndr0 );
     749
     750    /* Conditions at the tropopause in the troposphere */
     751    rt = s + ht;
     752    atmt ( robs, tdkok, alpha, gamm2, delm2, c1, c2, c3, c4, c5, c6, rt,
     753           &tt, &dnt, &rdndrt );
     754    zt = asin ( sk0 / ( rt * dnt ) );
     755    ft = refi ( rt, dnt, rdndrt );
     756
     757    /* Conditions at the tropopause in the stratosphere */
     758    atms ( rt, tt, dnt, gamal, rt, &dnts, &rdndrp );
     759    zts = asin ( sk0 / ( rt * dnts ) );
     760    fts = refi ( rt, dnts, rdndrp );
     761
     762    /* At the stratosphere limit */
     763    rs = s + hs;
     764    atms ( rt, tt, dnt, gamal, rs, &dns, &rdndrs );
     765    zs = asin ( sk0 / ( rs * dns ) );
     766    fs = refi ( rs, dns, rdndrs );
     767
     768    /*
     769    ** Integrate the refraction integral in two parts;  first in the
     770    ** troposphere (k=1), then in the stratosphere (k=2).
     771    */
     772
     773    /* Initialize previous refraction to ensure at least two iterations */
     774    refold = 1e6;
     775
     776    /*
     777    ** Start off with 8 strips for the troposphere integration, and then
     778    ** use the final troposphere value for the stratosphere integration,
     779    ** which tends to need more strips.
     780    */
     781    is = 8;
     782
     783    /* Troposphere then stratosphere */
     784    for ( k = 1; k <= 2; k++ ) {
     785
     786        /* Start z, z range, and start and end values */
     787        if ( k == 1 ) {
     788            z0 = zobs2;
     789            zrange = zt - z0;
     790            fb = f0;
     791            ff = ft;
     792        } else {
     793            z0 = zts;
     794            zrange = zs - z0;
     795            fb = fts;
     796            ff = fs;
     797        }
     798
     799        /* Sums of odd and even values */
     800        fo = 0.0;
     801        fe = 0.0;
     802
     803        /* First time through loop we have to do every point */
     804        n = 1;
     805
     806        /* Start of iteration loop (terminates at specified precision) */
     807        for ( ; ; ) {
     808
     809            /* Strip width */
     810            h = zrange / (double) is;
     811
     812            /* Initialize distance from Earth centre for quadrature pass */
     813            r = ( k == 1 ) ? robs : rt;
     814
     815            /* One pass (no need to compute evens after first time) */
     816            for ( i = 1; i < is; i += n ) {
     817
     818                /* Sine of observed zenith distance */
     819                sz = sin ( z0 + h * (double) i );
     820
     821                /* Find r (to nearest metre, maximum four iterations) */
     822                if ( sz > 1e-20 ) {
     823                    w = sk0 / sz;
     824                    rg = r;
     825                    j = 0;
     826                    do {
     827                        if ( k == 1 ) {
     828                            atmt ( robs, tdkok, alpha, gamm2, delm2,
     829                                   c1, c2, c3, c4, c5, c6, rg,
     830                                   &tg, &dn, &rdndr );
     831                        } else {
     832                            atms ( rt, tt, dnt, gamal, rg, &dn, &rdndr );
     833                        }
     834                        dr = ( rg * dn - w ) / ( dn + rdndr );
     835                        rg -= dr;
     836                    } while ( fabs ( dr ) > 1.0 && j++ <= 4 );
     837                    r = rg;
     838                }
     839
     840                /* Find refractive index and integrand at r */
     841                if ( k == 1 ) {
     842                    atmt ( robs, tdkok, alpha, gamm2, delm2,
     843                           c1, c2, c3, c4, c5, c6, r,
     844                           &t, &dn, &rdndr );
     845                } else {
     846                    atms ( rt, tt, dnt, gamal, r, &dn, &rdndr );
     847                }
     848                f = refi ( r, dn, rdndr );
     849
     850                /* Accumulate odd and (first time only) even values */
     851                if ( n == 1 && i % 2 == 0 ) {
     852                    fe += f;
     853                } else {
     854                    fo += f;
     855                }
     856            }
     857
     858            /* Evaluate the integrand using Simpson's Rule */
     859            refp = h * ( fb + 4.0 * fo + 2.0 * fe + ff ) / 3.0;
     860
     861            /* Has the required precision been reached? */
     862            if ( fabs ( refp - refold ) > tol ) {
     863
     864                /* No: prepare for next iteration */
     865                refold = refp;   /* Save current value for convergence test */
     866                is += is;        /* Double the number of strips */
     867                fe += fo;        /* Sum of all = sum of evens next time */
     868                fo = 0.0;        /* Reset odds accumulator */
     869                n = 2;           /* Skip even values next time */
     870
     871            } else {
     872
     873                /* Yes: save troposphere component and terminate loop */
     874                if ( k == 1 ) reft = refp;
     875                break;
     876            }
     877        }
     878    }
     879
     880    /* Result */
     881    *ref = reft + refp;
     882    if ( zobs1 < 0.0 ) *ref = - ( *ref );
     883
     884    *ref+=Pidiv2-zobs1;
     885}
     886
     887/*--------------------------------------------------------------------------*/
     888
     889void Astro::atmt ( double robs, double tdkok, double alpha, double gamm2,
     890                   double delm2, double c1, double c2, double c3,
     891                   double c4, double c5, double c6, double r,
     892                   double *t, double *dn, double *rdndr )
     893/*
     894**  - - - - -
     895**   a t m t
     896**  - - - - -
     897**
     898**  Internal routine used by slaRefro:
     899**
     900**    refractive index and derivative with respect to height for the
     901**    troposphere.
     902**
     903**  Given:
     904**    robs    double   height of observer from centre of the Earth (metre)
     905**    tdkok   double   temperature at the observer (deg K)
     906**    alpha   double   alpha          )
     907**    gamm2   double   gamma minus 2  ) see ES
     908**    delm2   double   delta minus 2  )
     909**    c1      double   useful term  )
     910**    c2      double   useful term  )
     911**    c3      double   useful term  ) see source of
     912**    c4      double   useful term  ) slaRefro main routine
     913**    c5      double   useful term  )
     914**    c6      double   useful term  )
     915**    r       double   current distance from the centre of the Earth (metre)
     916**
     917**  Returned:
     918**    *t      double   temperature at r (deg K)
     919**    *dn     double   refractive index at r
     920**    *rdndr  double   r * rate the refractive index is changing at r
     921**
     922**  This routine is derived from the ATMOSTRO routine (C.Hohenkerk,
     923**  HMNAO), with enhancements specified by A.T.Sinclair (RGO) to
     924**  handle the radio case.
     925**
     926**  Note that in the optical case c5 and c6 are zero.
     927*/
     928{
     929    double w, tt0, tt0gm2, tt0dm2;
     930
     931    w = tdkok - alpha * ( r - robs );
     932    w = gmin ( w, 320.0 );
     933    w = gmax ( w, 200.0 );
     934    tt0 = w / tdkok;
     935    tt0gm2 = pow ( tt0, gamm2 );
     936    tt0dm2 = pow ( tt0, delm2 );
     937    *t = w;
     938    *dn = 1.0 + ( c1 * tt0gm2 - ( c2 - c5 / w ) * tt0dm2 ) * tt0;
     939    *rdndr = r * ( - c3 * tt0gm2 + ( c4 - c6 / tt0 ) * tt0dm2 );
     940}
     941
     942/*--------------------------------------------------------------------------*/
     943
     944void Astro::atms ( double rt, double tt, double dnt, double gamal, double r,
     945                   double *dn, double *rdndr )
     946/*
     947**  - - - - -
     948**   a t m s
     949**  - - - - -
     950**
     951**  Internal routine used by slaRefro:
     952**
     953**   refractive index and derivative with respect to height for the
     954**   stratosphere.
     955**
     956**  Given:
     957**    rt      double   height of tropopause from centre of the Earth (metre)
     958**    tt      double   temperature at the tropopause (deg k)
     959**    dnt     double   refractive index at the tropopause
     960**    gamal   double   constant of the atmospheric model = g*md/r
     961**    r       double   current distance from the centre of the Earth (metre)
     962**
     963**  Returned:
     964**    *dn     double   refractive index at r
     965**    *rdndr  double   r * rate the refractive index is changing at r
     966**
     967**  This routine is derived from the ATMOSSTR routine (C.Hohenkerk, HMNAO).
     968*/
     969{
     970    double b, w;
     971
     972    b = gamal / tt;
     973    w = ( dnt - 1.0 ) * exp ( - b * ( r - rt ) );
     974    *dn = 1.0 + w;
     975    *rdndr = - r * b * w;
     976}
     977
     978
     979
Note: See TracChangeset for help on using the changeset viewer.