Source code for mountastro.mountastro

# -*- coding: utf-8 -*-
import os
import sys
if sys.platform == 'win32':
    import winreg
import time
import math
import numpy as np
import matplotlib.pyplot as plt
import ast

try:
    from .mountaxis import Mountaxis
except:    
    from mountaxis import Mountaxis

try:    
    from .mounttools import Mounttools
except:
    from mounttools import Mounttools

try:
    from .mountlog import Mountlog
except:
    from mountlog import Mountlog

try:
    from .mountpad import Mountpad
except:
    from mountpad import Mountpad

path = os.path.join(os.path.dirname(__file__), '..')
if path not in sys.path:
    sys.path.append(path)    
# --- celme imports
modulename = 'celme'
if modulename in dir():
    del celme
if modulename not in dir():    
    import celme
    
# #####################################################################
# #####################################################################
# #####################################################################
# Class Mountastro
# #####################################################################
# #####################################################################
# This is an abstract Class
# This class does not communicate to devices
# Use a child class to use the protocol language
# #####################################################################

[docs]class Mountastro(Mounttools): """ Class to define a mount. The first element of args is a string to define the mount type amongst: * HADEC: Eqquatorial mount. * HADECROT: Eqquatorial mount + field rotator. * AZELEV: Altazimutal mount. * AZELEVROT: Altazimutal mount + field rotator. Dictionnary of motion parameters are: * NAME: A string to identify the mount in memory. * LABEL: A string to identify the mount for prints. :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") A mount is constituted of many axes instanciated automatically using the Mountaxis class. The class Mountastro provides methods to convert astronomical coordinates into low level orders for controlers (i.e. increments). For example: * radec_coord(): To read the current position of the mount axes. * radec_synchronize(): To synchronize the current position of the mount axes. * radec_goto(): To slew the mount axes to a target position. These methods are implemented only in simulation mode in the Mountastro class. All these mods call a second one prefixed by _my_ to communicate physically with the controller. For example, radec_coord() calls _my_radec_coord(). In the class Mountastro the methos _my_* are only abstracts. The concrete methods will be defined in another class specific to the langage of the controller. For exemple the AstroMECCA commands are written in the class Mount_Astromecca which inherites metohds of the Mountastro class. :Example: :: >>> mymount = Mountastro_Astromecca("HADEC", name = "My telescope") >>> mymount.set_channel_params("SERIAL", port="COM1", baud_rate=115200) >>> ra, dec, pierside = mymount.radec_coords() """ # === Constant for error codes NO_ERROR = 0 ERR_UNKNOWN = 1 ERR_FILE_NOT_EXISTS = 101 # === Constants # === Private variables _last_errno = NO_ERROR # --- Axis parameters axisp = None axisb = None # === constants for saving coords SAVE_NONE = 0 SAVE_AS_SIMU = 1 SAVE_AS_REAL = 2 SAVE_ALL = 3 # === Constants for returning long or short outputs OUTPUT_SHORT = 0 OUTPUT_LONG = 1 # === LX200_0X06 = 0x06 LX200_HIGH_PRECISION = "HIGH PRECISION" LX200_LOW_PRECISION = "LOW PRECISION" # === List of Threads for the pad GUI _threads = [] _drift_hadec_ha_deg_per_sec = 0 _drift_hadec_dec_deg_per_sec = 0 _drift_radec_ra_deg_per_sec = 0 _drift_radec_dec_deg_per_sec = 0 _sideral_sec_per_day = 86164.0 # --- last actions _last_goto_drift_deg_per_secs = [] # --- slewing and tracking states for ASCOM _slewing_state = False _tracking_state = False # ===================================================================== # ===================================================================== # Private methods # ===================================================================== # ===================================================================== def _create(self, mount_name, mount_type): self._delete() def _delete(self): self._last_errno = self.NO_ERROR # ===================================================================== # ===================================================================== # Methods for using a pad GUI # ===================================================================== # ===================================================================== # to use it: # mymount = Mountastro("HADEC", name="Test Mount") # try: # mymount.pad_create("pad_dev1") # except (KeyboardInterrupt, SystemExit): # pass # except: # raise # ===================================================================== def pad_create(self, pad_type): Mountpad(self, pad_type) # ===================================================================== # ===================================================================== # Methods for debug # ===================================================================== # =====================================================================
[docs] def disp(self): """ Display a summary of the Mount parameters, the current values of encoders and celestial angles. This method is used to check and debug the mount. """ incsimus = ["" for kaxis in range(Mountaxis.AXIS_MAX)] increals, incsimus = self.read_encs(incsimus) increalb, rotrealb, celrealb, increalp, rotrealp, celrealp, piersidereal, incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu = self.enc2cel(incsimus, output_format=self.OUTPUT_LONG, save=self.SAVE_ALL) if self._mount_type=="HADEC": hareal, decreal = self.cel2hadec(celrealb, celrealp, "H0.2", "d+090.1") hasimu, decsimu = self.cel2hadec(celsimub, celsimup, "H0.2", "d+090.1") if self._mount_type=="AZELEV": azreal, elevreal = self.cel2azelev(celrealb, celrealp, "D0.2", "d+090.1") azsimu, elevsimu = self.cel2azelev(celsimub, celsimup, "D0.2", "d+090.1") print("{}".format(20*"-")) print("MOUNT name = {} ".format(self._name)) print("mount_type = {} {}".format(self._mount_type, self._mount_params["CONFIGURATION"])) print("home = {} ".format(self.site.gps)) for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue for disp_real in (False,True): if disp_real==True and current_axis.real == False: continue # --- get inc and compute rot, cel if disp_real==True: msg_simu = "REAL" inc = increals[kaxis] else: msg_simu = "SIMU" inc = incsimus[kaxis] rot, pierside = current_axis.inc2rot(inc, self.SAVE_NONE) cel = current_axis.rot2ang(rot, pierside, self.SAVE_NONE) # --- additional informations msg_coord = "" if self._mount_type=="HADEC": if current_axis.axis_type=="HA": if disp_real==True: msg_coord = hareal else: msg_coord = hasimu if current_axis.axis_type=="DEC": if disp_real==True: msg_coord = decreal else: msg_coord = decsimu if self._mount_type=="AZELEV": if current_axis.axis_type=="AZ": if disp_real==True: msg_coord = azreal else: msg_coord = azsimu if current_axis.axis_type=="ELEV": if disp_real==True: msg_coord = elevreal else: msg_coord = elevsimu # --- label_pierside = "Unknown" if pierside==1: label_pierside = self._mount_params["LABEL_REGULAR"] elif pierside==-1: label_pierside = self._mount_params["LABEL_FLIPED"] # --- print("{} {} {} {}".format(20*"-",current_axis.name,msg_simu,current_axis.axis_type)) print("inc = {:12.1f} : ".format(inc)) print("rot = {:12.7f} : ".format(rot)) print("pierside = {:d} : {}".format(pierside, label_pierside)) print("cel = {:12.7f} : {} {}".format(cel,current_axis.axis_type,msg_coord))
def set_param(self, key, value): self._mount_params[key] = value def get_param(self, key): keys = self._mount_params.keys() if key in keys: value = self._mount_params[key] else: value = None return value def get_params(self): return self._mount_params # ===================================================================== # ===================================================================== # Methods to read the encoders # ===================================================================== # ===================================================================== # Level 1 # =====================================================================
[docs] def _my_read_encs(self, incsimus:list)->list: """ Read the real raw increment values of the axes :param incsimus: List of simulated increments. :type incsimus: list :returns: List of real increments. :rtype: list Inputs are simulated increments. Outputs are real increments if a real mount exists. This is an abstract method. Please overload it according your language protocol to read real increments. This conversion method is at level 1/4. """ # --- abstract method. # --- Please overload it according your language protocol increals = incsimus return increals
[docs] def read_encs(self, simulation_incs:list)->list: """ Read the simulated and real raw increment values of the axes :param incsimus: List of simulated increments. :type incsimus: list :returns: List of real increments. :rtype: list For the simulation: * if simulation_incs=="" the value is calculated from simu_update_inc * if simulation_incs==34.5 the value is taken equal to 34.5 For real: * if the axis is not real then real=simulated value * else the encoder is read Output = Raw calues of encoders (inc) This conversion method is at level 1/4. """ # === Simulated values of inc incsimus = [0 for kaxis in range(Mountaxis.AXIS_MAX)] # --- Loop over all the possible axis types for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue # --- This axis is valid. We compute the simulation simulation_inc = simulation_incs[kaxis] if isinstance(simulation_inc,(int,float))==True: incsimus[kaxis] = simulation_inc else: incsimus[kaxis] = current_axis.simu_update_inc() # === Read real values of inc if possible increals = self._my_read_encs(incsimus) # --- if self._record_positions==True: with open("positions.txt","a",encoding='utf-8') as fid: msg = "{} {} {}\n".format(time.time(), increals[Mountaxis.BASE], incsimus[Mountaxis.BASE]) fid.write(msg) # --- return (increals, incsimus)
# ===================================================================== # ===================================================================== # Methods to convert encoders into celestial # ===================================================================== # ===================================================================== # Level 2 # =====================================================================
[docs] def enc2cel(self, simulation_incs:list, output_format=OUTPUT_SHORT, save=SAVE_NONE)->tuple: """ Read encoder values and convert them into celestial apparent coordinates :param simulation_incs: List of simulated increments. :type simulation_incs: list :param output_format: OUTPUT_SHORT or OUTPUT_LONG. :type output_format: int :param save: SAVE_NONE or SAVE_AS_SIMU or SAVE_AS_REAL or SAVE_ALL. :type save: int :returns: Celestial coordinates (degrees) :rtype: tuple of 3 or 14 elements Input = Raw encoder values (inc) Output = HA, Dec, pier side (any celme Angle units) This conversion method is at level 2/4. """ increals, incsimus = self.read_encs(simulation_incs) # --- shortcuts axisb = self.axis[Mountaxis.BASE] axisp = self.axis[Mountaxis.POLAR] incsimub = incsimus[Mountaxis.BASE] incsimup = incsimus[Mountaxis.POLAR] increalb = increals[Mountaxis.BASE] increalp = increals[Mountaxis.POLAR] # --- update the axis parameters (start with polar axis to deduce pierside) # --- update for simulations if save==self.SAVE_ALL or save==self.SAVE_AS_SIMU: savesimu = self.SAVE_AS_SIMU else: savesimu = self.SAVE_NONE rotsimup, piersidesimu = axisp.inc2rot(incsimup, savesimu) celsimup = axisp.rot2ang(rotsimup, piersidesimu, savesimu) rotsimub, dummy = axisb.inc2rot(incsimub, savesimu) celsimub = axisb.rot2ang(rotsimub, piersidesimu, savesimu) # --- update for real if save==self.SAVE_ALL or save==self.SAVE_AS_REAL: savereal = self.SAVE_AS_REAL else: savereal = self.SAVE_NONE rotrealp, piersidereal = axisp.inc2rot(increalp, savereal) celrealp = axisp.rot2ang(rotrealp, piersidereal, savereal) rotrealb, dummy = axisb.inc2rot(increalb, savereal) celrealb = axisb.rot2ang(rotrealb, piersidereal, savereal) # --- select results as simulation or real if axisp.real == False: celp = celsimup pierside = piersidesimu else: celp = celrealp pierside = piersidereal if axisb.real == False: celb = celsimub else: celb = celrealb # --- if output_format==self.OUTPUT_SHORT: return celb, celp, pierside else: # all angle output return increalb, rotrealb, celrealb, increalp, rotrealp, celrealp, piersidereal, incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu
[docs] def cel2enc(self, celb:float, celp:float, pierside:int, output_format=OUTPUT_SHORT, save=SAVE_NONE) -> tuple: """ Convert celestial apparent coordinates into encoder values. :param celb: Celestial coordinate of the base axis (degrees). :type celb: float :param celp: Celestial coordinate of the polar axis (degrees). :type celp: float :param pierside: Pier side: Mountaxis().PIERSIDE_AUTO or Mountaxis().PIERSIDE_POS1 or Mountaxis().PIERSIDE_POS2. :type pierside: int :param output_format: OUTPUT_SHORT or OUTPUT_LONG. :type output_format: int :param save: SAVE_NONE or SAVE_AS_SIMU or SAVE_AS_REAL or SAVE_ALL. :type save: int :returns: Increment coordinates :rtype: tuple of 2 or 14 elements Input = celb, celp, pier side (deg units) Output = Raw encoder values (inc) This conversion method is at level 2/4. """ # --- shortcuts axisb = self.axis[Mountaxis.BASE] axisp = self.axis[Mountaxis.POLAR] # --- update for simulations if save==self.SAVE_ALL or save==self.SAVE_AS_SIMU: savesimu = self.SAVE_AS_SIMU else: savesimu = self.SAVE_NONE celsimub = celb celsimup = celp piersidesimu = pierside rotsimup = axisp.ang2rot(celsimup, pierside, savesimu) incsimup = axisp.rot2inc(rotsimup, savesimu) rotsimub = axisb.ang2rot(celsimub, pierside, savesimu) incsimub = axisb.rot2inc(rotsimub, savesimu) # --- update for real if save==self.SAVE_ALL or save==self.SAVE_AS_REAL: savereal = self.SAVE_AS_REAL else: savereal = self.SAVE_NONE celrealb = celb celrealp = celp piersidereal = pierside rotrealp = axisp.ang2rot(celrealp, pierside, savereal) increalp = axisp.rot2inc(rotrealp, savereal) rotrealb = axisb.ang2rot(celrealb, pierside, savereal) increalb = axisb.rot2inc(rotrealb, savereal) # --- select results as simulation or real if axisp.real == False: incp = incsimup else: incp = increalp if axisb.real == False: incb = incsimub else: incb = increalb # --- update the output axis parameters if output_format==self.OUTPUT_SHORT: # short output return incb, incp else: # all angle output return increalb, rotrealb, celrealb, increalp, rotrealp, celrealp, piersidereal, incsimub, rotsimub, celsimup, incsimup, rotsimup, celsimup, piersidesimu
[docs] def speedslew(self, *args)-> tuple: """ Update the speed slewing velocities (deg/sec). The order of velocities must be respected. :returns: tuple of velocities :rtype: tuple of 1 to 3 elements This conversion method is at level 2/4. """ argc = len(args) karg = 0 speedslew = () for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue # --- read or update the speed if karg<argc: current_axis.slew_deg_per_sec = abs(args[karg]) # slew speed is always positive speedslew += (current_axis.slew_deg_per_sec,) return speedslew
# ===================================================================== # ===================================================================== # Methods to convert celestial into astronomical coordinates # ===================================================================== # ===================================================================== # Level 3 # =====================================================================
[docs] def hadec2cel(self, ha:celme.Angle, dec:celme.Angle)->tuple: """ Convert (H.A,Dec) coordinates into celestial coordinates. :param ha: Hour angle (unit defined in celme.Angle). :type ha: celme.Angle :param dec: Declination (unit defined in celme.Angle). :type dec: celme.Angle :returns: tuple of celestial coordinates (basis, polar, rotation) (degrees) :rtype: tuple of 3 elements This conversion method is at level 3/4. """ # --- ha = celme.Angle(ha).deg() dec = celme.Angle(dec).deg() # --- meca = celme.Mechanics() if self._mount_type.find("HADEC")>=0: # --- astro coord are HADEC and cel are HADEC celb = ha celp = dec celr = 0 if self._mount_type.find("AZELEV")>=0: # --- astro coord are HADEC and cel are AZELEV ha *= self._d2r dec *= self._d2r latitude = celme.Angle(self.site.latitude).rad() az, elev = meca._mc_hd2ah(ha, dec, latitude) rotator = meca._mc_hd2parallactic(ha, dec, latitude) celb = az * self._r2d celp = elev * self._r2d celr = rotator * self._r2d # --- return celb, celp, celr
[docs] def azelev2cel(self, az:celme.Angle, elev:celme.Angle)->tuple: """ Convert (azimuth, elevation) coordinates into celestial coordinates. :param ha: Azimuth (unit defined in celme.Angle). :type ha: celme.Angle :param dec: Elevation (unit defined in celme.Angle). :type dec: celme.Angle :returns: tuple of celestial coordinates (basis, polar, rotation) (degrees) :rtype: tuple of 3 elements This conversion method is at level 3/4. """ # --- az = celme.Angle(az).deg() elev = celme.Angle(elev).deg() # --- meca = celme.Mechanics() if self._mount_type.find("HADEC")>=0: # --- astro coord are AZELEV and cel are HADEC az *= self._d2r elev *= self._d2r latitude = celme.Angle(self.site.latitude).rad() ha, dec= meca._mc_ah2hd(az, elev, latitude) rotator = meca._mc_hd2parallactic(ha, dec, latitude) celb = ha * self._r2d celp = dec * self._r2d celr = rotator * self._r2d if self._mount_type.find("AZELEV")>=0: # --- astro coord are AZELEV and cel are AZELEV celb = az celp = elev celr = 0 # --- return celb, celp, celr
[docs] def astro2cel(self, astro_type:str, base:celme.Angle, polar:celme.Angle, base_deg_per_sec:str="", polar_deg_per_sec:str="")->tuple: """ Convert any astronomical coordinate system (astro_type) into celestial coordinates and the corresponding velocities. astro_type can be "HADEC" or "AZELEV": * base = ha or az (according astro_type) * polar = dec or elev (according astro_type) * base_deg_per_sec = dha or daz (according astro_type) * polar_deg_per_sec = ddec or delev (according astro_type) :param astro_type: Specification of the astronomical coordinate system. :type astro_type: str :param base: Hour angle or azimuth (unit defined in celme.Angle). :type base: celme.Angle :param polar: Declination or elevation (unit defined in celme.Angle). :type polar: celme.Angle :param base_deg_per_sec: Declination or elevation (unit defined in celme.Angle). :type base_deg_per_sec: celme.Angle :param base_deg_per_sec: Declination or elevation (unit defined in celme.Angle). :type base_deg_per_sec: celme.Angle :returns: tuple of celestial coordinates (basis, polar, rotation) (degrees) and their derivatives (deg/sec) :rtype: tuple of 6 elements This conversion method is at level 3/4. """ base = celme.Angle(base).deg() polar = celme.Angle(polar).deg() # --- special case when the drifts are sideral if base_deg_per_sec=="sideral" or polar_deg_per_sec=="sideral": # --- compute ha,dec if astro_type.find("AZELEV")>=0: meca = celme.Mechanics() # --- here all angles are in radians latitude = celme.Angle(self.site.latitude).rad() az = base * self._d2r elev = polar * self._d2r ha, dec = meca._mc_ah2hd(az, elev, latitude) ha *= self._r2d dec *= self._r2d if astro_type.find("HADEC")>=0: ha = base dec = polar # --- if base_deg_per_sec=="sideral": dha = 360.0/self._sideral_sec_per_day else: dha = base_deg_per_sec if base_deg_per_sec=="sideral": ddec = 0 else: ddec = polar_deg_per_sec astro_type = "HADEC" base = ha polar = dec base_deg_per_sec = dha polar_deg_per_sec = ddec # --- compute the derivative dt = 1.0 # sec base1 = base - 0.5*dt*base_deg_per_sec polar1 = polar - 0.5*dt*polar_deg_per_sec base2 = base + 0.5*dt*base_deg_per_sec polar2 = polar + 0.5*dt*polar_deg_per_sec if astro_type.find("HADEC")>=0: celb1, celp1, celr1 = self.hadec2cel(base1, polar1) celb2, celp2, celr2 = self.hadec2cel(base2, polar2) celb, celp, celr = self.hadec2cel(base, polar) if astro_type.find("AZELEV")>=0: celb1, celp1, celr1 = self.azelev2cel(base1, polar1) celb2, celp2, celr2 = self.azelev2cel(base2, polar2) celb, celp, celr = self.azelev2cel(base, polar) # --- dcelb = (celb2-celb1)/dt dcelp = (celp2-celp1)/dt dcelr = (celr2-celr1)/dt return celb, celp, celr, dcelb, dcelp, dcelr
[docs] def cel2astro(self, celb:float, celp:float, unit_ha:str="", unit_dec:str="", unit_az:str="", unit_elev:str="")->tuple: """ Convert celestial coordinates into (H.A., Dec) (azimuth, elevation, rotation) astronomical coordinates It is possible to choice the unit of the outputs for each astronomical coordinate. :param celb: Celestial base coordinate (degrees). :type celb: float :param celp: Celestial polar coordinate (degrees). :type celp: float :param unit_ha: Unit of the Hour angle (unit defined in celme.Angle). :type unit_ha: str :param unit_dec: Unit of the déclination (unit defined in celme.Angle). :type unit_dec: str :param unit_az: Unit of the azimuth (unit defined in celme.Angle). :type unit_az: str :param unit_elelv: Unit of the elevation (unit defined in celme.Angle). :type unit_elev: str :returns: tuple of celestial coordinates (ha, dec, az, elev, rotation) (degrees) :rtype: tuple of 5 elements This conversion method is at level 3/4. """ meca = celme.Mechanics() # --- here all angles are in radians latitude = celme.Angle(self.site.latitude).rad() if self._mount_type.find("HADEC")>=0: ha = celb * self._d2r dec = celp * self._d2r az, elev = meca._mc_hd2ah(ha, dec, latitude) if self._mount_type.find("AZELEV")>=0: az = celb * self._d2r elev = celp * self._d2r ha, dec = meca._mc_ah2hd(az, elev, latitude) # --- rotator computation rotator = meca._mc_hd2parallactic(ha, dec, latitude) # --- conversion into degrees ha *= self._r2d dec *= self._r2d az *= self._r2d elev *= self._r2d rotator *= self._r2d # --- default units if unit_ha=="": unit_ha="H0.2" if unit_dec=="": unit_ha="d+090.1" if unit_az=="": unit_az="D0.2" if unit_elev=="": unit_elev="d+090.1" # --- conversion into sexagesimal if needed if unit_ha!="deg": ha = celme.Angle(ha).sexagesimal(unit_ha) if unit_dec!="deg": dec = celme.Angle(dec).sexagesimal(unit_dec) if unit_az!="deg": az = celme.Angle(az).sexagesimal(unit_az) if unit_elev!="deg": elev = celme.Angle(elev).sexagesimal(unit_elev) return ha, dec, az, elev, rotator
[docs] def cel2hadec(self, celb:float, celp:float, unit_ha:str="", unit_dec:str="")->tuple: """ Convert celestial coordinates into (H.A., Dec) astronomical coordinates It is possible to choice the unit of the outputs for each astronomical coordinate. :param celb: Celestial base coordinate (degrees). :type celb: float :param celp: Celestial polar coordinate (degrees). :type celp: float :param unit_ha: Unit of the Hour angle (unit defined in celme.Angle). :type unit_ha: str :param unit_dec: Unit of the déclination (unit defined in celme.Angle). :type unit_dec: str :returns: tuple of celestial coordinates (ha, dec) (degrees) :rtype: tuple of 2 elements This conversion method is at level 3/4. """ # --- ha, dec, az, elev, rotator = self.cel2astro(celb, celp, unit_ha, unit_dec, "", "") return ha, dec
[docs] def cel2azelev(self, celb:float, celp:float, unit_az:str="", unit_elev:str="")->tuple: """ Convert celestial coordinates into (azimuth, elevation, rotation) astronomical coordinates It is possible to choice the unit of the outputs for each astronomical coordinate. :param celb: Celestial base coordinate (degrees). :type celb: float :param celp: Celestial polar coordinate (degrees). :type celp: float :param unit_az: Unit of the azimuth (unit defined in celme.Angle). :type unit_az: str :param unit_elelv: Unit of the elevation (unit defined in celme.Angle). :type unit_elev: str :returns: tuple of celestial coordinates (az, elev, rotation) (degrees) :rtype: tuple of 3 elements This conversion method is at level 3/4. """ # --- ha, dec, az, elev, rotator = self.cel2astro(celb, celp, "", "", unit_az, unit_elev) return az, elev, rotator
# ===================================================================== # ===================================================================== # Methods hadec for users # ===================================================================== # ===================================================================== # Level 4 # =====================================================================
[docs] def hadec_travel_compute(self, ha_target:celme.Angle, dec_target:celme.Angle, pierside_target:int=Mountaxis.PIERSIDE_AUTO)->tuple: """ Compute the duration of a slewing from the current position to a target. :param ha_target: Hour angle (unit defined in celme.Angle). :type ha_target: celme.Angle :param dec_target: Declination (unit defined in celme.Angle). :type dec_target: celme.Angle :param pierside_target: Mountaxis().PIERSIDE_AUTO or Mountaxis().PIERSIDE_POS1 or Mountaxis().PIERSIDE_POS2. :type pierside_target: int :returns: tuple of celestial coordinates (elevmin, ts, elevs) :rtype: tuple of 3 elements Returned values are: * elevmin: Minimum elevation (degrees). * ts: List of time from the start of slewing (sec). * elevs: List of computed elevations for each element of ts (degrees). This conversion method is at level 4/4. """ # --- shortcuts axisb = self.axis[Mountaxis.BASE] axisp = self.axis[Mountaxis.POLAR] # === Read the current position incsimus = ["" for kaxis in range(Mountaxis.AXIS_MAX)] increalb, rotrealb, celrealb, increalp, rotrealp, celrealp, piersidereal, incsimub, rotsimub, celsimup, incsimup, rotsimup, celsimup, piersidesimu = self.enc2cel(incsimus,self.OUTPUT_LONG, self.SAVE_ALL) if axisb.real==True: incb_start = increalb else: incb_start = incsimub if axisp.real==True: incp_start = increalp pierside_start = piersidereal else: incp_start = incsimup pierside_start = piersidesimu # === Read the target position if pierside_target==Mountaxis.PIERSIDE_AUTO: pierside_target = pierside_start celb, celp, celr, dcelb, dcelp, dcelr = self.astro2cel("HADEC", ha_target, dec_target, self._hadec_speeddrift_ha_deg_per_sec, self._hadec_speeddrift_dec_deg_per_sec) increalb, rotrealb, celrealb, increalp, rotrealp, celrealp, piersidereal, incsimub, rotsimub, celsimup, incsimup, rotsimup, celsimup, piersidesimu = self.cel2enc(celb, celp, pierside_target, self.OUTPUT_LONG, self.SAVE_NONE) if axisb.real==True: incb_target = increalb else: incb_target = incsimub if axisp.real==True: incp_target = increalp else: incp_target = incsimup # --- delta incs for the travel (signed incs) dincb = incb_target - incb_start dincp = incp_target - incp_start #print("dincb={} incb_target={} incb_start={} dincp={} incp_target={} incp_start={}".format(dincb, incb_target, incb_start, dincp, incp_target, incp_start)) # --- slew velocity (positive inc/sec) inc_per_secb = axisb.slew_deg_per_sec * axisb.inc_per_deg if dincb<0: inc_per_secb *= -1 inc_per_secp = axisp.slew_deg_per_sec * axisb.inc_per_deg if dincp<0: inc_per_secp *= -1 #print("inc_per_secb={} inc_per_secp={}".format(inc_per_secb, inc_per_secp)) # --- delays of slewing (sec) delayb = abs(dincb / inc_per_secb) delayp = abs(dincp / inc_per_secp) if delayb>delayp: delay = delayb else: delay = delayp return delayb, delayp #print("delayb={} delayp={} delay={}".format(delayb, delayp, delay)) # --- fraction of a turn of 360 deg (no unit) fincb = abs(dincb / axisb._inc_per_sky_rev) fincp = abs(dincp / axisp._inc_per_sky_rev) if fincb>fincp: finc = fincb else: finc = fincp # --- Compute the number of positions during the travel ddeg = 1.0 ; # increment in degrees npos = math.ceil(finc*360.0/ddeg) if npos<3: npos = 3 #print("fincb={} fincp={} finc={}".format(fincb, fincp, finc)) # --- list of positions ts = np.linspace(0,delay,npos) ; # sec pincbs = ts * 0 pincps = ts * 0 for kpos in range(0,npos): t = ts[kpos] ; # sec pincbs[kpos] = incb_start + inc_per_secb * t if dincb>=0 and pincbs[kpos]>incb_target: pincbs[kpos] = incb_target if dincb<0 and pincbs[kpos]<incb_target: pincbs[kpos] = incb_target pincps[kpos] = incp_start + inc_per_secp * t if dincp>=0 and pincps[kpos]>incp_target: pincps[kpos] = incp_target if dincp<0 and pincps[kpos]<incp_target: pincps[kpos] = incp_target print("pincbs={} pincps={}".format(pincbs, pincps)) # --- utc time #jdnow = celme.Date("now").jd() #jds = jdnow + ts/86400. # --- compute the az,elev azims = ts * 0 elevs = ts * 0 elevmin = 90 incposs = ["" for kaxis in range(Mountaxis.AXIS_MAX)] for kpos in range(0,npos): incposs[Mountaxis.BASE] = pincbs[kpos] ; # inc incposs[Mountaxis.POLAR] = pincbs[kpos] ; # inc celb, celp, pierside = self.enc2cel(incposs,self.OUTPUT_SHORT, self.SAVE_NONE) ha, dec, az, elev, rotator = self.cel2astro(celb, celp, unit_ha="deg", unit_dec="deg", unit_az="deg", unit_elev="deg") azims[kpos] = az elevs[kpos] = dec elevmin = np.amin(elevs) # --- return return elevmin, ts, elevs
def _my_hadec_synchronize(self, ha:celme.Angle, dec:celme.Angle, pierside:int="")->tuple: # --- abstract method. # --- Please overload it according your language protocol err = self.NO_ERROR res = 0 return err, res
[docs] def hadec_synchronize(self, ha:celme.Angle, dec:celme.Angle, pierside:int="")-> tuple: """ Synchronize the encoders of the current position with the given astronomical coordinates. :param ha: Hour angle (unit defined in celme.Angle). :type ha: celme.Angle :param dec: Declination (unit defined in celme.Angle). :type dec: celme.Angle :param pierside: * Mountaxis().PIERSIDE_AUTO * Mountaxis().PIERSIDE_POS1 * Mountaxis().PIERSIDE_POS2. :type pierside: int :returns: tuple of astronomical coordinates (ha, dec, pierside) :rtype: tuple of 3 elements :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.hadec_synchronize("12h28m47s", "+5d45m28s", Mountaxis().PIERSIDE_POS1) """ # === Read the current position incsimus = ["" for kaxis in range(Mountaxis.AXIS_MAX)] increalb, rotrealb, celrealb, increalp, rotrealp, celrealp, piersidereal, incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu = self.enc2cel(incsimus, self.OUTPUT_LONG, self.SAVE_ALL) # === Assign the side at the current position if not notified if pierside=="": pierside = piersidereal # === Target celestial position celb, celp, celr = self.hadec2cel(ha, dec) # --- update inc0 on each axis for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue if current_axis.real==True: if kaxis == Mountaxis.BASE: inc = increalb cel = celb elif kaxis == Mountaxis.POLAR: inc = increalp cel = celp else: if kaxis == Mountaxis.BASE: inc = incsimub cel = celb elif kaxis == Mountaxis.POLAR: inc = incsimup cel = celp print("kaxis={} inc={} cel={}".format(kaxis,inc,cel)) current_axis.update_inc0(inc, cel, pierside) # --- Real inits err,res = self._my_hadec_synchronize(ha, dec, pierside) # --- return the current new position return self.hadec_coord()
[docs] def hadec_coord(self, **kwargs:dict)->tuple: """ Read the current astronomical coordinates. :param kwargs: Parameters for output units. :type kwargs: dict :returns: tuple of (ha, dec, pierside) :rtype: tuple Dictionnary of unit parameters are: * UNIT_HA: A string (uspzad format). * UNIT_DEC: A string (uspzad format). :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.hadec_coords("H0.2", "d+090.1") """ # --- Dicos of optional and mandatory parameters params_optional = {} params_optional["UNIT_HA"] = (str,'H0.2') params_optional["UNIT_DEC"] = (str,'d+090.1') params_mandatory = {} # --- Decode parameters params = self.decode_kwargs(params_optional, params_mandatory, **kwargs) # --- Get the simu and real coordinates of all axis incsimus = ["" for kaxis in range(Mountaxis.AXIS_MAX)] celb, celp, pierside = self.enc2cel(incsimus,self.OUTPUT_SHORT, self.SAVE_ALL) ha, dec = self.cel2hadec(celb, celp, params["UNIT_HA"], params["UNIT_DEC"]) return ha, dec, pierside
def _my_hadec_drift(self, hadec_speeddrift_ha_deg_per_sec:float, hadec_speeddrift_dec_deg_per_sec:float): # --- abstract method. # --- Please overload it according your language protocol err = self.NO_ERROR res = 0 return err, res
[docs] def hadec_drift(self, hadec_speeddrift_ha_deg_per_sec:float, hadec_speeddrift_dec_deg_per_sec:float): """ Drift the mount :param hadec_speeddrift_ha_deg_per_sec: Hour angle drift (deg/sec) :type hadec_speeddrift_ha_deg_per_sec: float :param hadec_speeddrift_dec_deg_per_sec: Declination drift (deg/sec) :type hadec_speeddrift_dec_deg_per_sec: float :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.hadec_drift(0.0,-0.05) """ err = self.NO_ERROR res = 0 ha_target = 0 # any value is correct dec_target = 0 # any value is correct # --- drifts after slewing param = hadec_speeddrift_ha_deg_per_sec if param == "sideral": param = 360.0/self._sideral_sec_per_day else: param = float(param) hadec_speeddrift_ha_deg_per_sec = param param = hadec_speeddrift_dec_deg_per_sec if param == "sideral": param = 0 else: param = float(param) hadec_speeddrift_dec_deg_per_sec = param # --- Update the drift parmeters for next movings self._hadec_speeddrift_ha_deg_per_sec = hadec_speeddrift_ha_deg_per_sec self._hadec_speeddrift_dec_deg_per_sec = hadec_speeddrift_dec_deg_per_sec # --- Compute incs of the target and the drifts (for any mount_type) celb, celp, celr, dcelb, dcelp, dcelr = self.astro2cel("HADEC", ha_target, dec_target, hadec_speeddrift_ha_deg_per_sec, hadec_speeddrift_dec_deg_per_sec) # --- for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue # === Target drift if kaxis == Mountaxis.BASE: dcel = dcelb elif kaxis == Mountaxis.POLAR: dcel = dcelp elif kaxis == Mountaxis.ROTATOR: dcel = dcelr # === Drift Velocity inc/sec inc_per_sec_drift = dcel * current_axis.senseinc * current_axis.inc_per_deg if self.site.latitude>=0: inc_per_sec_drift *= -1 # === Simulation. It runs even if there is a real hardware) current_axis.simu_motion_start("DRIFT", frame='inc', drift=inc_per_sec_drift) # === Real hardware err, res = self._my_hadec_drift( hadec_speeddrift_ha_deg_per_sec, hadec_speeddrift_dec_deg_per_sec ) # --- Update ASCOM attributes self.slewing_state = False self.tracking_state = True # --- return (err, res)
def _my_hadec_goto(self, ha_target, dec_target, pierside_target): # --- abstract method. # --- Please overload it according your language protocol err = self.NO_ERROR res = 0 return err, res
[docs] def hadec_goto(self, ha:celme.Angle, dec:celme.Angle, **kwargs): """ Slew the mount to a target defined by astronomical coordinates. :param ha: Hour angle target (unit defined in celme.Angle). :type ha: celme.Angle :param dec: Declination target (unit defined in celme.Angle). :type dec: celme.Angle :param kwargs: Parameters for pointing. :type kwargs: dict Dictionnary of pointing parameters are: * BLOCKING: A boolean to block the programm until the mount is arrived (False by default). * SIDE: Integer to indicate the back flip action: * PIERSIDE_AUTO (=0) back flip is performed if needed * PIERSIDE_POS1 (=1) pointing in normal position * PIERSIDE_POS2 (=-1) pointing in back flip position :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.hadec_goto("12h28m47s", "+5d45m28s", side = Mountaxis().PIERSIDE_AUTO) """ err = self.NO_ERROR res = 0 # --- Dicos of optional and mandatory parameters params_optional = {} params_optional["BLOCKING"] = (bool,False) params_optional["SIDE"] = (int,Mountaxis.PIERSIDE_AUTO) params_optional["DRIFT_HA"] = (str,"0") params_optional["DRIFT_DEC"] = (str,"0") params_mandatory = {} # --- Decode parameters params = self.decode_kwargs(params_optional, params_mandatory, **kwargs) # === Read the current position incsimus = ["" for kaxis in range(Mountaxis.AXIS_MAX)] celb, celp, pierside = self.enc2cel(incsimus,self.OUTPUT_SHORT, self.SAVE_ALL) ha_start, dec_start = self.cel2hadec(celb, celp, "deg", "deg") pierside_start = pierside # === Target celestial position for slewing # --- convert angles into deg [-180 ; 180] ha_target = math.fmod(720+celme.Angle(ha).deg(), 360) if ha_target>180: ha_target -= 360 dec_target = math.fmod(720+celme.Angle(dec).deg(), 360) if dec_target>180: dec_target -= 360 # --- compute the target pierside if self.get_param("CAN_REVERSE")==True: lim_side_east = self.get_param("LIME_REVERSE") ; # Tube west = PIERSIDE_POS1 = [-180 : lim_side_east] lim_side_west = self.get_param("LIMW_REVERSE") ; # Tube east = PIERSIDE_POS2 = [lim_side_west : +180] if params["SIDE"]==Mountaxis.PIERSIDE_AUTO: if ha_target>lim_side_west and ha_target<lim_side_east: # --- the target position is in the both possibilitiy range pierside_target = pierside_start else: if ha_target>lim_side_east: # --- the target is after the limit of side=PIERSIDE_POS1 pierside_target = Mountaxis.PIERSIDE_POS2 else: # --- the target is before the limit of side=PIERSIDE_POS2 pierside_target = Mountaxis.PIERSIDE_POS1 else: pierside_target = params["SIDE"] else: pierside_target = Mountaxis.PIERSIDE_POS1 #print("ha_start={:.4f} ha_target={:.4f} pierside_start={} params[\"SIDE\"]={} pierside_target={}".format(ha_start, ha_target, pierside_start, params["SIDE"], pierside_target)) # --- drifts after slewing param = params["DRIFT_HA"] if param == "sideral": param = 360.0/self._sideral_sec_per_day else: param = float(param) hadec_speeddrift_ha_deg_per_sec = param param = params["DRIFT_DEC"] if param == "sideral": param = 0 else: param = float(param) hadec_speeddrift_dec_deg_per_sec = param #print("HADEC drifts : {} {}".format(hadec_speeddrift_ha_deg_per_sec,hadec_speeddrift_dec_deg_per_sec)) # --- Update the drift parmeters for next movings self._hadec_speeddrift_ha_deg_per_sec = hadec_speeddrift_ha_deg_per_sec self._hadec_speeddrift_dec_deg_per_sec = hadec_speeddrift_dec_deg_per_sec # --- Compute incs of the target and the drifts (for any mount_type) #print("GOTO ha={} dec={}".format(ha_target, dec_target)) celb, celp, celr, dcelb, dcelp, dcelr = self.astro2cel("HADEC", ha_target, dec_target, hadec_speeddrift_ha_deg_per_sec, hadec_speeddrift_dec_deg_per_sec) incb, incp = self.cel2enc(celb, celp, pierside_target, self.OUTPUT_SHORT, self.SAVE_NONE) increalb, rotrealb, celrealb, increalp, rotrealp, celrealp, piersidereal, incsimub, rotsimub, celsimup, incsimup, rotsimup, celsimup, piersidesimu = self.cel2enc(celb, celp, pierside_target, self.OUTPUT_LONG, self.SAVE_NONE) #print("celrealb={:.4f} rotrealb={:.4f} pierside_target={} -> increalb={} incb={} incp={}".format(celrealb, rotrealb, piersidereal, increalb, incb, incp)) incr = 0 # --- for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue # === Target position and drift if kaxis == Mountaxis.BASE: inc = incb dcel = dcelb elif kaxis == Mountaxis.POLAR: inc = incp dcel = dcelp elif kaxis == Mountaxis.ROTATOR: inc = incr dcel = dcelr dslw = current_axis.slew_deg_per_sec # === Slew Velocity inc/sec inc_per_sec_slew = dslw * current_axis.senseinc * current_axis.inc_per_deg # === Drift Velocity inc/sec inc_per_sec_drift = dcel * current_axis.senseinc * current_axis.inc_per_deg if self.site.latitude>=0: inc_per_sec_drift *= -1 # === Simulation. It runs even if there is a real hardware) current_axis.simu_motion_start("SLEW", frame='inc', velocity=inc_per_sec_slew, position=inc, drift=inc_per_sec_drift) # === Real hardware err, res = self._my_hadec_goto(ha_target, dec_target, pierside_target) # === Wait the end of motion if needed if params["BLOCKING"]==True and dcelb==0 and dcelb==0 and dcelr==0: coord1 = self.hadec_coord(unit_ha="deg", unit_dec="deg") t0 = time.time() while True: time.sleep(0.1) dt = time.time()-t0 if dt>60: break coord2 = self.hadec_coord(unit_ha="deg", unit_dec="deg") if coord1 == coord2: break coord1 = coord2 # --- Update ASCOM attributes self.slewing_state = True self.tracking_state = False # --- return (err, res)
def _my_hadec_move(self, ha_move_deg_per_sec, dec_move_deg_per_sec): # --- abstract method. # --- Please overload it according your language protocol err = self.NO_ERROR res = 0 return err, res
[docs] def hadec_move(self, ha_move_deg_per_sec, dec_move_deg_per_sec) -> tuple: """ Slew or modify the drift of the mount. :param ha_drift_deg_per_sec: Hour angle velocity (deg/s). :type ha_drift_deg_per_sec: float :param dec_drift_deg_per_sec: Declination velocity (deg/s). :type dec_drift_deg_per_sec: float :returns: tuple of (error code, result) :rtype: tuple :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.hadec_move(0.1,-0.2) """ err = self.NO_ERROR res = 0 ha_target = 0 # any value is correct dec_target = 0 # any value is correct # --- Compute incs of the target and the drifts (for any mount_type) celb, celp, celr, dcelb, dcelp, dcelr = self.astro2cel("HADEC", ha_target, dec_target, self._hadec_speeddrift_ha_deg_per_sec, self._hadec_speeddrift_dec_deg_per_sec) # --- for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue # === Target position and drift if kaxis == Mountaxis.BASE: dcel = dcelb vel = ha_move_deg_per_sec elif kaxis == Mountaxis.POLAR: dcel = dcelp vel = dec_move_deg_per_sec elif kaxis == Mountaxis.ROTATOR: dcel = dcelr vel = 0 # === Slew Velocity inc/sec inc_per_sec_slew = vel * current_axis.senseinc * current_axis.inc_per_deg # === Drift Velocity inc/sec inc_per_sec_drift = dcel * current_axis.senseinc * current_axis.inc_per_deg if self.site.latitude>=0: inc_per_sec_drift *= -1 # === Simulation. It runs even if there is a real hardware) current_axis.simu_motion_start("MOVE", frame='inc', velocity=inc_per_sec_slew, drift=inc_per_sec_drift) # === Real hardware err, res = self._my_hadec_move(ha_move_deg_per_sec, dec_move_deg_per_sec) # --- Update ASCOM attributes self.slewing_state = True self.tracking_state = False # --- return err, res
def _my_hadec_move_stop(self): # --- abstract method. # --- Please overload it according your language protocol err = self.NO_ERROR res = 0 return err, res
[docs] def hadec_move_stop(self): """ Stop the mount motion. :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.hadec_move_stop() """ err = self.NO_ERROR res = 0 # --- shortcuts axisb = self.axis[Mountaxis.BASE] axisp = self.axis[Mountaxis.POLAR] # === Simulation. It runs even if there is a real hardware) axisb.simu_motion_stop_move() axisp.simu_motion_stop_move() # === Real hardware err, res = self._my_hadec_move_stop() # --- Update ASCOM attributes self.slewing_state = False self.tracking_state = True # --- return err, res
def _my_hadec_stop(self): # --- abstract method. # --- Please overload it according your language protocol err = self.NO_ERROR res = 0 return err, res
[docs] def hadec_stop(self): """ Stop any motion of the mount. :returns: tuple of (error code, result) :rtype: tuple :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.hadec_stop() """ # === Real hardware err, res = self._my_hadec_stop() # === Read the current position incsimus = ["" for kaxis in range(Mountaxis.AXIS_MAX)] celb, celp, pierside = self.enc2cel(incsimus,self.OUTPUT_SHORT, self.SAVE_ALL) # === Stop the simulation and update the simulated position by the real one if exists for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue current_axis.simu_motion_stop() if current_axis.real == True: current_axis.synchro_real2simu() # --- Update ASCOM attributes self.slewing_state = False self.tracking_state = False # --- return err, res
def hadec_park(self, ha:celme.Angle, dec:celme.Angle, pierside:int): # === Target celestial position for slewing # --- convert angles into deg [-180 ; 180] ha_target = math.fmod(720+celme.Angle(ha).deg(), 360) if ha_target>180: ha_target -= 360 dec_target = math.fmod(720+celme.Angle(dec).deg(), 360) if dec_target>180: dec_target -= 360 #err, res = self._my_goto_park(incb, incp, incr) params = {} params["DRIFT_HA"] = 0 params["DRIFT_DEC"] = 0 params["SIDE"] = pierside # --- cal the HADEC GOTO err, res = self.hadec_goto(ha_target, dec_target, **params) # --- Update ASCOM attributes #self.slewing_state = True #self.tracking_state = False # --- return err, res # ===================================================================== # ===================================================================== # Methods radec for users # ===================================================================== # ===================================================================== # Level 4 # ===================================================================== def radec_app2cat(self, jd, ha, dec, params): longuai = self.site.longitude*self._d2r ha *= self._d2r dec *= self._d2r meca = celme.Mechanics() ra = meca._mc_hd2ad(jd, longuai, ha) equinox = celme.Date(params["EQUINOX"]).jd() # --- calcul de la precession ---*/ #print("ha={} ra={} equinox={}".format(ha,ra,equinox)) ra_equinox, dec_equinox = meca._mc_precad(jd,ra,dec,equinox) #ra_equinox, dec_equinox = (ra,dec) #print("ra_2000={} dec_2000={}".format(ra_equinox/self._d2r,dec_equinox/self._d2r)) ra_equinox *= self._r2d dec_equinox *= self._r2d #print("ha={} dec={} ra={} ra_2000={} dec_2000={}".format(ha/self._d2r,dec/self._d2r,ra/self._d2r,ra_equinox,dec_equinox)) return ra_equinox, dec_equinox
[docs] def radec_coord(self, **kwargs): """ Read the current astronomical coordinates. :param kwargs: Parameters for output units. :type kwargs: dict :returns: tuple of (ra, dec, pierside) :rtype: tuple Dictionnary of unit parameters are: * EQUINOX: Equinox of the ra,dec coordinates (J2000 by default). * UNIT_RA: A string (uspzad format). * UNIT_DEC: A string (uspzad format). :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.radec_coords("H0.2", "d+090.1") """ # --- Dicos of optional and mandatory parameters params_optional = {} params_optional["UNIT_RA"] = (str,'H0.2') params_optional["UNIT_DEC"] = (str,'d+090.1') params_optional["EQUINOX"] = (str,'J2000') params_mandatory = {} # --- Decode parameters params = self.decode_kwargs(params_optional, params_mandatory, **kwargs) # --- Get HA,Dec ha, dec, pierside = self.hadec_coord(UNIT_HA="deg",UNIT_DEC="deg") jd = celme.Date("now").jd() # --- app2cat ra_equinox, dec_equinox = self.radec_app2cat(jd, ha, dec, params) # --- Output unit conversion if params["UNIT_RA"]!="deg": ra_equinox = celme.Angle(ra_equinox).sexagesimal(params["UNIT_RA"]) if params["UNIT_DEC"]!="deg": dec_equinox = celme.Angle(dec_equinox).sexagesimal(params["UNIT_DEC"]) return ra_equinox, dec_equinox, pierside
def radec_cat2app(self, jd, ra_equinox, dec_equinox, params): equinox = celme.Date(params["EQUINOX"]).jd() meca = celme.Mechanics() ra_equinox *= self._d2r dec_equinox *= self._d2r # --- calcul de la precession ---*/ ra, dec = meca._mc_precad(equinox,ra_equinox,dec_equinox,jd) #print("ra({})={} dec({})={}".format(params["EQUINOX"], ra_equinox*self._r2d, params["EQUINOX"], dec_equinox*self._r2d)) #print("ra(date)={} dec(date)={}".format(ra*self._r2d, dec*self._r2d)) #ra, dec = (ra_equinox,dec_equinox) longuai = celme.Angle(self.site.longitude).rad() ha = meca._mc_ad2hd(jd, longuai, ra) ha *= self._r2d dec *= self._r2d return ha, dec
[docs] def radec_synchronize(self, ra_angle:celme.Angle, dec_angle:celme.Angle, **kwargs)->tuple: """ Synchronize the encoders of the current position with the given astronomical coordinates. :param ra_angle: Right ascension (unit defined in celme.Angle). :type ra_angle: celme.Angle :param dec_angle: Declination (unit defined in celme.Angle). :type dec_angle: celme.Angle :param pierside: * Mountaxis().PIERSIDE_AUTO * Mountaxis().PIERSIDE_POS1 * Mountaxis().PIERSIDE_POS2. :type pierside: int :param kwargs: Pointing parameters :type kwargs: dict :returns: tuple of astronomical coordinates (ra, dec, pierside) :rtype: tuple of 3 elements Dictionnary of pointing parameters are: * EQUINOX: Equinox of the ra,dec coordinates (J2000 by default). * SIDE: Integer to indicate the back flip action: * PIERSIDE_AUTO (=0) back flip is performed if needed * PIERSIDE_POS1 (=1) pointing in normal position * PIERSIDE_POS2 (=-1) pointing in back flip position :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.radec_synchronize("12h28m47s", "+5d45m28s", Mountaxis().PIERSIDE_POS1) """ # --- Dicos of optional and mandatory parameters params_optional = {} params_optional["EQUINOX"] = (str,'J2000') params_optional["SIDE"] = (int,Mountaxis.PIERSIDE_AUTO) params_mandatory = {} # --- Decode parameters params = self.decode_kwargs(params_optional, params_mandatory, **kwargs) pierside_target = params["SIDE"] # --- Get HA,Dec ra_equinox = celme.Angle(ra_angle).deg() dec_equinox = celme.Angle(dec_angle).deg() jd = celme.Date("now").jd() # --- cat2app ha_target, dec_target = self.radec_cat2app(jd, ra_equinox, dec_equinox,params) self.hadec_synchronize(ha_target, dec_target, pierside_target) return self.radec_coord()
[docs] def radec_goto(self, ra_angle:celme.Angle, dec_angle:celme.Angle, **kwargs): """ Slew the mount to a target defined by astronomical coordinates. :param ha_angle: Right ascension target (unit defined in celme.Angle). :type ha_angle: celme.Angle :param dec_angle: Declination target (unit defined in celme.Angle). :type dec_angle: celme.Angle :param kwargs: Parameters for pointing. :type kwargs: dict :returns: tuple of (error code, result) :rtype: tuple Dictionnary of pointing parameters are: * EQUINOX: Equinox of the ra,dec coordinates (J2000 by default). * BLOCKING: A boolean to block the programm until the mount is arrived (False by default). * SIDE: Integer to indicate the back flip action: * PIERSIDE_AUTO (=0) back flip is performed if needed * PIERSIDE_POS1 (=1) pointing in normal position * PIERSIDE_POS2 (=-1) pointing in back flip position :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.radec_goto("12h28m47s", "+5d45m28s", side = Mountaxis().PIERSIDE_AUTO) """ # --- Dicos of optional and mandatory parameters params_optional = {} params_optional["EQUINOX"] = (str,'J2000') params_optional["BLOCKING"] = (bool,False) params_optional["SIDE"] = (int,Mountaxis.PIERSIDE_AUTO) params_optional["DRIFT_RA"] = (str,"sideral") params_optional["DRIFT_DEC"] = (str,"0") params_mandatory = {} # --- Decode parameters params = self.decode_kwargs(params_optional, params_mandatory, **kwargs) pierside_target = params["SIDE"] # --- Get HA,Dec ra_equinox = celme.Angle(ra_angle).deg() dec_equinox = celme.Angle(dec_angle).deg() jd = celme.Date("now").jd() # --- cat2app ha_target, dec_target = self.radec_cat2app(jd, ra_equinox, dec_equinox, params) # --- compute the time of slewing delayb, delayp = self.hadec_travel_compute(ha_target, dec_target, pierside_target) # --- Temporal offset to anticipate the delay of slewing dt = 2.4 + delayb ; # (sec) #print("delayb={} dt={}".format(delayb,dt)) jd += dt/86400 # --- cat2app ha_target, dec_target = self.radec_cat2app(jd, ra_equinox, dec_equinox, params) # --- drifts after slewing param = params["DRIFT_RA"] if param == "sideral": param = 0 else: param = float(param) param = 360./self._sideral_sec_per_day - param params["DRIFT_HA"] = param # - param = params["DRIFT_DEC"] if param == "sideral": param = 0 else: param = float(param) params["DRIFT_DEC"] = param # --- Update the drift parmeters for next movings self._radec_speeddrift_ha_deg_per_sec = params["DRIFT_HA"] self._radec_speeddrift_dec_deg_per_sec = params["DRIFT_DEC"] # --- cal the HADEC GOTO #print("=== _drift_hadec_ha_deg_per_sec={}".format(self._drift_hadec_ha_deg_per_sec)) err, res = self.hadec_goto(ha_target, dec_target, **params) # --- Update ASCOM attributes self.slewing_state = True self.tracking_state = False # --- return (err, res)
# ===================================================================== # ===================================================================== # Methods inc for users # ===================================================================== # ===================================================================== def _my_inc_goto(self, axis_id:int, inc:float, inc_per_sec_slew:float): # --- abstract method. # --- Please overload it according your language protocol err = self.NO_ERROR res = 0 return err, res def inc_goto(self, axis_id:int, inc:float, inc_per_sec_slew:float): # === Simulation. It runs even if there is a real hardware) err = self.NO_ERROR res = 0 kaxis = axis_id current_axis = self.axis[kaxis] current_axis.simu_motion_start("ABSOLUTE", frame='inc', velocity=inc_per_sec_slew, position=inc, drift=0) # === Real err, res = self._my_inc_goto(axis_id, inc, inc_per_sec_slew) return err, res def _my_goto_park(self, incb:float, incp:float, incr:float): # --- abstract method. # --- Please overload it according your language protocol err = self.NO_ERROR res = 0 return err, res def goto_park(self, incb="", incp="", incr=""): err, res = self._my_goto_park(incb, incp, incr) # --- Update ASCOM attributes #self.slewing_state = True #self.tracking_state = False # --- return err, res
[docs] def plot_rot(self, lati, azim, elev, rotb, rotp, outfile=""): """ Vizualize the rotation angles of the mount according the local coordinates # --- Site latitude lati (deg) : Site latitude # --- Observer view elev = 15 # turn around the X axis azim = 140 # turn around the Z axis (azim=0 means W foreground, azim=90 means N foreground) # --- rob, rotp """ if lati=="": lati = self.site.latitude if lati>=0: latitude = lati cards="SNEW" else: latitude = -lati cards="NSWE" toplots = [] # --- cartesian frame options = {"linewisth":0.5} toplots.append(["text",[0, 0, 0],"o",'k',{}]) toplots.append(["line",[0, 0, 0],[1, 0, 0],'k',options]) toplots.append(["line",[0, 0, 0],[-1, 0, 0],'k',options]) toplots.append(["text",[1, 0, 0],cards[0],'k',{}]) toplots.append(["text",[-1, 0, 0],cards[1],'k',{}]) toplots.append(["text",[0, 1, 0],cards[2],'k',{}]) toplots.append(["text",[0, -1, 0],cards[3],'k',{}]) toplots.append(["line",[0, 0, 0],[0, 1, 0],'k',options]) toplots.append(["line",[0, 0, 0],[0, -1, 0],'k',options]) toplots.append(["line",[0, 0, 0],[0, 0, 1],'k',options]) toplots.append(["line",[0, 0, 0],[0, 0, -1],'k',options]) toplots.append(["text",[0, 0, 1.1],"zenith",'k',options]) # --- great circle tangeant to the projection plane toplots.append(["circle",1,[[90,0,0], [-elev,0,0], [0,0,-azim]],0,360,'k',options]) #toplots.append(["circle",1,[[90,-elev,-azim]],0,360,'k',{"linewisth":1}]) # --- local horizon toplots.append(["circle",1,[[0,0,0]],0,360,'k',{"linewisth":0.5}]) # --- local meridian toplots.append(["circle",1,[[90,0,0]],0,360,'k',{"linewisth":0.5}]) # --- local equator rotxyzs = [[0, latitude, 0]] options = {"linewisth":2} color = 'r' toplots.append(["circle",1,rotxyzs,0,360,color,options]) toplots.append(["linefrom0",1,rotxyzs,color,options]) toplots.append(["linefrom0",-1,rotxyzs,color,options]) toplots.append(["textfrom0",1.05,rotxyzs,"x",color,options]) rotxyzs = [[0, 0, 90]] toplots.append(["linefrom0",1,rotxyzs,color,options]) toplots.append(["linefrom0",-1,rotxyzs,color,options]) toplots.append(["textfrom0",1.15,rotxyzs,"y",color,options]) rotxyzs = [[0, latitude+90, 0]] toplots.append(["linefrom0",1,rotxyzs,color,options]) toplots.append(["linefrom0",-1,rotxyzs,color,options]) toplots.append(["textfrom0",1.05,rotxyzs,"z = visible pole ({})".format(cards[1]),color,options]) # --- pointing Rotp rotxyzs = [[90,0,0], [0,0,rotb], [0, latitude, 0]] options = {"linewisth":0.5} color = 'r' toplots.append(["circle",1,rotxyzs,0,360,color,options]) options = {"linewisth":2} color = 'g' toplots.append(["circle",1,rotxyzs,90,90-rotp,color,options]) rotxyzs = [[0,90-rotp,0], [0,0,rotb], [0, latitude, 0]] options = {"linewisth":1} toplots.append(["linefrom0",1,rotxyzs,color,options]) toplots.append(["textfrom0",1.1,rotxyzs,"rotp",color,options]) rotxyzs = [[0,90,0], [0,0,rotb], [0, latitude, 0]] toplots.append(["linefrom0",1,rotxyzs,color,options]) # --- pointing Rotb rotxyzs = [[0,0,rotb], [0, latitude, 0]] options = {"linewisth":2} color = 'b' toplots.append(["circle",1,rotxyzs,0,-rotb,color,options]) options = {"linewisth":1} toplots.append(["linefrom0",1,rotxyzs,color,options]) toplots.append(["textfrom0",1.3,rotxyzs,"rotb",color,options]) rotxyzs = [[0, latitude, 0]] options = {"linewisth":1} toplots.append(["linefrom0",1,rotxyzs,color,options]) # === fig = plt.figure() #ax = fig.add_subplot(1,1,1,projection='3d') #ax.view_init(elev=elev, azim=azim) ax = fig.add_subplot(1,1,1) for toplot in toplots: ptype = toplot[0] if ptype=="line": dummy, xyz1, xyz2, color, options = toplot xyz0s = [] xyz0 = np.array(xyz1) xyz0s.append(xyz0) xyz0 = np.array(xyz2) xyz0s.append(xyz0) na = len(xyz0s) elif ptype=="text": dummy, xyz, text, color, options = toplot xyz0s = [] xyz0 = np.array(xyz) xyz0s.append(xyz0) na = len(xyz0s) elif ptype=="linefrom0": dummy, length, rotxyzs, color, options = toplot xyz0s = [] xyz0 = np.array([0, 0, 0]) xyz0s.append(xyz0) xyz0 = np.array([length, 0 ,0]) xyz0s.append(xyz0) na = len(xyz0s) elif ptype=="textfrom0": dummy, length, rotxyzs, text, color, options = toplot xyz0s = [] xyz0 = np.array([length, 0 ,0]) xyz0s.append(xyz0) na = len(xyz0s) elif ptype=="circle": dummy, radius, rotxyzs, ang1, ang2, color, options = toplot na = 50 alphas =np.linspace(ang1,ang2,na) # --- cercle dans le plan (x,y) xyz0s = [] for alpha in alphas: alpha = math.radians(alpha) x = radius*math.cos(alpha) y = radius*math.sin(alpha) z = 0 xyz0 = np.array([x, y, z]) xyz0s.append(xyz0) na = len(xyz0s) else: continue # --- rotations if ptype=="linefrom0" or ptype=="textfrom0" or ptype=="circle": for rotxyz in rotxyzs: rotx, roty, rotz = rotxyz cosrx = math.cos(math.radians(rotx)) sinrx = math.sin(math.radians(rotx)) cosry = math.cos(math.radians(roty)) sinry = math.sin(math.radians(roty)) cosrz = math.cos(math.radians(rotz)) sinrz = math.sin(math.radians(rotz)) rotrx = np.array([ [1, 0, 0], [0, cosrx, -sinrx], [0, sinrx, cosrx] ]) rotry = np.array([ [cosry, 0, -sinry], [0, 1, 0], [sinry, 0, cosry] ]) rotrz = np.array([ [cosrz, -sinrz, 0], [sinrz, cosrz, 0] , [0, 0, 1] ]) xyz1s = [] for xyz in xyz0s: xyz = np.dot(rotrx, xyz) xyz = np.dot(rotry, xyz) xyz = np.dot(rotrz, xyz) xyz1s.append(xyz) xyz0s = xyz1s # ready for a second rotation else: xyz1s = xyz0s # --- projections cosaz = math.cos(math.radians(azim)) sinaz = math.sin(math.radians(azim)) cosel = math.cos(math.radians(elev)) sinel = math.sin(math.radians(elev)) rotaz = np.array([ [cosaz, -sinaz, 0], [sinaz, cosaz, 0] , [0, 0, 1] ]) #rotel = np.array([ [cosel, 0, -sinel], [0, 1, 0], [sinel, 0, cosel] ]) rotel = np.array([ [1, 0, 0], [0, cosel, -sinel], [0, sinel, cosel] ]) xyz2s = [] for xyz in xyz1s: xyz = np.dot(rotaz, xyz) xyz = np.dot(rotel, xyz) xyz2s.append(xyz) # --- plot if ptype=="textfrom0" or ptype=="text": x,y,z = xyz2s[0] h = ax.text(x,z,text) h.set_color(color) else: for ka in range(0,na-1): xyz1 = xyz2s[ka] xyz2 = xyz2s[ka+1] x = [xyz1[0], xyz2[0]] y = [xyz1[1], xyz2[1]] z = [xyz1[2], xyz2[2]] if y[0]>1e-3 or y[1]>1e-3: symbol = color+':' else: symbol = color+'-' h = ax.plot(x,z,symbol,'linewidth',1.0) for option in options.items(): key = option[0] val = option[1] if key=="linewisth": h[0].set_linewidth(val) # --- ax.axis('equal') #fig.patch.set_visible(False) ax.axis('off') plt.title("Rotation angles for latitude {} deg".format(lati)) plt.show() if outfile!="": plt.savefig(outfile, facecolor='w', edgecolor='w')
#def pad(self, pad_type="dev"): # self.pad = Mountpad(self,pad_type) # self.pad.start()
[docs] def remote_command_protocol(self, remote_command_protocol="LX200"): """ Set the langage protocol when this code is used as server. :param remote_command_protocol: Command to execute :type remote_command_protocol: str :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.remote_command_protocol("LX200") """ remote_command_protocol = remote_command_protocol.upper() self._remote_command_protocol=remote_command_protocol # === specific default states if remote_command_protocol=="LX200": self._remote_command_protocol="LX200" self._lx200_precision = self.LX200_HIGH_PRECISION self._lx200_setra_deg = 0.0 self._lx200_setdec_deg = 0.0 self._lx200_24_12_hour_time_format = 24 self._lx200_slew_rate = 5 if remote_command_protocol=="ASCOM": self._remote_command_protocol="ASCOM" self._ascom_TargetRightAscension = 0.0 self._ascom_TargetDeclination = 0.0 self._ascom_RightAscensionRate = 0.0 # arcsec/sec self._ascom_DeclinationRate = 0.0 # arcsec/sec self._ascom_TrackingRate = 0 # 0=sideral 1=lunar 2=soler 3=King self._ascom_SideOfPier = -1 # -1=unknown 0=tube east seeing western 1=tube west seeing eastern
def _langage_ascom_trackingrate2hadrift(self)->float: # 0=sideral 1=lunar 2=soler 3=King ha_drift_deg_per_sec = 360.0/self._sideral_sec_per_day # sideral if self._ascom_TrackingRate == 1: ha_drift_deg_per_sec = 14.685/3600. # lunar elif self._ascom_TrackingRate == 2: ha_drift_deg_per_sec = 360.0/86400. # solar elif self._ascom_TrackingRate == 3: ha_drift_deg_per_sec = 15.0369/3600. # King return ha_drift_deg_per_sec
[docs] def _langage_mcs_req_decode(self, command:str)->tuple: """ Format of the request messages: message = "{typemsg: {action: {cmd: val}}}" typemsg, action, cmd, val = self._langage_mcs_req_decode(command) """ message = "" acolade = 0 cmd = "" for car in command: if car=="{": acolade += 1 if car=="}": acolade -= 1 cmd += car if acolade == 0: # --- end of the command message = cmd break # --- typemsg = "" action = "" cmd = "" val = "" # --- cast the str into dict obj = ast.literal_eval(message) if isinstance(obj,dict): typemsg = list(obj.keys())[0] obj = obj[typemsg] if isinstance(obj,dict): action = list(obj.keys())[0] obj = obj[action] if isinstance(obj,dict): cmd = list(obj.keys())[0] val = obj[cmd] else: cmd = str(obj) else: action = str(obj) else: typemsg = str(obj) #print("typemsg={} action={} cmd={} val={}".format(typemsg, action, cmd, val)) return typemsg, action, cmd, val
def _my_remote_command_processing(self, command): # --- abstract method. # --- Please overload it according your language protocol res = "" return res
[docs] def remote_command_processing(self, command): """ Execute a command when this code is used as server. :param command: Command to execute :type command: str :returns: error code :rtype: int :Example: :: >>> mymount = Mountastro("HADEC", name = "My telescope") >>> mymount.remote_command_protocol("LX200") >>> mymount.remote_command_processing(":GD") """ res = "" if self._remote_command_protocol=="LX200": #if command.startswith(":GD")!=True and command.startswith(":GR")!=True: # print("RECU command={}".format(command)) # --- 0x06 - Special command if command.startswith(chr(6))==True: res = "P" # --- C - Sync Control elif command.startswith(":CM")==True: self.radec_synchronize(self._lx200_setra_deg, self._lx200_setdec_deg) # --- G - Get Telescope Information elif command.startswith(":Gc")==True: res = str(self._lx200_24_12_hour_time_format) elif command.startswith(":GC")==True: iso = celme.Date("now").iso() res = iso[5:7] + "/" + iso[8:10] + "/" + iso[2:4] elif command.startswith(":GD")==True: ra, dec, side = self.radec_coord(UNIT_RA="H:0.0",UNIT_DEC="d:+090.0", equinox="now") dec = dec[0:3] + "*" + dec[4:6] + "'" + dec[7:9] if self._lx200_precision == self.LX200_LOW_PRECISION: dec = dec[0:6] res = dec elif command.startswith(":Gg")==True: deg = self.site.longitude res = celme.Angle(deg).sexagesimal("d +0180") res = res[0:4] + "*" + res[5:7] elif command.startswith(":GL")==True: iso = celme.Date("now").iso() res = iso[11:19] elif command.startswith(":Gm")==True: ha, dec, side = self.hadec_coord() if side==1: res = "W" else: res = "E" elif command.startswith(":GR")==True: ra, dec, side = self.radec_coord(UNIT_RA="H:0.0",UNIT_DEC="d:+090.0", equinox="now") if self._lx200_precision == self.LX200_LOW_PRECISION: sec = int(ra[6:8]) sec = "{:.1f}".format(sec/60.0) ra = ra[0:5]+sec[1:] res = ra elif command.startswith(":Gt")==True: deg = self.site.latitude res = celme.Angle(deg).sexagesimal("d +090") res = res[0:3] + "*" + res[4:6] # --- H - Time Format Command elif command.startswith(":H")==True: if self._lx200_24_12_hour_time_format == 24: self._lx200_24_12_hour_time_format = 12 else: self._lx200_24_12_hour_time_format = 24 # --- M - Telescope Movement Commands elif command.startswith(":Me")==True: self.hadec_move(self._lx200_slew_rate,0) elif command.startswith(":Mn")==True: self.hadec_move(0,-self._lx200_slew_rate) elif command.startswith(":Ms")==True: self.hadec_move(0,self._lx200_slew_rate) elif command.startswith(":Mw")==True: #print("radec_move({},{})".format(-self._lx200_slew_rate,0)) self.hadec_move(-self._lx200_slew_rate,0) elif command.startswith(":MS")==True: self.radec_goto(self._lx200_setra_deg, self._lx200_setdec_deg, equinox="J2000.0") # --- P - High Precision Toggle elif command.startswith(":P")==True: res = self._lx200_precision # --- Q - Movement Commands elif command.startswith(":Qe")==True: self.hadec_move_stop() elif command.startswith(":Qn")==True: self.hadec_move_stop() elif command.startswith(":Qs")==True: self.hadec_move_stop() elif command.startswith(":Qw")==True: self.hadec_move_stop() elif command.startswith(":Q")==True: self.hadec_stop() # --- R - Slew Rate Commands elif command.startswith(":RC")==True: self._lx200_slew_rate = 0.5 * 360.0/self._sideral_sec_per_day elif command.startswith(":RG")==True: self._lx200_slew_rate = 5 * 360.0/self._sideral_sec_per_day elif command.startswith(":RM")==True: self._lx200_slew_rate = 0.5 elif command.startswith(":RS")==True: self._lx200_slew_rate = 5 # --- S - Telescope Set Commands elif command.startswith(":Sr")==True: angle = command[3:] self._lx200_setra_deg = celme.Angle(angle).deg() * 15.0 res = str(1) elif command.startswith(":Sd")==True: angle = command[3:] self._lx200_setdec_deg = celme.Angle(angle).deg() res = str(1) elif command.startswith(":Sg")==True: angle = command[3:] self.site.longitude = celme.Angle(angle).deg() res = str(1) elif command.startswith(":Sts")==True: angle = command[3:] self.site.latitude = celme.Angle(angle).deg() res = str(1) # --- U - Precision Toggle elif command.startswith(":U")==True: if self._lx200_precision == self.LX200_HIGH_PRECISION: self._lx200_precision = self.LX200_LOW_PRECISION else: self._lx200_precision = self.LX200_HIGH_PRECISION if self._remote_command_protocol=="MCS": # --- Split the string to a list of dictionaries # String example : command = {"req":{"cmd":{"PRESET":5}}}{"req":{"get": ""}}} # List of dictionaries : commands = ['{"req":{"cmd":{"PRESET":5}}}', '{"req":{"get": ""}}'] #print("MCS {} commands={}".format(self._remote_command_protocol,commands)) # --- Loop over commands # # --- process one command typemsg, action, cmd, val = self._langage_mcs_req_decode(command) typemsg_found = False action_found = False cmd_found = False err_msg_typemsg = "Type message not found amongst " err_msg_action = "Action not found amongst " err_msg_cmd = "Cmd not found amongst " if typemsg == "req": typemsg_found = True if action == "do": action_found = True if cmd == "exec": cmd_found = True print("val={}".format(val)) exec(f"""locals()['temp'] = {val}""") response = {cmd : str(locals()['temp'])} if cmd_found == False: response = {"error" : err_msg_cmd + "exec."} if action == "get": action_found = True response = [] if cmd == "radec" or cmd == "": # --- return only radec coords cmd_found = True ra, dec, side = self.radec_coord(UNIT_RA="deg",UNIT_DEC="deg") res = {"ra":ra, "dec":dec, "side":side} response.append(res) if cmd == "hadec" or cmd == "": # --- return only hadec coords cmd_found = True ha, dec, side = self.hadec_coord(UNIT_RA="deg",UNIT_DEC="deg") res = {"ha":ha, "dec":dec, "side":side} response.append(res) if cmd_found == False: response = {"error" : err_msg_cmd + "radec, hadec."} if action_found == False: response = {"error" : err_msg_action + "do, get."} if typemsg_found == False: response = {"error" : err_msg_typemsg + "req."} res = {typemsg : {action : {cmd : response}}} print("{} res={}".format(self._remote_command_protocol,res)) res = str(res) if self._remote_command_protocol=="ASCOM": command = command.replace(",",".") mots = command.split() print("="*20) print("mots recus ={}".format(mots)) msg = "" # --- if command.startswith("RightAscension")==True: ra, dec, side = self.radec_coord(UNIT_RA="deg") print("{} ra={}".format(mots[0],ra)) msg = "{}".format(float(ra)/15.) # --- if command.startswith("RightAscensionRate")==True: n = len(mots) if n>1: # --- Set print("mots[1]={}".format(mots[1])) self._ascom_RightAscensionRate = float(mots[1]) msg = mots[1] else: # --- Get msg = str(self._ascom_RightAscensionRate) # --- if command.startswith("Declination")==True: ra, dec, side = self.radec_coord(UNIT_DEC="deg") print("{} dec={}".format(mots[0],dec)) msg = str(dec) # --- if command.startswith("SlewToCoordinates")==True or command.startswith("SlewToCoordinatesAsync")==True: ra = float(mots[1])*15. dec = float(mots[2]) ra_drift_deg_per_sec = self._langage_ascom_trackingrate2hadrift() - 360.0/self._sideral_sec_per_day dec_drift_deg_per_sec = 0.0 if self._ascom_RightAscensionRate != 0: # self._ascom_RightAscensionRate arcsec/sec ra_drift_deg_per_sec += self._ascom_RightAscensionRate / 3600.0 if self._ascom_DeclinationRate != 0: dec_drift_deg_per_sec += self._ascom_ascom_DeclinationRate / 3600.0 print("{} ra={} dec={}".format(mots[0],ra,dec)) self.radec_goto(ra, dec, EQUINOX="NOW", RA_DRIFT=ra_drift_deg_per_sec, DEC_DRIFT=dec_drift_deg_per_sec) # --- if command.startswith("SlewToTarget")==True or command.startswith("SlewToTargetAsync")==True: ra = self._ascom_TargetRightAscension * 15 dec = self._ascom_TargetDeclination ra_drift_deg_per_sec = self._langage_ascom_trackingrate2hadrift() - 360.0/self._sideral_sec_per_day dec_drift_deg_per_sec = 0.0 if self._ascom_RightAscensionRate != 0: # self._ascom_RightAscensionRate arcsec/sec ra_drift_deg_per_sec += self._ascom_RightAscensionRate / 3600.0 if self._ascom_DeclinationRate != 0: dec_drift_deg_per_sec += self._ascom_ascom_DeclinationRate / 3600.0 print("{} ra={} dec={}".format(mots[0],ra,dec)) self.radec_goto(ra, dec, EQUINOX="NOW", RA_DRIFT=ra_drift_deg_per_sec, DEC_DRIFT=dec_drift_deg_per_sec) # --- if command.startswith("SyncToCoordinates")==True or command.startswith("SyncToCoordinatesAsync")==True: ra = float(mots[1])*15. dec = float(mots[2]) sideofpier = self._ascom_SideOfPier side = 0 if sideofpier == 0: side = -1 if sideofpier == 1: side = 1 print("{} ra={} dec={}".format(mots[0],ra,dec)) self.radec_synchronize(ra, dec, EQUINOX="NOW", SIDE=side) # --- if command.startswith("SyncToTarget")==True or command.startswith("SyncToTargetAsync")==True: ra = self._ascom_TargetRightAscension * 15 dec = self._ascom_TargetDeclination sideofpier = self._ascom_SideOfPier side = 0 if sideofpier == 0: side = -1 if sideofpier == 1: side = 1 print("{} ra={} dec={}".format(mots[0],ra,dec)) self.radec_synchronize(ra, dec, EQUINOX="NOW", SIDE=side) # --- if command.startswith("MoveAxis")==True: kaxis = int(mots[1]) rate = float(mots[2]) print("{} kaxis={} move={}".format(mots[0],kaxis,rate)) dra = 0 ddec = 0 if kaxis==0: dra = rate ddec = 0 if kaxis==1: dra = 0 ddec = rate if rate==0: self.hadec_move_stop() else: self.hadec_move(dra, ddec) # --- if command.startswith("Slewing")==True: self.update_motion_states() if self.slewing_state==False: msg = "False" else: msg = "True" # --- if command.startswith("AbortSlew")==True: self.hadec_stop() # --- if command.startswith("SideOfPier")==True: # -1=unknown 0=tube east seeing western 1=tube west seeing eastern n = len(mots) if n>1: # --- Set print("mots[1]={}".format(mots[1])) self._ascom_SideOfPier = int(mots[1]) msg = mots[1] else: # --- Get ra, dec, side = self.radec_coord(UNIT_RA="deg") sideofpier = -1 if side==-1: sideofpier = 0 if side==1: sideofpier = 1 self._ascom_SideOfPier = sideofpier msg = str(self._ascom_SideOfPier) # --- if command.startswith("Tracking")==True: n = len(mots) if n>1: # --- Set print("mots[1]={}".format(mots[1])) if mots[1]=="True": ha_drift_deg_per_sec = self._langage_ascom_trackingrate2hadrift() dec_drift_deg_per_sec = 0 if self._ascom_RightAscensionRate != 0: # self._ascom_RightAscensionRate arcsec/sec ha_drift_deg_per_sec -= self._ascom_RightAscensionRate / 3600.0 if self._ascom_DeclinationRate != 0: dec_drift_deg_per_sec += self._ascom_ascom_DeclinationRate / 3600.0 self.hadec_drift(ha_drift_deg_per_sec, dec_drift_deg_per_sec) elif mots[1]=="False": self.hadec_stop() msg = mots[1] else: # --- Get self.update_motion_states() if self.tracking_state==False: msg = "False" else: msg = "True" # --- if command.startswith("TargetRightAscension")==True: n = len(mots) if n>1: # --- Set print("mots[1]={}".format(mots[1])) self._ascom_TargetRightAscension = float(mots[1]) msg = mots[1] else: # --- Get msg = str(self._ascom_TargetRightAscension) # --- if command.startswith("TargetDeclination")==True: n = len(mots) if n>1: # --- Set print("mots[1]={}".format(mots[1])) self._ascom_TargetDeclination = float(mots[1]) msg = mots[1] else: # --- Get msg = str(self._ascom_TargetDeclination) # --- if command.startswith("TrackingRate")==True: n = len(mots) if n>1: # --- Set print("mots[1]={}".format(mots[1])) trackingRate = int(mots[1]) if trackingRate<0: trackingRate = 0 if trackingRate>3: trackingRate = 3 self._ascom_TrackingRate = trackingRate msg = mots[1] else: # --- Get msg = str(self._ascom_TrackingRate) # --- if command.startswith("Park")==True: ha = 270.0 dec = 90.0 side = 1 print("{} ha={} dec={} side={}".format(mots[0],ha,dec,side)) self.hadec_goto(ha, dec, side=side) # --- send the EQMOD answer to ASCOM msg = msg.replace(".",",") print("msg renvoyé ={}".format(msg)) res = msg else: pass #print("{} res={}".format(self._remote_command_protocol,res)) if res=="": res = self._my_remote_command_processing(command) return res
# ===================================================================== # ===================================================================== # ASCOM methods # ===================================================================== # ===================================================================== def _update_motion_states(self): # --- abstract method. # --- Please overload it according your language protocol axes_motion_state_reals = [] for kaxis in range(Mountaxis.AXIS_MAX): axes_motion_state_reals.append(Mountaxis.MOTION_STATE_UNKNOWN) return axes_motion_state_reals
[docs] def update_motion_states(self): """ Get the current motions states of the axes :returns: slewing_state, tracking_state, list of axes_motion_states :rtype: tuple slewing_state and tracking_state are booleans. These states are computer according the combination of all active axes (real and simulated ones). axes_motion_states is a list of states for each axis combining real and simulated. """ # === Real hardware axes_motion_state_reals = self._update_motion_states() # --- Get the current simulated motions incsimus = ["" for kaxis in range(Mountaxis.AXIS_MAX)] increals, incsimus = self.read_encs(incsimus) # --- Combine real and simulation motions axes_motion_states = [] motion_states = [] for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: axes_motion_states.append(Mountaxis.MOTION_STATE_UNKNOWN) continue if current_axis.real == True: motion_state = axes_motion_state_reals[kaxis] else: motion_state = current_axis.motion_state_simu motion_states.append(motion_state) axes_motion_states.append(motion_state) # - Count the motion axis states ks = 0 kd = 0 km = 0 for motion_state in motion_states: if motion_state == Mountaxis.MOTION_STATE_SLEWING: ks += 1 if motion_state == Mountaxis.MOTION_STATE_DRIFTING: kd += 1 if motion_state == Mountaxis.MOTION_STATE_MOVING: km += 1 # - Set the global motion state self.slewing_state = False self.tracking_state = False if km > 0 or ks > 0: self.slewing_state = True self.tracking_state = False if kd > 0 and km == 0 and km == 0: self.slewing_state = False self.tracking_state = True # --- return slewing, tracking states and all axis states return self.slewing_state, self.tracking_state, axes_motion_states
[docs] def _get_slewing_state(self) -> bool: """ True if telescope is currently moving in response to one of the Slew methods or the MoveAxis(TelescopeAxes, Double) method, False at all other times. Reading the property will raise an error if the value is unavailable. If the telescope is not capable of asynchronous slewing, this property will always be False. The definition of "slewing" excludes motion caused by sidereal tracking, PulseGuide, RightAscensionRate, and DeclinationRate. It reflects only motion caused by one of the Slew commands, flipping caused by changing the SideOfPier property, or MoveAxis(TelescopeAxes, Double). """ return self._slewing_state
[docs] def _set_slewing_state(self, state:bool): """ True if telescope is moving in response to one of the Slew methods or the MoveAxis(TelescopeAxes, Double) method, False at all other times. Internal for the Python code """ self._slewing_state = state
[docs] def _get_tracking_state(self) -> bool: """ The state of the telescope's sidereal tracking drive. Tracking Read must be implemented and must not throw a PropertyNotImplementedException. Tracking Write can throw a PropertyNotImplementedException. Changing the value of this property will turn the sidereal drive on and off. However, some telescopes may not support changing the value of this property and thus may not support turning tracking on and off. See the CanSetTracking property. """ return self._tracking_state
[docs] def _set_tracking_state(self, state:bool): """ The state of the telescope's sidereal tracking drive. See details in the documentation of _get_tracking_state() """ self._tracking_state = state
tracking_state = property(_get_tracking_state, _set_tracking_state) slewing_state = property(_get_slewing_state, _set_slewing_state) # ===================================================================== # ===================================================================== # Special methods # ===================================================================== # ===================================================================== def __init__(self, *args, **kwargs): """ Conversion from Uniform Python object into protocol language Usage : Mountastro("HADEC", name="SCX11") """ # --- Dico of optional parameters for all axis_types param_optionals = {} param_optionals["MODEL"] = (str, "") param_optionals["MANUFACTURER"] = (str, "") param_optionals["SERIAL_NUMBER"] = (str, "") param_optionals["REAL"] = (bool, False) param_optionals["DESCRIPTION"] = (str, "No description.") param_optionals["SITE"] = (celme.Site,"GPS 0 E 45 100") param_optionals["LABEL_REGULAR"] = (str,"Regular") param_optionals["LABEL_FLIPED"] = (str,"Fliped") param_optionals["CONFIGURATION"] = (str,"German") param_optionals["CAN_REVERSE"] = (bool,True) param_optionals["LIME_REVERSE"] = (float,30) param_optionals["LIMW_REVERSE"] = (float,-30) # --- Dico of axis_types and their parameters mount_types = {} mount_types["HADEC"]= {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Equatorial"]} } mount_types["HADECROT"]= {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Equatorial"]} } mount_types["AZELEV"]= {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Altazimutal"]} } mount_types["AZELEVROT"]= {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Altazimutal"]} } # --- Decode args and kwargs parameters self._mount_params = self.decode_args_kwargs(0, mount_types, param_optionals, *args, **kwargs) # === self._mount_type = self._mount_params["SELECTED_ARG"] self._name = self._mount_params["NAME"] self._description = self._mount_params["DESCRIPTION"] self._model = self._mount_params["MODEL"] self._manufacturer = self._mount_params["MANUFACTURER"] self._serial_number= self._mount_params["SERIAL_NUMBER"] # === local if type(self._mount_params["SITE"])==celme.site.Site: self.site = self._mount_params["SITE"] else: self.site = celme.Home(self._mount_params["SITE"]) # === Initial state real or simulation real = self._mount_params["REAL"] # === Initialisation of axis list according the mount_type self.axis = [None for kaxis in range(Mountaxis.AXIS_MAX)] # or Mountaxis.AXIS_NOT_DEFINED if self._mount_type.find("HA")>=0: current_axis = Mountaxis("HA", name = "Hour angle") current_axis.update_inc0(0,0,current_axis.PIERSIDE_POS1) self.axis[Mountaxis.BASE] = current_axis if self._mount_type.find("DEC")>=0: current_axis = Mountaxis("DEC", name = "Declination") current_axis.update_inc0(0,90,current_axis.PIERSIDE_POS1) self.axis[Mountaxis.POLAR] = current_axis if self._mount_type.find("AZ")>=0: current_axis = Mountaxis("AZ", name = "Azimuth") current_axis.update_inc0(0,0,current_axis.PIERSIDE_POS1) self.axis[Mountaxis.BASE] = current_axis if self._mount_type.find("ELEV")>=0: current_axis = Mountaxis("ELEV", name = "Elevation") current_axis.update_inc0(0,90,current_axis.PIERSIDE_POS1) self.axis[Mountaxis.POLAR] = current_axis if self._mount_type.find("ROT")>=0: current_axis = Mountaxis("ROT", name = "Rotator") current_axis.update_inc0(0,0,current_axis.PIERSIDE_POS1) self.axis[Mountaxis.ROT] = current_axis if self._mount_type.find("ROLL")>=0: current_axis = Mountaxis("ROLL", name = "Roll") current_axis.update_inc0(0,0,current_axis.PIERSIDE_POS1) if self._mount_type.find("PITCH")>=0: current_axis = Mountaxis("PITCH", name = "Pitch") current_axis.update_inc0(0,90,current_axis.PIERSIDE_POS1) self.axis[Mountaxis.POLAR] = current_axis if self._mount_type.find("YAW")>=0: current_axis = Mountaxis("YAW", name = "Yaw") current_axis.update_inc0(0,0,current_axis.PIERSIDE_POS1) self.axis[Mountaxis.YAW] = current_axis # === Default Setup ratio_wheel_puley = 1 ; # 5.25 ratio_puley_motor = 100.0 ; # harmonic reducer inc_per_motor_rev = 1000.0 ; # IMC parameter. System Confg -> System Parameters - Distance/Revolution for kaxis in range(Mountaxis.AXIS_MAX): current_axis = self.axis[kaxis] if current_axis == None: continue current_axis.slewmax_deg_per_sec = 30 current_axis.slew_deg_per_sec = 30 current_axis.real = real current_axis.latitude = self.site.latitude current_axis.ratio_wheel_puley = ratio_wheel_puley current_axis.ratio_puley_motor = ratio_puley_motor current_axis.inc_per_motor_rev = inc_per_motor_rev # === Pads self._pads = [] # === Remote commands self._remote_command_protocol="" self.remote_command_protocol("LX200") if sys.platform == 'win32': # - not used now but interresting with winreg.OpenKey(winreg.HKEY_CURRENT_USER,r"Control Panel\International") as access_key: value, vtype = winreg.QueryValueEx(access_key,'sDecimal') # === Log positions self._record_positions = False # === self._hadec_speeddrift_ha_deg_per_sec = 0.0 self._hadec_speeddrift_dec_deg_per_sec = 0.0 self._radec_speeddrift_ha_deg_per_sec = -360.0/self._sideral_sec_per_day self._radec_speeddrift_dec_deg_per_sec = 0.0 # === self.slewing_state = False self.tracking_state = False # === Log path_data = os.getcwd() self.log = Mountlog(self._name,self.site.gps,path_data) self.log.print("Launch log")
# ##################################################################### # ##################################################################### # ##################################################################### # Main # ##################################################################### # ##################################################################### # ##################################################################### if __name__ == "__main__": cwd = os.getcwd() example = 4 print("Example = {}".format(example)) if example == 1: # === SCX11 home = celme.Home("GPS 2.25 E 43.567 148") site = celme.Site(home) mount = Mountastro("HADEC", name="Test Mount",site=site) if example == 2: home = celme.Home("GPS 2.25 E 43.567 148") #horizon = [(0,0), (360,0)] site = celme.Site(home) mount_scx11 = Mountastro("HADEC", name="Guitalens Mount", manufacturer="Astro MECCA", model="TM350", site=site) mount_eqmod = Mountastro("HADEC", name="Guitalens Mount", manufacturer="Astro MECCA", model="EQ 6", site=site) # --- shortcuts mount_scx11_axisb = mount_scx11.axis[Mountaxis.BASE] mount_scx11_axisp = mount_scx11.axis[Mountaxis.POLAR] mount_eqmod_axisb = mount_eqmod.axis[Mountaxis.BASE] mount_eqmod_axisp = mount_eqmod.axis[Mountaxis.POLAR] # --- simulation or not mount_scx11_axisb.real = False mount_scx11_axisp.real = False mount_eqmod_axisb.real = False mount_eqmod_axisp.real = False # ======= SCX11 incsimus = [0 for kaxis in range(Mountaxis.AXIS_MAX)] increals, incsimus = mount_scx11.read_encs(incsimus) # --- incb, rotb, celb, incp, rotp, celp, pierside, incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu = mount_scx11.enc2cel(incsimus, mount_scx11.OUTPUT_LONG, mount_scx11.SAVE_ALL) print("SCX11 incb={:.0f} rotb={:.3f} celb={:.3f} incp={:.0f} rotp={:.3f} celp={:.3f} pierside={:.0f}".format(incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu)) incb, rotb, celb, incp, rotp, celp, pierside, incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu = mount_scx11.enc2cel(incsimus, mount_scx11.OUTPUT_LONG, mount_scx11.SAVE_ALL) print("SCX11 incb={:.0f} rotb={:.3f} celb={:.3f} incp={:.0f} rotp={:.3f} celp={:.3f} pierside={:.0f}".format(incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu)) # --- update EQMOD parameters incb, rotb, celb, incp, rotp, celp, pierside, incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu = mount_eqmod.enc2cel(incsimus, mount_scx11.OUTPUT_LONG, mount_scx11.SAVE_ALL) print("EQMOD incb={:.0f} rotb={:.3f} celb={:.3f} incp={:.0f} rotp={:.3f} celp={:.3f} pierside={:.0f}".format(incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu)) incb, rotb, celb, incp, rotp, celp, pierside, incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu = mount_eqmod.enc2cel(incsimus, mount_scx11.OUTPUT_LONG, mount_scx11.SAVE_ALL) print("EQMOD incb={:.0f} rotb={:.3f} celb={:.3f} incp={:.0f} rotp={:.3f} celp={:.3f} pierside={:.0f}".format(incsimub, rotsimub, celsimub, incsimup, rotsimup, celsimup, piersidesimu)) if example == 3: if False: # --- Site latitude latitude = 30 # --- observer view elev = 15 # tourne autour de l'axe x azim = 140 # tourne autour de de l'axe z (azim=0 on regarde W devant, azim=90 N devant) outfile = cwd+"/rotbp_n.png" else: # --- Site latitude latitude = -30 # --- observer view elev = 15 # tourne autour de l'axe x azim = 140 # tourne autour de de l'axe z (azim=0 on regarde W devant, azim=90 N devant) outfile = cwd+"/rotbp_s.png" # --- rob, rotp rotb = 60 rotp = 30 home = celme.Home("GPS 2.25 E 43.567 148") site = celme.Site(home) mount = Mountastro("HADEC", name="Example Mount", site=site) mount.plot_rot(latitude, azim, elev, rotb, rotp, outfile) if example==4: home = celme.Home("GPS 2.25 E 43.567 148") #horizon = [(0,0), (360,0)] site = celme.Site(home) mount_scx11 = Mountastro("HADEC", name="Guitalens Mount", manufacturer="Astro MECCA", model="TM350", site=site) # --- shortcuts mount_scx11_axisb = mount_scx11.axis[Mountaxis.BASE] mount_scx11_axisp = mount_scx11.axis[Mountaxis.POLAR] # --- simulation or not mount_scx11_axisb.real = False mount_scx11_axisp.real = False # ======= SCX11 mount_scx11.speedslew(10,10) ha_start = 70 dec_start = 60 ha_target = 10 dec_target = -25 pierside_target = 1 res = mount_scx11.hadec_travel_compute(ha_target, dec_target, pierside_target)