001    package edu.nrao.sss.astronomy;
002    
003    import java.math.BigDecimal;
004    import java.util.Calendar;
005    import java.util.Date;
006    import java.util.Random;
007    
008    import edu.nrao.sss.math.Polynomial;
009    import edu.nrao.sss.math.PolynomialTerm;
010    import edu.nrao.sss.measure.AngularVelocityUnits;
011    import edu.nrao.sss.measure.ArcUnits;
012    import edu.nrao.sss.measure.Distance;
013    import edu.nrao.sss.measure.DistanceUnits;
014    import edu.nrao.sss.measure.Latitude;
015    import edu.nrao.sss.measure.Longitude;
016    import edu.nrao.sss.measure.TimeInterval;
017    import edu.nrao.sss.util.EnumerationUtility;
018    
019    /**
020     * Helper for test classes; builds sky positions with randomized data.
021     * <p>
022     * <b>Version Info:</b>
023     * <table style="margin-left:2em">
024     *   <tr><td>$Revision$</td></tr>
025     *   <tr><td>$Date$</td></tr>
026     *   <tr><td>$Author$</td></tr>
027     * </table></p>
028     * 
029     * @author David M. Harland
030     * @since 2007-04-19
031     */
032    public class SkyPositionBuilder
033    {
034      private static final EnumerationUtility ENUM_UTIL =
035        EnumerationUtility.getSharedInstance();
036    
037      private static final String INFO_ORIGIN = "SkyPositionBuilder.java";
038      
039      private Random random = new Random(System.currentTimeMillis());
040    
041      private EphemerisTableBuilder ephemBuilder = new EphemerisTableBuilder();
042      
043      //Used for coordinating multiple intervals
044      private Date timeIntervalEnd = null;
045    
046      //============================================================================
047      // 
048      //============================================================================
049    
050      public SkyPosition makePosition()
051      {
052        SkyPosition position;
053        
054        double posType = random.nextDouble();
055        
056        if      (posType < 0.20)   position = makeSimplePosition();
057        else if (posType < 0.40)   position = makePolynomialPosition();
058        else if (posType < 0.60)   position = makePolynomialTable();
059        else if (posType < 0.80)   position = makeEphemerisTable();
060        else if (posType < 0.90)   position = makeOrbit();
061        else                       position = makeSolarSystemPosition();
062        
063        return position;
064      }
065      
066      public SimpleSkyPosition makeSimplePosition()
067      {
068        SimpleSkyPosition position = new SimpleSkyPosition();
069    
070        position.setCoordinateSystem(ENUM_UTIL.getRandomValueFor(CelestialCoordinateSystem.class));
071        position.setEpoch(random.nextDouble() < 0.2 ? Epoch.B1950 : Epoch.J2000);
072        position.setOriginOfInformation(INFO_ORIGIN);
073        
074        Longitude longitude = makeLongitude();
075        Latitude  latitude  = makeLatitude();
076        Distance  distance  = makeBigDistance();
077        
078        position.getLongitude().set(longitude.getValue(), longitude.getUnits());
079        position.getLatitude().set(  latitude.getValue(),  latitude.getUnits());
080        position.getDistance().set(  distance.getValue(),  distance.getUnits());
081        
082        BigDecimal error = BigDecimal.valueOf(random.nextDouble() * 1e-4);
083        
084        position.getLongitudeUncertainty().set(longitude.getValue().multiply(error),
085                                               longitude.getUnits());
086        
087        error = BigDecimal.valueOf(random.nextDouble() * 1e-4);
088    
089        position.getLatitudeUncertainty().set(latitude.getValue().multiply(error),
090                                              latitude.getUnits());
091        
092        error = BigDecimal.valueOf(random.nextDouble() * 1e-4);
093    
094        position.getDistanceUncertainty().set(error.multiply(distance.getValue()),
095                                              distance.getUnits());
096        
097        return position;
098      }
099      
100      public EphemerisTable makeEphemerisTable()
101      {
102        return ephemBuilder.makeTable();
103      }
104      
105      public Orbit makeOrbit()
106      {
107        Orbit orbit = new Orbit();
108    
109        orbit.setOriginOfInformation(INFO_ORIGIN);
110    
111        orbit.setArgumentOfPeriapsis(360.0 * random.nextDouble());
112        orbit.setEccentricity(0.5 * random.nextDouble());
113        orbit.setInclination(45.0 * random.nextDouble());
114        orbit.setMeanAnomaly(360.0 * random.nextDouble());
115        orbit.setRightAscensionAscendingNode(
116          new Longitude(Double.toString(360.0 * random.nextDouble())));
117        orbit.setSemiMajorAxis(0.1 + 49.9 * random.nextDouble());
118        
119        return orbit;
120      }
121      
122      public PolynomialPositionTable makePolynomialTable()
123      {
124        return makePolynomialTable(false);
125      }
126      
127      public PolynomialPositionTable makePolynomialTable(boolean wellFormed)
128      {
129        PolynomialPositionTable table = new PolynomialPositionTable();
130        
131        int size = 3 + random.nextInt(17);
132        
133        for (int p=0; p < size; p++)
134        {
135          table.add(makePolynomialPosition());
136          
137          if (!wellFormed)
138            timeIntervalEnd = null;
139        }
140        
141        timeIntervalEnd = null;
142        
143        return table;
144      }
145      
146      public PolynomialPosition makePolynomialPosition()
147      {
148        return makePolynomialPosition(new PolynomialPosition());
149      }
150      
151      private PolynomialPosition makePolynomialPosition(PolynomialPosition pp)
152      {
153        pp.setValidTime(makeTimeInterval(timeIntervalEnd));
154        pp.setReferenceTime(pp.getValidTime().getStart());
155        pp.setEpoch(random.nextDouble() < 0.2 ? Epoch.B1950 : Epoch.J2000);
156        pp.setOriginOfInformation(INFO_ORIGIN);
157        pp.setLatitudeAtTimeZero(new Latitude(Double.toString(-90.0 + 180.0 * random.nextDouble())));
158        pp.setLongitudeAtTimeZero(new Longitude(Double.toString(360.0 * random.nextDouble())));
159        Polynomial eqn = new Polynomial();
160        eqn.add(new PolynomialTerm(-100.0 + 200.0 * random.nextDouble(), 1));
161        pp.setRadialMotion(eqn);
162        
163        //0, 1, or 2 motion terms
164        double d0;
165        int count = random.nextInt(3);
166        if (count < 1.0)  //very far away sources
167        {
168          d0 = 100.0 + 10000.0 * random.nextDouble();
169          pp.setDistanceAtTimeZero(new Distance(BigDecimal.valueOf(d0),
170                                                DistanceUnits.LIGHT_YEAR));
171        }
172        else if (count < 2.0)  //far away sources
173        {
174          d0 = 5.0 + 100.0 * random.nextDouble();
175          pp.setDistanceAtTimeZero(new Distance(BigDecimal.valueOf(d0),
176                                                DistanceUnits.LIGHT_YEAR));
177          
178          pp.setLatitudeVelocityUnits(AngularVelocityUnits.MILLI_ARC_SECONDS_PER_YEAR);
179          eqn = new Polynomial();
180          eqn.add(new PolynomialTerm(-500.0 + 1000.0 * random.nextDouble(), 1));
181          pp.setLatitudeMotion(eqn);
182    
183          pp.setLongitudeVelocityUnits(AngularVelocityUnits.MILLI_ARC_SECONDS_PER_YEAR);
184          eqn = new Polynomial();
185          eqn.add(new PolynomialTerm(-500.0 + 1000.0 * random.nextDouble(), 1));
186          pp.setLongitudeMotion(eqn);
187        }
188        else  //nearby sources
189        {
190          d0 = 3.0 + 97.0 * random.nextDouble();
191          pp.setDistanceAtTimeZero(new Distance(BigDecimal.valueOf(d0),
192                                                DistanceUnits.ASTRONOMICAL_UNIT));
193    
194          pp.setLatitudeVelocityUnits(AngularVelocityUnits.ARC_SECONDS_PER_DAY);
195          eqn = new Polynomial();
196          eqn.add(new PolynomialTerm(-50.0 + 100.0 * random.nextDouble(), 1));
197          eqn.add(new PolynomialTerm(-5.0 + 10.0 * random.nextDouble(), 2));
198          pp.setLatitudeMotion(eqn);
199    
200          pp.setLongitudeVelocityUnits(AngularVelocityUnits.ARC_SECONDS_PER_DAY);
201          eqn = new Polynomial();
202          eqn.add(new PolynomialTerm(-50.0 + 100.0 * random.nextDouble(), 1));
203          eqn.add(new PolynomialTerm(-5.0 + 10.0 * random.nextDouble(), 2));
204          pp.setLongitudeMotion(eqn);
205        }
206        
207        return pp;
208      }
209      
210      private static final String[] BODIES = 
211      {
212        "Mercury", "Venus", "Mars", "Ceres", "Vesta", "Jupiter", "Saturn",
213        "Uranus", "Neptune", "Pluto", "Ida"
214      };
215      
216      public SolarSystemBodyPosition makeSolarSystemPosition()
217      {
218        SolarSystemBodyPosition position =
219          new SolarSystemBodyPosition(BODIES[random.nextInt(BODIES.length)]);
220        
221        //TODO more random stuff
222        
223        return position;
224      }
225    
226      //============================================================================
227      // HELPERS
228      //============================================================================
229    
230      private TimeInterval makeTimeInterval(Date startTime)
231      {
232        int year, month, day;
233        Date from;
234        Calendar cal = Calendar.getInstance();
235        
236        if (startTime == null)
237        {
238          year  = 1950 + random.nextInt(55);
239          month = random.nextInt(12);
240          day   = 1 + random.nextInt(27);
241          cal.set(year, month, day);
242          
243          from = cal.getTime();
244        }
245        else
246        {
247          cal.setTime(startTime);
248          
249          from = startTime;
250        }
251        
252        cal.add(Calendar.DATE, random.nextInt(3000));
253        cal.add(Calendar.SECOND, random.nextInt(24*60*60));
254        
255        timeIntervalEnd = cal.getTime();
256        
257        return new TimeInterval(from, timeIntervalEnd);
258      }
259    
260      private Longitude makeLongitude()
261      {
262        ArcUnits units = ENUM_UTIL.getRandomValueFor(ArcUnits.class); 
263        
264        BigDecimal value = units.toFullCircle().multiply(new BigDecimal(random.nextDouble()));
265        
266        return new Longitude(value, units);
267      }
268    
269      private Latitude makeLatitude()
270      {
271        ArcUnits units = ENUM_UTIL.getRandomValueFor(ArcUnits.class); 
272        
273        BigDecimal value = units.toQuarterCircle().multiply(new BigDecimal(random.nextDouble()));
274        
275        if (random.nextDouble() < 0.5)
276          value = value.negate();
277        
278        return new Latitude(value, units);
279      }
280      
281      private Distance makeBigDistance()
282      {
283        DistanceUnits units = null;
284        switch(random.nextInt(3))
285        {
286          case 0:  units = DistanceUnits.KILOMETER;          break;
287          case 1:  units = DistanceUnits.ASTRONOMICAL_UNIT;  break;
288          case 2:  units = DistanceUnits.LIGHT_YEAR;         break;
289        }
290    
291        BigDecimal au = BigDecimal.valueOf(random.nextInt(1000000) + 1);
292        BigDecimal value = DistanceUnits.ASTRONOMICAL_UNIT.toUnits(units).multiply(au);
293        
294        return new Distance(value, units);
295      }
296    }