def set_frame(self, minx, miny, width, height): # passed in pixels are the "size" of the current binning # # # INDI expects us to set frame in 1x1 pixels # ccd_frame = indihelper.getNumber(self.cam, 'CCD_FRAME') if ccd_frame is None: return False binx, biny = self.get_binning() if None in [binx, biny]: logging.error('set_frame: unable to determine binning!') return False logging.debug(f'set_frame: binning = {binx} {biny}') ccd_x = indihelper.findNumber(ccd_frame, 'X') ccd_y = indihelper.findNumber(ccd_frame, 'Y') ccd_w = indihelper.findNumber(ccd_frame, 'WIDTH') ccd_h = indihelper.findNumber(ccd_frame, 'HEIGHT') # logging.debug(f'set_frame: {minx} {miny} {width} {height} {ccd_x.value} ' # f'{ccd_y.value} {ccd_w.value} {ccd_h.value}') if None in [ccd_x, ccd_y, ccd_w, ccd_h]: return False ccd_x.value = minx * binx ccd_y.value = miny * biny ccd_w.value = width * binx ccd_h.value = height * biny self.backend.indiclient.sendNewNumber(ccd_frame) return True
def get_position_radec(self): """Returns tuple of (ra, dec) with ra in decimal hours and dec in degrees""" ra = indihelper.getfindNumberValue(self.mount, 'EQUATORIAL_EOD_COORD', 'RA') dec = indihelper.getfindNumberValue(self.mount, 'EQUATORIAL_EOD_COORD', 'DEC') return (ra, dec)
def stop_exposure(self): # had trouble using the setfindSwitchState function for some reason! retries = 5 i = 0 sw_prop = None while i < retries: #logging.info('Trying to get CCD_ABORT_EXPOSURE') sw_prop = indihelper.getSwitch(self.cam, 'CCD_ABORT_EXPOSURE') #logging.info(f'sw_prop = {sw_prop}') if sw_prop is not None: break time.sleep(0.1) i += 1 if sw_prop is None: return False sw = None i = 0 while i < retries: logging.debug('Trying to get ABORT') sw = indihelper.findSwitch(sw_prop, 'ABORT') logging.debug(f'sw = {sw}') if sw is not None: break time.sleep(0.1) if sw is None: return False sw.s = PyIndi.ISS_ON self.backend.indiclient.sendNewSwitch(sw_prop) return True
def get_frame(self): ccd_frame = indihelper.getNumber(self.cam, 'CCD_FRAME') if ccd_frame is None: return None ccd_x = indihelper.findNumber(ccd_frame, 'X') ccd_y = indihelper.findNumber(ccd_frame, 'Y') ccd_w = indihelper.findNumber(ccd_frame, 'WIDTH') ccd_h = indihelper.findNumber(ccd_frame, 'HEIGHT') if None in [ccd_x, ccd_y, ccd_w, ccd_h]: return (None, None, None, None) # called expects pixels are the "size" of the current binning # # # INDI returns frame in 1x1 pixels # binx, biny = self.get_binning() if None in [binx, biny]: logging.error('get_frame: unable to determine binning!') return False # logging.debug(f'get_frame: binning = {binx} {biny}') return (ccd_x.value // binx, ccd_y.value // biny, ccd_w.value // binx, ccd_h.value // biny)
def start_exposure(self, expos): logging.debug(f'Exposing image for {expos} seconds') # FIXME currently always requesting a light frame # FIXME need to check return codes of all steps if self.cam: ccd_exposure = indihelper.getNumber(self.cam, 'CCD_EXPOSURE') if ccd_exposure is None: return False # we should inform the indi server that we want to receive the # 'CCD1' blob from this device # FIXME not good to reference global 'config' here - we already pass # a device object should combine? self.backend.indiclient.setBLOBMode(PyIndi.B_ALSO, self.name, 'CCD1') ccd_ccd1 = self.cam.getBLOB('CCD1') while not ccd_ccd1: time.sleep(0.25) ccd_ccd1 = self.device.getBLOB('CCD1') self.backend.indiclient.clearBlobEvent() ccd_expnum = indihelper.findNumber(ccd_exposure, 'CCD_EXPOSURE_VALUE') if ccd_expnum is None: return False ccd_expnum.value = expos self.backend.indiclient.sendNewNumber(ccd_exposure) return True else: return False
def get_binning(self): ccd_bin = indihelper.getNumber(self.cam, 'CCD_BINNING') binx = indihelper.findNumber(ccd_bin, 'HOR_BIN') biny = indihelper.findNumber(ccd_bin, 'VER_BIN') if binx is None or biny is None: return None, None return (binx.value, biny.value)
def is_moving(self): state = indihelper.getNumberState(self.filterwheel, 'FILTER_SLOT') if state is None: return None return state == PyIndi.IPS_BUSY state = indihelper.getfindLightState(self.filterwheel, 'FILTER_SLOT') return state == PyIndi.IPS_BUSY
def get_pier_side(self): """Returns 'EAST' or 'WEST' or None if could not be determined.""" if indihelper.getfindSwitchState(self.mount, 'TELESCOPE_PIER_SIDE', 'PIER_EAST'): return 'EAST' elif indihelper.getfindSwitchState(self.mount, 'TELESCOPE_PIER_SIDE', 'PIER_WEST'): return 'WEST' else: return None
def sync(self, ra, dec): """Sync to ra/dec with ra in decimal hours and dec in degrees""" logging.debug(f'sync to {ra} {dec}') #logging.debug('finding) ON_COORD_SET switch') indihelper.setfindSwitchState(self.backend.indiclient, self.mount, 'ON_COORD_SET', 'SYNC', True) indihelper.setfindSwitchState(self.backend.indiclient, self.mount, 'ON_COORD_SET', 'SLEW', False) indihelper.setfindSwitchState(self.backend.indiclient, self.mount, 'ON_COORD_SET', 'TRACK', False) #logging.debug('getNumber EQUATORIAL_EOD_COORD') eq_coord = indihelper.getNumber(self.mount, 'EQUATORIAL_EOD_COORD') if eq_coord is None: return False #logging.debug('findNumber "RA"') ra_coord = indihelper.findNumber(eq_coord, 'RA') if ra_coord is None: return False #logging.debug('findNumber "DEC"') dec_coord = indihelper.findNumber(eq_coord, 'DEC') if dec_coord is None: return False #logging.debug(f'{ra_coord.value} {dec_coord.value}') ra_coord.value = ra dec_coord.value = dec #logging.debug(f'{ra_coord.value} {dec_coord.value}') #logging.debug('sending Number') self.backend.indiclient.sendNewNumber(eq_coord) return True
def set_tracking(self, onoff): rc = indihelper.setfindSwitchState(self.backend.indiclient, self.mount, 'TELESCOPE_TRACK_STATE', 'TRACK_ON', onoff) if not rc: return rc rc = indihelper.setfindSwitchState(self.backend.indiclient, self.mount, 'TELESCOPE_TRACK_STATE', 'TRACK_OFF', not onoff) return rc
def set_binning(self, binx, biny): ccd_bin = indihelper.getNumber(self.cam, 'CCD_BINNING') if ccd_bin is None: return False num_binx = indihelper.findNumber(ccd_bin, 'HOR_BIN') num_biny = indihelper.findNumber(ccd_bin, 'VER_BIN') if num_binx is None or num_biny is None: return False num_binx.value = binx num_biny.value = biny self.backend.indiclient.sendNewNumber(ccd_bin) return True
def get_cooler_power(self): if self.camera_has_cooler_power is False: return None cool_power = indihelper.getNumber(self.cam, 'CCD_COOLER_POWER') if cool_power is None: self.camera_has_cooler_power = False return None num = indihelper.findNumber(cool_power, 'CCD_COOLER_VALUE') if num is None: self.camera_has_cooler_power = False return None return num.value
def set_cooler_state(self, onoff): rc = indihelper.setfindSwitchState(self.backend.indiclient, self.cam, 'CCD_COOLER', 'COOLER_ON', onoff) if not rc: return rc rc = indihelper.setfindSwitchState(self.backend.indiclient, self.cam, 'CCD_COOLER', 'COOLER_OFF', not onoff) return rc
def get_min_max_exposure(self): if self.cam: ccd_exposure = indihelper.getNumber(self.cam, 'CCD_EXPOSURE') if ccd_exposure is None: return None ccd_expnum = indihelper.findNumber(ccd_exposure, 'CCD_EXPOSURE_VALUE') if ccd_expnum is None: return None return (ccd_expnum.min, ccd_expnum.max) else: return None
def get_camera_gain(self): """ Looks for camera specific gain - only works for ASI afaik""" if self.cam: ccd_controls = indihelper.getNumber(self.cam, 'CCD_CONTROLS') if ccd_controls is None: return None ccd_gain = indihelper.findNumber(ccd_controls, 'Gain') if ccd_gain is None: return None return ccd_gain.value else: return None
def get_camera_offset(self): """ Looks for camera specific offset - only works for ASI afaik""" if self.cam: ccd_controls = indihelper.getNumber(self.cam, 'CCD_CONTROLS') if ccd_controls is None: return None ccd_offset = indihelper.findNumber(ccd_controls, 'Offset') if ccd_offset is None: return None return ccd_offset.value else: return None
def get_camera_usbbandwidth(self): """ Looks for camera specific usb traffic - only works for ASI afaik""" if self.cam: ccd_controls = indihelper.getNumber(self.cam, 'CCD_CONTROLS') if ccd_controls is None: return None ccd_usb = indihelper.findNumber(ccd_controls, 'BandWidth') if ccd_usb is None: return None return ccd_usb.value else: return None
def get_position_altaz(self): """Returns tuple of (alt, az) in degrees""" # # NOTE seems like some (most?) INDI GEM drivers don't return alt/az # if self.has_altaz_coord is not False: az = indihelper.getfindNumberValue(self.mount, 'HORIZONTAL_COORD', 'AZ') alt = indihelper.getfindNumberValue(self.mount, 'HORIZONTAL_COORD', 'ALT') if az is None or alt is None: self.has_altaz_coord = False else: az = None alt = None return (alt, az)
def get_max_binning(self): if self.cam: ccd_bin = indihelper.getNumber(self.cam, 'CCD_BINNING') #logging.info(f'ccd_bin={ccd_bin}') if ccd_bin is None: return None ccd_hbin = indihelper.findNumber(ccd_bin, 'HOR_BIN') #logging.info(f'ccd_hbin={indihelper.dump_Number(ccd_hbin)}') if ccd_hbin is None: return None return ccd_hbin.max else: return None
def get_position(self): """ Position starts at 0! """ pos = indihelper.getfindNumberValue(self.filterwheel, 'FILTER_SLOT', 'FILTER_SLOT_VALUE') if pos is not None: return int(pos) - 1 return None
def set_target_temperature(self, temp_c): # FIXME Handling ccd temperature needs to be more robust self.temperature_target = temp_c return indihelper.setfindNumberValue(self.backend.indiclient, self.cam, 'CCD_TEMPERATURE', 'CCD_TEMPERATURE_VALUE', temp_c)
def get_info(self): ccd_info = indihelper.getNumber(self.cam, 'CCD_INFO') if ccd_info is None: return None maxx = indihelper.findNumber(ccd_info, 'CCD_MAX_X') maxy = indihelper.findNumber(ccd_info, 'CCD_MAX_Y') pix_size = indihelper.findNumber(ccd_info, 'CCD_PIXEL_SIZE') pix_size_x = indihelper.findNumber(ccd_info, 'CCD_PIXEL_SIZE_X') pix_size_y = indihelper.findNumber(ccd_info, 'CCD_PIXEL_SIZE_Y') bpp = indihelper.findNumber(ccd_info, 'CCD_BITSPERPIXEL') # if maxx is None or maxy is None is pix_size is None \ # or pix_size_x is None or pix_size_y is None or bpp is None: if None in [maxx, maxy, pix_size, pix_size_x, pix_size_y, bpp]: return None obj = self.CCD_INFO() obj.CCD_MAX_X = maxx.value obj.CCD_MAX_Y = maxy.value obj.CCD_PIXEL_SIZE = pix_size.value obj.CCD_PIXEL_SIZE_X = pix_size_x.value obj.CCD_PIXEL_SIZE_Y = pix_size_y.value obj.CCD_BITSPERPIXEL = bpp.value return obj
def get_max_absolute_position(self): # Moonlite driver defines max travel if self.focuser_has_max_travel is not False: maxpos = indihelper.getfindNumberValue(self.focuser, 'FOCUS_MAXTRAVEL', 'MAXTRAVEL') if maxpos is not None: return maxpos else: self.focuser_has_max_travel = False # try using max value for abs pos slider p = indihelper.getfindNumber(self.focuser, 'ABS_FOCUS_POSITION', 'FOCUS_ABSOLUTE_POSITION') if p is not None: return p.max return None
def handle_set_buttons(self, id): print('handle_set_buttons: ', self, id) devicename, pvname, pname = id.split('::') property = self.device_propvectors[devicename][pvname].properties[ pname] property_type = self.device_propvectors[devicename][pvname].type if property_type == PyIndi.INDI_TEXT: value = property.value_widget.text() print(devicename, pvname, pname, value) device = self.indiclient.getDevice(devicename) indihelper.setfindTextText(self.indiclient, device, pvname, pname, value) # device = self.indiclient.getDevice(devicename) # print(device) # indi_propvector = indihelper.getText(device, pvname) # print(indi_propvector) # indi_text = indihelper.findText(indi_propvector, pname) # print(indi_text) # indi_text.value = value # print(indihelper.dump_ITextVectorProperty(indi_propvector)) # self.indiclient.sendNewText(indi_propvector) elif property_type == PyIndi.INDI_NUMBER: value = property.value_widget.value() print(devicename, pvname, pname, value) device = self.indiclient.getDevice(devicename) indihelper.setfindNumberValue(self.indiclient, device, pvname, pname, value) # indi_propvector = indihelper.getNumber(device, pvname) # indi_num = indihelper.findNumber(indi_propvector, pname) # indi_num.value = value # self.indiclient.sendNewNumber(indi_propvector) elif property_type == PyIndi.INDI_SWITCH: value = property.value_widget.isChecked() #print(property.value_widget, devicename, pvname, pname, value) device = self.indiclient.getDevice(devicename) # indihelper.setfindSwitchState(self.indiclient, device, pvname, pname, value) indi_propvector = indihelper.getSwitch(device, pvname) prop = indihelper.findSwitch(indi_propvector, pname) prop.s = value # depending on rule turn off other buttons if indi_propvector.r != PyIndi.ISR_NOFMANY and value: for i in range(0, indi_propvector.nsp): s = indi_propvector[i] #print('scanning for buttons to turn off', s.name, pname) if s.name != pname: s.s = PyIndi.ISS_OFF self.indiclient.sendNewSwitch(indi_propvector) final_indi_propvector = indihelper.getSwitch(device, pvname) print(indihelper.dump_ISwitchVectorProperty(final_indi_propvector))
def get_names(self): # lookup filter names from driver filter_name_prop = indihelper.getText(self.filterwheel, 'FILTER_NAME') if filter_name_prop is None: return None names = [] for i in range(0, filter_name_prop.ntp): names.append(filter_name_prop[i].text) return names
def get_current_temperature(self): if self.focuser_has_temperature is not False: curtemp = indihelper.getfindNumberValue(self.focuser, 'FOCUS_TEMPERATURE', 'TEMPERATURE') if curtemp is not None: return curtemp else: self.focuser_has_temperature = False return None
def connect(self, name): logging.debug(f'Connecting to mount device: {name}') if self.mount is not None: logging.warning('Mount.connect() self.mount is not None!') mount = indihelper.connectDevice(self.backend.indiclient, name) #logging.debug(f'connectDevice returned {mount}') if mount is not None: self.name = name self.mount = mount return True return False
def connect(self, name): logging.debug(f'Connecting to filterwheel device: {name}') if self.filterwheel is not None: logging.warning('FilterWheel.connect() self.filterwheel is not None!') fw = indihelper.connectDevice(self.backend.indiclient, name) #logging.info(f'connectDevice returned {fw}') if fw is not None: self.name = name self.filterwheel = fw return True return False
def set_position(self, pos): """Sends request to driver to move filter wheel position This DOES NOT wait for filter to move into position! Use is_moving() method to check if its done. Positions start at 1 so we add one since backend defines position starting at 0 like ASCOM! """ if pos < self.get_num_positions(): return indihelper.setfindNumberValue(self.backend.indiclient, self.filterwheel, 'FILTER_SLOT', 'FILTER_SLOT_VALUE', pos + 1) else: return False
def slew(self, ra, dec): """Slew to ra/dec with ra in decimal hours and dec in degrees""" indihelper.setfindSwitchState(self.backend.indiclient, self.mount, 'ON_COORD_SET', 'SLEW', False) indihelper.setfindSwitchState(self.backend.indiclient, self.mount, 'ON_COORD_SET', 'TRACK', True) indihelper.setfindSwitchState(self.backend.indiclient, self.mount, 'ON_COORD_SET', 'SYNC', False) eq_coord = indihelper.getNumber(self.mount, 'EQUATORIAL_EOD_COORD') if eq_coord is None: return False self.slew_eqcoord = eq_coord ra_coord = indihelper.findNumber(eq_coord, 'RA') if ra_coord is None: return False dec_coord = indihelper.findNumber(eq_coord, 'DEC') if dec_coord is None: return False ra_coord.value = ra dec_coord.value = dec self.backend.indiclient.sendNewNumber(eq_coord) return True