Changeset 504 for BAORadio/libindi/libindi/drivers/telescope/astro.cpp
- Timestamp:
- Jun 28, 2011, 12:31:47 PM (13 years ago)
- 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 1 10 #include "astro.h" 2 11 3 //////////////////// 4 // Routines Astro // 5 //////////////////// 12 13 /************************************************************************************** 14 ** Constructeur de la classe Astro 15 ***************************************************************************************/ 6 16 7 17 Astro::Astro() 8 18 { 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; 25 42 } 26 43 … … 30 47 31 48 32 //0<=Angle<=2*PI 49 /************************************************************************************** 50 ** Initialisation des variables globales de la classe Astro 51 ***************************************************************************************/ 52 53 void 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 63 void Astro::DefinirPressionTemp(double P, double T) 64 { 65 Pression = P; 66 Temp = T; 67 } 68 69 void 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 33 82 double Astro::VerifAngle(double Angle) 34 83 { … … 40 89 41 90 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 98 double 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 123 string 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 164 double 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 201 void 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 ***************************************************************************************/ 44 224 45 225 void Astro::Nutation() 46 226 { 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 278 void 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 293 void 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 323 void 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 340 void 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 ***************************************************************************************/ 92 358 93 359 void Astro::CalculJJ(double Heure) 94 360 { 95 361 JJ = CalculJJ(Annee, Mois, Jour, Heure); 362 } 363 364 double Astro::CalculJJ(double A, double M, double J, double Heure) 365 { 96 366 long y, a, b, c, e, m; 97 367 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; 102 371 103 372 y = year + 4800; … … 131 400 132 401 gregor: 133 b = (a /4) - a;402 b = (a / 4) - a; 134 403 } 135 404 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 ***************************************************************************************/ 156 415 157 416 double Astro::TSL(double JJ, double HeureSiderale, double Longitude) 158 417 { 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; 163 420 rd += HeureSiderale; 164 421 rd *= Pi2; 422 // temps sidéral apparent 165 423 rd += CorP * cos(ep); 166 424 rd -= Longitude; … … 170 428 171 429 430 /************************************************************************************** 431 ** routine principale des calculs 432 ***************************************************************************************/ 433 172 434 void Astro::CalculTSL() 173 435 { 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; 177 439 178 440 CalculJJ(hs); … … 182 444 Nutation(); 183 445 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 457 void 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 203 468 if (ac<0.0) az=Pi-az; 204 469 205 az=VerifAngle(Pi+az); 206 207 *azi=az; 470 *azi=VerifAngle(Pi+az); 208 471 *hau=ht; 209 472 } 210 473 474 475 /************************************************************************************** 476 ** réfraction atmosphérique : formule simple de Jean Meeus 477 ** la hauteur ht est exprimée en radians 478 ***************************************************************************************/ 479 480 double 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 519 double Astro::slaDrange(double angle ) 520 { 521 return fmod(angle, Pi); 522 } 523 524 float Astro::slaRange(float angle ) 525 { 526 return fmodf(angle, Pi); 527 } 528 529 void 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 889 void 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 944 void 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.