def read_parfile(self, parfilename: str = None) -> None: """ Read a parameter file and create sub-dictionaries for saving parameters between sessions. Args: parfilename: Name of parameter file """ if parfilename is None: parfilename = self.par_file if parfilename is None: azcam.AzcamWarning("Parameter file is not defined") return self.par_file = parfilename if not os.path.exists(parfilename): azcam.AzcamWarning(f"Parameter file not found: {parfilename}") return cp = configparser.ConfigParser() cp.read(parfilename) sections = cp.sections() # sectionname & value case sensitive, name is not for sectionname in sections: self.par_dict[sectionname] = {} for name, value in cp.items(sectionname): name = name.lower() self.par_dict[sectionname][name] = value return
def show_menu(configs: dict) -> str: """ Interative: Show a menu and wait for selection. "blank" may be used to display an empty line. print() is allowed here as this is for interactive use only. Args: configs: Dictionary of strings which are menu items Returns: string associated with item selected or empty string. """ if len(configs) == 1: choice = configs[list(configs.keys())[0]] return choice CONFIRMED = 0 choice = "" while not CONFIRMED: print("Select configuration number from list below:\n") i = 0 for c in configs: if c == "blank": print("") else: i += 1 print("%1d.....%s" % (i, c)) print("") print("Enter configuration number: ", end="") choiceindex = input() if choiceindex == "q": azcam.AzcamWarning("Quit detected") return try: choiceindex = int(choiceindex) except ValueError: print("Bad keyboard input...try again\n") continue choiceindex = int(choiceindex) choiceindex = choiceindex - 1 # zero based # remove blanks for x in configs: if x == "blank": configs.remove("blank") if choiceindex < 0 or choiceindex > len(configs) - 1: print("invalid selection - %d\n" % (choiceindex + 1)) continue # get choice configlist = list(configs.keys()) # is order OK? choice = configs[configlist[choiceindex]] CONFIRMED = 1 print("") return choice
def read_header(self): """ Returns telescope header info. returns [Header[]]: Each element Header[i] contains the sublist (keyword, value, comment, and type). Example: Header[2][1] is the value of keyword 2 and Header[2][3] is its type. Type is one of str, int, or float. """ if not self.enabled: azcam.AzcamWarning("telescope not enabled") return header = [] data = self.Tserver.azcam_all() keywords = list(data.keys()) keywords.sort() list1 = [] for key in list(self.Tserver.keywords): try: t = self.header.typestrings[key] v = data[self.Tserver.keywords[key]] c = self.header.comments[key] list1 = [key, v, c, t] header.append(list1) except Exception as message: azcam.log("ERROR", key, message) continue # store value in Header self.header.set_keyword(list1[0], list1[1], list1[2], list1[3]) return header
def get_keyword(self, keyword): """ Reads an telescope keyword value. Keyword is the name of the keyword to be read. This command will read hardware to obtain the keyword value. """ if not self.enabled: azcam.AzcamWarning(f"{self.description} is not enabled") return data = self.Tserver.azcam_all() keyword = keyword.lower() reply = data[keyword] # parse RA and DEC specially if keyword == "RA": reply = "%s:%s:%s" % (reply[0:2], reply[2:4], reply[4:]) elif keyword == "DEC": reply = "%s:%s:%s" % (reply[0:3], reply[3:5], reply[5:]) else: pass # store value in Header self.set_keyword(keyword, reply) reply, t = self.header.convert_type(reply, self.header.typestrings[keyword]) return [reply, self.comments[keyword], t]
def wait_for_move(self): """ Wait for telescope to stop moving. """ if not self.enabled: azcam.AzcamWarning("telescope not enabled") return # loop for up to ~20 seconds for i in range(200): reply = self.get_keyword("MOTION") try: motion = int(reply[0]) except: raise azcam.AzcamError("bad MOTION status keyword: %s" % reply[1]) if not motion: return time.sleep(0.1) # stop the telescope command = "CANCEL" reply = self.Tserver.command(command) raise azcam.AzcamError("stopped motion flag not detected")
def initialize(self) -> None: """ Initialize the tool. """ if self.initialized: return if not self.enabled: azcam.AzcamWarning(f"{self.description} is not enabled") return self.initialized = 1 return
def initialize(self): """ Initialize display. """ if self.initialized: return if not self.enabled: azcam.AzcamWarning("Display is not enabled") return self.set_display(self.default_display) return
def offset(self, RA, Dec): """ Offsets telescope in arcsecs. """ if not self.enabled: azcam.AzcamWarning("telescope not enabled") return reply = self.Tserver.comSTEPRA(RA) reply = self.Tserver.comSTEPDEC(Dec) # wait for motion to stop reply = self.wait_for_move() return
def expose(self, exposure_time=-1, imagetype="", title=""): """ Make a complete exposure. exposure_time is the exposure time in seconds imagetype is the type of exposure ('zero', 'object', 'flat', ...) title is the image title. """ # allow custom operations self.start() azcam.log("Exposure started") # if last exposure was aborted, warn before clearing flag if self.exposure_flag == self.exposureflags["ABORT"]: azcam.AzcamWarning("Previous exposure was aborted") # begin if self.exposure_flag != self.exposureflags["ABORT"]: self.begin(exposure_time, imagetype, title) # integrate if self.exposure_flag != self.exposureflags["ABORT"]: self.integrate() # readout if (self.exposure_flag != self.exposureflags["ABORT"] and self.exposure_flag == self.exposureflags["READ"]): try: self.readout() except azcam.AzcamError: pass # end if self.exposure_flag != self.exposureflags["ABORT"]: self.end() self.exposure_flag = self.exposureflags["NONE"] self.completed = 1 azcam.log("Exposure finished") # allow custom operations self.finish() return
def read_file(self, filename=""): """ Read a header file and import the data into the header. """ # if no file specified, just skip if filename == "": return if not os.path.exists(filename): azcam.AzcamWarning("Header file not found:%s" % filename) return with open(filename, "r") as f1: for line in f1.readlines(): line = line.rstrip() line = line.lstrip() if line.startswith("#"): break if len(line) == 0: break tokens = line.split(" ", 1) keyword = tokens[0] if keyword == "COMMENT" and len(tokens) == 1: value = "" comment = "" else: nslash = tokens[1].find("/") if nslash == -1: value = tokens[1].strip() comment = "" else: comment = tokens[1][nslash + 1 :].strip() value = tokens[1][:nslash].strip() typ, val = azcam.utils.get_datatype(value) self.set_keyword(keyword, val, comment, typ) self.filename = filename return
def initialize(self): """ Initializes the telescope interface. """ if self.initialized: return if not self.enabled: azcam.AzcamWarning(f"{self.description} is not enabled") return self.Tserver = TelescopeNG() # add keywords self.define_keywords() self.initialized = 1 return
def initialize(self): """ Initialize Ds9. :return None: """ if self.initialized: return if os.name == "posix": self.xpaset_app = "xpaset" self.xpaget_app = "xpaget" self.xpaaccess_app = "xpaaccess" self.xpans = "xpans" else: self.ds9_app = os.path.join(self.root, "ds9.exe") self.ds9_app = os.path.abspath(self.ds9_app) self.xpaset_app = os.path.join(self.root, "xpaset.exe") self.xpaset_app = os.path.abspath(self.xpaset_app) self.xpaget_app = os.path.join(self.root, "xpaget.exe") self.xpaget_app = os.path.abspath(self.xpaget_app) self.xpaaccess_app = os.path.join(self.root, "xpaaccess.exe") self.xpaaccess_app = os.path.abspath(self.xpaaccess_app) self.xpans = os.path.join(self.root, "xpans.exe") self.xpans = os.path.abspath(self.xpans) if not self.enabled: azcam.AzcamWarning("Display is not enabled") return self.set_display(self.default_display) self.initialized = 1 return
def display(self, image, extension_number=-1): """ Display a file in ds9, making a copy so locking does not occur. If specified for an MEF file, only extension_number is displayed. :param image: a filename or an image object :param int extension_number: FITS extension number of image, -1 for all :return None: """ self.initialize() # test, could be slow but seems to work nicely self.set_display() ds9 = self.host + ":" + self.port if type(image) == str: filename1 = azcam.utils.make_image_filename(image) else: filename1 = image.filename ext = os.path.splitext(filename1)[-1] # copy image file so it is not locked by ds9 filename = os.path.join(tempfile.gettempdir(), "tempdisplayfile" + ext) shutil.copyfile(filename1, filename) if ext == ".fits": im = pyfits.open(filename) ne = max(0, len(im) - 1) im.close() if ne in [0, 1]: # s = self.xpaset_app + " " + ds9 + "fits iraf < " + filename s = [self.xpaset_app, ds9, f"fits iraf < {filename}"] else: if extension_number == -1: # s = self.xpaset_app + " " + ds9 + "fits mosaicimage iraf < " + filename s = [ self.xpaset_app, ds9, f"fits mosaicimage iraf < {filename}" ] else: s = [ self.xpaset_app, ds9, "fits", f"[{extension_number}] < {filename}", ] elif ext == ".bin": NumCols, NumRows = self.size_x, self.size_y s = [ self.xpaset_app, ds9, "array", f"[xdim={NumCols},ydim={NumRows},bitpix=-16] < {filename}", ] else: azcam.AzcamWarning("invalid image extension") return # execute XPA command to display file s = " ".join(s) p = subprocess.Popen( s, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, ) stdout, stderr = p.communicate() stdout = stdout.decode("utf-8") return
def guide(self, number_exposures=1): """ Make a complete guider exposure sequence. NumberExposures is the number of exposures to make, -1 loop forever """ AbortFlag = 0 number_exposures = int(number_exposures) # system must be reset once before an exposure can be made if not azcam.db.tools["controller"].is_reset: azcam.db.tools["controller"].reset() # parameters for faster operation flusharray = self.flush_array azcam.log("Guide started") # this loop continues even for errors since data is sent to a seperate client receiving images LoopCount = 0 while True: if 0: self.begin(exposure_time=-1, imagetype="object", title="guide image") # integrate self.integrate() # readout if self.exposure_flag == self.exposureflags["READ"]: try: self.readout() self.guide_status = 1 # image read OK self.guide_image_copy = self.image except Exception: self.guide_status = 2 # image not read OK, but don't stop guide loop self.image = self.guide_image_copy # image writing self.end() self.exposure_flag = self.exposureflags["NONE"] else: self.expose(-1, "object", "guide image") AbortFlag = azcam.db.abortflag if AbortFlag: break if number_exposures == -1: continue else: LoopCount += 1 if LoopCount >= number_exposures: break # finish self.guide_status = 0 self.flush_array = flusharray if AbortFlag: azcam.AzcamWarning("Guide aborted") else: azcam.log("Guide finished") return
def set_par(self, parameter: str, value: typing.Any = None) -> None: """ Set the value of a parameter in the parameters dictionary. Args: parameter (str): name of the parameter value (Any): value of the parameter. Defaults to None. Returns: None """ parameter = parameter.lower() if azcam.db.mode == "console": try: azcam.db.tools["server"].command(f"parameters.set_par {parameter} {value}") except azcam.AzcamError: return return None # special cases if parameter == "imagefilename": azcam.db.tools["exposure"].image.filename = value return None elif parameter == "imagetitle": if value is None or value == "" or value == "None": azcam.db.tools["exposure"].set_image_title("") else: azcam.db.tools["exposure"].set_image_title(f"{value}") return None elif parameter == "exposuretime": azcam.db.tools["exposure"].set_exposuretime(value) return None elif parameter == "logcommands": azcam.db.tools["cmdserver"].logcommands = int(value) return None # parameter must be in parameters try: attribute = azcam.db.pardict[parameter] except KeyError: azcam.AzcamWarning(f"Parameter {parameter} not available for set_par") return None # object must be a tool tokens = attribute.split(".") numtokens = len(tokens) if numtokens < 2: azcam.log("%s not valid for parameter %s" % (attribute, parameter)) return None # first try to set value type _, value = azcam.utils.get_datatype(value) object1 = tokens[0] # run through tools try: obj = azcam.db.tools[object1] for i in range(1, numtokens - 1): obj = getattr(obj, tokens[i]) # last time is actual object try: setattr(obj, tokens[-1], value) except AttributeError: pass # azcam.AzcamWarning(f"Could not set parameter: {parameter}") except KeyError: pass return None
def get_par(self, parameter: str) -> typing.Any: """ Return the value of a parameter in the parameters dictionary. Args: parameter (str): name of the parameter Returns: value (Any): value of the parameter """ parameter = parameter.lower() value = None if azcam.db.mode == "console": try: reply = azcam.db.tools["server"].command(f"parameters.get_par {parameter}") except azcam.AzcamError: return _, value = azcam.utils.get_datatype(reply) return value # special cases if parameter == "imagefilename": value = azcam.db.tools["exposure"].get_filename() return value elif parameter == "imagetitle": value = azcam.db.tools["exposure"].get_image_title() return value elif parameter == "exposuretime": value = azcam.db.tools["exposure"].get_exposuretime() return value elif parameter == "exposurecompleted": value = azcam.db.tools["exposure"].finished() return value elif parameter == "exposuretimeremaining": value = azcam.db.tools["exposure"].get_exposuretime_remaining() return value elif parameter == "pixelsremaining": value = azcam.db.tools["exposure"].get_pixels_remaining() return value elif parameter == "camtemp": value = azcam.db.tools["tempcon"].get_temperatures()[0] return value elif parameter == "dewtemp": value = azcam.db.tools["tempcon"].get_temperatures()[1] return value elif parameter == "temperatures": camtemp = azcam.db.tools["tempcon"].get_temperatures()[0] dewtemp = azcam.db.tools["tempcon"].get_temperatures()[1] return [camtemp, dewtemp] elif parameter == "logcommands": value = azcam.db.tools["cmdserver"].logcommands return value elif parameter == "wd": value = azcam.utils.curdir() return value elif parameter == "logdata": value = azcam.db.logger.get_logdata() return value # parameter must be in parameters try: attribute = azcam.db.pardict[parameter] except KeyError: azcam.AzcamWarning(f"Parameter {parameter} not available for get_par") return None tokens = attribute.split(".") numtokens = len(tokens) # a tool and attribute is required if numtokens == 1: return None object1 = tokens[0] # object1 must be a tool try: obj = azcam.db.tools[object1] for i in range(1, numtokens): try: obj = getattr(obj, tokens[i]) except AttributeError: pass value = obj # last time is value except KeyError: value = None return value