001 package edu.nrao.sss.model.project; 002 003 import edu.nrao.sss.astronomy.CelestialCoordinateSystem; 004 import edu.nrao.sss.astronomy.DopplerTracker; 005 import edu.nrao.sss.astronomy.Epoch; 006 import edu.nrao.sss.astronomy.SkyPosition; 007 import edu.nrao.sss.astronomy.CoordinateConversionException; 008 import edu.nrao.sss.astronomy.VelocityFrame; 009 import edu.nrao.sss.measure.ArcUnits; 010 import edu.nrao.sss.measure.Angle; 011 import edu.nrao.sss.measure.Frequency; 012 import edu.nrao.sss.measure.FrequencyUnits; 013 import edu.nrao.sss.measure.JulianDate; 014 import edu.nrao.sss.measure.LinearVelocityUnits; 015 import edu.nrao.sss.measure.LocalSiderealTime; 016 import edu.nrao.sss.model.project.scan.Scan; 017 import edu.nrao.sss.model.project.scan.ScanIntent; 018 import edu.nrao.sss.model.project.scan.DelayScan; 019 import edu.nrao.sss.model.project.scan.FocusScan; 020 import edu.nrao.sss.model.project.scan.PointingScan; 021 import edu.nrao.sss.model.project.scan.SimpleScan; 022 import edu.nrao.sss.model.project.scan.SwitchingScan; 023 import edu.nrao.sss.model.project.scan.TippingOrder; 024 import edu.nrao.sss.model.project.scan.TippingScan; 025 import edu.nrao.sss.model.project.scan.AntennaWrap; 026 import edu.nrao.sss.model.source.Source; 027 import edu.nrao.sss.model.resource.CorrelatorName; 028 import edu.nrao.sss.model.resource.Resource; 029 import edu.nrao.sss.model.resource.ReceiverBand; 030 import edu.nrao.sss.model.resource.TelescopeBackend; 031 import edu.nrao.sss.model.resource.vla.CorrelatorMode; 032 import edu.nrao.sss.model.resource.vla.IFCode; 033 import edu.nrao.sss.model.resource.vla.IFPair; 034 import edu.nrao.sss.model.resource.vla.ProcessingType; 035 import edu.nrao.sss.model.resource.vla.VlaConfiguration; 036 import edu.nrao.sss.util.StringUtil; 037 import edu.nrao.sss.util.SourceNotFoundException; 038 import edu.nrao.sss.validation.FailureSeverity; 039 import edu.nrao.sss.validation.ValidationException; 040 import edu.nrao.sss.validation.ValidationFailure; 041 042 import java.math.BigDecimal; 043 import java.util.Date; 044 import java.util.List; 045 import java.util.Map; 046 import java.util.Set; 047 048 import org.apache.log4j.Logger; 049 050 /** 051 * This class provides methods for converting a scheduling block to a Jython 052 * Observe Script for the EVLA Monitor and Control system. 053 * <p>This class expects the M&C system to provide the generated script with 054 * this set of header code: 055 * <code><pre> 056 * from edu.nrao.evla.observe import Array 057 * from edu.nrao.evla.observe import Antenna 058 * from edu.nrao.evla.observe import LoIfSetup 059 * from edu.nrao.evla.observe import VLALoIfSetup 060 * from edu.nrao.evla.observe import Source 061 * from edu.nrao.evla.observe import Subarray 062 * from edu.nrao.evla.observe import Fringefinders 063 * from edu.nrao.evla.observe import VlaCorrelatorSetup 064 * from edu.nrao.evla.observe import Intention 065 * from edu.nrao.evla.observe import AzElInterferometerModel 066 * </pre></code> 067 * as well as defining the {@code array} and {@code subarray} variables to 068 * point at appropriate {@code edu.nrao.evla.observe.Array} and {@code 069 * edu.nrao.evla.observe.Subarray} java objects.</p> 070 * <p>The scripts generated by this class will also depend on standard helper 071 * scripts being available at {@code /home/mchost/evla/include}. Specifically:</p> 072 * <table border="1"> 073 * <thead><tr><th>Script</th><th>Use</th></tr></thead> 074 * <tbody> 075 * <tr><td>focus.py</td><td></td></tr> 076 * <tr><td>nodder.py</td><td></td></tr> 077 * <tr><td>nod.py</td><td></td></tr> 078 * <tr><td>onsource.py</td><td></td></tr> 079 * <tr><td>pointCycle.py</td><td></td></tr> 080 * <tr><td>point.py</td><td></td></tr> 081 * <tr><td>printers.py</td><td></td></tr> 082 * <tr><td>radecname.py</td><td></td></tr> 083 * <tr><td>raster.py</td><td></td></tr> 084 * <tr><td>refPoint.py</td><td></td></tr> 085 * <tr><td>sdvlbi.py</td><td></td></tr> 086 * <tr><td>tip.py</td><td></td></tr> 087 * <tr><td>tmjd.py</td><td></td></tr> 088 * <tr><td>vlapoint.py</td><td></td></tr> 089 * <tr><td>vlaraster.py</td><td></td></tr> 090 * </tbody> 091 * </table> 092 */ 093 public class ObserveScriptBuilder extends AbstractScheduleIterator 094 { 095 private static final Logger log = Logger.getLogger(ObserveScriptBuilder.class); 096 097 private static final StringUtil sUtil = StringUtil.getInstance(); 098 099 private StringBuilder script = null; 100 private StringBuilder indentString = null; 101 private String projectCode = null; 102 private Date overrideStart = null; 103 private boolean onLastScan = false; 104 private boolean foundTippingScan = false; 105 private boolean foundPtgScan = false; 106 private boolean swappedIFs = false; 107 108 /** 109 * Creates an ObserveScriptBuilder. 110 */ 111 public ObserveScriptBuilder() {} 112 113 /** 114 * @return {@code getScriptAsStringBuilder(sb).toString()}. 115 */ 116 public String toScript(SchedulingBlock sb) 117 throws ValidationException 118 { 119 return getScriptAsStringBuilder(sb).toString(); 120 } 121 122 /** 123 * @return {@code getScriptAsStringBuilder(sb, start).toString()}. 124 */ 125 public String toScript(SchedulingBlock sb, Date start) 126 throws ValidationException 127 { 128 return getScriptAsStringBuilder(sb, start).toString(); 129 } 130 131 /** 132 * @return a StringBuilder holding python code that will carry out 133 * instructions represented by {@code sb}. The python code will calculate 134 * all source positions and scan start times using startTime as a reference. 135 */ 136 public StringBuilder getScriptAsStringBuilder(SchedulingBlock sb) 137 throws ValidationException 138 { 139 return getScriptAsStringBuilder(sb, null); 140 } 141 142 /** 143 * If start != null, override the SB's start time to be {@code start} even if 144 * sb is supposed to be dynamically scheduled. NOTE: in the event that sb is 145 * dynamic, a dynamic schedule is still generated, but {@code start} is used to 146 * calculate all slew times. 147 * 148 * @see #getScriptAsStringBuilder(SchedulingBlock) 149 */ 150 public StringBuilder getScriptAsStringBuilder(SchedulingBlock sb, Date start) 151 throws ValidationException 152 { 153 this.overrideStart = start; 154 155 this.script = new StringBuilder(); 156 this.indentString = new StringBuilder(); 157 158 this.updatedRefPtgResultsAvailable = false; 159 this.refPtgResultsAvailable = false; 160 this.refPtgInUse = false; 161 this.foundTippingScan = false; 162 this.foundPtgScan = false; 163 this.onLastScan = false; 164 165 this.projectCode = sb.getProgramBlock().getProject().getProjectCode(); 166 167 try 168 { 169 iterateOverSchedulingBlock(sb); 170 } 171 172 catch (CoordinateConversionException cce) 173 { 174 getValidationFailures().add(new ValidationFailure( 175 "There has been an internal error trying to convert coordinate systems.", 176 "There has been an internal error trying to convert coordinate systems." + cce.getMessage(), 177 FailureSeverity.ERROR, 178 sb, 179 "", 180 "" 181 )); 182 183 log.error(cce); 184 } 185 186 catch (IllegalArgumentException iae) 187 { 188 getValidationFailures().add(new ValidationFailure( 189 iae.getMessage(), 190 iae.getMessage(), 191 FailureSeverity.ERROR, 192 sb, 193 "", 194 "" 195 )); 196 } 197 198 catch (IllegalStateException ise) 199 { 200 getValidationFailures().add(new ValidationFailure( 201 ise.getMessage(), 202 ise.getMessage(), 203 FailureSeverity.ERROR, 204 sb, 205 "", 206 "" 207 )); 208 } 209 210 // This is being caught by validation, only need to handle it separately 211 // here if we're not validating for some reason. 212 catch (SourceNotFoundException snf) 213 { 214 if (!getValidatesSchedulingBlock()) 215 { 216 getValidationFailures().add(new ValidationFailure( 217 snf.getMessage(), 218 snf.getMessage(), 219 FailureSeverity.ERROR, 220 sb, 221 "", 222 "" 223 )); 224 } 225 } 226 227 return this.script; 228 } 229 230 public void setSimulatorStartPosition(Angle az, Angle el) 231 throws IllegalArgumentException 232 { 233 this.sim.setCurrentAntennaAzimuth(az); 234 this.sim.setCurrentAntennaElevation(el); 235 } 236 237 protected void beforeIteratingAction() 238 { 239 // override the start time (even if we're dynamically scheduled) if a time 240 // was specified. 241 if (this.overrideStart != null) 242 { 243 this.startMjd = new JulianDate(this.overrideStart); 244 this.dynamicSched = false; 245 } 246 247 if (this.dynamicSched) 248 { 249 this.script.append(this.indentString); 250 this.script.append("startTime = array.time()\n"); 251 } 252 } 253 254 protected void beforeLastScanAction(Scan scan) 255 { 256 this.onLastScan = true; 257 } 258 259 protected void afterLastScanAction(Scan scan) 260 { 261 this.script.insert(0, "programName = 'OPT'\n"); 262 this.script.insert(0, "obsCode = '\"" + this.projectCode + "\"'\n"); 263 264 if (this.foundTippingScan) 265 this.script.insert(0, "execfile('/home/mchost/evla/include/tip.py')\n\n"); 266 267 if (this.foundPtgScan) 268 { 269 this.script.insert(0, "execfile('/home/mchost/evla/include/vlapoint.py')\n"); 270 this.script.insert(0, "execfile('/home/mchost/evla/include/point.py')\n"); 271 this.script.insert(0, "execfile('/home/mchost/evla/include/raster.py')\n"); 272 } 273 274 this.script.insert(0, "execfile('/home/mchost/evla/include/printers.py')\n"); 275 this.script.insert(0, "from math import cos, sin, asin, pi, fabs\n"); 276 this.script.insert(0, getFormattedOperatorComments(scan.getSchedulingBlock())); 277 this.script.insert(0, "# EVLA Project Code: " + this.projectCode + "\n\n"); 278 279 // Output a bogus termination source to ensure our last scan actually 280 // finishes (this is due somewhat to the fact that the Executor scans ahead 281 // 1 scan) 282 this.script.append(this.indentString); 283 this.script.append("\n\n# Bogus Terminations Scan (to ensure the user's last scan gets its full time).\n"); 284 285 this.script.append(this.indentString); 286 this.script.append("myPrint('Setting up termination source.')\n"); 287 288 this.script.append(this.indentString); 289 this.script.append("intentionEnd = Intention()\n"); 290 291 this.script.append(this.indentString); 292 this.script.append("intentionEnd.addIntent('suppressData=\"true\"')\n"); 293 294 Source src = scan.getSource(this.startMjd.toDate()); 295 296 this.script.append(this.indentString); 297 this.script.append("src = subarray.getSource()\n"); 298 299 this.script.append(this.indentString); 300 this.script.append("src.setIntention(intentionEnd)\n"); 301 302 this.script.append(this.indentString); 303 this.script.append("subarray.setSource(src)\n"); 304 305 // Since this is the last scan, if we're dynamic, startTime didn't 306 // automatically get updated, so we do it here. 307 this.script.append(this.indentString); 308 this.script.append("subarray.execute("); 309 if (this.dynamicSched) 310 { 311 this.script.append("startTime + "); 312 this.script.append(sUtil.formatNoScientificNotation(this.endMjd.modifiedValue().subtract(this.startMjd.modifiedValue()), 1, 8)); 313 } 314 315 else 316 this.script.append(sUtil.formatNoScientificNotation(this.endMjd.modifiedValue(), 1, 8)); 317 318 this.script.append(")\n"); 319 } 320 321 /** 322 * Generates python code that is necessary regardless of the scan mode, that 323 * must come before each scan. 324 */ 325 protected void beforeScanAction(Scan scan) 326 { 327 swappedIFs = false; 328 329 this.script.append(this.indentString); 330 this.script.append("\n\n# Scan num. "); 331 this.script.append(this.currentScanNumber); 332 this.script.append("\n"); 333 334 String loifVar = "loif"; 335 String loifVarVla = "vlaloif"; 336 String corrmodeVar = "corrmode"; 337 338 if (!scan.getUseResourceOfPriorScan()) 339 { 340 createLoIfSetup(loifVar, loifVarVla, scan, this.script); 341 createVlaCorrelatorSetup(corrmodeVar, loifVarVla, scan, this.script); 342 } 343 344 // TippingScans do not have a source set, so only do the source/intention 345 // setup if this is not a TippingScan 346 String operatorMessage = ""; 347 if (!(scan instanceof TippingScan)) 348 { 349 Source src = scan.getSource(this.startMjd.toDate()); 350 351 operatorMessage += "Setting up source " + src.getName() + " "; 352 353 String sourceVar = "src"; 354 createSource(sourceVar, src, scan, this.script); 355 356 if (!scan.getUseResourceOfPriorScan()) 357 { 358 //intents = Intention() 359 this.script.append(this.indentString); 360 this.script.append("intents = Intention()\n"); 361 } 362 363 // Make a new intents object out of the old one but don't allow the 364 // CalibratorCode and ObserveMode entries to be copied over. 365 else 366 { 367 this.script.append(this.indentString); 368 this.script.append("newIntents = Intention()\n"); 369 this.script.append(this.indentString); 370 this.script.append("for i in intents.getIntents():\n"); 371 this.script.append(this.indentString); 372 this.script.append("\tif not (i.startswith('ObserveMode') or i.startswith('CalibratorCode')):\n"); 373 this.script.append(this.indentString); 374 this.script.append("\t\tnewIntents.addIntent(i)\n"); 375 this.script.append(this.indentString); 376 this.script.append("intents = newIntents\n"); 377 } 378 379 // intents.addIntent("ObsCode", "projCode") 380 this.script.append(this.indentString); 381 this.script.append("intents.addIntent('ObsCode', obsCode)\n"); 382 383 // intents.addIntent("ScanNumber","1") 384 this.script.append(this.indentString); 385 this.script.append("intents.addIntent('ScanNumber="); 386 this.script.append(this.currentScanNumber); 387 this.script.append("')\n"); 388 389 // intents.addIntent("SourceQualifier","1") 390 /* TODO: Figure out if scientists actually want this or not and how to implement it. 391 this.script.append(this.indentString); 392 this.script.append("intents.addIntent('SourceQualifier', '"); 393 this.script.append(this.currentScanNumber); 394 this.script.append("')\n"); 395 */ 396 397 this.script.append(this.indentString); 398 this.script.append("intents.addIntent('stopLST', "); 399 400 if (this.dynamicSched) 401 { 402 // intents.addIntent('stopLST', str(array.lst(array.time()+140./86400))) 403 this.script.append("str(array.lst(startTime + "); 404 this.script.append(sUtil.formatNoScientificNotation(this.endMjd.modifiedValue().subtract(this.startMjd.modifiedValue()), 1, 8)); 405 this.script.append("))"); 406 } 407 408 else 409 { 410 LocalSiderealTime stopLST = new LocalSiderealTime(this.endMjd); 411 this.script.append("'"); 412 this.script.append(sUtil.formatNoScientificNotation(stopLST.toTimeOfDay().toFractionOfDay(), 1, 8)); 413 this.script.append("'"); 414 } 415 416 this.script.append(")\n"); 417 418 Set<ScanIntent> intents = scan.getIntents(); 419 if (!intents.isEmpty()) 420 { 421 this.script.append(this.indentString); 422 this.script.append("intents.addIntent('ScanIntent=\""); 423 424 String observeMode = null; 425 boolean setCalCode = false; 426 427 // Fill the "ScanIntent" Intention 428 for (ScanIntent intent : intents) 429 { 430 this.script.append(intent.getSdmText()); 431 this.script.append(" "); 432 433 switch (intent) 434 { 435 // Not supported yet. 436 case CALIBRATE_ABSOLUTE_POSITION: 437 break; 438 439 case CALIBRATE_BANDPASS: 440 setCalCode = true; 441 break; 442 443 case CALIBRATE_COMPLEX_GAIN: 444 setCalCode = true; 445 break; 446 447 // Not supported yet. 448 case CALIBRATE_DELAY_AMPLITUDE_STYLE: 449 break; 450 451 case CALIBRATE_DELAY_PHASE_STYLE: 452 setCalCode = true; 453 observeMode = "D"; 454 break; 455 456 case CALIBRATE_FLUX_DENSITY_SCALE: 457 setCalCode = true; 458 break; 459 460 // Not supported yet. 461 case CALIBRATE_FOCUS: 462 break; 463 464 case CALIBRATE_OFFSET_POINTING: 465 setCalCode = true; 466 observeMode = "IR"; 467 break; 468 469 case CALIBRATE_POLARIZATION_ANGLE: 470 setCalCode = true; 471 break; 472 473 case CALIBRATE_POLARIZATION_LEAKAGE: 474 setCalCode = true; 475 break; 476 477 // Not supported yet. 478 case DETERMINE_ANTENNA_GLOBAL_POINTING_MODEL: 479 break; 480 481 // Not supported yet. 482 case DETERMINE_AUTOPHASE: 483 break; 484 485 case DETERMINE_OPACITY_TIPPING_STYLE: 486 observeMode = "TE"; 487 break; 488 489 // Not supported yet. 490 case DETERMINE_RFI: 491 break; 492 493 // Not supported yet. 494 case DETERMINE_SINGLE_DISH_POINTING: 495 break; 496 497 // Not supported yet. 498 case MAP_ANTENNA_SURFACE: 499 break; 500 501 // Not supported yet. 502 case OBSERVE_PULSAR: 503 break; 504 505 case OBSERVE_TARGET: 506 break; 507 508 // Not supported yet. 509 case OTHER: 510 break; 511 512 // Not supported yet. 513 case TIME_PULSAR: 514 break; 515 } 516 } 517 518 // remove the last space 519 this.script.deleteCharAt(this.script.length() - 1); 520 521 // finish the method call. 522 this.script.append("\"')\n"); 523 524 // append an ObserveMode if necessary 525 if (observeMode != null) 526 { 527 // intents.addIntent("ObserveMode","D|IR") 528 this.script.append(this.indentString); 529 this.script.append("intents.addIntent('ObserveMode=\""); 530 this.script.append(observeMode); 531 this.script.append("\"')\n"); 532 } 533 534 String calCode = " "; 535 if (setCalCode) 536 { 537 //Now we know it's a calibrator so default it to "Z" 538 calCode = "Z"; 539 540 // Look up A,B,C,T calibrator code in the source. If we can't find 541 // one, leave it set to 'Z'; 542 Map<String,String> udvs = src.getUserDefinedValues(); 543 String posErr = udvs.get("Positional Error"); 544 545 if (posErr != null && posErr.length() > 0) 546 { 547 if (posErr.startsWith("< 0.002")) 548 calCode = "A"; 549 550 else if (posErr.startsWith("0.002 - 0.01")) 551 calCode = "B"; 552 553 else if (posErr.startsWith("0.01 - 0.15")) 554 calCode = "C"; 555 556 else if (posErr.startsWith("> 0.15")) 557 calCode = "T"; 558 } 559 } 560 561 //intents.addIntent("CalibratorCode", "Z") 562 this.script.append(this.indentString); 563 this.script.append("intents.addIntent('CalibratorCode=\""); 564 this.script.append(calCode); 565 this.script.append("\"')\n"); 566 } 567 568 // Only output these intentions if getUseResourceOfPriorScan() is false. 569 // If it is true, we still need these intentions set, however, we are 570 // doing that by re-using the previous intents script variable so they 571 // are already set! 572 if (!scan.getUseResourceOfPriorScan()) 573 { 574 VlaConfiguration conf = getVlaConfiguration(scan); 575 CorrelatorMode mode = conf.getCorrelatorMode(); 576 char[] restFrame = null; 577 char[] velocConv = null; 578 579 Date start = this.startMjd.toDate(); 580 581 //intents.addIntent('BandwidthCode="4444"') 582 /* 2008-Dec-31 email from Ken S. to Emmanuel M. said this is not needed 583 this.script.append("intents.addIntent('BandwidthCode=\""); 584 this.script.append(conf.getBandwidthCode(IFCode.A).getCodeNumber()); 585 this.script.append(conf.getBandwidthCode(IFCode.B).getCodeNumber()); 586 this.script.append(conf.getBandwidthCode(IFCode.C).getCodeNumber()); 587 this.script.append(conf.getBandwidthCode(IFCode.D).getCodeNumber()); 588 this.script.append("\"')\n"); 589 */ 590 if (mode.uses(IFPair.AC) && !conf.isSkyFrequency(IFPair.AC)) 591 { 592 Frequency acCenter = conf.getCentralFrequency(IFPair.AC); 593 594 // varName.append('RestFreqA=xyz') 595 BigDecimal f = acCenter.toUnits(FrequencyUnits.MEGAHERTZ); 596 this.script.append(this.indentString); 597 this.script.append("intents.addIntent('RestFreqA="); 598 this.script.append(sUtil.formatNoScientificNotation(f, 0, 8)); 599 this.script.append("')\n"); 600 601 this.script.append(this.indentString); 602 this.script.append("intents.addIntent('RestFreqC="); 603 this.script.append(sUtil.formatNoScientificNotation(f, 0, 8)); 604 this.script.append("')\n"); 605 606 DopplerTracker acTracker = scan.getDopplerTracker("AC", start, acCenter); 607 608 f = acTracker.getSourceVelocity().toUnits(LinearVelocityUnits.KILOMETERS_PER_SECOND); 609 this.script.append(this.indentString); 610 this.script.append("intents.addIntent('VelocityA="); 611 this.script.append(sUtil.formatNoScientificNotation(f, 0, 8)); 612 this.script.append("')\n"); 613 614 this.script.append(this.indentString); 615 this.script.append("intents.addIntent('VelocityC="); 616 this.script.append(sUtil.formatNoScientificNotation(f, 0, 8)); 617 this.script.append("')\n"); 618 619 // Check Velocity Frame 620 restFrame = new char[4]; 621 restFrame[0] = 'L'; 622 restFrame[1] = 'L'; 623 restFrame[2] = 'L'; 624 restFrame[3] = 'L'; 625 VelocityFrame frame = acTracker.getVelocityFrame(); 626 switch (frame) 627 { 628 case TOPOCENTRIC: 629 restFrame[0] = 'T'; 630 restFrame[2] = 'T'; 631 break; 632 633 case GEOCENTRIC: 634 restFrame[0] = 'G'; 635 restFrame[2] = 'G'; 636 break; 637 638 case BARYCENTRIC: 639 restFrame[0] = 'B'; 640 restFrame[2] = 'B'; 641 break; 642 643 case LSR_KINEMATIC: 644 restFrame[0] = 'L'; 645 restFrame[2] = 'L'; 646 break; 647 648 default: 649 throw new IllegalArgumentException("We do not support the rest frame: " + frame); 650 } 651 652 // Check Velocity Convention 653 char velocConvCode = freqVelSwitch(acTracker); 654 655 velocConv = new char[4]; 656 velocConv[0] = velocConvCode; 657 velocConv[1] = 'V'; 658 velocConv[2] = velocConvCode; 659 velocConv[3] = 'V'; 660 } 661 662 if (mode.uses(IFPair.BD) && !conf.isSkyFrequency(IFPair.BD)) 663 { 664 Frequency bdCenter = conf.getCentralFrequency(IFPair.BD); 665 666 // varName.append('RestFreqB=xyz') 667 BigDecimal f = bdCenter.toUnits(FrequencyUnits.MEGAHERTZ); 668 this.script.append(this.indentString); 669 this.script.append("intents.addIntent('RestFreqB="); 670 this.script.append(sUtil.formatNoScientificNotation(f, 0, 8)); 671 this.script.append("')\n"); 672 673 this.script.append(this.indentString); 674 this.script.append("intents.addIntent('RestFreqD="); 675 this.script.append(sUtil.formatNoScientificNotation(f, 0, 8)); 676 this.script.append("')\n"); 677 678 DopplerTracker bdTracker = scan.getDopplerTracker("BD", start, bdCenter); 679 680 f = bdTracker.getSourceVelocity().toUnits(LinearVelocityUnits.KILOMETERS_PER_SECOND); 681 this.script.append(this.indentString); 682 this.script.append("intents.addIntent('VelocityB="); 683 this.script.append(sUtil.formatNoScientificNotation(f, 0, 8)); 684 this.script.append("')\n"); 685 686 this.script.append(this.indentString); 687 this.script.append("intents.addIntent('VelocityD="); 688 this.script.append(sUtil.formatNoScientificNotation(f, 0, 8)); 689 this.script.append("')\n"); 690 691 // Check Velocity Frame 692 if (restFrame == null) 693 { 694 restFrame = new char[4]; 695 restFrame[0] = 'L'; 696 restFrame[1] = 'L'; 697 restFrame[2] = 'L'; 698 restFrame[3] = 'L'; 699 } 700 701 VelocityFrame frame = bdTracker.getVelocityFrame(); 702 switch (frame) 703 { 704 case TOPOCENTRIC: 705 restFrame[1] = 'T'; 706 restFrame[3] = 'T'; 707 break; 708 709 case GEOCENTRIC: 710 restFrame[1] = 'G'; 711 restFrame[3] = 'G'; 712 break; 713 714 case BARYCENTRIC: 715 restFrame[1] = 'B'; 716 restFrame[3] = 'B'; 717 break; 718 719 case LSR_KINEMATIC: 720 restFrame[1] = 'L'; 721 restFrame[3] = 'L'; 722 break; 723 724 default: 725 throw new IllegalArgumentException("We do not support the rest frame: " + frame); 726 } 727 728 // Check Velocity Convention 729 char velocConvCode = freqVelSwitch(bdTracker); 730 731 if (velocConv == null) 732 { 733 velocConv = new char[4]; 734 velocConv[0] = 'V'; 735 velocConv[2] = 'V'; 736 } 737 velocConv[1] = velocConvCode; 738 velocConv[3] = velocConvCode; 739 } 740 741 if (restFrame != null) 742 { 743 this.script.append(this.indentString); 744 this.script.append("intents.addIntent('RestFrame=\""); 745 this.script.append(restFrame); 746 this.script.append("\"')\n"); 747 } 748 749 if (velocConv != null) 750 { 751 this.script.append(this.indentString); 752 this.script.append("intents.addIntent('FreqVelSwitch=\""); 753 this.script.append(velocConv); 754 this.script.append("\"')\n"); 755 } 756 } 757 758 //src.setIntention(intents) 759 this.script.append(this.indentString); 760 this.script.append(sourceVar); 761 this.script.append(".setIntention(intents)\n"); 762 763 //subarray.setSource(src) 764 this.script.append(this.indentString); 765 this.script.append("subarray.setSource("); 766 this.script.append(sourceVar); 767 this.script.append(")\n"); 768 } 769 770 else 771 operatorMessage += "Setting up tipping scan "; 772 773 774 // get the receiver band for output message 775 if (!scan.getUseResourceOfPriorScan()) 776 { 777 Resource resrc = scan.getResource(); 778 if (resrc != null) 779 { 780 Set<ReceiverBand> bands = resrc.getAntennaElectronics().getActiveReceivers(); 781 ReceiverBand first = bands.iterator().next(); 782 operatorMessage += "at " + first.getDisplayName() + "-band "; 783 } 784 785 else 786 operatorMessage += "using previous hardware configuration "; 787 } 788 789 operatorMessage += " [source #" + this.currentScanNumber + "]."; 790 791 if (!scan.getUseResourceOfPriorScan()) 792 { 793 //subarray.setLoIfSetup(loifVar) 794 this.script.append(this.indentString); 795 this.script.append("subarray.setLoIfSetup("); 796 this.script.append(loifVar); 797 this.script.append(")\n"); 798 799 //subarray.setVLALoIfSetup(loifVarVla) 800 this.script.append(this.indentString); 801 this.script.append("subarray.setVLALoIfSetup("); 802 this.script.append(loifVarVla); 803 this.script.append(")\n"); 804 805 //subarray.setCorrelatorSetup(corrmode) 806 this.script.append(this.indentString); 807 this.script.append("subarray.setCorrelatorSetup("); 808 this.script.append(corrmodeVar); 809 this.script.append(")\n"); 810 } 811 812 if (scan.getAllowOverTheTop()) 813 { 814 this.script.append(this.indentString); 815 this.script.append("subarray.setOverTheTop(1)\n"); 816 } 817 818 AntennaWrap wrap = scan.getAntennaWrap(); 819 this.script.append(this.indentString); 820 switch(wrap) 821 { 822 case COUNTERCLOCKWISE: 823 this.script.append("subarray.setWrap(-1)\n"); 824 break; 825 826 case CLOCKWISE: 827 this.script.append("subarray.setWrap(1)\n"); 828 break; 829 830 default: 831 this.script.append("subarray.setWrap(0)\n"); 832 } 833 834 //Now take care of the state for Ref. Pointing results. 835 if (scan.getApplyLastReferencePointing()) 836 { 837 if (this.refPtgResultsAvailable) 838 { 839 if (!this.refPtgInUse || this.updatedRefPtgResultsAvailable) 840 { 841 this.script.append(this.indentString); 842 this.script.append("subarray.useRefPointing(refptg)\n"); 843 this.refPtgInUse = true; 844 this.updatedRefPtgResultsAvailable = false; 845 } 846 } 847 848 else 849 { 850 //Error! This should have been caught by validation before getting here. 851 throw new IllegalStateException("There are no pointing results to apply for scan: " + 852 scan.getName() + ". This should have been caught by validation!"); 853 } 854 } 855 856 else 857 { 858 if (this.refPtgInUse) 859 { 860 this.script.append(this.indentString); 861 this.script.append("subarray.noRefPointing()\n"); 862 this.refPtgInUse = false; 863 } 864 } 865 866 this.script.append(this.indentString); 867 this.script.append("myPrint('"); 868 this.script.append(operatorMessage); 869 this.script.append("')\n"); 870 this.script.append("printTime('startTime=',"); 871 872 if (this.dynamicSched) 873 this.script.append("startTime"); 874 875 else 876 this.script.append(sUtil.formatNoScientificNotation(this.startMjd.modifiedValue(), 1, 8)); 877 878 this.script.append(")\n"); 879 } 880 881 //Returns a character for use in this kind of line: 882 // intention.addIntent('FreqVelSwitch="VVVV"') 883 private char freqVelSwitch(DopplerTracker dt) 884 { 885 //Issue: turns out DopplerTracker assumes a convention based on velocity units. 886 //Actually, DT uses SpectralLine class, which makes that assumption. 887 //The only conventions it uses is Radio & Redshift. The script logic handles 888 //only Radio and Optical. Luckily, when Z is converted to km/s, the Optical 889 //convention is then equivalent to the Redshift one. 890 891 //TODO this code points out that the DopplerTracker and/or associated classes 892 // need to become convention aware. 893 894 if (dt.getSourceVelocity().getUnits().equals(LinearVelocityUnits.Z)) 895 return 'Z'; 896 else 897 return 'V'; 898 } 899 900 protected void scanAction(Scan s) 901 { 902 switch(s.getMode()) 903 { 904 case AMPLITUDE_DELAY_CALIBRATING: 905 delayCalibratingToScript((DelayScan)s); 906 break; 907 908 case REFERENCE_FOCUSING: 909 focusingToScript((FocusScan)s); 910 break; 911 912 case FAST_SWITCHING: 913 switchingToScript((SwitchingScan)s); 914 break; 915 916 case TIPPING: 917 tippingToScript((TippingScan)s); 918 break; 919 920 case HOLOGRAPHY: 921 holographyToScript((PointingScan)s); 922 break; 923 924 case MOSAICKING: 925 mosaicingToScript((PointingScan)s); 926 break; 927 928 case SINGLE_DISH_POINTING: 929 singleDishPointingToScript((PointingScan)s); 930 break; 931 932 case INTERFEROMETRIC_POINTING: 933 interferometricPointingToScript((PointingScan)s); 934 break; 935 936 case STANDARD_OBSERVING: 937 default: 938 simpleScanToScript((SimpleScan)s); 939 } 940 } 941 942 /** 943 * Generates python code that is necessary regardless of the scan mode, that 944 * must come after each scan. 945 */ 946 protected void afterScanAction(Scan scan) 947 { 948 // Don't bother incrementing the startTime if this is the last scan. 949 if (this.dynamicSched && !this.onLastScan) 950 { 951 this.script.append(this.indentString); 952 this.script.append("startTime += "); 953 this.script.append(sUtil.formatNoScientificNotation(this.endMjd.modifiedValue().subtract(this.startMjd.modifiedValue()), 1, 8)); 954 this.script.append("\n"); 955 } 956 } 957 958 /** 959 * Returns the operator comments contained in {@code sb} formatted by 960 * placing "#"'s at the beginning of the string and before and after any line 961 * breaks in the comments. Also includes any comments in {@code sb}'s 962 * Project. This is currently being used to contain the user's contact info. 963 */ 964 private String getFormattedOperatorComments(SchedulingBlock sb) 965 { 966 String comments = sb.getProgramBlock().getProject().getComments() + "\n" + 967 sb.getCommentsToOperator(); 968 969 comments = comments.replaceAll("\n", "\n# "); 970 971 // TODO: remove this hard coded comment when we have nothing but evla 972 // antennas left. 973 return "# " + comments + "\n# This script should only be run on EVLA antennas.\n\n\n"; 974 } 975 976 private VlaConfiguration getVlaConfiguration(Scan scan) 977 { 978 Resource resrc = scan.getResource(); 979 if (resrc != null) 980 { 981 List<TelescopeBackend> backends = resrc.getBackends(); 982 if (backends != null && !backends.isEmpty()) 983 { 984 TelescopeBackend conf = backends.get(0); 985 if (conf.getName().equals(CorrelatorName.VLA)) 986 return (VlaConfiguration)conf; 987 988 else 989 { 990 //ERROR, this should have been caught by validation before getting here! 991 throw new IllegalStateException("The backend assigned to resource: " + 992 resrc.getName() + " is not the VLA correlator. This should have been caught by validation!"); 993 } 994 } 995 996 else 997 { 998 //ERROR, this should have been caught by validation before getting here! 999 throw new IllegalStateException("There are is no backend assigned to resource: " + 1000 resrc.getName() + ". This should have been caught by validation!"); 1001 } 1002 } 1003 1004 else 1005 { 1006 //ERROR, this should have been caught by validation before getting here! 1007 throw new IllegalStateException("There is no resource assigned to scan: " + 1008 scan.getName() + ". This should have been caught by validation!"); 1009 } 1010 } 1011 1012 /** 1013 * Adds a new python definition for the LoIfSetup used by {@code scan} to 1014 * {@code builder} with the variable name {@code varName}. 1015 */ 1016 private void createLoIfSetup(String varName, String vlaVarName, Scan scan, StringBuilder builder) 1017 { 1018 VlaConfiguration conf = getVlaConfiguration(scan); 1019 1020 //Now output a resource definition 1021 Frequency acCenter = conf.getCentralFrequency(IFPair.AC); 1022 Frequency bdCenter = conf.getCentralFrequency(IFPair.BD); 1023 1024 Date start = this.startMjd.toDate(); 1025 1026 CorrelatorMode mode = conf.getCorrelatorMode(); 1027 1028 DopplerTracker acTracker = (mode.uses(IFPair.AC) && !conf.isSkyFrequency(IFPair.AC))? 1029 scan.getDopplerTracker("AC", start, acCenter) : null; 1030 1031 DopplerTracker bdTracker = (mode.uses(IFPair.BD) && !conf.isSkyFrequency(IFPair.BD))? 1032 scan.getDopplerTracker("BD", start, bdCenter) : null; 1033 1034 try 1035 { 1036 String[] params = conf.calcLoIfSetupParams(acTracker, bdTracker, start); 1037 1038 builder.append(varName).append(" = LoIfSetup(\""); 1039 builder.append(params[0]).append("\","); 1040 builder.append(params[1]).append(","); 1041 builder.append(params[2]).append(","); 1042 builder.append(params[3]).append(")\n"); 1043 1044 builder.append(vlaVarName).append(" = VLALoIfSetup(\""); 1045 builder.append(params[0]).append("\")\n"); 1046 1047 swappedIFs = params[4].equals("swapped"); 1048 } 1049 1050 catch (CoordinateConversionException cce) 1051 { 1052 getValidationFailures().add(new ValidationFailure( 1053 "There has been an internal error trying to convert coordinate systems.", 1054 "There has been an internal error trying to convert coordinate systems." + cce.getMessage(), 1055 FailureSeverity.ERROR, 1056 scan.getSchedulingBlock(), 1057 "", 1058 "" 1059 )); 1060 1061 log.error(cce); 1062 } 1063 } 1064 1065 /** 1066 * Adds a new python definition for the VlaCorrelatorSetup used by {@code 1067 * scan} to {@code builder} with the variable name {@code varName}. 1068 */ 1069 private void createVlaCorrelatorSetup(String varName, String vlaLoifVarName, 1070 Scan scan, StringBuilder builder) 1071 { 1072 VlaConfiguration conf = getVlaConfiguration(scan); 1073 1074 //corrmode = VlaCorrelatorSetup() 1075 builder.append(varName); 1076 builder.append(" = VlaCorrelatorSetup()\n"); 1077 1078 CorrelatorMode mode = conf.getCorrelatorMode(); 1079 1080 // CONTINUUM is the default, no need to set it unless we're not 1081 // CONTINUUM. (saves bytes in the script). 1082 if (!CorrelatorMode.CONTINUUM.equals(mode)) 1083 { 1084 //corrmode.setMode("...") 1085 builder.append(varName); 1086 builder.append(".setMode(\""); 1087 builder.append(conf.getCorrelatorMode().getCode()); 1088 builder.append("\")\n"); 1089 } 1090 1091 //corrmode.setBandwidth(...) 1092 int a = conf.getBandwidthCode(IFCode.A).getCodeNumber(); 1093 int b = conf.getBandwidthCode(IFCode.B).getCodeNumber(); 1094 int c = conf.getBandwidthCode(IFCode.C).getCodeNumber(); 1095 int d = conf.getBandwidthCode(IFCode.D).getCodeNumber(); 1096 1097 if (swappedIFs) 1098 { 1099 int oldA = a; a = b; b = oldA; 1100 int oldC = c; c = d; d = oldC; 1101 } 1102 1103 builder.append(varName); 1104 builder.append(".setBandwidth("); 1105 builder.append(a); 1106 builder.append(","); 1107 builder.append(b); 1108 builder.append(","); 1109 builder.append(c); 1110 builder.append(","); 1111 builder.append(d); 1112 builder.append(")\n"); 1113 1114 //corrmode.setIntegration(...) 1115 builder.append(varName); 1116 builder.append(".setIntegration("); 1117 builder.append(sUtil.formatNoScientificNotation(conf.getIntegrationTime(), 1, 8)); 1118 builder.append(")\n"); 1119 1120 String ptCode = ProcessingType.getCombinationCode(conf.getSpectralProcessing()); 1121 1122 //If a blank code was returned don't bother setting it, as that is the 1123 //default anyway. 1124 if (!" ".equals(ptCode)) 1125 { 1126 //corrmode.setProcessing("...") 1127 builder.append(varName); 1128 builder.append(".setProcessing(\""); 1129 builder.append(ptCode); 1130 builder.append("\")\n"); 1131 } 1132 1133 //VLA LO/IF bandwidths (which really shouldn't be needed) 1134 builder.append(vlaLoifVarName).append(".setBandwidthA(").append(a).append(")\n"); 1135 builder.append(vlaLoifVarName).append(".setBandwidthB(").append(b).append(")\n"); 1136 builder.append(vlaLoifVarName).append(".setBandwidthC(").append(c).append(")\n"); 1137 builder.append(vlaLoifVarName).append(".setBandwidthD(").append(d).append(")\n"); 1138 } 1139 1140 /** 1141 * Adds a new python definition for the Source {@code s} to {@code builder} 1142 * with the variable name {@code varName}. 1143 */ 1144 private void createSource(String varName, Source s, Scan scan, StringBuilder builder) 1145 { 1146 createSource(varName, s, scan, builder, false); 1147 } 1148 1149 private void createSource(String varName, Source s, Scan scan, StringBuilder builder, boolean bogusFinishScan) 1150 { 1151 SkyPosition pos; 1152 1153 Date startTime = this.startMjd.toDate(); 1154 1155 try 1156 { 1157 EVLA_LST.setSolarTime(startTime); 1158 pos = s.getPosition().toPosition(CelestialCoordinateSystem.EQUATORIAL, 1159 Epoch.J2000, EVLA_LOCATION, EVLA_LST); 1160 } 1161 catch (CoordinateConversionException cce) 1162 { 1163 getValidationFailures().add(new ValidationFailure( 1164 "There has been an internal error trying to convert coordinate systems.", 1165 "There has been an internal error trying to convert coordinate systems." + cce.getMessage(), 1166 FailureSeverity.ERROR, 1167 scan.getSchedulingBlock(), 1168 "", 1169 "" 1170 )); 1171 1172 log.error(cce); 1173 pos = null; 1174 } 1175 1176 String ra = sUtil.formatNoScientificNotation(pos.getLongitude(startTime).toUnits(ArcUnits.RADIAN), 1, 10); 1177 String dec = sUtil.formatNoScientificNotation(pos.getLatitude(startTime).toUnits(ArcUnits.RADIAN), 1, 10); 1178 1179 //src = Source(raInRad, decInRad) 1180 //src.setName("Name") 1181 builder.append(varName); 1182 builder.append(" = Source("); 1183 builder.append(ra); 1184 builder.append(", "); 1185 builder.append(dec); 1186 builder.append(")\n"); 1187 builder.append(varName); 1188 builder.append(".setName(\""); 1189 builder.append(bogusFinishScan? "FINISH" : s.getName()); 1190 builder.append("\")\n"); 1191 } 1192 1193 /** 1194 * Generates python code specific to a SimpleScan. 1195 */ 1196 private void simpleScanToScript(SimpleScan scan) 1197 { 1198 //subarray.execute(mjd) 1199 this.script.append(this.indentString); 1200 this.script.append("subarray.execute("); 1201 1202 if (this.dynamicSched) 1203 this.script.append("startTime"); 1204 1205 else 1206 this.script.append(sUtil.formatNoScientificNotation(this.startMjd.modifiedValue(), 1, 8)); 1207 1208 this.script.append(")\n"); 1209 } 1210 1211 /** 1212 * Generates python code specific to a DelayScan. 1213 */ 1214 private void delayCalibratingToScript(DelayScan scan) {} 1215 1216 /** 1217 * Generates python code specific to a FocusScan. 1218 */ 1219 private void focusingToScript(FocusScan scan) {} 1220 1221 /** 1222 * Generates python code specific to a Holography Scan. 1223 */ 1224 private void holographyToScript(PointingScan scan) {} 1225 1226 /** 1227 * Generates python code specific to a Mosaicing Scan. 1228 */ 1229 private void mosaicingToScript(PointingScan scan) {} 1230 1231 /** 1232 * Generates python code specific to a Single Dish Pointing Scan. 1233 */ 1234 private void singleDishPointingToScript(PointingScan scan) {} 1235 1236 private boolean updatedRefPtgResultsAvailable = false; 1237 private boolean refPtgResultsAvailable = false; 1238 private boolean refPtgInUse = false; 1239 1240 /** 1241 * Generates python code specific to a Interferometric Pointing Scan. 1242 */ 1243 private void interferometricPointingToScript(PointingScan scan) 1244 { 1245 this.foundPtgScan = true; 1246 1247 /* 1248 Must save the results and keep track of any scans that have the 1249 applyLastPointing flag set to true. If it's true, 1250 subarray.useRefPointing(ptg) else subarray.noRefPointing(). 1251 */ 1252 this.script.append(this.indentString); 1253 this.script.append("refptg = vlaPointing("); 1254 1255 if (this.dynamicSched) 1256 { 1257 this.script.append("startTime, "); 1258 this.script.append("startTime + "); 1259 this.script.append(sUtil.formatNoScientificNotation(this.endMjd.modifiedValue().subtract(this.startMjd.modifiedValue()), 1, 8)); 1260 this.script.append(", "); 1261 } 1262 1263 else 1264 { 1265 this.script.append(sUtil.formatNoScientificNotation(this.startMjd.modifiedValue(), 1, 8)); 1266 this.script.append(", "); 1267 this.script.append(sUtil.formatNoScientificNotation(this.endMjd.modifiedValue(), 1, 8)); 1268 this.script.append(", "); 1269 } 1270 1271 this.script.append("10.0, 2, [])\n"); 1272 1273 // update a flag if we are using ptg results already and have just updated the ptg (i.e. secondary ptg). 1274 this.updatedRefPtgResultsAvailable = this.refPtgResultsAvailable && this.refPtgInUse; 1275 this.refPtgResultsAvailable = true; 1276 } 1277 1278 /** 1279 * Generates python code specific to a SwitchingScan. 1280 */ 1281 private void switchingToScript(SwitchingScan scan) {} 1282 1283 /** 1284 * Generates python code specific to a TippingScan. 1285 */ 1286 private void tippingToScript(TippingScan scan) 1287 { 1288 this.foundTippingScan = true; 1289 1290 this.script.append(this.indentString); 1291 this.script.append("src = Source("); 1292 this.script.append(sUtil.formatNoScientificNotation(scan.getAzimuth().toUnits(ArcUnits.RADIAN), 1, 8)); 1293 if (scan.getOrder().equals(TippingOrder.LOW_TO_HIGH)) 1294 { 1295 // 20 deg. in rad 1296 this.script.append(",0.349065850399)\n"); 1297 this.script.append(this.indentString); 1298 this.script.append("src.setName('TIPUP')\n"); 1299 } 1300 1301 else 1302 { 1303 // 55 deg. in rad 1304 this.script.append(",0.959931088597)\n"); 1305 this.script.append(this.indentString); 1306 this.script.append("src.setName('TIPDOWN')\n"); 1307 } 1308 1309 this.script.append(this.indentString); 1310 this.script.append("intents = Intention()\n"); 1311 this.script.append(this.indentString); 1312 this.script.append("intents.addIntent('ScanIntent=\"SKYDIP\"')\n"); 1313 this.script.append(this.indentString); 1314 this.script.append("src.setIntention(intents)\n"); 1315 1316 this.script.append(this.indentString); 1317 this.script.append("subarray.setSource(src)\n"); 1318 1319 this.script.append(this.indentString); 1320 this.script.append("vlaTip(7,0,"); 1321 1322 if (this.dynamicSched) 1323 { 1324 this.script.append("startTime, "); 1325 this.script.append("startTime + "); 1326 this.script.append(sUtil.formatNoScientificNotation(this.endMjd.modifiedValue().subtract(this.startMjd.modifiedValue()), 1, 8)); 1327 } 1328 1329 else 1330 { 1331 this.script.append(sUtil.formatNoScientificNotation(this.startMjd.modifiedValue(), 1, 8)); 1332 this.script.append(", "); 1333 this.script.append(sUtil.formatNoScientificNotation(this.endMjd.modifiedValue(), 1, 8)); 1334 } 1335 1336 this.script.append(")\n"); 1337 } 1338 }