001 package edu.nrao.sss.astronomy; 002 003 import java.math.BigDecimal; 004 import java.util.Date; 005 006 import javax.xml.bind.annotation.XmlElement; 007 import javax.xml.bind.annotation.XmlRootElement; 008 import javax.xml.bind.annotation.XmlTransient; 009 010 import edu.nrao.sss.math.MathUtil; 011 import edu.nrao.sss.math.Polynomial; 012 import edu.nrao.sss.measure.AngularVelocityUnits; 013 import edu.nrao.sss.measure.Distance; 014 import edu.nrao.sss.measure.DistanceUnits; 015 import edu.nrao.sss.measure.Latitude; 016 import edu.nrao.sss.measure.Longitude; 017 import edu.nrao.sss.measure.LinearVelocityUnits; 018 import edu.nrao.sss.measure.TimeDuration; 019 import edu.nrao.sss.measure.TimeInterval; 020 import edu.nrao.sss.measure.TimeUnits; 021 022 /** 023 * A position of an astronomical source that is described by a collection 024 * of polynomial equations. 025 * <p> 026 * The viewpoint of this class is that the position of a source may be described 027 * by its location (right ascension, declination, and distance) at a particular 028 * reference time and a set of equations that describe the motions of the source 029 * away from that location.</p> 030 * <p> 031 * <b>Version Info:</b> 032 * <table style="margin-left:2em"> 033 * <tr><td>$Revision: 1707 $</td></tr> 034 * <tr><td>$Date: 2008-11-14 10:23:59 -0700 (Fri, 14 Nov 2008) $</td></tr> 035 * <tr><td>$Author: dharland $</td></tr> 036 * </table></p> 037 * 038 * @author David M. Harland 039 * @since 2006-03-24 040 */ 041 @XmlRootElement 042 public class PolynomialPosition 043 extends AbsSkyPos 044 implements Comparable<PolynomialPosition> 045 { 046 private static final long DEFAULT_TIME = 0L; 047 048 //Reference properties 049 private Date referenceTime; 050 private TimeInterval validTime; 051 052 //Position at time zero 053 private Longitude longitudeAtTimeZero; 054 private Latitude latitudeAtTimeZero; 055 private Distance distanceAtTimeZero; 056 057 //Position movements 058 private Polynomial longitudeMotion; 059 private Polynomial latitudeMotion; 060 private Polynomial radialMotion; 061 private AngularVelocityUnits longitudeVelocityUnits; 062 private AngularVelocityUnits latitudeVelocityUnits; 063 private LinearVelocityUnits radialVelocityUnits; 064 065 //Uncertainties 066 private Longitude longitudeUncertainty; 067 private Latitude latitudeUncertainty; 068 private Distance distanceUncertainty; 069 070 /** Creates a new instance. */ 071 public PolynomialPosition() 072 { 073 super(); 074 075 initialize(); 076 077 validTime = new TimeInterval(); 078 referenceTime = new Date(DEFAULT_TIME); 079 080 longitudeAtTimeZero = new Longitude(); 081 latitudeAtTimeZero = new Latitude(); 082 distanceAtTimeZero = new Distance(MathUtil.POSITIVE_INFINITY); 083 084 longitudeMotion = new Polynomial(); 085 latitudeMotion = new Polynomial(); 086 radialMotion = new Polynomial(); 087 088 longitudeUncertainty = new Longitude(); 089 latitudeUncertainty = new Latitude(); 090 distanceUncertainty = new Distance(); 091 } 092 093 private void initialize() 094 { 095 longitudeVelocityUnits = AngularVelocityUnits.getDefault(); 096 latitudeVelocityUnits = AngularVelocityUnits.getDefault(); 097 radialVelocityUnits = LinearVelocityUnits.KILOMETERS_PER_SECOND; 098 } 099 100 /** 101 * Resets this position to its initial state. 102 * A reset position has the same state as one created 103 * via the {@link #PolynomialPosition()} constructor. 104 */ 105 public void reset() 106 { 107 super.reset(); 108 109 initialize(); 110 111 validTime.reset(); 112 referenceTime.setTime(DEFAULT_TIME); 113 114 longitudeAtTimeZero.reset(); 115 latitudeAtTimeZero.reset(); 116 distanceAtTimeZero.set(MathUtil.POSITIVE_INFINITY, DistanceUnits.KILOMETER); 117 118 longitudeMotion.clear(); 119 latitudeMotion.clear(); 120 radialMotion.clear(); 121 122 longitudeUncertainty.reset(); 123 latitudeUncertainty.reset(); 124 distanceUncertainty.reset(); 125 } 126 127 /* (non-Javadoc) 128 * @see edu.nrao.sss.astronomy.SkyPosition#isMoving() 129 */ 130 public boolean isMoving() 131 { 132 //Return true if any of the motion variables have values 133 return longitudeMotion.getNumberOfTerms() > 0 || 134 latitudeMotion.getNumberOfTerms() > 0 || 135 radialMotion.getNumberOfTerms() > 0; 136 } 137 138 //=========================================================================== 139 // REFERENCE PROPERTIES 140 //=========================================================================== 141 142 /* (non-Javadoc) 143 * @see SkyPosition#getType() 144 */ 145 public SkyPositionType getType() { return SkyPositionType.POLYNOMIAL; } 146 147 /** 148 * Sets the time at which the delta-t terms in the motion polynomials 149 * evaluate to zero. 150 * <p> 151 * The motion polynomials are returned by the methods 152 * {@link #getLatitudeMotion()}, 153 * {@link #getLongitudeMotion()}, and 154 * {@link #getRadialMotion()}. 155 * At the reference time t<sub>0</sub>, these equations should 156 * evaluate to zero.</p> 157 * <p> 158 * If {@code referenceTime} is <i>null</i>, an 159 * {@code IllegalArgumentException} will be thrown.</p> 160 * 161 * @param referenceTime t<sub>0</sub> for the motion terms. 162 */ 163 public void setReferenceTime(Date referenceTime) 164 { 165 if (referenceTime == null) 166 throw new IllegalArgumentException("null is not a legal argument"); 167 168 this.referenceTime = referenceTime; 169 } 170 171 /** 172 * Returns the time at which the delta-t terms in the motion polynomials 173 * evaluate to zero. 174 * See {@link #setReferenceTime(Date)} for more information. 175 * 176 * @return t<sub>0</sub> for the motion terms. 177 */ 178 public Date getReferenceTime() 179 { 180 return referenceTime; 181 } 182 183 /** 184 * Sets the interval of time for which this position is valid. 185 * <p> 186 * If {@code interval} is <i>null</i>, it will be treated as 187 * an non-null interval of zero length.</p> 188 * 189 * @param interval the interval of time for which this position is valid. 190 */ 191 public void setValidTime(TimeInterval interval) 192 { 193 if (interval == null) 194 validTime.reset(); 195 else 196 validTime = interval; 197 } 198 199 /** 200 * Returns the interval of time for which this position is valid. 201 * 202 * @return the interval of time for which this position is valid. 203 */ 204 public TimeInterval getValidTime() 205 { 206 return validTime; 207 } 208 209 /** 210 * Returns <i>true</i> if this position is valid for 211 * the given point in time. 212 * 213 * @param time the point in time to be checked. 214 */ 215 public boolean isValidFor(Date time) 216 { 217 return validTime.contains(time); 218 } 219 220 //=========================================================================== 221 // MOTION POLYNOMIALS 222 //=========================================================================== 223 224 /** 225 * Returns the polynomial, in delta-t, for calculating change in the 226 * longitude of this position since the reference time. 227 * <p> 228 * The independent variable should be <b>delta-t</b>, with time measured in 229 * units determined by the value sent to 230 * {@link #setLongitudeVelocityUnits(AngularVelocityUnits)}.</p> 231 * <p> 232 * The polynomial returned is the one held by this source position, 233 * so any changes made to it will be reflected here. 234 * Note that this means that client objects need never use the {@code set} 235 * method. Instead, they may just use this method and manipulate the 236 * polynomial.</p> 237 * 238 * @return the equation for calculating the change in the 239 * longitude of this position since the reference time. 240 */ 241 public Polynomial getLongitudeMotion() 242 { 243 return longitudeMotion; 244 } 245 246 /** 247 * Sets this position's longitude motion equation to {@code longitudeEqn}. 248 * <p> 249 * See {@link #getLongitudeMotion()} for an explanation of the 250 * independent variable and its coefficients.</p> 251 * <p> 252 * This method will save a reference to {@code longitudeEqn}; it will 253 * not make and store a clone. This means that any changes made 254 * to {@code longitudeEqn} after calling this method will be reflected 255 * in this position. 256 * Note that it is not necessary to use this {@code set} method; clients 257 * may choose to interact only with the {@code get} method and manipulate 258 * the returned polynomial.</p> 259 * <p> 260 * If {@code longitudeEqn} is <i>null</i>, a new empty polynomial will be 261 * created and used for this motion.</p> 262 * 263 * @param longitudeEqn the longitude motion equation for this position. 264 */ 265 public void setLongitudeMotion(Polynomial longitudeEqn) 266 { 267 longitudeMotion = (longitudeEqn == null) ? new Polynomial() 268 : longitudeEqn; 269 } 270 271 /** 272 * Returns the polynomial, in delta-t, for calculating change in the 273 * latitude of this position since the reference time. 274 * <p> 275 * The independent variable should be <b>delta-t</b>, with time measured in 276 * units determined by the value sent to 277 * {@link #setLatitudeVelocityUnits(AngularVelocityUnits)}.</p> 278 * <p> 279 * The polynomial returned is the one held by this source position, 280 * so any changes made to it will be reflected here. 281 * Note that this means that client objects need never use the {@code set} 282 * method. Instead, they may just use this method and manipulate the 283 * polynomial.</p> 284 * 285 * @return the equation for calculating the change in the 286 * latitude of this position since the reference time. 287 */ 288 public Polynomial getLatitudeMotion() 289 { 290 return latitudeMotion; 291 } 292 293 /** 294 * Sets this position's latitude motion equation to {@code latitudeEqn}. 295 * <p> 296 * See {@link #getLatitudeMotion()} for an explanation of the 297 * independent variable and its coefficients.</p> 298 * <p> 299 * This method will save a reference to {@code latitudeEqn}; it will 300 * not make and store a clone. This means that any changes made 301 * to {@code latitudeEqn} after calling this method will be reflected 302 * in this position. 303 * Note that it is not necessary to use this {@code set} method; clients 304 * may choose to interact only with the {@code get} method and manipulate 305 * the returned polynomial.</p> 306 * <p> 307 * If {@code latitudeEqn} is <i>null</i>, a new empty polynomial will be 308 * created and used for this motion.</p> 309 * 310 * @param latitudeEqn the latitude motion equation for this position. 311 */ 312 public void setLatitudeMotion(Polynomial latitudeEqn) 313 { 314 latitudeMotion = (latitudeEqn == null) ? new Polynomial() 315 : latitudeEqn; 316 } 317 318 /** 319 * Returns the polynomial, in delta-t, for calculating change in the 320 * distance of this position since the reference time. 321 * <p> 322 * The independent variable should be <b>delta-t</b>, with time measured in 323 * units determined by the value sent to 324 * {@link #setRadialVelocityUnits(LinearVelocityUnits)}.</p> 325 * <p> 326 * The polynomial returned is the one held by this source position, 327 * so any changes made to it will be reflected here. 328 * Note that this means that client objects need never use the {@code set} 329 * method. Instead, they may just use this method and manipulate the 330 * polynomial.</p> 331 * 332 * @return the equation for calculating the change in the 333 * distance of this position since the reference time. 334 */ 335 public Polynomial getRadialMotion() 336 { 337 return radialMotion; 338 } 339 340 /** 341 * Sets this position's distance motion equation to {@code distanceEquation}. 342 * <p> 343 * See {@link #getRadialMotion()} for an explanation of the 344 * independent variable and its coefficients.</p> 345 * <p> 346 * This method will save a reference to {@code distanceEquation}; it will 347 * not make and store a clone. This means that any changes made 348 * to {@code distanceEquation} after calling this method will be reflected 349 * in this position. 350 * Note that it is not necessary to use this {@code set} method; clients 351 * may choose to interact only with the {@code get} method and manipulate 352 * the returned polynomial.</p> 353 * <p> 354 * If {@code distanceEquation} is <i>null</i>, a new empty polynomial will be 355 * created and used for this motion.</p> 356 * 357 * @param distanceEquation the distance motion equation for this position. 358 */ 359 public void setRadialMotion(Polynomial distanceEquation) 360 { 361 this.radialMotion = (distanceEquation == null) ? new Polynomial() 362 : distanceEquation; 363 } 364 365 //=========================================================================== 366 // UNITS OF VELOCITY 367 //=========================================================================== 368 369 /** 370 * Sets the units that will be used to interpret the 371 * longitude motion polynomial. 372 * The default value of this property is 373 * {@code AngularVelocityUnits.MILLI_ARC_SECONDS_PER_YEAR}. 374 * <p> 375 * This property will be used by the 376 * {@link #getLongitude(Date) getLongitude} methods. 377 * Those methods will assume that all terms 378 * will be based on the logical extension of these units.</p> 379 * 380 * @param units the units to use when interpreting the longitude 381 * motion polynomial. If {@code units} is <i>null</i>, 382 * this method will do nothing. 383 */ 384 public void setLongitudeVelocityUnits(AngularVelocityUnits units) 385 { 386 if (units != null) 387 longitudeVelocityUnits = units; 388 } 389 390 /** 391 * Sets the units that will be used to interpret the 392 * declination motion polynomial. 393 * The default value of this property is 394 * {@code AngularVelocityUnits.MILLI_ARC_SECONDS_PER_YEAR}. 395 * <p> 396 * This property will be used by the 397 * {@link #getLatitude(Date) getLatitude} methods. 398 * Those methods will assume that all terms 399 * will be based on the logical extension of these units.</p> 400 * 401 * @param units the units to use when interpreting the declination 402 * motion polynomial. If {@code units} is <i>null</i>, 403 * this method will do nothing. 404 */ 405 public void setLatitudeVelocityUnits(AngularVelocityUnits units) 406 { 407 if (units != null) 408 latitudeVelocityUnits = units; 409 } 410 411 /** 412 * Sets the units that will be used to interpret the 413 * distance motion polynomial. 414 * The default value of this property is 415 * {@code LinearVelocityUnits.KILOMETERS_PER_SECOND}. 416 * <p> 417 * This property will be used by the 418 * {@link #getDistance(Date) getDistance} methods. 419 * Those methods will assume that all terms 420 * will be based on the logical extension of these units.</p> 421 * 422 * @param units the units to use when interpreting the distance 423 * motion polynomial. If {@code units} is <i>null</i>, 424 * it will be replaced by a non-null default type. 425 */ 426 public void setRadialVelocityUnits(LinearVelocityUnits units) 427 { 428 if (units == null) 429 units = LinearVelocityUnits.getDefault(); 430 431 radialVelocityUnits = units; 432 } 433 434 /** 435 * Returns the units that will be used to interpret the 436 * the longitude motion polynomial. 437 * 438 * @see #setLongitudeVelocityUnits(AngularVelocityUnits) 439 */ 440 @XmlTransient 441 public AngularVelocityUnits getLongitudeVelocityUnits() 442 { 443 return longitudeVelocityUnits; 444 } 445 446 /** 447 * Returns the units that will be used to interpret the 448 * the latitude motion polynomial. 449 * 450 * @see #setLatitudeVelocityUnits(AngularVelocityUnits) 451 */ 452 @XmlTransient 453 public AngularVelocityUnits getLatitudeVelocityUnits() 454 { 455 return latitudeVelocityUnits; 456 } 457 458 /** 459 * Returns the units that will be used to interpret the 460 * the distance motion polynomial. 461 * 462 * @see #setRadialVelocityUnits(LinearVelocityUnits) 463 */ 464 @XmlTransient 465 public LinearVelocityUnits getRadialVelocityUnits() 466 { 467 return radialVelocityUnits; 468 } 469 470 //We'll use the shorter symbol for persistent storage 471 @XmlElement(name="longitudeVelocityUnits") 472 @SuppressWarnings("unused") 473 private String getLonVSymbol() { return getLongitudeVelocityUnits().getSymbol(); } 474 @SuppressWarnings("unused") 475 private void setLonVSymbol(String u) 476 { 477 setLongitudeVelocityUnits(AngularVelocityUnits.fromString(u)); 478 } 479 @XmlElement(name="latitudeVelocityUnits") 480 @SuppressWarnings("unused") 481 private String getLatVSymbol() { return getLatitudeVelocityUnits().getSymbol(); } 482 @SuppressWarnings("unused") 483 private void setLatVSymbol(String u) 484 { 485 setLatitudeVelocityUnits(AngularVelocityUnits.fromString(u)); 486 } 487 @XmlElement(name="radialVelocityUnits") 488 @SuppressWarnings("unused") 489 private String getRadialVSymbol() { return getRadialVelocityUnits().getSymbol(); } 490 @SuppressWarnings("unused") 491 private void setRadialVSymbol(String u) 492 { 493 setRadialVelocityUnits(LinearVelocityUnits.fromString(u)); 494 } 495 496 //=========================================================================== 497 // POSITION AT TIME ZERO 498 //=========================================================================== 499 500 /** 501 * Sets the longitude of this position as of the reference time. 502 * <p> 503 * This method will save a reference to {@code longitude}; it will 504 * not make and store a clone. This means that any changes made 505 * to {@code longitude} after calling this method will be reflected 506 * in this position.</p> 507 * 508 * @param longitude the longitude of this position as of the reference time. 509 * If this value is <i>null</i> it will be treated 510 * as a new longitude built via the no-argument 511 * constructor. 512 */ 513 public void setLongitudeAtTimeZero(Longitude longitude) 514 { 515 if (longitude == null) 516 longitude = new Longitude(); 517 518 longitudeAtTimeZero = longitude; 519 } 520 521 /** 522 * Returns the longitude of this position as of the reference time. 523 * 524 * @return the longitude of this position as of the reference time. 525 * This value is guaranteed to be non-<i>null</i>. 526 */ 527 public Longitude getLongitudeAtTimeZero() 528 { 529 return longitudeAtTimeZero; 530 } 531 532 /** 533 * Sets the latitude of this position as of the reference time. 534 * <p> 535 * This method will save a reference to {@code latitude}; it will 536 * not make and store a clone. This means that any changes made 537 * to {@code latitude} after calling this method will be reflected 538 * in this position.</p> 539 * 540 * @param latitude the latitude of this position as of the reference time. 541 * If this value is <i>null</i> it will be treated 542 * as a new latitude built via the no-argument 543 * constructor. 544 */ 545 public void setLatitudeAtTimeZero(Latitude latitude) 546 { 547 latitudeAtTimeZero = latitude; 548 } 549 550 /** 551 * Returns the latitude of this position as of the reference time. 552 * 553 * @return the latitude of this position as of the reference time. 554 * This value is guaranteed to be non-<i>null</i>. 555 */ 556 public Latitude getLatitudeAtTimeZero() 557 { 558 return latitudeAtTimeZero; 559 } 560 561 /** 562 * Sets the distance of this position as of the reference time. 563 * <p> 564 * This method will save a reference to {@code distance}; it will 565 * not make and store a clone. This means that any changes made 566 * to {@code distance} after calling this method will be reflected 567 * in this position.</p> 568 * 569 * @param distance the distance of this position as of the reference time. 570 * If this value is <i>null</i> it will be treated 571 * as a new distance built via the no-argument 572 * constructor. 573 */ 574 public void setDistanceAtTimeZero(Distance distance) 575 { 576 distanceAtTimeZero = distance; 577 } 578 579 /** 580 * Returns the distance of this position as of the reference time. 581 * 582 * @return the distance of this position as of the reference time. 583 * This value is guaranteed to be non-<i>null</i>. 584 */ 585 public Distance getDistanceAtTimeZero() 586 { 587 return distanceAtTimeZero; 588 } 589 590 //=========================================================================== 591 // EVALUATING THE POSITION AT TIME T 592 //=========================================================================== 593 594 /** 595 * Returns the longitude of this position at the given point in time. 596 * <p> 597 * The returned value is guaranteed to be non-null. 598 * Note that the object returned is not held internally by this position. 599 * This means that any changes made to the returned object by clients 600 * will <i>not</i> be reflected in this position.</p> 601 * 602 * @param time the point in time for which the longitude is sought. 603 * 604 * @return the longitude of this position at the given point in time. 605 */ 606 public Longitude getLongitude(Date time) 607 { 608 //TODO: What should happen if time is not valid? 609 610 //Create a new RA from the value at time zero 611 Longitude answer = longitudeAtTimeZero.clone(); 612 613 //Get delta-T in appropriate units 614 double deltaTime = 615 getDeltaTime(time, longitudeVelocityUnits.getTimeUnits()).doubleValue(); 616 617 //Add the movement in RA since time zero 618 BigDecimal movement = 619 BigDecimal.valueOf(longitudeMotion.calculateFor(deltaTime)); 620 621 answer.add(movement, longitudeVelocityUnits.getArcUnits()); 622 623 return answer; 624 } 625 626 /** 627 * Returns the latitude of this position at the given point in time. 628 * <p> 629 * The returned value is guaranteed to be non-null. 630 * Note that the object returned is not held internally by this position. 631 * This means that any changes made to the returned object by clients 632 * will <i>not</i> be reflected in this position.</p> 633 * 634 * @param time the point in time for which the latitude is sought. 635 * 636 * @return the latitude of this position at the given point in time. 637 */ 638 public Latitude getLatitude(Date time) 639 { 640 //TODO: What should happen if time is not valid? 641 642 //Create a new declination from the value at time zero 643 Latitude answer = latitudeAtTimeZero.clone(); 644 645 //Get delta-T in appropriate units 646 double deltaTime = 647 getDeltaTime(time, latitudeVelocityUnits.getTimeUnits()).doubleValue(); 648 649 //Add the movement in declination since time zero 650 BigDecimal movement = 651 BigDecimal.valueOf(latitudeMotion.calculateFor(deltaTime)); 652 653 answer.add(movement, latitudeVelocityUnits.getArcUnits()); 654 655 return answer; 656 } 657 658 /** 659 * Returns the distance of this position at the given point in time. 660 * <p> 661 * The returned value is guaranteed to be non-null. 662 * Note that the object returned is not held internally by this position. 663 * This means that any changes made to the returned object by clients 664 * will <i>not</i> be reflected in this position.</p> 665 * 666 * @param time the point in time for which the distance is sought. 667 * 668 * @return the distance of this position at the given point in time. 669 */ 670 public Distance getDistance(Date time) 671 { 672 //TODO: What should happen if time is not valid? 673 674 //Get delta-T in appropriate units 675 BigDecimal deltaTime = 676 getDeltaTime(time, radialVelocityUnits.getTimeUnits()); 677 678 //Create a new distance from the motion equation 679 double dist = radialMotion.calculateFor(deltaTime.doubleValue()); 680 Distance answer = 681 new Distance(BigDecimal.valueOf(dist), 682 radialVelocityUnits.getDistanceUnits()); 683 684 //Add the distance from time zero 685 answer.add(distanceAtTimeZero); 686 687 return answer; 688 } 689 690 /** 691 * Returns the difference between {@code time} and this position's 692 * reference time in {@code units}. 693 */ 694 private BigDecimal getDeltaTime(Date time, TimeUnits units) 695 { 696 long milliseconds = time.getTime() - referenceTime.getTime(); 697 698 boolean negative = (milliseconds < 0.0); 699 700 if (negative) 701 milliseconds = -milliseconds; 702 703 TimeDuration td = new TimeDuration(new BigDecimal(milliseconds), 704 TimeUnits.MILLISECOND); 705 706 BigDecimal delta = td.toUnits(units); 707 708 return negative ? delta.negate() : delta; 709 } 710 711 //=========================================================================== 712 // SPACE: UNCERTAINTIES 713 //=========================================================================== 714 715 /** 716 * Sets the uncertainty level in the longitude of this position. 717 * <p> 718 * Note that, unless {@code uncertainty} is <i>null</i>, this position 719 * will hold a reference to {@code uncertainty}. That is, this method 720 * will not make a copy of {@code uncertainty}. This means that any 721 * changes made to {@code uncertainty} will be reflected here. Note, too, 722 * that this method is not strictly required. Clients could instead 723 * call {@link #getLongitudeUncertainty} and operate on the returned 724 * object.</p> 725 * 726 * @param uncertainty the uncertainty level in the longitude of 727 * this position. An {@code uncertainty} of <i>null</i> 728 * will be treated as an uncertainty of zero. 729 */ 730 public void setLongitudeUncertainty(Longitude uncertainty) 731 { 732 if (uncertainty != null) 733 longitudeUncertainty = uncertainty; 734 else 735 longitudeUncertainty.reset(); 736 } 737 738 /** 739 * Sets the uncertainty level in the latitude of this position. 740 * <p> 741 * Note that, unless {@code uncertainty} is <i>null</i>, this position 742 * will hold a reference to {@code uncertainty}. That is, this method 743 * will not make a copy of {@code uncertainty}. This means that any 744 * changes made to {@code uncertainty} will be reflected here. Note, too, 745 * that this method is not strictly required. Clients could instead 746 * call {@link #getLatitudeUncertainty} and operate on the returned 747 * object.</p> 748 * 749 * @param uncertainty the uncertainty level in the latitude of 750 * this position. An {@code uncertainty} of <i>null</i> 751 * will be treated as an uncertainty of zero. 752 */ 753 public void setLatitudeUncertainty(Latitude uncertainty) 754 { 755 if (uncertainty != null) 756 latitudeUncertainty = uncertainty; 757 else 758 latitudeUncertainty.reset(); 759 } 760 761 /** 762 * Sets the uncertainty level in the distance of this position. 763 * <p> 764 * Note that, unless {@code uncertainty} is <i>null</i>, this position 765 * will hold a reference to {@code uncertainty}. That is, this method 766 * will not make a copy of {@code uncertainty}. This means that any 767 * changes made to {@code uncertainty} will be reflected here. Note, too, 768 * that this method is not strictly required. Clients could instead 769 * call {@link #getDistanceUncertainty} and operate on the returned 770 * object.</p> 771 * 772 * @param uncertainty the uncertainty level in the distance of 773 * this position. An {@code uncertainty} of <i>null</i> 774 * will be treated as an uncertainty of zero. 775 */ 776 public void setDistanceUncertainty(Distance uncertainty) 777 { 778 if (uncertainty != null) 779 distanceUncertainty = uncertainty; 780 else 781 distanceUncertainty.reset(); 782 } 783 784 /** 785 * Returns the uncertainty in the longitude of this position. 786 * <p> 787 * The returned object is guaranteed to be non-null and is also a reference 788 * to the internal uncertainty held by this position. This means any changes 789 * made by clients to the returned object will be reflected in this 790 * position.</p> 791 * 792 * @return the uncertainty in the longitude of this position. 793 */ 794 @Override 795 public Longitude getLongitudeUncertainty() 796 { 797 return longitudeUncertainty; 798 } 799 800 /** 801 * Returns the uncertainty in the latitude of this position. 802 * <p> 803 * The returned object is guaranteed to be non-null and is also a reference 804 * to the internal uncertainty held by this position. This means any changes 805 * made by clients to the returned object will be reflected in this 806 * position.</p> 807 * 808 * @return the uncertainty in the latitude of this position. 809 */ 810 @Override 811 public Latitude getLatitudeUncertainty() 812 { 813 return latitudeUncertainty; 814 } 815 816 /** 817 * Returns the uncertainty in the distance of this position. 818 * <p> 819 * The returned object is guaranteed to be non-null and is also a reference 820 * to the internal uncertainty held by this position. This means any changes 821 * made by clients to the returned object will be reflected in this 822 * position.</p> 823 * 824 * @return the uncertainty in the distance of this position. 825 */ 826 @Override 827 public Distance getDistanceUncertainty() 828 { 829 return distanceUncertainty; 830 } 831 832 //============================================================================ 833 // 834 //============================================================================ 835 836 /** 837 * Returns a copy of this polynomial position. 838 * <p> 839 * If anything goes wrong during the cloning procedure, 840 * a {@code RuntimeException} will be thrown.</p> 841 */ 842 @Override 843 public PolynomialPosition clone() 844 { 845 PolynomialPosition clone = null; 846 847 try 848 { 849 //This line takes care of the primitive fields properly 850 clone = (PolynomialPosition)super.clone(); 851 852 clone.referenceTime = (Date)this.referenceTime.clone(); 853 clone.validTime = this.validTime.clone(); 854 855 clone.longitudeAtTimeZero = this.longitudeAtTimeZero.clone(); 856 clone.latitudeAtTimeZero = this.latitudeAtTimeZero.clone(); 857 clone.distanceAtTimeZero = this.distanceAtTimeZero.clone(); 858 859 clone.longitudeMotion = this.longitudeMotion.clone(); 860 clone.latitudeMotion = this.latitudeMotion.clone(); 861 clone.radialMotion = this.radialMotion.clone(); 862 863 clone.longitudeUncertainty = this.longitudeUncertainty.clone(); 864 clone.latitudeUncertainty = this.latitudeUncertainty.clone(); 865 clone.distanceUncertainty = this.distanceUncertainty.clone(); 866 } 867 catch (Exception ex) 868 { 869 throw new RuntimeException(ex); 870 } 871 872 return clone; 873 } 874 875 /** Returns <i>true</i> if {@code o} is equal to this position. */ 876 @Override 877 public boolean equals(Object o) 878 { 879 //Quick exit if o is this 880 if (o == this) 881 return true; 882 883 //Quick exit if super class determines not equal 884 if (!super.equals(o)) 885 return false; 886 887 //Super class determined o is non-null & of same class 888 PolynomialPosition other = (PolynomialPosition)o; 889 890 return other.referenceTime.equals(this.referenceTime) && 891 other.validTime.equals (this.validTime ) && 892 893 other.longitudeAtTimeZero.equals(this.longitudeAtTimeZero) && 894 other.latitudeAtTimeZero.equals (this.latitudeAtTimeZero ) && 895 other.distanceAtTimeZero.equals (this.distanceAtTimeZero ) && 896 897 other.longitudeMotion.equals(this.longitudeMotion) && 898 other.latitudeMotion.equals (this.latitudeMotion ) && 899 other.radialMotion.equals (this.radialMotion ) && 900 901 other.longitudeVelocityUnits.equals(this.longitudeVelocityUnits) && 902 other.latitudeVelocityUnits.equals (this.latitudeVelocityUnits ) && 903 other.radialVelocityUnits.equals (this.radialVelocityUnits ) && 904 905 other.longitudeUncertainty.equals(this.longitudeUncertainty) && 906 other.latitudeUncertainty.equals (this.latitudeUncertainty ) && 907 other.distanceUncertainty.equals (this.distanceUncertainty ); 908 } 909 910 /** Returns a hash code value for this position. */ 911 @Override 912 public int hashCode() 913 { 914 //Taken from the Effective Java book by Joshua Bloch. 915 //The constants 17 & 37 are arbitrary & carry no meaning. 916 917 //You MUST keep this method in synch with equals() 918 919 int result = super.hashCode(); 920 921 result = 37 * result + referenceTime.hashCode(); 922 result = 37 * result + validTime.hashCode(); 923 924 result = 37 * result + longitudeAtTimeZero.hashCode(); 925 result = 37 * result + latitudeAtTimeZero.hashCode(); 926 result = 37 * result + distanceAtTimeZero.hashCode(); 927 928 result = 37 * result + longitudeMotion.hashCode(); 929 result = 37 * result + latitudeMotion.hashCode(); 930 result = 37 * result + radialMotion.hashCode(); 931 932 result = 37 * result + longitudeVelocityUnits.hashCode(); 933 result = 37 * result + latitudeVelocityUnits.hashCode(); 934 result = 37 * result + radialVelocityUnits.hashCode(); 935 936 result = 37 * result + longitudeUncertainty.hashCode(); 937 result = 37 * result + latitudeUncertainty.hashCode(); 938 result = 37 * result + distanceUncertainty.hashCode(); 939 940 return result; 941 } 942 943 /** 944 * Compares this position to {@code other} for order. 945 * <p> 946 * One position is deemed to be "less than" the other if it has 947 * a valid time range that is less than that of the other. 948 * In the case that two positions have the same valid time range, 949 * the reference time is used as a tie-breaker. If these are the 950 * same other attributes are compared until a difference is found. 951 * If none are found, the return value is zero.</p> 952 * 953 * @param other the time interval to which this one is compared. 954 * 955 * @return a negative integer, zero, or a positive integer as this interval 956 * is less than, equal to, or greater than the other interval. 957 */ 958 public int compareTo(PolynomialPosition other) 959 { 960 //TODO rethink comparison for all sky positions? 961 962 //First compare the valid time intervals 963 int answer = this.validTime.compareTo(other.validTime); 964 if (answer != 0) 965 return answer; 966 967 //Reference time 968 answer = this.referenceTime.compareTo(other.referenceTime); 969 if (answer != 0) 970 return answer; 971 972 //Epoch 973 answer = this.getEpoch().compareTo(other.getEpoch()); 974 if (answer != 0) 975 return answer; 976 977 //R.A. t=0 978 answer = this.longitudeAtTimeZero.compareTo(other.longitudeAtTimeZero); 979 if (answer != 0) 980 return answer; 981 982 //Latitude t=0 983 answer = this.latitudeAtTimeZero.compareTo(other.latitudeAtTimeZero); 984 if (answer != 0) 985 return answer; 986 987 //Distance t=0 988 answer = this.distanceAtTimeZero.compareTo(other.distanceAtTimeZero); 989 if (answer != 0) 990 return answer; 991 992 //R.A. motion 993 answer = this.longitudeMotion.compareTo(other.longitudeMotion); 994 if (answer != 0) 995 return answer; 996 997 //Latitude motion 998 answer = this.latitudeMotion.compareTo(other.latitudeMotion); 999 if (answer != 0) 1000 return answer; 1001 1002 //Distance motion 1003 answer = this.radialMotion.compareTo(other.radialMotion); 1004 if (answer != 0) 1005 return answer; 1006 1007 //R.A. uncertainty 1008 answer = this.longitudeUncertainty.compareTo(other.longitudeUncertainty); 1009 if (answer != 0) 1010 return answer; 1011 1012 //Latitude uncertainty 1013 answer = this.latitudeUncertainty.compareTo(other.latitudeUncertainty); 1014 if (answer != 0) 1015 return answer; 1016 1017 //Distance uncertainty 1018 answer = this.distanceUncertainty.compareTo(other.distanceUncertainty); 1019 if (answer != 0) 1020 return answer; 1021 1022 //Coordinate system 1023 answer = this.getCoordinateSystem().compareTo(other.getCoordinateSystem()); 1024 if (answer != 0) 1025 return answer; 1026 1027 //No differences found 1028 return 0; 1029 } 1030 }