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.geom.EarthPosition; 011 import edu.nrao.sss.geom.SphericalPosition; 012 import edu.nrao.sss.measure.Angle; 013 import edu.nrao.sss.measure.Distance; 014 import edu.nrao.sss.measure.Latitude; 015 import edu.nrao.sss.measure.LocalSiderealTime; 016 import edu.nrao.sss.measure.Longitude; 017 import edu.nrao.sss.measure.TimeDuration; 018 import edu.nrao.sss.measure.TimeInterval; 019 import edu.nrao.sss.util.SourceNotFoundException; 020 021 /** 022 * Position information for a solar system body. 023 * <p> 024 * <b>Version Info:</b> 025 * <table style="margin-left:2em"> 026 * <tr><td>$Revision: 1256 $</td></tr> 027 * <tr><td>$Date: 2008-04-29 15:55:50 -0600 (Tue, 29 Apr 2008) $</td></tr> 028 * <tr><td>$Author: dharland $</td></tr> 029 * </table></p> 030 * 031 * @author David M. Harland 032 * @since 2007-04-18 033 */ 034 @XmlRootElement 035 public class SolarSystemBodyPosition 036 implements SkyPosition 037 { 038 private static final String DEFAULT_NAME = ""; 039 private static final SourceLocator DEFAULT_LOCATOR = new SkyBotResolver(); 040 041 private static final BigDecimal INITIAL_REFRESH_HOURS = new BigDecimal("0.5"); 042 043 @XmlElement private String bodyName; 044 045 private TimeDuration refreshRate; 046 private TimeInterval validTime; 047 private SourceLocator locator; 048 049 //The SkyPosition methods are delegated to this object 050 private SkyPosition skyPos; 051 052 /** Here for JAXB. */ 053 SolarSystemBodyPosition() { this("AUTO-GENERATED"); } 054 055 /** Creates a new instance. */ 056 public SolarSystemBodyPosition(String nameOfBody) 057 { 058 initialize(); 059 060 bodyName = (nameOfBody == null) ? DEFAULT_NAME : nameOfBody; 061 062 refreshRate = new TimeDuration(INITIAL_REFRESH_HOURS); 063 validTime = new TimeInterval(); 064 } 065 066 private void initialize() 067 { 068 //bodyName intentionally NOT set 069 locator = DEFAULT_LOCATOR; 070 skyPos = null; 071 } 072 073 /** 074 * Resets the internals of this position, except for its body name. 075 */ 076 public void reset() 077 { 078 initialize(); 079 080 refreshRate.reset(); 081 validTime.reset(); 082 } 083 084 //============================================================================ 085 // 086 //============================================================================ 087 088 /** 089 * Returns the name of the solar system body this position represents. 090 * @return the name of the solar system body this position represents. 091 */ 092 public String getBodyName() { return bodyName; } 093 094 /** 095 * Sets the locator that this position will use for determining the location 096 * of the object whose name is given by {@link #getBodyName()}. 097 * If {@code newLocator} is <i>null</i>, this method does nothing. 098 * 099 * @param newLocator the locator to use for getting position information 100 * for this body. 101 */ 102 public void setLocator(SourceLocator newLocator) 103 { 104 if (newLocator != null) 105 locator = newLocator; 106 } 107 108 /** 109 * Sets the refresh rate for this position to {@code newRate}. 110 * If {@code newRate} is <i>null</i>, the refresh rate will be set to 111 * zero. 112 * <p> 113 * The refresh rate refers to the amount of time that must 114 * pass before this position will request new information from its 115 * {@link #setLocator(SourceLocator) source locator}. Each time the source 116 * locator is successfully queried, the position and time of the query are 117 * cached. If a subsequent request is made in the interval 118 * <tt>[queryTime...queryTime + newRate]</tt> (or in the interval 119 * <tt>[queryTime - newRate...queryTime]</tt>, the reported position is the 120 * cached position. Clients can ensure that cached values are never used by 121 * setting the refresh rate to zero, which is the default state for this 122 * object.</p> 123 * 124 * @param newRate the refresh rate for locating this body's position. 125 */ 126 public void setRefreshRate(TimeDuration newRate) 127 { 128 if (newRate != null) 129 refreshRate.set(newRate); 130 else 131 refreshRate.reset(); 132 } 133 134 /** 135 * Returns a copy of this position's refresh rate 136 * (see {@link #setRefreshRate(TimeDuration)} for a description). 137 * 138 * @return a copy of this position's refresh rate. 139 */ 140 @XmlTransient 141 public TimeDuration getRefreshRate() 142 { 143 return refreshRate.clone(); 144 } 145 146 /* (non-Javadoc) 147 * @see edu.nrao.sss.astronomy.SkyPosition#isMoving() 148 */ 149 public boolean isMoving() { return true; } 150 151 //============================================================================ 152 // 153 //============================================================================ 154 155 private SkyPosition getNonNullPosition(Date time) 156 { 157 if ((skyPos == null) || !validTime.contains(time)) 158 refreshPosition(time); 159 160 return skyPos == null ? new SimpleSkyPosition() : skyPos; 161 } 162 163 private void refreshPosition(Date time) 164 { 165 try 166 { 167 //Get the latest position 168 skyPos = locator.findPosition(bodyName); 169 170 //Reset the valid time range 171 TimeDuration doubleRate = refreshRate.clone(); 172 doubleRate.multiplyBy("2.0"); 173 validTime = doubleRate.toIntervalCenteredOn(time); 174 } 175 catch (SourceNotFoundException ex) //TODO log? 176 { 177 skyPos = null; //Forces another lookup attempt 178 } 179 } 180 181 //============================================================================ 182 // INTERFACE SkyPosition 183 //============================================================================ 184 185 //TODO document methods for how they behave for invalid times 186 187 public SkyPositionType getType() {return SkyPositionType.INTERNAL_EPHEMERIS;} 188 189 public CelestialCoordinateSystem getCoordinateSystem() 190 { 191 return getNonNullPosition(new Date()).getCoordinateSystem(); 192 } 193 194 public Epoch getEpoch() 195 { 196 return getNonNullPosition(new Date()).getEpoch(); 197 } 198 199 public String getOriginOfInformation() 200 { 201 return getNonNullPosition(new Date()).getOriginOfInformation(); 202 } 203 204 //---------------------------------------------------------------------------- 205 // Current-time position 206 //---------------------------------------------------------------------------- 207 208 /** 209 * Returns the current longitude of this position. 210 * <p> 211 * The returned value is guaranteed to be non-null. 212 * Note that the object returned is not held internally by this position. 213 * This means that any changes made to the returned object by clients 214 * will <i>not</i> be reflected in this position.</p> 215 * 216 * @return the current longitude of this position. 217 */ 218 public Longitude getLongitude() 219 { 220 return getLongitude(new Date()); 221 } 222 223 /** 224 * Returns the current latitude of this position. 225 * <p> 226 * The returned value is guaranteed to be non-null. 227 * Note that the object returned is not held internally by this position. 228 * This means that any changes made to the returned object by clients 229 * will <i>not</i> be reflected in this position.</p> 230 * 231 * @return the current latitude of this position. 232 */ 233 public Latitude getLatitude() 234 { 235 return getLatitude(new Date()); 236 } 237 238 /** 239 * Returns the current distance of this position. 240 * <p> 241 * The returned value is guaranteed to be non-null. 242 * Note that the object returned is not held internally by this position. 243 * This means that any changes made to the returned object by clients 244 * will <i>not</i> be reflected in this position.</p> 245 * 246 * @return the current distance of this position. 247 */ 248 public Distance getDistance() 249 { 250 return getDistance(new Date()); 251 } 252 253 /** 254 * Returns the longitude of this position at the given point in time. 255 * <p> 256 * The returned value is guaranteed to be non-null. 257 * Note that the object returned is not held internally by this position. 258 * This means that any changes made to the returned object by clients 259 * will <i>not</i> be reflected in this position.</p> 260 * 261 * @param time the point in time for which the longitude is sought. 262 * 263 * @return the longitude of this position at the given point in time. 264 */ 265 public Longitude getLongitude(Date time) 266 { 267 return getNonNullPosition(time).getLongitude(time); 268 } 269 270 /** 271 * Returns the latitude of this position at the given point in time. 272 * <p> 273 * The returned value is guaranteed to be non-null. 274 * Note that the object returned is not held internally by this position. 275 * This means that any changes made to the returned object by clients 276 * will <i>not</i> be reflected in this position.</p> 277 * 278 * @param time the point in time for which the latitude is sought. 279 * 280 * @return the latitude of this position at the given point in time. 281 */ 282 public Latitude getLatitude(Date time) 283 { 284 return getNonNullPosition(time).getLatitude(time); 285 } 286 287 /** 288 * Returns the distance of this position at the given point in time. 289 * <p> 290 * The returned value is guaranteed to be non-null. 291 * Note that the object returned is not held internally by this position. 292 * This means that any changes made to the returned object by clients 293 * will <i>not</i> be reflected in this position.</p> 294 * 295 * @param time the point in time for which the distance is sought. 296 * 297 * @return the distance of this position at the given point in time. 298 */ 299 public Distance getDistance(Date time) 300 { 301 return getNonNullPosition(time).getDistance(time); 302 } 303 304 /** 305 * Returns the uncertainty in the longitude of this position. 306 * <p> 307 * The returned value is guaranteed to be non-null. 308 * Note that the object returned is not held internally by this position. 309 * This means that any changes made to the returned object by clients 310 * will <i>not</i> be reflected in this position.</p> 311 * 312 * @return the uncertainty in the longitude of this position. 313 */ 314 public Longitude getLongitudeUncertainty() 315 { 316 return getNonNullPosition(new Date()).getLongitudeUncertainty(); 317 } 318 319 /** 320 * Returns the uncertainty in the latitude of this position. 321 * <p> 322 * The returned value is guaranteed to be non-null. 323 * Note that the object returned is not held internally by this position. 324 * This means that any changes made to the returned object by clients 325 * will <i>not</i> be reflected in this position.</p> 326 * 327 * @return the uncertainty in the latitude of this position. 328 */ 329 public Latitude getLatitudeUncertainty() 330 { 331 return getNonNullPosition(new Date()).getLatitudeUncertainty(); 332 } 333 334 /** 335 * Returns the uncertainty in the distance of this position. 336 * <p> 337 * The returned value is guaranteed to be non-null. 338 * Note that the object returned is not held internally by this position. 339 * This means that any changes made to the returned object by clients 340 * will <i>not</i> be reflected in this position.</p> 341 * 342 * @return the uncertainty in the distance of this position. 343 */ 344 public Distance getDistanceUncertainty() 345 { 346 return getNonNullPosition(new Date()).getDistanceUncertainty(); 347 } 348 349 /* (non-Javadoc) 350 * @see SphericalPosition#getAngularSeparation(SphericalPosition) 351 */ 352 public Angle getAngularSeparation(SphericalPosition other) 353 { 354 return getAngularSeparation(other, new Date()); 355 } 356 357 /* (non-Javadoc) 358 * @see SphericalPosition#getAngularSeparation(SphericalPosition, Date) 359 */ 360 public Angle getAngularSeparation(SphericalPosition other, Date time) 361 { 362 return getNonNullPosition(time).getAngularSeparation(other, time); 363 } 364 365 //============================================================================ 366 // 367 //============================================================================ 368 369 /* (non-Javadoc) 370 * @see SkyPosition#toPosition(CelestialCoordinateSystem, Epoch) 371 */ 372 public SkyPosition toPosition(CelestialCoordinateSystem system, 373 Epoch epoch, 374 EarthPosition observer, 375 LocalSiderealTime lst) 376 throws CoordinateConversionException 377 { 378 return toPosition(system, epoch, observer, lst,AbsSkyPos.DEFAULT_CONVERTER); 379 } 380 381 /* (non-Javadoc) 382 * @see SkyPosition#toPosition(CelestialCoordinateSystem, Epoch, 383 * CelestialCoordinateConverter) 384 */ 385 public SkyPosition toPosition(CelestialCoordinateSystem system, 386 Epoch epoch, 387 EarthPosition observer, 388 LocalSiderealTime lst, 389 CelestialCoordinateConverter converter) 390 throws CoordinateConversionException 391 { 392 return converter.createFrom(this, system, epoch, observer, lst); 393 } 394 395 //============================================================================ 396 // 397 //============================================================================ 398 399 /** 400 * Returns a copy of this body. 401 * <p> 402 * The object returned is almost a deep-copy of this position. 403 * The source locator used by the returned copy, however, is a reference to 404 * the same locator used by this position.</p> 405 * <p> 406 * If anything goes wrong during the cloning procedure, 407 * a {@code RuntimeException} will be thrown.</p> 408 */ 409 @Override 410 public SolarSystemBodyPosition clone() 411 { 412 SolarSystemBodyPosition clone = null; 413 414 try 415 { 416 //This line takes care of the primitive & immutable fields properly 417 clone = (SolarSystemBodyPosition)super.clone(); 418 419 clone.refreshRate = this.refreshRate.clone(); 420 clone.validTime = this.validTime.clone(); 421 422 if (this.skyPos != null) 423 clone.skyPos = this.skyPos.clone(); 424 425 //Intentionally NOT cloned 426 clone.locator = DEFAULT_LOCATOR; 427 } 428 catch (Exception ex) 429 { 430 throw new RuntimeException(ex); 431 } 432 433 return clone; 434 } 435 436 /** Returns <i>true</i> if {@code o} is equal to this body. */ 437 @Override 438 public boolean equals(Object o) 439 { 440 //Quick exit if o is this 441 if (o == this) 442 return true; 443 444 //Quick exit if o is null 445 if (o == null) 446 return false; 447 448 //Quick exit if classes are different 449 if (!o.getClass().equals(this.getClass())) 450 return false; 451 452 SolarSystemBodyPosition other = (SolarSystemBodyPosition)o; 453 454 //The following properties are intentionally absent: 455 // refreshRate, validTime, locator, skyPos 456 457 return this.bodyName.equalsIgnoreCase(other.bodyName); 458 } 459 460 /** Returns a hash code value for this body. */ 461 @Override 462 public int hashCode() 463 { 464 //Taken from the Effective Java book by Joshua Bloch. 465 //The constants 17 & 37 are arbitrary & carry no meaning. 466 int result = 17; 467 468 //You MUST keep this method in synch with equals() 469 470 result = 37 * result + bodyName.hashCode(); 471 472 return result; 473 } 474 }