001    package edu.nrao.sss.model.project.scan;
002    
003    import java.math.BigDecimal;
004    import java.util.*;
005    
006    import edu.nrao.sss.astronomy.VelocityConvention;
007    import edu.nrao.sss.astronomy.VelocityFrame;
008    import edu.nrao.sss.measure.*;
009    import edu.nrao.sss.model.project.*;
010    import edu.nrao.sss.model.proposal.Proposal;
011    import edu.nrao.sss.model.resource.*;
012    import edu.nrao.sss.model.source.*;
013    import edu.nrao.sss.util.EnumerationUtility;
014    
015    /**
016     * Helper for test classes; builds scans and their components.
017     * <p>
018     * <b>Version Info:</b>
019     * <table style="margin-left:2em">
020     *   <tr><td>$Revision: 2274 $</td></tr>
021     *   <tr><td>$Date: 2009-04-28 14:04:21 -0600 (Tue, 28 Apr 2009) $</td></tr>
022     *   <tr><td>$Author: dharland $</td></tr>
023     * </table></p>
024     *  
025     * @author David M. Harland
026     * @since 2006-10-12
027     */
028    public class ScanBuilder
029    {
030      private static final EnumerationUtility ENUM_UTIL =
031        EnumerationUtility.getSharedInstance();
032    
033      private Random random = new Random(System.currentTimeMillis());
034    
035      private boolean setId            = false;
036      private Long    nextScanId       = 1L;
037    
038      private SourceBuilder   sourceBuilder   = new SourceBuilder();
039      private ResourceBuilder resourceBuilder = new ResourceBuilder();
040    
041      //============================================================================
042      // PUBLIC METHODS
043      //============================================================================
044      
045      /**
046       * Tells the factory methods whether or not they should set the object
047       * identifiers.
048       */
049      public void setIdentifiers(boolean setIds)
050      {
051        setId = setIds;
052      }
053    
054    
055      public void makeScansFor(ScanLoop parentLoop)
056      {
057        int elementCount = 1 + random.nextInt(4);
058        
059        for (int e=0; e < elementCount; e++)
060        {
061          double x = random.nextDouble();
062        
063          if (x < 0.2)
064          {
065            ScanLoop innerLoop = makeScanLoop();
066            
067            innerLoop = parentLoop.addCopyOf(innerLoop);
068          }
069          else
070          {
071            makeScanFor(parentLoop);
072          }
073        }
074      }
075    
076      public ScanLoop makeScanLoop()
077      {
078        ScanLoop loop = new ScanLoop();
079    
080        initScanLoopElement(loop);
081        
082        loop.setName(loop.getName() + " loop");
083        
084        loop.setIterationCount(1 + random.nextInt(9));
085        
086        int hours = 20 + random.nextInt(80);
087        loop.setMaximumDuration(new TimeDuration(new BigDecimal(hours)));
088        
089        makeScansFor(loop);
090        
091        return loop;
092      }
093    
094      public Scan makeScan()
095      {
096        return makeScanFor((ScanLoop)null);
097      }
098      
099      public Scan makeScanFor(ScanLoop parentLoop)
100      {
101        Scan newScan;
102        
103        double x = random.nextDouble();
104    
105        if      (x < 0.12)  newScan = makeDelayScanFor(parentLoop);
106        else if (x < 0.24)  newScan = makeFocusScanFor(parentLoop);
107        else if (x < 0.36)  newScan = makePointingScanFor(parentLoop);
108        else if (x < 0.48)  newScan = makeSwitchingScanFor(parentLoop);
109        else if (x < 0.60)  newScan = makeTippingScanFor(parentLoop);
110        else                newScan = makeSimpleScanFor(parentLoop);
111    
112        return newScan;
113      }
114    
115      private void initScan(Scan scan, ScanLoop parentLoop)
116      {
117        initScanLoopElement(scan);
118    
119        if (parentLoop != null)
120          parentLoop.add(scan);
121      
122        scan.setAllowOverTheTop(random.nextBoolean());
123        scan.setApplyLastPhase(random.nextBoolean());
124        scan.setApplyLastReferenceFocus(random.nextBoolean());
125        scan.setApplyLastReferencePointing(random.nextBoolean());
126        scan.setSolarObserving(random.nextBoolean());
127        
128        int max = AntennaWrap.values().length;
129        scan.setAntennaWrap(AntennaWrap.values()[random.nextInt(max)]);
130        
131        List<ScanIntent> intents = new ArrayList<ScanIntent>(scan.getMode().getValidIntents());
132        while (intents.size() > 2)
133          intents.remove(random.nextInt(intents.size()));
134        scan.setIntents(new HashSet<ScanIntent>(intents));
135        
136        Project       project  = scan.getProject();
137        TelescopeType teleType = (project == null) ? null : project.getTelescope();
138    
139        //Reference antennas
140        if (TelescopeType.VLA.equals(teleType))
141          initRefAntennas(scan);
142        
143        //Source
144        Proposal proposal = (project == null) ? null : project.getProposal();
145        if (proposal != null)
146        {
147          List<Source> sources = new ArrayList<Source>(proposal.getSources());
148          scan.setSourceCatalogEntry(sources.get(random.nextInt(sources.size())).clone());
149        }
150        else
151        {
152          if (random.nextDouble() < 0.667)
153            scan.setSourceCatalogEntry(sourceBuilder.makeSource(makeCode()));
154          else
155            scan.setSourceCatalogEntry(sourceBuilder.makeTable(makeCode()));
156        }
157    
158        //80% of time use own resource, 10% use prev but have own, 10% use prev & have none
159        if (random.nextDouble() < 0.2)
160        {
161          scan.setUseResourceOfPriorScan(true);
162          
163          if (random.nextDouble() < 0.5)
164            scan.setResource(resourceBuilder.makeResource(makeCode(), teleType));
165        }
166        else
167        {
168          scan.setResource(resourceBuilder.makeResource(makeCode(), teleType));
169        }
170        
171        fillScanTimeSpec(scan.getTimeSpec());
172        
173        addDopplerSpecs(scan);
174        setComment(scan);
175      }
176        
177      private void initRefAntennas(Scan scan)
178      {
179        AntennaSpecifier idType = ENUM_UTIL.getRandomValueFor(AntennaSpecifier.class);
180        AntennaSelection sel = scan.getReferenceAntennas();
181        sel.setIdType(idType);
182        
183        int max = idType.equals(AntennaSpecifier.ANTENNA_ID) ? 27 : 20; //20?
184        
185        int count = 1 + random.nextInt(3);
186        for (int a=0; a < count; a++)
187        {
188          String id = "";
189          if (idType.equals(AntennaSpecifier.PAD_ID))
190          {
191            double direction = random.nextDouble();
192            if      (direction < 0.34)  id = "N";
193            else if (direction < 0.67)  id = "E";
194            else                        id = "W";
195          }
196          id = id + Integer.toString(1+ random.nextInt(max));
197          sel.addUserPick(id);
198        }
199      }
200    
201      private void initScanLoopElement(ScanLoopElement sle)
202      {
203        if (setId)
204          sle.setId(nextScanId++);
205        
206        Date now = new Date();
207        sle.setCreatedOn(now);
208        sle.setLastUpdatedOn(now);
209    
210        long id = 1L + (long)(random.nextInt(1000));
211        sle.setCreatedBy(id);
212        sle.setLastUpdatedBy(id);
213    
214        StringBuilder name = new StringBuilder();
215    
216        int numChars = random.nextInt(3)+3;
217        for (int i=0; i < numChars; i++)
218          name.append((char)('a'+random.nextInt(25)));
219    
220        sle.setName(name.toString());
221    
222        sle.setComments("Created by ProjectBuilder.java at " + new Date());
223      }
224    
225      public SimpleScan makeSimpleScanFor(ScanLoop parentLoop)
226      {
227        SimpleScan scan = (SimpleScan)Scan.createFor(ScanMode.STANDARD_OBSERVING);
228    
229        initScan(scan, parentLoop);
230    
231        return scan;
232      }
233      
234      public DelayScan makeDelayScanFor(ScanLoop parentLoop)
235      {
236        DelayScan scan = (DelayScan)Scan.createFor(ScanMode.AMPLITUDE_DELAY_CALIBRATING);
237    
238        initScan(scan, parentLoop);
239        
240        List<DelaySetting> delays = scan.getDelays();
241        
242        for (double nanos=-100.0; nanos <= +100.0; nanos += 10.0)
243        {
244          DelaySetting ds = new DelaySetting();
245          ds.setNanoseconds(nanos);
246          ds.setTimeAtSetting(new TimeDuration("90.0", TimeUnits.SECOND));
247          delays.add(ds);
248        }
249        
250        return scan;
251      }
252    
253      public FocusScan makeFocusScanFor(ScanLoop parentLoop)
254      {
255        FocusScan scan = (FocusScan)Scan.createFor(ScanMode.REFERENCE_FOCUSING);
256    
257        initScan(scan, parentLoop);
258    
259        List<FocusOffset> offsets = scan.getOffsets();
260    
261        for (double mm=-1.5; mm <= 1.5; mm+=0.5)
262        {
263          FocusOffset fo = new FocusOffset();
264          fo.setOffsetLength(new Distance(BigDecimal.valueOf(mm), DistanceUnits.MILLIMETER));
265          fo.setTimeAtOffset(new TimeDuration("20.0", TimeUnits.SECOND));
266          offsets.add(fo);
267        }
268        
269        return scan;
270      }
271    
272      public PointingScan makePointingScanFor(ScanLoop parentLoop)
273      {
274        PointingScan scan =
275          (PointingScan)Scan.createFor(ScanMode.INTERFEROMETRIC_POINTING);
276    
277        initScan(scan, parentLoop);
278    
279        SortedSet<String> patterns  = PointingScan.getNamesOfStandardPatterns();
280    
281        int choice = random.nextInt(patterns.size());
282    
283        int p = 0;
284        for (String name : patterns)
285        {
286          if (p == choice)
287          {
288            scan.setPositions(name, new TimeDuration("0.75", TimeUnits.MINUTE));
289            break;
290          }
291          p++;
292        }
293        
294        return scan;
295      }
296      
297      public SwitchingScan makeSwitchingScanFor(ScanLoop parentLoop)
298      {
299        SwitchingScan scan = (SwitchingScan)Scan.createFor(ScanMode.FAST_SWITCHING);
300        
301        initScan(scan, parentLoop);
302        
303        scan.setApplyAutoPhasing(random.nextBoolean());
304        scan.setTimeOnTarget(new TimeDuration(new BigDecimal(15 + random.nextInt(45)),
305                                              TimeUnits.SECOND));
306    
307        Project  project  = scan.getProject();
308        Proposal proposal = (project == null) ? null : project.getProposal();
309    
310        List<SwitchSetting> settings = scan.getSwitchSettings();
311        int count = 1 + random.nextInt(2);
312        for (int s=0; s < count; s++)
313        {
314          SwitchSetting ss = new SwitchSetting();
315    
316          ss.setUseForAutophasing(random.nextBoolean());
317          ss.setTimeAtSetting(new TimeDuration(new BigDecimal(15 + random.nextInt(45)),
318                                               TimeUnits.SECOND));
319    
320          ss.setResource(resourceBuilder.makeResource(makeCode()));
321    
322          if (proposal != null)
323          {
324            List<Source> sources = new ArrayList<Source>(proposal.getSources());
325            ss.setSourceCatalogEntry(sources.get(random.nextInt(sources.size())).clone());
326          }
327          else
328            ss.setSourceCatalogEntry(sourceBuilder.makeSource(makeCode()));
329          
330          settings.add(ss);
331        }
332        
333        return scan;
334      }
335      
336      public TippingScan makeTippingScanFor(ScanLoop parentLoop)
337      {
338        TippingScan scan = (TippingScan)Scan.createFor(ScanMode.TIPPING);
339        
340        initScan(scan, parentLoop);
341        
342        scan.setSourceCatalogEntry(null);
343        
344        scan.setAllowReverseOrder(random.nextBoolean());
345        scan.setOrder(random.nextBoolean() ? TippingOrder.HIGH_TO_LOW
346                                           : TippingOrder.LOW_TO_HIGH);
347        scan.setAzimuth(new Angle(new BigDecimal(random.nextInt(360))));
348        
349        BigDecimal minEl = BigDecimal.valueOf( 5.0 + (double)random.nextInt(15));
350        BigDecimal maxEl = BigDecimal.valueOf(70.0 + (double)random.nextInt(15));
351        
352        scan.addElevations(new Angle(minEl), new Angle(maxEl),
353                           5 + random.nextInt(5),
354                           new TimeDuration("2.0", TimeUnits.MINUTE));
355        return scan;
356      }
357      
358      public void fillScanTimeSpec(ScanTimeSpecification spec)
359      {
360        if (random.nextBoolean())  //DURATION
361        {
362          ScanTimeType type = ScanTimeType.ON_SOURCE_SIDEREAL;
363          
364          switch (random.nextInt(4))
365          {
366            case 0: type = ScanTimeType.ON_SOURCE_SIDEREAL; break;
367            case 1: type = ScanTimeType.ON_SOURCE_UT;       break;
368            case 2: type = ScanTimeType.DURATION_SIDEREAL;  break;
369            case 3: type = ScanTimeType.DURATION_UT;        break;
370          }
371          int minutes = random.nextInt(50) + 10;
372          int seconds = random.nextInt(50) + 10;
373          spec.set(type, TimeDuration.parse(""+minutes+":"+seconds));
374        }
375        else  //POINT IN TIME (no longer supporting START times)
376        {
377          if (random.nextBoolean())  //LST
378          {
379            TimeOfDay tod = new TimeOfDay();
380            tod.set(BigDecimal.valueOf(random.nextInt(86400)));
381            spec.set(ScanTimeType.STOP_LST, tod);
382          }
383          else                       //UT
384          {
385            Calendar cal = Calendar.getInstance();
386            cal.set(random.nextInt(5) + 2008,
387                    random.nextInt(12),
388                    random.nextInt(27) + 1,
389                    random.nextInt(24),
390                    random.nextInt(60),
391                    random.nextInt(60));
392            spec.set(ScanTimeType.STOP_UT, cal.getTime());
393          }
394        }
395      }
396    
397      public void addDopplerSpecs(Scan scan)
398      {
399        int specCount = random.nextInt(5);
400        
401        for (int s=0; s < specCount; s++)
402          scan.setDopplerSpecs(makeCode(), makeDopplerSpecs());
403      }
404      
405      public ScanDopplerSpecs makeDopplerSpecs()
406      {
407        ScanDopplerSpecs specs = new ScanDopplerSpecs();
408        
409        //Add position info to 2 of every 3 created
410        if (random.nextInt(3) % 3 != 0)
411          specs.setPosition(sourceBuilder.makeSource("").getPosition());
412        
413        //Add velocity info to 1 of every 3 created
414        if (random.nextInt(3) % 3 == 0)
415        {
416          VelocityFrame restFrame = VelocityFrame.UNKNOWN;
417          
418          while (restFrame.equals(VelocityFrame.UNKNOWN) ||
419                 restFrame.equals(VelocityFrame.COSMIC_MICROWAVE_BACKGROUND))
420            restFrame = ENUM_UTIL.getRandomValueFor(VelocityFrame.class);
421          
422          BigDecimal kms = BigDecimal.valueOf((double)random.nextInt(10000)/10.0);
423          LinearVelocity velocity = new LinearVelocity(kms);
424          
425          VelocityConvention vc = VelocityConvention.RELATIVISTIC;
426          while (vc.equals(VelocityConvention.RELATIVISTIC))
427            vc = ENUM_UTIL.getRandomValueFor(VelocityConvention.class);
428          
429          specs.setVelocity(velocity, restFrame, vc);
430        }
431        
432        return specs;
433      }
434      
435      private static final String[] COMMENTS =
436      {
437        "I have nothing to say, but this space was here, so I used it.",
438        "I took a stand against the man from Japan in the tan sedan who planned to ban this grand scan.",
439        "If I want your opinion, I'll give it to you.",
440        "This scan is a scam, Ma'm.",
441        "No comment."
442      };
443    
444      /** Sometimes sets comment, sometimes removes comments. */
445      public void setComment(Scan scan)
446      {
447        int size  = COMMENTS.length;
448        int index = random.nextInt(2 * size);
449    
450        scan.setComments(index < size ? COMMENTS[index] : null);
451      }
452      
453      //============================================================================
454      // HELPERS
455      //============================================================================
456    
457      private String makeCode()
458      {
459        StringBuilder srcName = new StringBuilder();
460    
461        int numChars = random.nextInt(3)+3;
462        for (int i=0; i < numChars; i++)
463          srcName.append((char)('a'+random.nextInt(25)));
464    
465        return srcName.toString();
466      }
467    }