def __init__(self, sr): """Create widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.file = None self.nicfpsModel = TUI.Inst.NICFPS.NICFPSModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) self.tccModel = TUI.TCC.TCCModel.getModel() row = 0 # standard exposure status widget expStatusWdg = ExposeStatusWdg(master=sr.master, instName=InstName, helpURL=HelpURL) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg(master=sr.master, instName=InstName, expTypes="object", helpURL=HelpURL) self.expWdg.numExpWdg.helpText = "# of exposures at each spacing" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 gr = self.expWdg.gridder # add file widget self.fileWdg = RO.Wdg.FileWdg(master=self.expWdg, helpText="file of x y z etalon positions", helpURL=HelpURL) gr.gridWdg("Data File", self.fileWdg, colSpan=3) if sr.debug: self.expWdg.timeWdg.set(3) self.expWdg.fileNameWdg.set("debugtest")
def __init__(self, sr): """Create widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.file = None self.nicfpsModel = TUI.Inst.NICFPS.NICFPSModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) self.tccModel = TUI.TCC.TCCModel.getModel() row = 0 # standard exposure status widget expStatusWdg = ExposeStatusWdg( master=sr.master, instName=InstName, helpURL=HelpURL, ) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg( master=sr.master, instName=InstName, expTypes="object", helpURL=HelpURL, ) self.expWdg.numExpWdg.helpText = "# of exposures at each spacing" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 gr = self.expWdg.gridder # add file widget self.fileWdg = RO.Wdg.FileWdg( master=self.expWdg, helpText="file of x y z etalon positions", helpURL=HelpURL, ) gr.gridWdg("Data File", self.fileWdg, colSpan=3) if sr.debug: self.expWdg.timeWdg.set(3) self.expWdg.fileNameWdg.set("debugtest")
def __init__(self, sr): """Display exposure status and a few user input widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.expModel = ExposeModel.getModel(InstName) self.spicamModel = TUI.Inst.SPIcam.SPIcamModel.getModel() self.tccModel = TUI.TCC.TCCModel.getModel() self.sr = sr row = 0 expStatusWdg = ExposeStatusWdg( master=sr.master, instName=InstName, helpURL=HelpURL, ) expStatusWdg.grid(row=row, column=0, columnspan=3, sticky="w") row += 1 self.expWdg = ExposeInputWdg( master=sr.master, instName=InstName, expTypes="object", helpURL=HelpURL, ) self.expWdg.grid(row=row, column=0, columnspan=3, sticky="w") row += 1 wdgFrame = Tkinter.Frame(sr.master) gr = RO.Wdg.Gridder(wdgFrame, sticky="w") self.filterWdg = RO.Wdg.OptionMenu( master=self.expWdg, items=[], helpText="filter", helpURL=HelpURL, defMenu="Current", autoIsCurrent=True, ) self.expWdg.gridder.gridWdg("Filter", self.filterWdg, sticky="w", colSpan=3) self.spicamModel.filterNames.addCallback(self.filterWdg.setItems) self.spicamModel.filterName.addIndexedCallback( self.filterWdg.setDefault, 0)
def __init__(self, sr): """Display exposure status and a few user input widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.expModel = ExposeModel.getModel(InstName) self.spicamModel = TUI.Inst.SPIcam.SPIcamModel.getModel() self.tccModel = TUI.TCC.TCCModel.getModel() self.sr = sr row = 0 expStatusWdg = ExposeStatusWdg( master = sr.master, instName = InstName, helpURL = HelpURL, ) expStatusWdg.grid(row=row, column=0, columnspan=3, sticky="w") row += 1 self.expWdg = ExposeInputWdg( master = sr.master, instName = InstName, expTypes = "object", helpURL = HelpURL, ) self.expWdg.grid(row=row, column=0, columnspan=3, sticky="w") row += 1 self.filterWdg = RO.Wdg.OptionMenu( master = self.expWdg, items = [], helpText = "filter", helpURL = HelpURL, defMenu = "Current", autoIsCurrent = True, ) self.expWdg.gridder.gridWdg("Filter", self.filterWdg, sticky="w", colSpan=3) self.spicamModel.filterNames.addCallback(self.filterWdg.setItems) self.spicamModel.filterName.addIndexedCallback(self.filterWdg.setDefault, 0)
class ScriptClass(object): def __init__(self, sr): """Create widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.file = None self.nicfpsModel = TUI.Inst.NICFPS.NICFPSModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) self.tccModel = TUI.TCC.TCCModel.getModel() row = 0 # standard exposure status widget expStatusWdg = ExposeStatusWdg(master=sr.master, instName=InstName, helpURL=HelpURL) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg(master=sr.master, instName=InstName, expTypes="object", helpURL=HelpURL) self.expWdg.numExpWdg.helpText = "# of exposures at each spacing" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 gr = self.expWdg.gridder # add file widget self.fileWdg = RO.Wdg.FileWdg(master=self.expWdg, helpText="file of x y z etalon positions", helpURL=HelpURL) gr.gridWdg("Data File", self.fileWdg, colSpan=3) if sr.debug: self.expWdg.timeWdg.set(3) self.expWdg.fileNameWdg.set("debugtest") def run(self, sr): """Take a calibration sequence. """ # get current NICFPS focal plane geometry from the TCC # but first make sure the current instrument # is actually NICFPS if not sr.debug: currInstName = sr.getKeyVar(self.tccModel.instName) if not currInstName.lower().startswith(InstName.lower()): raise sr.ScriptError("%s is not the current instrument!" % InstName) # get exposure data and verify we have enough info to proceed numExp = self.expWdg.numExpWdg.getNum() expCmdPrefix = self.expWdg.getString() if not expCmdPrefix: return # get data file and parse it fileName = self.fileWdg.getPath() if not fileName: raise sr.ScriptError("specify a calibration data file") self.file = file(fileName, "rU") if not self.file: raise sr.ScriptError("could not open %r" % fileName) if sr.debug: print "Reading file %r" % (fileName,) # read the file in advance, so we know how many lines of data there are xyzList = [] for rawLine in self.file: if sr.debug: print "Read:", rawLine, line = rawLine.strip() if not line: continue if line.startswith("#"): continue try: x, y, z = [int(val) for val in line.split(None, 3)] except Exception: raise sr.ScriptError("could not parse %r" % rawLine) xyzList.append((x, y, z)) self.file.close() self.file = None if sr.debug: print "xyzList =", xyzList numPositions = len(xyzList) totNumExp = numExp * numPositions for seqInd in range(numPositions): xyzPos = xyzList[seqInd] # Set etalon position one axis at a time sr.showMsg("Step %s of %s: set etalon x,y,z = %s " % (seqInd + 1, numPositions, xyzPos)) for axis, pos in zip(("x", "y", "z"), xyzPos): yield sr.waitCmd(actor=self.nicfpsModel.actor, cmdStr="fp set%s=%d" % (axis, pos)) # compute # of exposures & format expose command startNum = seqInd * numExp expCmdStr = "%s startNum=%d totNum=%d" % (expCmdPrefix, startNum, totNumExp) # take exposure sequence sr.showMsg("Step %s of %s: expose at etalon x,y,z = %s" % (seqInd + 1, numPositions, xyzPos)) yield sr.waitCmd(actor=self.expModel.actor, cmdStr=expCmdStr, abortCmdStr="abort") def end(self, sr): if self.file: self.file.close()
def __init__(self, sr): """Create widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.errStr = "" self.nicfpsModel = TUI.Inst.NICFPS.NICFPSModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) self.tccModel = TUI.TCC.TCCModel.getModel() row = 0 # standard exposure status widget expStatusWdg = ExposeStatusWdg( master=sr.master, instName=InstName, helpURL=HelpURL, ) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg( master=sr.master, instName=InstName, expTypes="object", helpURL=HelpURL, ) self.expWdg.numExpWdg.helpText = "# of exposures at each spacing" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 gr = self.expWdg.gridder # add etalon controls to exposure input widget self.begSeqIndWdg = RO.Wdg.IntEntry( master=self.expWdg, minValue=0, width=SpacingWidth, helpText="initial z index (to finish a partial run)", helpURL=HelpURL, ) gr.gridWdg("Initial Index", self.begSeqIndWdg, "(normally leave blank)") self.fpBegZWdg = RO.Wdg.IntEntry( master=self.expWdg, minValue=self.nicfpsModel.fpXYZLimConst[0], maxValue=self.nicfpsModel.fpXYZLimConst[1], width=SpacingWidth, helpText="initial etalon Z spacing", helpURL=HelpURL, ) gr.gridWdg("Initial Z", self.fpBegZWdg, "steps") self.fpDeltaZWdg = RO.Wdg.IntEntry( master=self.expWdg, minValue=self.nicfpsModel.fpXYZLimConst[0], maxValue=self.nicfpsModel.fpXYZLimConst[1], width=SpacingWidth, helpText="etalon Z spacing interval", helpURL=HelpURL, ) gr.gridWdg("Delta Z", self.fpDeltaZWdg, "steps") self.fpNumZWdg = RO.Wdg.IntEntry( master=self.expWdg, minValue=1, maxValue=9999, width=SpacingWidth, helpText="number of etalon Z spacings", helpURL=HelpURL, ) gr.gridWdg("Num Zs", self.fpNumZWdg, "steps") self.fpEndZWdg = RO.Wdg.IntLabel( master=self.expWdg, width=SpacingWidth, helpText="final etalon Z spacing", helpURL=HelpURL, anchor="e", ) self.fpEndZUnitsWdg = RO.Wdg.StrLabel( master=self.expWdg, text="steps", helpURL=HelpURL, anchor="w", ) gr.gridWdg("Final Z", self.fpEndZWdg, self.fpEndZUnitsWdg) self.fpNumPassesWdg = RO.Wdg.OptionMenu( master=self.expWdg, items=("1", "2", "3"), defValue="2", helpText="number of passes in which to sample Z", helpURL=HelpURL, ) gr.gridWdg("Num Passes", self.fpNumPassesWdg) self.currSeqIndWdg = RO.Wdg.IntLabel( master=self.expWdg, width=SpacingWidth, helpText="index of current Z spacing", helpURL=HelpURL, anchor="e", ) gr.gridWdg("Current Index", self.currSeqIndWdg) fpCurrWdg = RO.Wdg.IntLabel( master=self.expWdg, width=SpacingWidth, helpText="current actual etalon Z spacing", helpURL=HelpURL, anchor="e", ) gr.gridWdg("Current Z", fpCurrWdg, "steps") self.nicfpsModel.fpZ.addROWdg(fpCurrWdg) self.fpBegZWdg.addCallback(self.updEndZ, callNow=False) self.fpDeltaZWdg.addCallback(self.updEndZ, callNow=False) self.fpNumZWdg.addCallback(self.updEndZ, callNow=True)
class ScriptClass(object): def __init__(self, sr): """Create widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.errStr = "" self.nicfpsModel = TUI.Inst.NICFPS.NICFPSModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) self.tccModel = TUI.TCC.TCCModel.getModel() row = 0 # standard exposure status widget expStatusWdg = ExposeStatusWdg( master=sr.master, instName=InstName, helpURL=HelpURL, ) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg( master=sr.master, instName=InstName, expTypes="object", helpURL=HelpURL, ) self.expWdg.numExpWdg.helpText = "# of exposures at each spacing" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 gr = self.expWdg.gridder # add etalon controls to exposure input widget self.begSeqIndWdg = RO.Wdg.IntEntry( master=self.expWdg, minValue=0, width=SpacingWidth, helpText="initial z index (to finish a partial run)", helpURL=HelpURL, ) gr.gridWdg("Initial Index", self.begSeqIndWdg, "(normally leave blank)") self.fpBegZWdg = RO.Wdg.IntEntry( master=self.expWdg, minValue=self.nicfpsModel.fpXYZLimConst[0], maxValue=self.nicfpsModel.fpXYZLimConst[1], width=SpacingWidth, helpText="initial etalon Z spacing", helpURL=HelpURL, ) gr.gridWdg("Initial Z", self.fpBegZWdg, "steps") self.fpDeltaZWdg = RO.Wdg.IntEntry( master=self.expWdg, minValue=self.nicfpsModel.fpXYZLimConst[0], maxValue=self.nicfpsModel.fpXYZLimConst[1], width=SpacingWidth, helpText="etalon Z spacing interval", helpURL=HelpURL, ) gr.gridWdg("Delta Z", self.fpDeltaZWdg, "steps") self.fpNumZWdg = RO.Wdg.IntEntry( master=self.expWdg, minValue=1, maxValue=9999, width=SpacingWidth, helpText="number of etalon Z spacings", helpURL=HelpURL, ) gr.gridWdg("Num Zs", self.fpNumZWdg, "steps") self.fpEndZWdg = RO.Wdg.IntLabel( master=self.expWdg, width=SpacingWidth, helpText="final etalon Z spacing", helpURL=HelpURL, anchor="e", ) self.fpEndZUnitsWdg = RO.Wdg.StrLabel( master=self.expWdg, text="steps", helpURL=HelpURL, anchor="w", ) gr.gridWdg("Final Z", self.fpEndZWdg, self.fpEndZUnitsWdg) self.fpNumPassesWdg = RO.Wdg.OptionMenu( master=self.expWdg, items=("1", "2", "3"), defValue="2", helpText="number of passes in which to sample Z", helpURL=HelpURL, ) gr.gridWdg("Num Passes", self.fpNumPassesWdg) self.currSeqIndWdg = RO.Wdg.IntLabel( master=self.expWdg, width=SpacingWidth, helpText="index of current Z spacing", helpURL=HelpURL, anchor="e", ) gr.gridWdg("Current Index", self.currSeqIndWdg) fpCurrWdg = RO.Wdg.IntLabel( master=self.expWdg, width=SpacingWidth, helpText="current actual etalon Z spacing", helpURL=HelpURL, anchor="e", ) gr.gridWdg("Current Z", fpCurrWdg, "steps") self.nicfpsModel.fpZ.addROWdg(fpCurrWdg) self.fpBegZWdg.addCallback(self.updEndZ, callNow=False) self.fpDeltaZWdg.addCallback(self.updEndZ, callNow=False) self.fpNumZWdg.addCallback(self.updEndZ, callNow=True) def updEndZ(self, *args, **kargs): """Call when beg Z, delta Z or num Z changed to update end Z. """ begSpacing = self.fpBegZWdg.getNumOrNone() numSpacings = self.fpNumZWdg.getNumOrNone() deltaZ = self.fpDeltaZWdg.getNumOrNone() endZ = None self.errStr = "" if begSpacing == None: self.errStr = "specify initial Z" elif deltaZ == None: self.errStr = "specify delta z" elif numSpacings == None: self.errStr = "specify number of zs" else: endZ = begSpacing + (deltaZ * (numSpacings - 1)) # check range minZ, maxZ = self.nicfpsModel.fpXYZLimConst if endZ < minZ: self.errStr = "final Z < %s" % minZ elif endZ > maxZ: self.errStr = "final Z > %s" % maxZ if self.errStr: isCurrent = False self.fpEndZUnitsWdg.set("error: %s" % self.errStr, isCurrent=isCurrent) else: isCurrent = True self.fpEndZUnitsWdg.set("steps", isCurrent=isCurrent) self.fpEndZWdg.set(endZ, isCurrent=isCurrent) def run(self, sr): """Take an exposure sequence. """ # Make sure the current instrument is NICFPS if not sr.debug: currInstName = sr.getKeyVar(self.tccModel.instName) if not currInstName.lower().startswith(InstName.lower()): raise sr.ScriptError("%s is not the current instrument!" % InstName) # exposure command without startNum and totNum # get it now so that it will not change if the user messes # with the controls while the script is running numExp = self.expWdg.numExpWdg.getNum() expCmdPrefix = self.expWdg.getString() if not expCmdPrefix: raise sr.ScriptError("missing inputs") # print "got here; errStr =", self.errStr if self.errStr: raise sr.ScriptError(self.errStr) # get user data in advance begSeqInd = self.begSeqIndWdg.getNum() begSpacing = self.fpBegZWdg.getNum() numSpacings = self.fpNumZWdg.getNum() deltaZ = self.fpDeltaZWdg.getNum() numPasses = int(self.fpNumPassesWdg.getString()) totNumExp = numExp * numSpacings # for each pass through the data, create a list of multipliers, # where z = zo + delta-z * mult multList = range(numSpacings) seqPassMultList = [] for passInd in range(numPasses): for zMult in multList[passInd::numPasses]: seqInd = len(seqPassMultList) seqPassMultList.append((seqInd, passInd, zMult)) # print "seqPassMultList =", seqPassMultList for seqInd, passInd, zMult in seqPassMultList[begSeqInd:]: currSpacing = begSpacing + (deltaZ * zMult) self.currSeqIndWdg.set(seqInd) # command etalon spacing sr.showMsg("Set etalon Z = %d %s" % (currSpacing, "steps")) yield sr.waitCmd( actor=self.nicfpsModel.actor, cmdStr="fp setz=%d" % (currSpacing, ), ) # compute # of exposures & format expose command startNum = seqInd * numExp expCmdStr = "%s startNum=%d totNum=%d" % (expCmdPrefix, startNum, totNumExp) # take exposure sequence sr.showMsg("Expose at etalon Z = %d %s" % (currSpacing, "steps")) yield sr.waitCmd( actor=self.expModel.actor, cmdStr=expCmdStr, abortCmdStr="abort", )
def __init__(self, sr): """The setup script; run once when the script runner window is created. """ # if sr.debug True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.sr = sr self.begOffset = numpy.array((numpy.nan, numpy.nan)) self.currOffset = self.begOffset[:] self.tccModel = TUI.TCC.TCCModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) row=0 # standard exposure status widget expStatusWdg = ExposeStatusWdg(sr.master, InstName) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # create dither node controls ditherFrame = Tkinter.Frame(sr.master) # information about the dither nodes; each entry is: # - name of quadrant # - boresight offset multiplier in image x, image y ditherNodeData = [ ("Ctr", (0, 0)), ("UL", (-1, 1)), ("UR", (1, 1)), ("LR", (1, -1)), ("LL", (-1, -1)), ] self.ditherWdgSet = [] # (stateWdg, orderWdg, boolWdg), one per dither node for name, offMult in ditherNodeData: nodeFrame = Tkinter.Frame(ditherFrame) stateWdg = RO.Wdg.StrLabel( master = nodeFrame, width = 7, relief = "flat", helpText = "State of node in dither sequence", helpURL = HelpURL, ) orderWdg = RO.Wdg.IntLabel( master = nodeFrame, width = 1, relief = "flat", helpText = "Order of node in dither sequence", helpURL = HelpURL, ) boolWdg = RO.Wdg.Checkbutton( master = nodeFrame, text = name, callFunc = self.updOrder, defValue = True, relief = "flat", helpText = "Check to use this dither node", helpURL = HelpURL, ) # add attribute "offMult" to widget # so it can be read by "run" boolWdg.offMult = numpy.array(offMult, dtype=float) self.ditherWdgSet.append((stateWdg, orderWdg, boolWdg)) stateWdg.pack(side="left") orderWdg.pack(side="left") boolWdg.pack(side="left") # display quadrant checkbutton in appropriate location row = 1 - offMult[1] col = 1 + offMult[0] nodeFrame.grid(row=row, column=col) ditherFrame.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg(sr.master, InstName, expTypes="object") self.expWdg.numExpWdg.helpText = "# of pairs of exposures at each node" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 # add controls to exposure input widget frame self.boxSizeWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = 0, defValue = DefBoxSize, helpText = "size of dither box", helpURL = HelpURL, ) self.expWdg.gridder.gridWdg("Box Size", self.boxSizeWdg, "arcsec") self.doRandomWdg = RO.Wdg.Checkbutton( master = self.expWdg, defValue = DefDoRandom, helpText = "Add random scatter to dither pattern?", helpURL = HelpURL, ) self.expWdg.gridder.gridWdg("Randomize?", self.doRandomWdg) self.skyOffsetWdgSet = [] for ii in range(2): axisStr = ("RA", "Dec")[ii] unitsVar = Tkinter.StringVar() offsetWdg = RO.Wdg.DMSEntry( master = self.expWdg, minValue = -MaxOffset, maxValue = MaxOffset, defValue = DefOffset, isHours = False, isRelative = True, helpText = "Offset to sky in %s (typically)" % (axisStr,), helpURL = HelpURL, unitsVar = unitsVar, ) self.skyOffsetWdgSet.append(offsetWdg) self.expWdg.gridder.gridWdg( "Sky Offset %s" % (ii + 1,), offsetWdg, units = unitsVar, ) self.expWdg.gridder.allGridded() if sr.debug: # set useful debug defaults self.expWdg.timeWdg.set("1.0") self.expWdg.numExpWdg.set(2) self.expWdg.fileNameWdg.set("debug") self.ditherWdgSet[1][-1].setBool(False) self.ditherWdgSet[3][-1].setBool(False) self.updOrder()
class ScriptClass(object): def __init__(self, sr): """The setup script; run once when the script runner window is created. """ # if sr.debug True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.sr = sr self.begOffset = numpy.array((numpy.nan, numpy.nan)) self.currOffset = self.begOffset[:] self.tccModel = TUI.TCC.TCCModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) row=0 # standard exposure status widget expStatusWdg = ExposeStatusWdg(sr.master, InstName) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # create dither node controls ditherFrame = Tkinter.Frame(sr.master) # information about the dither nodes; each entry is: # - name of quadrant # - boresight offset multiplier in image x, image y ditherNodeData = [ ("Ctr", (0, 0)), ("UL", (-1, 1)), ("UR", (1, 1)), ("LR", (1, -1)), ("LL", (-1, -1)), ] self.ditherWdgSet = [] # (stateWdg, orderWdg, boolWdg), one per dither node for name, offMult in ditherNodeData: nodeFrame = Tkinter.Frame(ditherFrame) stateWdg = RO.Wdg.StrLabel( master = nodeFrame, width = 7, relief = "flat", helpText = "State of node in dither sequence", helpURL = HelpURL, ) orderWdg = RO.Wdg.IntLabel( master = nodeFrame, width = 1, relief = "flat", helpText = "Order of node in dither sequence", helpURL = HelpURL, ) boolWdg = RO.Wdg.Checkbutton( master = nodeFrame, text = name, callFunc = self.updOrder, defValue = True, relief = "flat", helpText = "Check to use this dither node", helpURL = HelpURL, ) # add attribute "offMult" to widget # so it can be read by "run" boolWdg.offMult = numpy.array(offMult, dtype=float) self.ditherWdgSet.append((stateWdg, orderWdg, boolWdg)) stateWdg.pack(side="left") orderWdg.pack(side="left") boolWdg.pack(side="left") # display quadrant checkbutton in appropriate location row = 1 - offMult[1] col = 1 + offMult[0] nodeFrame.grid(row=row, column=col) ditherFrame.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg(sr.master, InstName, expTypes="object") self.expWdg.numExpWdg.helpText = "# of pairs of exposures at each node" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 # add controls to exposure input widget frame self.boxSizeWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = 0, defValue = DefBoxSize, helpText = "size of dither box", helpURL = HelpURL, ) self.expWdg.gridder.gridWdg("Box Size", self.boxSizeWdg, "arcsec") self.doRandomWdg = RO.Wdg.Checkbutton( master = self.expWdg, defValue = DefDoRandom, helpText = "Add random scatter to dither pattern?", helpURL = HelpURL, ) self.expWdg.gridder.gridWdg("Randomize?", self.doRandomWdg) self.skyOffsetWdgSet = [] for ii in range(2): axisStr = ("RA", "Dec")[ii] unitsVar = Tkinter.StringVar() offsetWdg = RO.Wdg.DMSEntry( master = self.expWdg, minValue = -MaxOffset, maxValue = MaxOffset, defValue = DefOffset, isHours = False, isRelative = True, helpText = "Offset to sky in %s (typically)" % (axisStr,), helpURL = HelpURL, unitsVar = unitsVar, ) self.skyOffsetWdgSet.append(offsetWdg) self.expWdg.gridder.gridWdg( "Sky Offset %s" % (ii + 1,), offsetWdg, units = unitsVar, ) self.expWdg.gridder.allGridded() if sr.debug: # set useful debug defaults self.expWdg.timeWdg.set("1.0") self.expWdg.numExpWdg.set(2) self.expWdg.fileNameWdg.set("debug") self.ditherWdgSet[1][-1].setBool(False) self.ditherWdgSet[3][-1].setBool(False) self.updOrder() def end(self, sr): """If telescope offset, restore original position. """ self.updOrder(doForce=True) # restore original boresight position, if changed if self.needMove(self.begOffset): tccCmdStr = "offset arc %.7f, %.7f/pabs/vabs/computed" % tuple(self.begOffset) #print "sending tcc command %r" % tccCmdStr sr.startCmd( actor = "tcc", cmdStr = tccCmdStr, ) def needMove(self, desOffset): """Return True if telescope not at desired offset""" if numpy.any(numpy.isnan(self.currOffset)): return False return not numpy.allclose(self.currOffset, desOffset) def run(self, sr): """Take an exposure sequence. """ # clear node state for wdgSet in self.ditherWdgSet: wdgSet[0].set(None) # get current NICFPS focal plane geometry from the TCC # but first make sure the current instrument is actually NICFPS if not sr.debug: currInstName = sr.getKeyVar(self.tccModel.instName) if not currInstName.lower().startswith(InstName.lower()): raise sr.ScriptError("%s is not the current instrument!" % InstName) # record the current object offset position begArcPVTs = sr.getKeyVar(self.tccModel.objArcOff, ind=None) if not sr.debug: begOffset = [pvt.getPos() for pvt in begArcPVTs] if None in begOffset: raise sr.ScriptError("Current arc offset unknown") self.begOffset = numpy.array(begOffset, dtype=float) else: self.begOffset = numpy.zeros(2, dtype=float) self.currOffset = self.begOffset[:] #print "self.begOffset=%r" % self.begOffset ditherSize = self.boxSizeWdg.getNum() / 2.0 doRandom = self.doRandomWdg.getBool() # vector describing how far away from the object to move # in order to do the second dither pattern skyOffsetDeg = numpy.array([self.skyOffsetWdgSet[ii].getNum() for ii in range(2)]) / 3600.0 # record which points to use in the dither pattern in advance # (rather than allowing the user to change it during execution) doPtArr = [wdgs[-1].getBool() for wdgs in self.ditherWdgSet] # exposure command without startNum # get it now so that it will not change if the user messes # with the controls while the script is running numExp = self.expWdg.numExpWdg.getNumOrNone() if numExp == None: raise sr.ScriptError("must specify #Exp") numNodes = sum(doPtArr) totNumExp = numNodes * numExp * 2 if doRandom: # use randomization: take just one exposure and then apply a random offset expCmdPrefix = self.expWdg.getString(numExp = 1, totNum = totNumExp) else: # no randomization: take all #Exp exposures at once expCmdPrefix = self.expWdg.getString(totNum = totNumExp) if not expCmdPrefix: raise sr.ScriptError("missing inputs") # loop through each dither node # taking nExp exposures at each of: # node 1 source, node 1 sky, node 2 sky, node 2 source... ditherSizeDeg = ditherSize / 3600.0 #randomRangeDeg = ditherSizeDeg / 2.0 randomRangeDeg = RandomBoxSize / 3600.0 numExpTaken = 0 onSkyIter = itertools.cycle((False, True, True, False)) for ind, wdgSet in enumerate(self.ditherWdgSet): stateWdg, orderWdg, boolWdg = wdgSet if not doPtArr[ind]: stateWdg.set("Skipped") continue nodeName = str(boolWdg["text"]) # Expose on object and sky at this dither point for i in range(2): onSky = onSkyIter.next() if onSky: srcName = "Sky" else: srcName = "Source" stateWdg.set(srcName) srcNodeName = "%s %s" % (srcName, nodeName) desOffset = self.begOffset + (skyOffsetDeg * onSky) + (boolWdg.offMult * ditherSizeDeg) if doRandom: # apply random offset before each exposure at this position for expInd in range(numExp): if numExpTaken == 0: # do not randomize the first point; this saves a bit of time randomScatter = numpy.zeros(2, dtype=float) fullNodeName = "%s with no random scatter" % (srcNodeName,) else: randomScatter = (numpy.random.random(2) * randomRangeDeg) - (randomRangeDeg / 2.0) randomScatterArcSec = randomScatter * 3600.0 fullNodeName = "%s + %0.1f, %0.1f random scatter" % \ (srcNodeName, randomScatterArcSec[0], randomScatterArcSec[1]) #print "Adding randomScatter", randomScatter randomizedOffset = desOffset + randomScatter if self.needMove(randomizedOffset): # slew telescope randomScatterArcSec = randomScatter * 3600.0 sr.showMsg("Offset to %s" % (fullNodeName,)) yield self.waitOffset(randomizedOffset) # format exposure command startNum = numExpTaken + 1 expCmdStr = "%s startNum=%d" % (expCmdPrefix, startNum) # take exposure sequence sr.showMsg("Expose at %s" % (fullNodeName,)) yield sr.waitCmd( actor = self.expModel.actor, cmdStr = expCmdStr, abortCmdStr = "abort", ) numExpTaken += 1 else: # compute # of exposures & format expose command startNum = numExpTaken + 1 expCmdStr = "%s startNum=%d" % (expCmdPrefix, startNum) # offset telescope if self.needMove(desOffset): sr.showMsg("Offset to %s position" % (srcNodeName,)) yield self.waitOffset(desOffset) # take exposure sequence sr.showMsg("Expose on %s position" % (srcNodeName,)) yield sr.waitCmd( actor = self.expModel.actor, cmdStr = expCmdStr, abortCmdStr = "abort", ) numExpTaken += numExp stateWdg.set("Done") # slew back to starting position if self.needMove(self.begOffset): sr.showMsg("Finishing up: slewing to initial position") yield self.waitOffset(self.begOffset) def updOrder(self, wdg=None, doForce=False): """Update the order widgets If the script is executing then the widgets are left untouched unless doForce is True. This allows the order widgets to be correct while running even if the user messes with the checkboxes. """ if not doForce and self.sr.isExecuting(): return orderNum = 1 for stateWdg, orderWdg, boolWdg in self.ditherWdgSet: if boolWdg.getBool(): orderWdg.set(orderNum) orderNum += 1 else: orderWdg.set(None) def waitOffset(self, desOffset): """Offset the telescope""" tccCmdStr = "offset arc %.7f, %.7f/pabs/vabs/computed" % tuple(desOffset) self.currOffset = desOffset[:] yield self.sr.waitCmd( actor = "tcc", cmdStr = tccCmdStr, )
class ScriptClass(object): def __init__(self, sr): """Create widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.file = None self.nicfpsModel = TUI.Inst.NICFPS.NICFPSModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) self.tccModel = TUI.TCC.TCCModel.getModel() row = 0 # standard exposure status widget expStatusWdg = ExposeStatusWdg( master=sr.master, instName=InstName, helpURL=HelpURL, ) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg( master=sr.master, instName=InstName, expTypes="object", helpURL=HelpURL, ) self.expWdg.numExpWdg.helpText = "# of exposures at each spacing" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 gr = self.expWdg.gridder # add file widget self.fileWdg = RO.Wdg.FileWdg( master=self.expWdg, helpText="file of x y z etalon positions", helpURL=HelpURL, ) gr.gridWdg("Data File", self.fileWdg, colSpan=3) if sr.debug: self.expWdg.timeWdg.set(3) self.expWdg.fileNameWdg.set("debugtest") def run(self, sr): """Take a calibration sequence. """ # get current NICFPS focal plane geometry from the TCC # but first make sure the current instrument # is actually NICFPS if not sr.debug: currInstName = sr.getKeyVar(self.tccModel.instName) if not currInstName.lower().startswith(InstName.lower()): raise sr.ScriptError("%s is not the current instrument!" % InstName) # get exposure data and verify we have enough info to proceed numExp = self.expWdg.numExpWdg.getNum() expCmdPrefix = self.expWdg.getString() if not expCmdPrefix: return # get data file and parse it fileName = self.fileWdg.getPath() if not fileName: raise sr.ScriptError("specify a calibration data file") self.file = file(fileName, 'rU') if not self.file: raise sr.ScriptError("could not open %r" % fileName) if sr.debug: print "Reading file %r" % (fileName, ) # read the file in advance, so we know how many lines of data there are xyzList = [] for rawLine in self.file: if sr.debug: print "Read:", rawLine, line = rawLine.strip() if not line: continue if line.startswith("#"): continue try: x, y, z = [int(val) for val in line.split(None, 3)] except Exception: raise sr.ScriptError("could not parse %r" % rawLine) xyzList.append((x, y, z)) self.file.close() self.file = None if sr.debug: print "xyzList =", xyzList numPositions = len(xyzList) totNumExp = numExp * numPositions for seqInd in range(numPositions): xyzPos = xyzList[seqInd] # Set etalon position one axis at a time sr.showMsg("Step %s of %s: set etalon x,y,z = %s " % (seqInd + 1, numPositions, xyzPos)) for axis, pos in zip(("x", "y", "z"), xyzPos): yield sr.waitCmd( actor=self.nicfpsModel.actor, cmdStr="fp set%s=%d" % (axis, pos), ) # compute # of exposures & format expose command startNum = seqInd * numExp expCmdStr = "%s startNum=%d totNum=%d" % (expCmdPrefix, startNum, totNumExp) # take exposure sequence sr.showMsg("Step %s of %s: expose at etalon x,y,z = %s" % (seqInd + 1, numPositions, xyzPos)) yield sr.waitCmd( actor=self.expModel.actor, cmdStr=expCmdStr, abortCmdStr="abort", ) def end(self, sr): if self.file: self.file.close()
class ScriptClass(object): """Take a series of SPIcam twilight or morning flats """ def __init__(self, sr): """Display exposure status and a few user input widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.expModel = ExposeModel.getModel(InstName) self.spicamModel = TUI.Inst.SPIcam.SPIcamModel.getModel() self.tccModel = TUI.TCC.TCCModel.getModel() self.sr = sr row = 0 expStatusWdg = ExposeStatusWdg( master = sr.master, instName = InstName, helpURL = HelpURL, ) expStatusWdg.grid(row=row, column=0, columnspan=3, sticky="w") row += 1 self.expWdg = ExposeInputWdg( master = sr.master, instName = InstName, expTypes = "object", helpURL = HelpURL, ) self.expWdg.grid(row=row, column=0, columnspan=3, sticky="w") row += 1 self.filterWdg = RO.Wdg.OptionMenu( master = self.expWdg, items = [], helpText = "filter", helpURL = HelpURL, defMenu = "Current", autoIsCurrent = True, ) self.expWdg.gridder.gridWdg("Filter", self.filterWdg, sticky="w", colSpan=3) self.spicamModel.filterNames.addCallback(self.filterWdg.setItems) self.spicamModel.filterName.addIndexedCallback(self.filterWdg.setDefault, 0) def run(self, sr): """Take a series of SPIcam flats""" # record user inputs filtName = self.filterWdg.getString() expTime = self.expWdg.timeWdg.getNum() numExp = self.expWdg.numExpWdg.getNum() fileName = self.expWdg.fileNameWdg.getString() comment = self.expWdg.commentWdg.getString() if not filtName: raise sr.ScriptError("Specify filter") if expTime <= 0: raise sr.ScriptError("Specify exposure time") if numExp <= 0: raise sr.ScriptError("Specify number of exposures") nTimeFields = self.expWdg.timeWdg.getString().count(":") + 1 nTimeFields = max(1, min(3, nTimeFields)) self.expWdg.timeWdg.defFormat = (nTimeFields, 1) # morning or evening? utcHours = time.gmtime().tm_hour locHours = utcHours + (TUI.TCC.TelConst.Longitude / 15.0) locHours = RO.MathUtil.wrapPos(locHours * 15.0) / 15.0 isMorning = locHours < 12.0 # if filter is different, set it if not self.filterWdg.isDefault(): desFiltNum = self.filterWdg.getIndex() + 1 cmdStr = "filter %d" % (desFiltNum,) yield sr.waitCmd( actor = self.spicamModel.actor, cmdStr = cmdStr, ) for expNum in range(numExp): # offset telescope sr.showMsg("Dither %s of %s" % (expNum+1, numExp)) yield self.sr.waitCmd( actor = "tcc", cmdStr = "offset arc/computed %0.7f, %0.7f" % (DitherOffsetArcSec / 3600.0, 0.0) ) # compute next exposure time if isMorning: expTime = self.nextMorningExpTime(expTime) else: expTime = self.nextTwilightExpTime(expTime) self.expWdg.timeWdg.set(expTime) cmdStr = self.expModel.formatExpCmd( expType = "flat", expTime = expTime, fileName = fileName, numExp = 1, comment = comment, startNum = expNum + 1, totNum = numExp, ) sr.showMsg("Exposure %s of %s: %.1f sec" % (expNum+1, numExp, expTime)) yield sr.waitCmd( actor = "spicamExpose", cmdStr = cmdStr, abortCmdStr = "abort", ) def nextTwilightExpTime(self, prevExpTime): """Compute next exposure time for a twilight flat (compensating for the darkening sky) This equation is from the original SPIcam scripts; it blows up around 180 seconds, so a ceiling is used. """ maxExpTime = 160.0 temp = math.exp(-prevExpTime / 288.0) + math.exp(-(prevExpTime + 45.0) / 288.0) - 1.0 if temp <= 0: return maxExpTime desExpTime = -288.0 * (math.log(temp)) - (prevExpTime + 45.0) return min(maxExpTime, desExpTime) def nextMorningExpTime(self, prevExpTime): """Compute next exposure time for a morning flat (compensating for the brightening sky) The equation is from the original SPIcam scripts. """ temp = math.exp(prevExpTime / 288.0) + math.exp((prevExpTime + 45.0) / 288.0) - 1.0 desExpTime = 288.0 * (math.log(temp)) - (prevExpTime + 45.0) return max(self.expModel.instInfo.minExpTime, desExpTime)
class ScriptClass(object): """Take a series of SPIcam twilight or morning flats """ def __init__(self, sr): """Display exposure status and a few user input widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.expModel = ExposeModel.getModel(InstName) self.spicamModel = TUI.Inst.SPIcam.SPIcamModel.getModel() self.tccModel = TUI.TCC.TCCModel.getModel() self.sr = sr row = 0 expStatusWdg = ExposeStatusWdg( master=sr.master, instName=InstName, helpURL=HelpURL, ) expStatusWdg.grid(row=row, column=0, columnspan=3, sticky="w") row += 1 self.expWdg = ExposeInputWdg( master=sr.master, instName=InstName, expTypes="object", helpURL=HelpURL, ) self.expWdg.grid(row=row, column=0, columnspan=3, sticky="w") row += 1 wdgFrame = Tkinter.Frame(sr.master) gr = RO.Wdg.Gridder(wdgFrame, sticky="w") self.filterWdg = RO.Wdg.OptionMenu( master=self.expWdg, items=[], helpText="filter", helpURL=HelpURL, defMenu="Current", autoIsCurrent=True, ) self.expWdg.gridder.gridWdg("Filter", self.filterWdg, sticky="w", colSpan=3) self.spicamModel.filterNames.addCallback(self.filterWdg.setItems) self.spicamModel.filterName.addIndexedCallback( self.filterWdg.setDefault, 0) def run(self, sr): """Take a series of SPIcam flats""" # record user inputs filtName = self.filterWdg.getString() expTime = self.expWdg.timeWdg.getNum() numExp = self.expWdg.numExpWdg.getNum() fileName = self.expWdg.fileNameWdg.getString() comment = self.expWdg.commentWdg.getString() if not filtName: raise sr.ScriptError("Specify filter") if expTime <= 0: raise sr.ScriptError("Specify exposure time") if numExp <= 0: raise sr.ScriptError("Specify number of exposures") nTimeFields = self.expWdg.timeWdg.getString().count(":") + 1 nTimeFields = max(1, min(3, nTimeFields)) self.expWdg.timeWdg.defFormat = (nTimeFields, 1) # morning or evening? utcHours = time.gmtime().tm_hour locHours = utcHours + (TUI.TCC.TelConst.Longitude / 15.0) locHours = RO.MathUtil.wrapPos(locHours * 15.0) / 15.0 isMorning = locHours < 12.0 # if filter is different, set it if not self.filterWdg.isDefault(): desFiltNum = self.filterWdg.getIndex() + 1 cmdStr = "filter %d" % (desFiltNum, ) yield sr.waitCmd( actor=self.spicamModel.actor, cmdStr=cmdStr, ) for expNum in range(numExp): # offset telescope sr.showMsg("Dither %s of %s" % (expNum + 1, numExp)) yield self.sr.waitCmd(actor="tcc", cmdStr="offset arc/computed %0.7f, %0.7f" % (DitherOffsetArcSec / 3600.0, 0.0)) # compute next exposure time if isMorning: expTime = self.nextMorningExpTime(expTime) else: expTime = self.nextTwilightExpTime(expTime) self.expWdg.timeWdg.set(expTime) cmdStr = self.expModel.formatExpCmd( expType="flat", expTime=expTime, fileName=fileName, numExp=1, comment=comment, startNum=expNum + 1, totNum=numExp, ) sr.showMsg("Exposure %s of %s: %.1f sec" % (expNum + 1, numExp, expTime)) yield sr.waitCmd( actor="spicamExpose", cmdStr=cmdStr, abortCmdStr="abort", ) def nextTwilightExpTime(self, prevExpTime): """Compute next exposure time for a twilight flat (compensating for the darkening sky) This equation is from the original SPIcam scripts; it blows up around 180 seconds, so a ceiling is used. """ maxExpTime = 160.0 temp = math.exp(-prevExpTime / 288.0) + math.exp( -(prevExpTime + 45.0) / 288.0) - 1.0 if temp <= 0: return maxExpTime desExpTime = -288.0 * (math.log(temp)) - (prevExpTime + 45.0) return min(maxExpTime, desExpTime) def nextMorningExpTime(self, prevExpTime): """Compute next exposure time for a morning flat (compensating for the brightening sky) The equation is from the original SPIcam scripts. """ temp = math.exp(prevExpTime / 288.0) + math.exp( (prevExpTime + 45.0) / 288.0) - 1.0 desExpTime = 288.0 * (math.log(temp)) - (prevExpTime + 45.0) return max(self.expModel.instInfo.minExpTime, desExpTime)
def __init__(self, sr): """The setup script; run once when the script runner window is created. """ # if sr.debug True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.sr = sr self.begOffset = numpy.array((numpy.nan, numpy.nan)) self.currOffset = self.begOffset[:] self.tccModel = TUI.TCC.TCCModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) row=0 # standard exposure status widget expStatusWdg = ExposeStatusWdg( master = sr.master, instName = InstName, helpURL = HelpURL, ) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # create dither node controls ditherFrame = Tkinter.Frame(sr.master) # information about the dither nodes; each entry is: # - name of quadrant # - boresight offset multiplier in image x, image y ditherNodeData = [ ("Ctr", (0, 0)), ("UL", (-1, 1)), ("UR", (1, 1)), ("LR", (1, -1)), ("LL", (-1, -1)), ] self.ditherWdgSet = [] # (stateWdg, orderWdg, boolWdg), one per dither node for name, offMult in ditherNodeData: nodeFrame = Tkinter.Frame(ditherFrame) stateWdg = RO.Wdg.StrLabel( master = nodeFrame, width = 7, relief = "flat", helpText = "State of node in dither sequence", helpURL = HelpURL, ) orderWdg = RO.Wdg.IntLabel( master = nodeFrame, width = 1, relief = "flat", helpText = "Order of node in dither sequence", helpURL = HelpURL, ) boolWdg = RO.Wdg.Checkbutton( master = nodeFrame, text = name, callFunc = self.updOrder, defValue = True, relief = "flat", helpText = "Check to use this dither node", helpURL = HelpURL, ) # add attribute "offMult" to widget # so it can be read by "run" boolWdg.offMult = numpy.array(offMult, dtype=float) self.ditherWdgSet.append((stateWdg, orderWdg, boolWdg)) stateWdg.pack(side="left") orderWdg.pack(side="left") boolWdg.pack(side="left") # display quadrant checkbutton in appropriate location row = 1 - offMult[1] col = 1 + offMult[0] nodeFrame.grid(row=row, column=col) ditherFrame.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg( master = sr.master, instName = InstName, expTypes = "object", helpURL = HelpURL, ) self.expWdg.numExpWdg.helpText = "# of exposures at each point" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 # add controls to exposure input widget frame self.boxSizeWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = 0, defValue = DefBoxSize, helpText = "size of dither box", helpURL = HelpURL, ) self.expWdg.gridder.gridWdg("Box Size", self.boxSizeWdg, "arcsec") self.doRandomWdg = RO.Wdg.Checkbutton( master = self.expWdg, defValue = DefDoRandom, helpText = "Add random scatter to dither pattern?", helpURL = HelpURL, ) self.expWdg.gridder.gridWdg("Randomize?", self.doRandomWdg) if sr.debug: # set useful debug defaults self.expWdg.timeWdg.set("1.0") self.expWdg.numExpWdg.set(2) self.expWdg.fileNameWdg.set("debug") self.ditherWdgSet[1][-1].setBool(False) self.ditherWdgSet[3][-1].setBool(False) self.updOrder()
class ScriptClass(object): def __init__(self, sr): """The setup script; run once when the script runner window is created. """ # if sr.debug True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.sr = sr self.begOffset = numpy.array((numpy.nan, numpy.nan)) self.currOffset = self.begOffset[:] self.tccModel = TUI.TCC.TCCModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) row=0 # standard exposure status widget expStatusWdg = ExposeStatusWdg( master = sr.master, instName = InstName, helpURL = HelpURL, ) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # create dither node controls ditherFrame = Tkinter.Frame(sr.master) # information about the dither nodes; each entry is: # - name of quadrant # - boresight offset multiplier in image x, image y ditherNodeData = [ ("Ctr", (0, 0)), ("UL", (-1, 1)), ("UR", (1, 1)), ("LR", (1, -1)), ("LL", (-1, -1)), ] self.ditherWdgSet = [] # (stateWdg, orderWdg, boolWdg), one per dither node for name, offMult in ditherNodeData: nodeFrame = Tkinter.Frame(ditherFrame) stateWdg = RO.Wdg.StrLabel( master = nodeFrame, width = 7, relief = "flat", helpText = "State of node in dither sequence", helpURL = HelpURL, ) orderWdg = RO.Wdg.IntLabel( master = nodeFrame, width = 1, relief = "flat", helpText = "Order of node in dither sequence", helpURL = HelpURL, ) boolWdg = RO.Wdg.Checkbutton( master = nodeFrame, text = name, callFunc = self.updOrder, defValue = True, relief = "flat", helpText = "Check to use this dither node", helpURL = HelpURL, ) # add attribute "offMult" to widget # so it can be read by "run" boolWdg.offMult = numpy.array(offMult, dtype=float) self.ditherWdgSet.append((stateWdg, orderWdg, boolWdg)) stateWdg.pack(side="left") orderWdg.pack(side="left") boolWdg.pack(side="left") # display quadrant checkbutton in appropriate location row = 1 - offMult[1] col = 1 + offMult[0] nodeFrame.grid(row=row, column=col) ditherFrame.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg( master = sr.master, instName = InstName, expTypes = "object", helpURL = HelpURL, ) self.expWdg.numExpWdg.helpText = "# of exposures at each point" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 # add controls to exposure input widget frame self.boxSizeWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = 0, defValue = DefBoxSize, helpText = "size of dither box", helpURL = HelpURL, ) self.expWdg.gridder.gridWdg("Box Size", self.boxSizeWdg, "arcsec") self.doRandomWdg = RO.Wdg.Checkbutton( master = self.expWdg, defValue = DefDoRandom, helpText = "Add random scatter to dither pattern?", helpURL = HelpURL, ) self.expWdg.gridder.gridWdg("Randomize?", self.doRandomWdg) if sr.debug: # set useful debug defaults self.expWdg.timeWdg.set("1.0") self.expWdg.numExpWdg.set(2) self.expWdg.fileNameWdg.set("debug") self.ditherWdgSet[1][-1].setBool(False) self.ditherWdgSet[3][-1].setBool(False) self.updOrder() def end(self, sr): """If telescope offset, restore original position. """ self.updOrder(doForce=True) # restore original boresight position, if changed if self.needMove(self.begOffset): tccCmdStr = "offset boresight %.7f, %.7f/pabs/vabs/computed" % tuple(self.begOffset) #print "sending tcc command %r" % tccCmdStr sr.startCmd( actor = "tcc", cmdStr = tccCmdStr, ) def needMove(self, desOffset): """Return True if telescope not at desired offset""" if numpy.any(numpy.isnan(self.currOffset)): return False return not numpy.allclose(self.currOffset, desOffset) def run(self, sr): """Take an exposure sequence. """ # clear node state for wdgSet in self.ditherWdgSet: wdgSet[0].set(None) # get current NICFPS focal plane geometry from the TCC # but first make sure the current instrument is actually NICFPS if not sr.debug: currInstName = sr.getKeyVar(self.tccModel.instName) if not currInstName.lower().startswith(InstName.lower()): raise sr.ScriptError("%s is not the current instrument!" % InstName) # record the current boresight position begBorePVTs = sr.getKeyVar(self.tccModel.boresight, ind=None) if not sr.debug: begOffset = [pvt.getPos() for pvt in begBorePVTs] if None in begOffset: raise sr.ScriptError("Current boresight position unknown") self.begOffset = numpy.array(begOffset, dtype=float) else: self.begOffset = numpy.zeros(2, dtype=float) self.currOffset = self.begOffset[:] #print "self.begOffset=%r" % self.begOffset ditherSize = self.boxSizeWdg.getNum() / 2.0 doRandom = self.doRandomWdg.getBool() # record which points to use in the dither pattern in advance # (rather than allowing the user to change it during execution) doPtArr = [wdgs[-1].getBool() for wdgs in self.ditherWdgSet] # exposure command without startNum and totNumExp # get it now so that it will not change if the user messes # with the controls while the script is running numExp = self.expWdg.numExpWdg.getNumOrNone() if numExp is None: raise sr.ScriptError("must specify #Exp") numNodes = sum(doPtArr) totNumExp = numNodes * numExp if doRandom: # use randomization: take just one exposure and then apply a random offset expCmdPrefix = self.expWdg.getString(numExp = 1, totNum = totNumExp) else: # no randomization: take all #Exp exposures at once expCmdPrefix = self.expWdg.getString(totNum = totNumExp) if not expCmdPrefix: raise sr.ScriptError("missing inputs") # loop through each dither node, # taking numExp exposures at each node ditherSizeDeg = ditherSize / 3600.0 #randomRangeDeg = ditherSizeDeg / 2.0 randomRangeDeg = RandomBoxSize / 3600.0 numExpTaken = 0 for ind, wdgSet in enumerate(self.ditherWdgSet): stateWdg, orderWdg, boolWdg = wdgSet if not doPtArr[ind]: stateWdg.set("Skipped") continue stateWdg.set("Running") nodeName = str(boolWdg["text"]) desOffset = self.begOffset + (boolWdg.offMult * ditherSizeDeg) if doRandom: # apply random offset before each exposure at this position for expInd in range(numExp): if numExpTaken == 0: # do not randomize the first point; this saves a bit of time randomScatter = numpy.zeros(2, dtype=float) fullNodeName = "%s with no random scatter" % (nodeName,) else: randomScatter = (numpy.random.random(2) * randomRangeDeg) - (randomRangeDeg / 2.0) randomScatterArcSec = randomScatter * 3600.0 fullNodeName = "%s + %0.1f, %0.1f random scatter" % \ (nodeName, randomScatterArcSec[0], randomScatterArcSec[1]) #print "Adding randomScatter", randomScatter randomizedOffset = desOffset + randomScatter if self.needMove(randomizedOffset): # slew telescope randomScatterArcSec = randomScatter * 3600.0 sr.showMsg("Offset to %s" % (fullNodeName,)) yield self.waitOffset(randomizedOffset) # format exposure command startNum = numExpTaken + 1 expCmdStr = "%s startNum=%d" % (expCmdPrefix, startNum) # take exposure sequence sr.showMsg("Expose at %s" % (fullNodeName,)) yield sr.waitCmd( actor = self.expModel.actor, cmdStr = expCmdStr, abortCmdStr = "abort", ) numExpTaken += 1 else: # no randomization; take all numExp exposures at this point if self.needMove(desOffset): # slew telescope sr.showMsg("Offset to %s position" % nodeName) yield self.waitOffset(desOffset) # format exposure command startNum = numExpTaken + 1 expCmdStr = "%s startNum=%d" % (expCmdPrefix, startNum) # take exposure sequence sr.showMsg("Expose at %s position" % nodeName) yield sr.waitCmd( actor = self.expModel.actor, cmdStr = expCmdStr, abortCmdStr = "abort", ) numExpTaken += numExp stateWdg.set("Done") # slew back to starting position if self.needMove(self.begOffset): sr.showMsg("Finishing up: slewing to initial position") yield self.waitOffset(self.begOffset) def updOrder(self, wdg=None, doForce=False): """Update the order widgets If the script is executing then the widgets are left untouched unless doForce is True. This allows the order widgets to be correct while running even if the user messes with the checkboxes. """ if not doForce and self.sr.isExecuting(): return orderNum = 1 for stateWdg, orderWdg, boolWdg in self.ditherWdgSet: if boolWdg.getBool(): orderWdg.set(orderNum) orderNum += 1 else: orderWdg.set(None) def waitOffset(self, desOffset): """Offset the telescope""" tccCmdStr = "offset boresight %.7f, %.7f/pabs/vabs/computed" % tuple(desOffset) self.currOffset = desOffset[:] yield self.sr.waitCmd( actor = "tcc", cmdStr = tccCmdStr, )
def __init__(self, sr): """Create widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.errStr = "" self.nicfpsModel = TUI.Inst.NICFPS.NICFPSModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) self.tccModel = TUI.TCC.TCCModel.getModel() row=0 # standard exposure status widget expStatusWdg = ExposeStatusWdg( master = sr.master, instName = InstName, helpURL = HelpURL, ) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg( master = sr.master, instName = InstName, expTypes = "object", helpURL = HelpURL, ) self.expWdg.numExpWdg.helpText = "# of exposures at each spacing" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 gr = self.expWdg.gridder # add etalon controls to exposure input widget self.begSeqIndWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = 0, width = SpacingWidth, helpText = "initial z index (to finish a partial run)", helpURL = HelpURL, ) gr.gridWdg("Initial Index", self.begSeqIndWdg, "(normally leave blank)") self.fpBegZWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = self.nicfpsModel.fpXYZLimConst[0], maxValue = self.nicfpsModel.fpXYZLimConst[1], width = SpacingWidth, helpText = "initial etalon Z spacing", helpURL = HelpURL, ) gr.gridWdg("Initial Z", self.fpBegZWdg, "steps") self.fpDeltaZWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = self.nicfpsModel.fpXYZLimConst[0], maxValue = self.nicfpsModel.fpXYZLimConst[1], width = SpacingWidth, helpText = "etalon Z spacing interval", helpURL = HelpURL, ) gr.gridWdg("Delta Z", self.fpDeltaZWdg, "steps") self.fpNumZWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = 1, maxValue = 9999, width = SpacingWidth, helpText = "number of etalon Z spacings", helpURL = HelpURL, ) gr.gridWdg("Num Zs", self.fpNumZWdg, "steps") self.fpEndZWdg = RO.Wdg.IntLabel( master = self.expWdg, width = SpacingWidth, helpText = "final etalon Z spacing", helpURL = HelpURL, anchor = "e", ) self.fpEndZUnitsWdg = RO.Wdg.StrLabel( master = self.expWdg, text = "steps", helpURL = HelpURL, anchor = "w", ) gr.gridWdg("Final Z", self.fpEndZWdg, self.fpEndZUnitsWdg) self.fpNumPassesWdg = RO.Wdg.OptionMenu( master = self.expWdg, items = ("1", "2", "3"), defValue = "2", helpText = "number of passes in which to sample Z", helpURL = HelpURL, ) gr.gridWdg("Num Passes", self.fpNumPassesWdg) self.currSeqIndWdg = RO.Wdg.IntLabel( master = self.expWdg, width = SpacingWidth, helpText = "index of current Z spacing", helpURL = HelpURL, anchor = "e", ) gr.gridWdg("Current Index", self.currSeqIndWdg) fpCurrWdg = RO.Wdg.IntLabel( master = self.expWdg, width = SpacingWidth, helpText = "current actual etalon Z spacing", helpURL = HelpURL, anchor = "e", ) gr.gridWdg("Current Z", fpCurrWdg, "steps") self.nicfpsModel.fpZ.addROWdg(fpCurrWdg) self.fpBegZWdg.addCallback(self.updEndZ, callNow=False) self.fpDeltaZWdg.addCallback(self.updEndZ, callNow=False) self.fpNumZWdg.addCallback(self.updEndZ, callNow=True)
class ScriptClass(object): def __init__(self, sr): """Create widgets. """ # if True, run in debug-only mode (which doesn't DO anything, it just pretends) sr.debug = False self.errStr = "" self.nicfpsModel = TUI.Inst.NICFPS.NICFPSModel.getModel() self.expModel = TUI.Inst.ExposeModel.getModel(InstName) self.tccModel = TUI.TCC.TCCModel.getModel() row=0 # standard exposure status widget expStatusWdg = ExposeStatusWdg( master = sr.master, instName = InstName, helpURL = HelpURL, ) expStatusWdg.grid(row=row, column=0, sticky="news") row += 1 # standard exposure input widget self.expWdg = ExposeInputWdg( master = sr.master, instName = InstName, expTypes = "object", helpURL = HelpURL, ) self.expWdg.numExpWdg.helpText = "# of exposures at each spacing" self.expWdg.grid(row=row, column=0, sticky="news") row += 1 gr = self.expWdg.gridder # add etalon controls to exposure input widget self.begSeqIndWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = 0, width = SpacingWidth, helpText = "initial z index (to finish a partial run)", helpURL = HelpURL, ) gr.gridWdg("Initial Index", self.begSeqIndWdg, "(normally leave blank)") self.fpBegZWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = self.nicfpsModel.fpXYZLimConst[0], maxValue = self.nicfpsModel.fpXYZLimConst[1], width = SpacingWidth, helpText = "initial etalon Z spacing", helpURL = HelpURL, ) gr.gridWdg("Initial Z", self.fpBegZWdg, "steps") self.fpDeltaZWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = self.nicfpsModel.fpXYZLimConst[0], maxValue = self.nicfpsModel.fpXYZLimConst[1], width = SpacingWidth, helpText = "etalon Z spacing interval", helpURL = HelpURL, ) gr.gridWdg("Delta Z", self.fpDeltaZWdg, "steps") self.fpNumZWdg = RO.Wdg.IntEntry( master = self.expWdg, minValue = 1, maxValue = 9999, width = SpacingWidth, helpText = "number of etalon Z spacings", helpURL = HelpURL, ) gr.gridWdg("Num Zs", self.fpNumZWdg, "steps") self.fpEndZWdg = RO.Wdg.IntLabel( master = self.expWdg, width = SpacingWidth, helpText = "final etalon Z spacing", helpURL = HelpURL, anchor = "e", ) self.fpEndZUnitsWdg = RO.Wdg.StrLabel( master = self.expWdg, text = "steps", helpURL = HelpURL, anchor = "w", ) gr.gridWdg("Final Z", self.fpEndZWdg, self.fpEndZUnitsWdg) self.fpNumPassesWdg = RO.Wdg.OptionMenu( master = self.expWdg, items = ("1", "2", "3"), defValue = "2", helpText = "number of passes in which to sample Z", helpURL = HelpURL, ) gr.gridWdg("Num Passes", self.fpNumPassesWdg) self.currSeqIndWdg = RO.Wdg.IntLabel( master = self.expWdg, width = SpacingWidth, helpText = "index of current Z spacing", helpURL = HelpURL, anchor = "e", ) gr.gridWdg("Current Index", self.currSeqIndWdg) fpCurrWdg = RO.Wdg.IntLabel( master = self.expWdg, width = SpacingWidth, helpText = "current actual etalon Z spacing", helpURL = HelpURL, anchor = "e", ) gr.gridWdg("Current Z", fpCurrWdg, "steps") self.nicfpsModel.fpZ.addROWdg(fpCurrWdg) self.fpBegZWdg.addCallback(self.updEndZ, callNow=False) self.fpDeltaZWdg.addCallback(self.updEndZ, callNow=False) self.fpNumZWdg.addCallback(self.updEndZ, callNow=True) def updEndZ(self, *args, **kargs): """Call when beg Z, delta Z or num Z changed to update end Z. """ begSpacing = self.fpBegZWdg.getNumOrNone() numSpacings = self.fpNumZWdg.getNumOrNone() deltaZ = self.fpDeltaZWdg.getNumOrNone() endZ = None self.errStr = "" if begSpacing is None: self.errStr = "specify initial Z" elif deltaZ is None: self.errStr = "specify delta z" elif numSpacings is None: self.errStr = "specify number of zs" else: endZ = begSpacing + (deltaZ * (numSpacings - 1)) # check range minZ, maxZ = self.nicfpsModel.fpXYZLimConst if endZ < minZ: self.errStr = "final Z < %s" % minZ elif endZ > maxZ: self.errStr = "final Z > %s" % maxZ if self.errStr: isCurrent = False self.fpEndZUnitsWdg.set("error: %s" % self.errStr, isCurrent=isCurrent) else: isCurrent = True self.fpEndZUnitsWdg.set("steps", isCurrent=isCurrent) self.fpEndZWdg.set(endZ, isCurrent = isCurrent) def run(self, sr): """Take an exposure sequence. """ # Make sure the current instrument is NICFPS if not sr.debug: currInstName = sr.getKeyVar(self.tccModel.instName) if not currInstName.lower().startswith(InstName.lower()): raise sr.ScriptError("%s is not the current instrument!" % InstName) # exposure command without startNum and totNum # get it now so that it will not change if the user messes # with the controls while the script is running numExp = self.expWdg.numExpWdg.getNum() expCmdPrefix = self.expWdg.getString() if not expCmdPrefix: raise sr.ScriptError("missing inputs") # print "got here; errStr =", self.errStr if self.errStr: raise sr.ScriptError(self.errStr) # get user data in advance begSeqInd = self.begSeqIndWdg.getNum() begSpacing = self.fpBegZWdg.getNum() numSpacings = self.fpNumZWdg.getNum() deltaZ = self.fpDeltaZWdg.getNum() numPasses = int(self.fpNumPassesWdg.getString()) totNumExp = numExp * numSpacings # for each pass through the data, create a list of multipliers, # where z = zo + delta-z * mult multList = range(numSpacings) seqPassMultList = [] for passInd in range(numPasses): for zMult in multList[passInd::numPasses]: seqInd = len(seqPassMultList) seqPassMultList.append((seqInd, passInd, zMult)) # print "seqPassMultList =", seqPassMultList for seqInd, passInd, zMult in seqPassMultList[begSeqInd:]: currSpacing = begSpacing + (deltaZ * zMult) self.currSeqIndWdg.set(seqInd) # command etalon spacing sr.showMsg("Set etalon Z = %d %s" % (currSpacing, "steps")) yield sr.waitCmd( actor = self.nicfpsModel.actor, cmdStr = "fp setz=%d" % (currSpacing,), ) # compute # of exposures & format expose command startNum = seqInd * numExp expCmdStr = "%s startNum=%d totNum=%d" % (expCmdPrefix, startNum, totNumExp) # take exposure sequence sr.showMsg("Expose at etalon Z = %d %s" % (currSpacing, "steps")) yield sr.waitCmd( actor = self.expModel.actor, cmdStr = expCmdStr, abortCmdStr = "abort", )