1 /* $Id: utility.c,v 1.18 2009-04-20 07:17:00 pkubanek Exp $ 2 ** 3 * Copyright (C) 1999, 2000 Juan Carlos Remis 4 * Copyright (C) 2002 Liam Girdwood <lgirdwood@gmail.com> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 * 20 */ 21 22 /*------------------------------------------------------------------------*/ 23 /* */ 24 /* Module: */ 25 /* */ 26 /* Description: */ 27 /* */ 28 /* */ 29 /* "CAVEAT UTILITOR". */ 30 /* */ 31 /* "Non sunt multiplicanda entia praeter necessitatem" */ 32 /* Guillermo de Occam. */ 33 /*------------------------------------------------------------------------*/ 34 /* Revision History: */ 35 /* */ 36 /*------------------------------------------------------------------------*/ 37 38 /**/ 39 module nova.utility; 40 41 import std.stdio; 42 import std.math; 43 44 import nova; 45 46 /* Golden ratio */ 47 enum GOLDEN = 1.61803398875; 48 49 enum DM_PI = (2*PI); 50 enum RADIAN = (180.0 / PI); 51 52 /* Conversion factors between degrees and radians */ 53 enum D2R = (1.7453292519943295769e-2); /* deg->radian */ 54 enum R2D = (5.7295779513082320877e1); /* radian->deg */ 55 enum R2S = (2.0626480624709635516e5); /* arc seconds per radian */ 56 enum S2R = (4.8481368110953599359e-6); /* radians per arc second */ 57 58 static string ln_version = LIBNOVA_VERSION; 59 60 extern (C) { 61 62 /** 63 * \fn double ln_rad_to_deg(double radians) 64 * \brief radians to degrees 65 * \ingroup conversion 66 * \param radians Angle in radians 67 * \return Angle in degrees 68 */ 69 static @nogc double ln_rad_to_deg(double radians) nothrow 70 { 71 return radians * R2D; 72 } 73 74 /** 75 * convert degrees to radians 76 * \ingroup conversion 77 * \param degrees Angle in degrees 78 * \return Angle in radians 79 */ 80 static @nogc double ln_deg_to_rad(double degrees) nothrow 81 { 82 return degrees * D2R; 83 } 84 85 /*! \fn char * ln_get_version (void) 86 * \return Null terminated version string. 87 * 88 * Return the libnova library version number string 89 * e.g. "0.4.0" 90 */ 91 @nogc string ln_get_version() nothrow 92 { 93 return ln_version; 94 } 95 96 /* convert hours:mins:secs to degrees */ 97 @nogc double ln_hms_to_deg(const ref ln_hms hms) nothrow 98 { 99 double degrees; 100 101 degrees = (cast(double)hms.hours / 24.0) * 360.0; 102 degrees += (cast(double)hms.minutes / 60.0) * 15.0; 103 degrees += (cast(double)hms.seconds / 60.0) * 0.25; 104 105 return degrees; 106 } 107 108 /* convert hours:mins:secs to radians */ 109 @nogc double ln_hms_to_rad(const ref ln_hms hms) nothrow 110 { 111 double radians; 112 113 radians = (cast(double)hms.hours / 24.0) * 2.0 * PI; 114 radians += (cast(double)hms.minutes / 60.0) * 2.0 * PI / 24.0; 115 radians += (cast(double)hms.seconds / 60.0) * 2.0 * PI / 1440.0; 116 117 return radians; 118 } 119 120 121 /* convert degrees to hh:mm:ss */ 122 @nogc void ln_deg_to_hms (double degrees, ref ln_hms hms) nothrow 123 { 124 double dtemp; 125 126 degrees = ln_range_degrees(degrees); 127 128 /* divide degrees by 15 to get the hours */ 129 dtemp = degrees / 15.0; 130 hms.hours = cast(ushort)dtemp; 131 132 /* multiply remainder by 60 to get minutes */ 133 dtemp = 60.0 * (dtemp - hms.hours); 134 hms.minutes = cast(ushort)dtemp; 135 136 /* multiply remainder by 60 to get seconds */ 137 hms.seconds = 60.0 * (dtemp - hms.minutes); 138 139 /* catch any overflows */ 140 if (hms.seconds > 59) { 141 hms.seconds = 0.0; 142 hms.minutes ++; 143 } 144 if (hms.minutes > 59) { 145 hms.minutes = 0; 146 hms.hours ++; 147 } 148 } 149 150 /* convert radians to hh:mm:ss */ 151 @nogc void ln_rad_to_hms (double radians, ref ln_hms hms) nothrow 152 { 153 double degrees; 154 155 radians = ln_range_radians(radians); 156 degrees = ln_rad_to_deg(radians); 157 158 ln_deg_to_hms(degrees, hms); 159 } 160 161 162 /* convert dms to degrees */ 163 @nogc double ln_dms_to_deg(const ref ln_dms dms) nothrow 164 { 165 double degrees; 166 167 degrees = fabs(cast(double)dms.degrees); 168 degrees += fabs(cast(double)dms.minutes / 60.0); 169 degrees += fabs(cast(double)dms.seconds / 3600.0); 170 171 // negative ? 172 if (dms.neg) 173 degrees *= -1.0; 174 175 return degrees; 176 } 177 178 /* convert dms to radians */ 179 @nogc double ln_dms_to_rad(const ref ln_dms dms) nothrow 180 { 181 double radians; 182 183 radians = fabs(cast(double)dms.degrees / 360.0 * 2.0 * PI); 184 radians += fabs(cast(double)dms.minutes / 21600.0 * 2.0 * PI); 185 radians += fabs(cast(double)dms.seconds / 1296000.0 * 2.0 * PI); 186 187 // negative ? 188 if (dms.neg) 189 radians *= -1.0; 190 191 return radians; 192 } 193 194 /* convert degrees to dms */ 195 @nogc void ln_deg_to_dms (double degrees, ref ln_dms dms) nothrow 196 { 197 double dtemp; 198 199 if (degrees >= 0.0) 200 dms.neg = 0; 201 else 202 dms.neg = 1; 203 204 degrees = fabs(degrees); 205 dms.degrees = cast(ushort)degrees; 206 207 /* multiply remainder by 60 to get minutes */ 208 dtemp = 60.0 * (degrees - dms.degrees); 209 dms.minutes = cast(ushort)dtemp; 210 211 /* multiply remainder by 60 to get seconds */ 212 dms.seconds = 60.0 * (dtemp - dms.minutes); 213 214 /* catch any overflows */ 215 if (dms.seconds > 59) { 216 dms.seconds = 0.0; 217 dms.minutes ++; 218 } 219 if (dms.minutes > 59) { 220 dms.minutes = 0; 221 dms.degrees ++; 222 } 223 } 224 225 /* convert radians to dms */ 226 @nogc void ln_rad_to_dms (double radians, ref ln_dms dms) nothrow 227 { 228 double degrees = ln_rad_to_deg(radians); 229 230 ln_deg_to_dms(degrees, dms); 231 } 232 233 234 /* puts a large angle in the correct range 0 - 360 degrees */ 235 @nogc double ln_range_degrees(double angle) nothrow 236 { 237 double temp; 238 239 if (angle >= 0.0 && angle < 360.0) 240 return angle; 241 242 temp = cast(int)(angle / 360); 243 if (angle < 0.0) 244 temp --; 245 temp *= 360.0; 246 return angle - temp; 247 } 248 249 /* puts a large angle in the correct range 0 - 2PI radians */ 250 @nogc double ln_range_radians(double angle) nothrow 251 { 252 double temp; 253 254 if (angle >= 0.0 && angle < (2.0 * PI)) 255 return angle; 256 257 temp = cast(int)(angle / (PI * 2.0)); 258 259 if (angle < 0.0) 260 temp --; 261 temp *= (PI * 2.0); 262 return angle - temp; 263 } 264 265 /* puts a large angle in the correct range -2PI - 2PI radians */ 266 /* preserve sign */ 267 @nogc double ln_range_radians2(double angle) nothrow 268 { 269 double temp; 270 271 if (angle > (-2.0 * PI) && angle < (2.0 * PI)) 272 return angle; 273 274 temp = cast(int)(angle / (PI * 2.0)); 275 temp *= (PI * 2.0); 276 return angle - temp; 277 } 278 279 280 /* add seconds to hms */ 281 @nogc void ln_add_secs_hms(ref ln_hms hms, double seconds) nothrow 282 { 283 ln_hms source_hms; 284 285 /* breaks double seconds int hms */ 286 source_hms.hours = cast(ushort)(seconds / 3600); 287 seconds -= source_hms.hours * 3600; 288 source_hms.minutes = cast(ushort)(seconds / 60); 289 seconds -= source_hms.minutes * 60; 290 source_hms.seconds = seconds; 291 292 /* add hms to hms */ 293 ln_add_hms(source_hms, hms); 294 } 295 296 297 /* add hms to hms */ 298 @nogc void ln_add_hms(const ref ln_hms source, ref ln_hms dest) nothrow 299 { 300 dest.seconds += source.seconds; 301 if (dest.seconds >= 60.0) { 302 /* carry */ 303 dest.minutes ++; 304 dest.seconds -= 60.0; 305 } else { 306 if (dest.seconds < 0.0) { 307 /* carry */ 308 dest.minutes --; 309 dest.seconds += 60.0; 310 } 311 } 312 313 dest.minutes += source.minutes; 314 if (dest.minutes >= 60) { 315 /* carry */ 316 dest.hours ++; 317 dest.minutes -= 60; 318 } else { 319 if (dest.seconds < 0.0) { 320 /* carry */ 321 dest.hours --; 322 dest.minutes += 60; 323 } 324 } 325 326 dest.hours += source.hours; 327 } 328 329 /*! \fn void ln_hequ_to_equ(struct lnh_equ_posn *hpos, struct ln_equ_posn *pos) 330 * \brief human readable equatorial position to double equatorial position 331 * \ingroup conversion 332 */ 333 @nogc void ln_hequ_to_equ(const ref lnh_equ_posn hpos, ref ln_equ_posn pos) nothrow 334 { 335 pos.ra = ln_hms_to_deg(hpos.ra); 336 pos.dec = ln_dms_to_deg(hpos.dec); 337 } 338 339 /*! \fn void ln_equ_to_hequ(struct ln_equ_posn *pos, struct lnh_equ_posn *hpos) 340 * \brief human double equatorial position to human readable equatorial position 341 * \ingroup conversion 342 */ 343 @nogc void ln_equ_to_hequ(const ref ln_equ_posn pos, ref lnh_equ_posn hpos) nothrow 344 { 345 ln_deg_to_hms(pos.ra, hpos.ra); 346 ln_deg_to_dms(pos.dec, hpos.dec); 347 } 348 349 /*! \fn void ln_hhrz_to_hrz(struct lnh_hrz_posn *hpos, struct ln_hrz_posn *pos) 350 * \brief human readable horizontal position to double horizontal position 351 * \ingroup conversion 352 */ 353 @nogc void ln_hhrz_to_hrz(const ref lnh_hrz_posn hpos, ref ln_hrz_posn pos) nothrow 354 { 355 pos.alt = ln_dms_to_deg(hpos.alt); 356 pos.az = ln_dms_to_deg(hpos.az); 357 } 358 359 /*! \fn void ln_hrz_to_hhrz(struct ln_hrz_posn *pos, struct lnh_hrz_posn *hpos) 360 * \brief double horizontal position to human readable horizontal position 361 * \ingroup conversion 362 */ 363 @nogc void ln_hrz_to_hhrz(const ref ln_hrz_posn pos, ref lnh_hrz_posn hpos) nothrow 364 { 365 ln_deg_to_dms(pos.alt, hpos.alt); 366 ln_deg_to_dms(pos.az, hpos.az); 367 } 368 369 /*! \fn const char * ln_hrz_to_nswe(struct ln_hrz_posn *pos); 370 * \brief returns direction of given azimuth - like N,S,W,E,NSW,... 371 * \ingroup conversion 372 */ 373 @nogc string ln_hrz_to_nswe(const ref ln_hrz_posn pos) nothrow 374 { 375 static immutable directions = 376 ["S", "SSW", "SW", "SWW", "W", "NWW", "NW", "NNW", 377 "N", "NNE", "NE", "NEE", "E", "SEE", "SE", "SSE"]; 378 379 return directions[cast(int)(pos.az / 22.5)]; 380 } 381 382 /*! \fn void ln_hlnlat_to_lnlat(struct lnh_lnlat_posn *hpos, struct ln_lnlat_posn *pos) 383 * \brief human readable long/lat position to double long/lat position 384 * \ingroup conversion 385 */ 386 @nogc void ln_hlnlat_to_lnlat(const ref lnh_lnlat_posn hpos, ref ln_lnlat_posn pos) nothrow 387 { 388 pos.lng = ln_dms_to_deg(hpos.lng); 389 pos.lat = ln_dms_to_deg(hpos.lat); 390 } 391 392 /*! \fn void ln_lnlat_to_hlnlat(struct ln_lnlat_posn *pos, struct lnh_lnlat_posn *hpos) 393 * \brief double long/lat position to human readable long/lat position 394 * \ingroup conversion 395 */ 396 @nogc void ln_lnlat_to_hlnlat(const ref ln_lnlat_posn pos, ref lnh_lnlat_posn hpos) nothrow 397 { 398 ln_deg_to_dms(pos.lng, hpos.lng); 399 ln_deg_to_dms(pos.lat, hpos.lat); 400 } 401 402 /* 403 * \fn double ln_get_rect_distance(struct ln_rect_posn *a, struct ln_rect_posn *b) 404 * \param a First rectangular coordinate 405 * \param b Second rectangular coordinate 406 * \return Distance between a and b. 407 * 408 * Calculate the distance between rectangular points a and b. 409 */ 410 @nogc double ln_get_rect_distance(const ref ln_rect_posn a, const ref ln_rect_posn b) nothrow 411 { 412 double x,y,z; 413 414 x = a.X - b.X; 415 y = a.Y - b.Y; 416 z = a.Z - b.Z; 417 418 x *= x; 419 y *= y; 420 z *= z; 421 422 return sqrt(x + y + z); 423 } 424 425 /* 426 * \fn double ln_get_light_time (double dist) 427 * \param dist Distance in AU 428 * \return Distance in light days. 429 * 430 * Convert units of AU into light days. 431 */ 432 @nogc double ln_get_light_time(double dist) nothrow 433 { 434 return dist * 0.005775183; 435 } 436 437 438 /* local types and macros */ 439 // typedef int BOOL; 440 // #define TRUE 1 441 // #define FALSE 0 442 // #define iswhite(c) ((c)== ' ' || (c)=='\t') 443 444 /* 445 []------------------------------------------------------------------------[] 446 | trim() & strip() | 447 | | 448 | strips trailing whitespaces from buf. | 449 | | 450 []------------------------------------------------------------------------[] 451 */ 452 /* 453 static char *trim(char *x) 454 { 455 char *y; 456 457 if(!x) 458 return(x); 459 y = x + strlen(x)-1; 460 while (y >= x && isspace(*y)) 461 *y-- = 0; // skip white space 462 return x; 463 } 464 */ 465 466 467 /* 468 []------------------------------------------------------------------------[] 469 | | 470 | skipwhite() | 471 | salta espacios en blanco | 472 | | 473 []------------------------------------------------------------------------[] 474 */ 475 /* 476 static void skipwhite(char **s) 477 { 478 while (iswhite(**s)) 479 (*s)++; 480 } 481 */ 482 483 484 /*! \fn double ln_get_dec_location(const char * s) 485 * \param s Location string 486 * \return angle in degrees 487 * 488 * Obtains Latitude, Longitude, RA or Declination from a string. 489 * 490 * If the last char is N/S doesn't accept more than 90 degrees. 491 * If it is E/W doesn't accept more than 180 degrees. 492 * If they are hours don't accept more than 24:00 493 * 494 * Any position can be expressed as follows: 495 * (please use a 8 bits charset if you want 496 * to view the degrees separator char '0xba') 497 * 498 * 42.30.35,53 499 * 90º0'0,01 W 500 * 42º30'35.53 N 501 * 42º30'35.53S 502 * 42º30'N 503 * -42.30.35.53 504 * 42:30:35.53 S 505 * +42.30.35.53 506 * +42º30 35,53 507 * 23h36'45,0 508 * 509 * 510 * 42:30:35.53 S = -42º30'35.53" 511 * + 42 30.35.53 S the same previous position, the plus (+) sign is 512 * considered like an error, the last 'S' has precedence over the sign 513 * 514 * 90º0'0,01 N ERROR: +- 90º0'00.00" latitude limit 515 * 516 */ 517 /* 518 double ln_get_dec_location(const char *s) 519 { 520 char *ptr, *dec, *hh, *ame, *tok_ptr; 521 BOOL negative = FALSE; 522 char delim1[] = " :.,;DdHhMm'\n\t"; 523 char delim2[] = " NSEWnsew\"\n\t"; 524 int dghh = 0, minutes = 0; 525 double seconds = 0.0, pos; 526 short count; 527 enum { 528 HOURS, DEGREES, LAT, LONG 529 } type; 530 531 if (s == NULL || !*s) 532 return -0.0; 533 534 count = strlen(s) + 1; 535 if ((ptr = (char *) alloca(count)) == NULL) 536 return -0.0; 537 538 memcpy(ptr, s, count); 539 trim(ptr); 540 skipwhite(&ptr); 541 if (*ptr == '+' || *ptr == '-') 542 negative = (char) (*ptr++ == '-' ? TRUE : negative); 543 544 // the last letter has precedence over the sign 545 if (strpbrk(ptr,"SsWw") != NULL) 546 negative = TRUE; 547 548 skipwhite(&ptr); 549 if ((hh = strpbrk(ptr,"Hh")) != NULL && hh < ptr + 3) { 550 type = HOURS; 551 if (negative) // if RA no negative numbers 552 negative = FALSE; 553 } else if ((ame = strpbrk(ptr,"SsNn")) != NULL) { 554 type = LAT; 555 if (ame == ptr) // the North/South found before data 556 ptr++; 557 } else 558 type = DEGREES; // unspecified, the caller must control it 559 if ((ptr = strtok_r(ptr,delim1, &tok_ptr)) != NULL) 560 dghh = atoi (ptr); 561 else 562 return (-0.0); 563 if ((ptr = strtok_r(NULL,delim1, &tok_ptr)) != NULL) { 564 minutes = atoi (ptr); 565 if (minutes > 59) 566 return -0.0; 567 } else 568 return -0.0; 569 570 if ((ptr = strtok_r(NULL,delim2,&tok_ptr)) != NULL) { 571 if ((dec = strchr(ptr,',')) != NULL) 572 *dec = '.'; 573 seconds = strtod (ptr, NULL); 574 if (seconds >= 60.0) 575 return -0.0; 576 } 577 578 if ((ptr = strtok(NULL," \n\t")) != NULL) { 579 skipwhite(&ptr); 580 if (*ptr == 'S' || *ptr == 'W' || *ptr == 's' || *ptr == 'w') 581 negative = TRUE; 582 } 583 584 pos = dghh + minutes /60.0 + seconds / 3600.0; 585 if (type == HOURS && pos > 24.0) 586 return -0.0; 587 if (type == LAT && pos > 90.0) 588 return -0.0; 589 if (negative == TRUE) 590 pos = 0.0 - pos; 591 592 return pos; 593 } 594 */ 595 596 597 /*! \fn const char * ln_get_humanr_location(double location) 598 * \param location Location angle in degress 599 * \return Angle string 600 * 601 * Obtains a human readable location in the form: ddºmm'ss.ss" 602 * String must be freed after use. 603 */ 604 @nogc string ln_get_humanr_location(real location) nothrow 605 { 606 import std.format; 607 608 real deg, min, sec; 609 610 sec = fabs(60.0 * (modf(location, deg))); 611 sec = 60.0 * (modf(sec, min)); 612 // TODO // return format("%+dº%d'%.2f\"",cast(int)deg, cast(int) min, sec); 613 return "TODO"; 614 } 615 616 @nogc double ln_interpolate3(double n, double y1, double y2, double y3) nothrow 617 { 618 double y, a, b, c; 619 620 /* equ 3.2 */ 621 a = y2 - y1; 622 b = y3 - y2; 623 c = b - a; 624 625 /* equ 3.3 */ 626 y = y2 + n / 2.0 * (a + b + n * c); 627 628 return y; 629 } 630 631 632 /*! \fn double ln_interpolate5 (double n, double y1, double y2, double y3, double y4, double y5) 633 * \return interpolation value 634 * \param n Interpolation factor 635 * \param y1 Argument 1 636 * \param y2 Argument 2 637 * \param y3 Argument 3 638 * \param y4 Argument 4 639 * \param y5 Argument 5 640 * 641 * Calculate an intermediate value of the 5 arguments for the given interpolation 642 * factor. 643 */ 644 @nogc double ln_interpolate5(double n, double y1, double y2, double y3, 645 double y4, double y5) nothrow 646 { 647 double y, A, B, C, D, E, F, G, H, J, K; 648 double n2, n3, n4; 649 650 /* equ 3.8 */ 651 A = y2 - y1; 652 B = y3 - y2; 653 C = y4 - y3; 654 D = y5 - y4; 655 E = B - A; 656 F = C - B; 657 G = D - C; 658 H = F - E; 659 J = G - F; 660 K = J - H; 661 662 y = 0.0; 663 n2 = n* n; 664 n3 = n2 * n; 665 n4 = n3 * n; 666 667 y += y3; 668 y += n * ((B + C ) / 2.0 - (H + J) / 12.0); 669 y += n2 * (F / 2.0 - K / 24.0); 670 y += n3 * ((H + J) / 12.0); 671 y += n4 * (K / 24.0); 672 673 return y; 674 } 675 676 677 /*! \fn double ln_find_zero(double (*f) (double, double *), double from, double to, double *arg) 678 * \param f Function to find zero (root place) 679 * \param from Lower bound of search interval 680 * \param to Upper bound of search interval 681 * \param arg Pointer to the other parameters of the function f 682 * 683 * Find zero of function f() at given interval by Newton method. 684 * 685 */ 686 @nogc alias find_zero_t = double function(double, ref double) nothrow; 687 688 @nogc double ln_find_zero(find_zero_t func, 689 double from, double to, ref double arg) nothrow 690 { 691 double x, x1, x2, f; 692 int i = 0; 693 694 x1 = to; 695 x = from; 696 697 do { 698 f = func(x1, arg); 699 x2 = x1 - f * (x1 - x) / (f - func(x, arg)); 700 x = x1; 701 x1 = x2; 702 } while ((fabs(x - x1) > 1e-6) && (i++ < 1000)); 703 704 return x2; 705 } 706 707 /*! \fn double ln_find_max(double (*f) (double, double *), double from, double to, double *arg) 708 * \param f Function to find maximum 709 * \param from Lower bound of search interval 710 * \param to Upper bound of search interval 711 * \param arg Pointer to the other parameters of the function f 712 * 713 * Find local maximum of function f() at given interval by Golden Section method. 714 * 715 */ 716 @nogc double ln_find_max(find_zero_t func, 717 double from, double to, ref double arg) nothrow 718 { 719 double a, b, xl, xu, eps; 720 721 a = from; 722 b = to; 723 xl = b - (b - a) / GOLDEN; 724 xu = a + (b - a) / GOLDEN; 725 eps = fabs(b - a); 726 727 do { 728 if (func(xl, arg) > func(xu, arg)) { 729 b = xu; 730 xu = xl; 731 xl = b - (b - a) / GOLDEN; 732 } else { 733 a = xl; 734 xl = xu; 735 xu = a + (b - a) / GOLDEN; 736 } 737 eps = fabs(b - a); 738 739 } while (eps > 1e-6); 740 741 return (xu + xl) * 0.5; 742 } 743 744 }