Source code for mountastro.mountaxis

# -*- coding: utf-8 -*-
import time
import math

import os

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

# #####################################################################
# #####################################################################
# #####################################################################
# Class Mountaxis
# #####################################################################
# #####################################################################
# #####################################################################

[docs]class Mountaxis(Mounttools): """ Class to define an axis of a mount. The first element of args is a string to define the axis type amongst: * HA: Hour angle axis of an eqquatorial mount. * DEC: Declination axis of an eqquatorial mount. * AZ: Azimuth axis of an altaz mount. * ELEV: Elevation axis of an altaz mount. * ROT: Paralactic roation axis of an altaz mount. * ROLL: Roll axis of an altalt mount. * PITCH: Pitch axis of an altalt mount. * YAW: Yaw axis of an altalt mount. Dictionnary of motion parameters are: * NAME: A string to identify the axis in memory. * LABEL: A string to identify the axis for prints. :Example: :: >>> axisb = Mountaxis("HA", name = "Hour angle", label= "H.A.") >>> axisp = Mountaxis("DEC", name = "Declination", label= "Dec.") A mount axis is defined by a frame called 'rot' constituted by two perpendicular axes: * Axisp: The polar axis, a great circle passing by the poles * Axisb: The base axis. Its axis is parallel to the pole direction The natural unit of 'rot' is degree. The definition of the 'rot' frame is compatible with equatorial and altaz or altalt mounts: * hadec : Equatorial, axisp = declination (dec), axisb = hour angle (ha) * altaz : Altaz , axisp = elevation (alt) , axisb = azimuth (az) * altalt : Altalt , axisp = pitch (pit) , axisb = roll (rol) The definition of 'rot=0' depends on the mount types: * Axisp: 'rot=0' at the pole direction upper horizon * Axisb: 'rot=0' depdns on the mount type (meridian, south, etc) The encoders provide another frame called 'enc' which shares the same rotational axis than 'rot'. The natural unit of 'enc' is increments. The celestial coordinate system provide another frame called 'cel' which shares the same rotational axis than 'rot'. The natural unit of 'cel' is degree. The 'cel' frame if fixed to the 'rot' frame. The zeroes are fixed by definition (see above). The 'enc' frame is considered as absolute encoders. As it is impossible to place the inc=0 of the encoder exactly on the rot=0, we define a inc0 = inc when rot=0. As a consequence, for a linear response encoder: rot = (inc-inc0) * deg_per_inc However, a rotational sense (senseinc) is indroduced to take accound the increasing increments are in the increasing angles of 'rot' or not: rot = (inc-inc0) * deg_per_inc * senseinc deg_per_inc is always positive. """ # === Constants for error codes NO_ERROR = 0 # === Constants for pier side PIERSIDE_AUTO = 0 PIERSIDE_POS1 = 1 PIERSIDE_POS2 = -1 # === Constants for senses POSITIVE = 1; NEGATIVE = -1; # === constants for saving coords SAVE_NONE = 0 SAVE_AS_SIMU = 1 SAVE_AS_REAL = 2 # === Indexes for real, simu,... REAL = 0 SIMU = 1 # === Axis types enc BASE = 0 POLAR = 1 ROT = 2 YAW = 3 # equivalent to az = second BASE AXIS_MAX = 4 # === Axis motion state MOTION_STATE_UNKNOWN = -1 MOTION_STATE_NOMOTION = 0 MOTION_STATE_SLEWING = 1 MOTION_STATE_DRIFTING = 2 MOTION_STATE_MOVING = 3 # === Identification of the axis _name = "Declination" _axis_type = "DEC" _latitude = 43 # === relations angle and inc # --- rotb0=0 when the tube is observing at the meridian _inc0 = 0 ; # value of the motor increment corresponding to _rot0 _senseinc = POSITIVE ; # +1 or -1 according the increasing of inc / rot _senseang = POSITIVE ; # +1 or -1 according the increasing of ang / rot # === relations mechanics and inc _ratio_wheel_puley = 5.25 _ratio_puley_motor = 100.0 ; # harmonic reducer _inc_per_motor_rev = 1000.0 ; # IMC parameter. System Confg -> System Parameters - Distance/Revolution _inc_per_sky_rev = None _inc_per_deg = None # === last values _inc = 0 _rot = 0 _ang = 0 _pierside = PIERSIDE_POS1 _incsimu = 0 _rotsimu = 0 _angsimu = 0 _piersidesimu = PIERSIDE_POS1 # === simulation _inc_simu = 0 _simu_signal_move = 0 _simu_current_velocity_deg_per_sec = 0 # === slew velicities _slewmax_deg_per_sec = 5 _slew_deg_per_sec = 5 # === motion state _motion_state = MOTION_STATE_NOMOTION _motion_state_simu = MOTION_STATE_NOMOTION # ===================================================================== # ===================================================================== # Private methods # ===================================================================== # =====================================================================
[docs] def _set_ratio_wheel_puley(self, ratio:float) -> int: """ Set the ratio between wheel and motor puley, in diameter. :param ratio: Ratio between wheel and puley (for exampe : 5.25) :type numerateur: float :returns: Error if ratio are not strictly positive. :rtype: int """ if ratio<=0: raise Exception("ratio must be strictly positive") self._ratio_wheel_puley = ratio self._incr_variables() return self.NO_ERROR
[docs] def _get_ratio_wheel_puley(self) -> float: """ Get the ratio between wheel and motor puley, in diameter. :returns: Ratio between wheel and motor puley (for example : 5.25) :rtype: float """ return self._ratio_wheel_puley
[docs] def _set_ratio_puley_motor(self, ratio:float) -> int: """ Set the ratio between pulley and motor, take care about the ratio of motor reducer type. :param ratio: Ratio between pulley and motor (for example : 100). :type ratio: float :returns: Error if ratio are not strictly positive. :rtype: int """ if ratio<=0: raise Exception("ratio must be strictly positive") self._ratio_puley_motor = ratio self._incr_variables() return self.NO_ERROR
[docs] def _get_ratio_puley_motor(self) -> float: """ Get the ratio between pulley and motor. :returns: Ratio between pulley and motor (for example : 100). :rtype: float """ return self._ratio_puley_motor
[docs] def _set_inc_per_motor_rev(self, nbr_inc:float) -> int: """ Set the number of increments for a single turn of the motor. :param nbr_inc: Number of increments for a single turn of the motor (for example : 1000). :type nbr_inc: float :returns: Error if ratio is not positive. :rtype: int """ if nbr_inc<=0: raise Exception("ratio must be strictly positive") self._inc_per_motor_rev = nbr_inc self._incr_variables() return self.NO_ERROR
[docs] def _get_inc_per_motor_rev(self) -> float: """ Get the number of increments for a single turn of the motor. :returns: Number of increments for a single turn of the motor (for example : 1000). :rtype: float """ return self._inc_per_motor_rev
[docs] def _incr_variables(self) -> float: """ Update and calculus of two parameters : - number of increments for a complete turn of an axis - number of increments for single decimal degrees. :returns: Number of increments for a complete turn of an axis and number of increments for single decimal degrees. :rtype: float """ self._inc_per_sky_rev = self.ratio_wheel_puley*self.ratio_puley_motor*self.inc_per_motor_rev self._inc_per_deg = self._inc_per_sky_rev/360.
[docs] def _set_inc0(self, inc0:float) -> int: """ Set the value of increments for "rot=0". When mount was initialized, the "inc0" are set by the fonction "update_inc0". :param inc0: Value of the increments for "rot=0" (for example : 1800) :returns: Error if increment is not positive. :rtype: int """ self._inc0 = inc0 return self.NO_ERROR
[docs] def _get_inc0(self) -> float: """ Get the value of increments for "rot=0". :returns: Number of increments for "rot=0" (for example : 1800) :rtype: float """ return self._inc0
[docs] def _set_inc(self, inc:float) -> int: """ Set the value for actual increments position of an axis, direct interogation of the controller. :param inc: Value of the increments for the actual position. :returns: Error if value is not a real. :rtype: int """ self._inc = inc return self.NO_ERROR
[docs] def _get_inc(self) -> float: """ Get the value for actual increments position of an axis, direct interogation of the controller. :returns: Number of increments (for example : 37265) :rtype: float """ return self._inc
[docs] def _set_incsimu(self, inc:float) -> int: """ Set the value for actual increments position of an axis in simulation mode. :param inc: Value of the increments for the simulated position. :returns: Error if value is not a real. :rtype: int """ self._incsimu = inc return self.NO_ERROR
[docs] def _get_incsimu(self) -> float: """ Get the value for actual increments position of an axis, direct interogation of the controller. Value are real if axle is real. :returns: Number of increments in simulation mode (for example : 37265) :rtype: float """ return self._incsimu
[docs] def _set_senseinc(self, sense:int) -> int: """ If progression of increments are positive and progression of rot0 are positive, senseinc are positive. However, senseinc are negative when progression are inverse. The sense depend of the physical rolling sense of motor cable system. :param sense: Value sense are "-1" or "1". :returns: Error if value is not a real. :rtype: int """ if sense>self.NEGATIVE: self._senseinc = self.POSITIVE else: self._senseinc = self.NEGATIVE return self.NO_ERROR
[docs] def _get_senseinc(self) -> int: """ If progression of increments are positive and progression of 'rot0' are positive, 'senseinc' are positive. However, 'senseinc' are negative when progression are inverse. The sense depend of the physical rolling sense of motor cable system. :returns: Value sense are "-1" or "1". :rtype: int """ return self._senseinc
[docs] def _set_ang(self, ang:float) -> int: """ Set the arrival angle of a calculated movement for a target. The orientation of the coordinate system are orthonormal. (Right hand rules, tom pouce for visible polar axis !) :param ang: Celestial angle of an axis (degrees) :returns: Error if value is not a real. :rtype: int """ self._ang = ang return self.NO_ERROR
[docs] def _get_ang(self) -> int: """ Get the arrival angle of a calculated movement for a target. The orientation of the coordinate system are orthonormal. (Right hand rules, tom pouce for visible polar axis !) :returns: Error if value is not a real. :rtype: int """ return self._ang
[docs] def _set_angsimu(self, ang:float) -> int: """ In simulation mode, set the arrival angle of a calculated movement for a target. The orientation of the coordinate system are orthonormal. (Right hand rules, tom pouce for visible polar axis !) :param ang: Celestial angle of an axis (degrees) :returns: Error if value is not a real. :rtype: int """ self._angsimu = ang return self.NO_ERROR
[docs] def _get_angsimu(self) -> int: """ In simulation mode, get the arrival angle of a calculated movement for a target. The orientation of the coordinate system are orthonormal. (Right hand rules, tom pouce for visible polar axis !) :returns: Error if value is not a real. :rtype: int """ return self._angsimu
[docs] def _set_senseang(self, sense:int) -> int: """ If progression of mechanical angles referentiel are positive and progression of rot0 are positive, 'set_senseang' are positive. However, 'set_senseang' are negative when progression are inverse. The sense depend of the orientation of celestial coordinates systems and mechanical coordinates systems. The orientation of the coordinate system are orthonormal. (Right hand rules, tom pouce for visible polar axis !) :param sense: Value sense are "-1" or "1". :returns: Error if value is not a real. :rtype: int """ if sense>self.NEGATIVE: self._senseang = self.POSITIVE else: self._senseang = self.NEGATIVE return self.NO_ERROR
[docs] def _get_senseang(self) -> int: """ If progression of mechanical angles referentiel are positive and progression of rot0 are positive, 'set_senseang' are positive. However, 'set_senseang' are negative when progression are inverse. The sense depend of the orientation of celestial coordinates systems and mechanical coordinates systems. The orientation of the coordinate system are orthonormal. (Right hand rules, tom pouce for visible polar axis !) :returns: Error if value is not a real. :rtype: int """ return self._senseang
[docs] def _set_real(self, real:bool) -> int: """ Set the axis in real mode or simulation mode. With simulation mode, the value of the axis are given by Mountaxis simulation value. :param real: True or False. :returns: Error if value is not a real. :rtype: int """ self._real = real return self.NO_ERROR
[docs] def _get_real(self) -> bool: """ Get the axis mode, real or simulation. :returns: True or False :rtype: bool """ return self._real
[docs] def _set_axis_type(self, axis_type:str) -> int: """ Set type and mechanical position of an axis on the mount. - BASE : Azimut or hour angle axis, - POLAR : Elevation or declination axix, - ROT : Derotator system for non equatorial mount (if equiped), - YAW : Equivalent to secondary azymtuh base (for Alt-Alt mount). :param axis_type : BASE = 0, POLAR = 1, ROT = 2, YAW = 3 :returns: Error if value is not a real. :rtype: int """ self._axis_type = axis_type return self.NO_ERROR
[docs] def _get_axis_type(self) -> int: """ Get type and mechanical position of an axis on the mount. - BASE : Azimut or hour angle axis, - POLAR : Elevation or declination axix, - ROT : Derotator system for non equatorial mount (if equiped), - YAW : Equivalent to secondary azymtuh base (for Alt-Alt mount). :returns: BASE = 0, POLAR = 1, ROT = 2, YAW = 3 :rtype: int """ return self._axis_type
[docs] def _set_inc_per_sky_rev(self, inc_per_sky_rev:float): """ .. attention:: no setting for this attribute :param inc_per_sky_rev: Incrment per sky turn :type inc_per_sky_rev: float :returns: Error if ratio is not positive. :rtype: int """ return self.NO_ERROR
[docs] def _get_inc_per_sky_rev(self) -> float: """ Get the number of increments for a single complete turn on the sky. :returns: Number of increments. :rtype: float """ return self._inc_per_sky_rev
[docs] def _set_inc_per_deg(self, inc_per_deg:float): """ .. attention:: no setting for this attribute :param inc_per_deg: Incrment per degree :type inc_per_deg: float :returns: Error if ratio is not positive. :rtype: int """ return self.NO_ERROR
[docs] def _get_inc_per_deg(self) -> float: """ Get the number of increments for a single degrees on the sky. :returns: Number of increments (for example : env 970000) :rtype: float """ return self._inc_per_deg
[docs] def _get_name(self) -> str: """ Get the nickname of the axis. :returns: Nickname of the axis (for example : Declination, ...) :rtype: str """ return self._name
[docs] def _set_name(self, name:str): # no setting for this attribute """ .. attention:: no setting for this attribute The name are setted at the instanciation of the mount axis. The name of an axis can have several value : - Declination, - Azimuth - Hour angle, - Elevation, - Rotator, - Roll, - Pitch, - Yaw, You cannot set the value cause it is an protected attribute. :param name: Name of the axis :type name: str :returns: Error if ratio is not positive. :rtype: int """ return self.NO_ERROR
[docs] def _get_latitude(self) -> str: """ Get the latitude of the observational site. Positive for north. :returns: Latitude of site (for example : 47,2 Degrees) :rtype: str """ return self._latitude
[docs] def _set_latitude(self, latitude_deg:float) -> int: """ Set the latitude of the observational site. Positive for north. :param latitude_deg: Latitude of site (for example : 47.2 Degrees) :type latitude_deg: float :returns: Error if value is not a real. :rtype: int """ self._latitude = latitude_deg return self.NO_ERROR
[docs] def _get_simu_current_velocity(self) -> int: """ Get the final cruising speed during the motion. Motion are celestial slewing speed or any other, like goto for example. :returns: Terminal velocity speed for a movement in degrees / sec. :rtype: int """ return self._simu_current_velocity_deg_per_sec
[docs] def _set_simu_current_velocity(self, simu_current_velocity_deg_per_sec:float): # no setting for this attribute """ .. attention:: no setting for this attribute :returns: Error if ratio is not positive. :rtype: int """ return self.NO_ERROR
[docs] def _get_slew_deg_per_sec(self) -> int: """ Get the setting speed of a goto motion. :returns: Speed for a goto movement in degrees / sec. :rtype: int """ return self._slew_deg_per_sec
[docs] def _set_slew_deg_per_sec(self, deg_per_sec:float) ->int: """ Set the setting speed for a goto motion. The value are limited by the maximun limited speed (_slewmax_deg_per_sec) :param deg_per_sec: Speed for a goto movement in degrees / sec (for example : 30). :type deg_per_sec: float :returns: Error if value is not a real. :rtype: int """ self._slew_deg_per_sec = abs(deg_per_sec) if self._slew_deg_per_sec > self._slewmax_deg_per_sec: self._slew_deg_per_sec = self._slewmax_deg_per_sec return self.NO_ERROR
[docs] def _get_slewmax_deg_per_sec(self) -> float: """ Get the maximum speed for slew motion. The value have a maximum, setting by a limit (_slewmax_deg_per_sec). :returns: Maximum speed for a goto movement in degrees / sec. :rtype: float """ return self._slew_deg_per_sec
[docs] def _set_slewmax_deg_per_sec(self, deg_per_sec:float) -> int: """ Set the maximum speed for slew motion. Set carrefully this parameter due to issue response of the mount. The value have a maximum (for example : 30) :param deg_per_sec: Speed for a slewing movement in degrees / sec (for example : 30) :type deg_per_sec: float :returns: Error if value is not a real. :rtype: int """ self._slewmax_deg_per_sec = abs(deg_per_sec) if self._slew_deg_per_sec > self._slewmax_deg_per_sec: self._slew_deg_per_sec = self._slewmax_deg_per_sec return self.NO_ERROR
[docs] def _get_language_protocol(self) -> str: """ Get the type of controller language protocol for an axis (for example : SCX 11 type, or another). :returns: Type of controller language. :rtype: str """ return self._language_protocol
[docs] def _set_language_protocol(self, language_protocol:str) -> int: """ Set the type of controller language protocol for an axis (for example : SCX 11 type, or another). :param language_protocol : Specified the protocol language type (for example : SCX11) :returns: Error if value is not a real. :rtype: int """ self._language_protocol = language_protocol return self.NO_ERROR
_motion_state = MOTION_STATE_NOMOTION _motion_state_simu = MOTION_STATE_NOMOTION
[docs] def _get_motion_state(self) -> int: """ Get the current motion state :returns: Moton state code (0=no motion, 1=slewing, 2=drifting, 3=moving). :rtype: int Slewiwng state is an absolute motion followed by a drift. Moving state is an infinite motion. If a Moving is stopped we retreive the Drift state. """ return self._motion_state
[docs] def _set_motion_state(self, motion_state:int): """ Set the current motion state :returns: Error code (0=no error). :rtype: int """ self._motion_state = motion_state return self.NO_ERROR
[docs] def _get_motion_state_simu(self) -> int: """ Get the current motion state for simulation :returns: Moton state code (0=no motion, 1=slewing, 2=drifting, 3=moving). :rtype: int Slewiwng state is an absolute motion followed by a drift. Moving state is an infinite motion. If a Moving is stopped we retreive the Drift state. """ return self._motion_state_simu
[docs] def _set_motion_state_simu(self, motion_state:int): """ Set the current motion state for simulation :returns: Error code (0=no error). :rtype: int """ self._motion_state_simu = motion_state return self.NO_ERROR
# ===================================================================== # ===================================================================== # Methods for users # ===================================================================== # ===================================================================== name = property(_get_name , _set_name) axis_type = property(_get_axis_type , _set_axis_type) latitude = property(_get_latitude , _set_latitude) language_protocol = property(_get_language_protocol , _set_language_protocol) ratio_wheel_puley = property(_get_ratio_wheel_puley , _set_ratio_wheel_puley) ratio_puley_motor = property(_get_ratio_puley_motor , _set_ratio_puley_motor) inc_per_motor_rev = property(_get_inc_per_motor_rev , _set_inc_per_motor_rev) inc_per_sky_rev = property(_get_inc_per_sky_rev , _set_inc_per_sky_rev) inc_per_deg = property(_get_inc_per_deg , _set_inc_per_deg) inc0 = property(_get_inc0 , _set_inc0) senseinc = property(_get_senseinc , _set_senseinc) senseang = property(_get_senseang , _set_senseang) real = property(_get_real , _set_real) inc = property(_get_inc , _set_inc) ang = property(_get_ang , _set_ang) incsimu = property(_get_incsimu , _set_incsimu) angsimu = property(_get_angsimu , _set_angsimu) slew_deg_per_sec = property(_get_slew_deg_per_sec , _set_slew_deg_per_sec) slewmax_deg_per_sec = property(_get_slewmax_deg_per_sec , _set_slewmax_deg_per_sec) simu_current_velocity = property(_get_simu_current_velocity, _set_simu_current_velocity) motion_state_simu = property(_get_motion_state_simu, _set_motion_state_simu) motion_state = property(_get_motion_state, _set_motion_state)
[docs] def disp(self): """ Get information about an axis and print it on the console. Usefull for debug. Instanciation of the axis are indispensable. However, the mountaxis module when running, have by default axisb et axisp instancied. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") >>> axisp = Mountaxis("DEC", name = "Unknown") :Usage: :: >>> axisb.disp() >>> axisp.disp() :Return table of an axis: :: -------------------- AXIS name = SCX11 axis_type = HA latitude = 43 real hardware = False -------------------- ratio_wheel_puley = 5.25 ratio_puley_motor = 100.0 inc_per_motor_rev = 1000.0 -------------------- inc_per_sky_rev = 525000.0 inc_per_deg = 1458.3333333333333 -------------------- senseinc = 1 : 1=positive inc0 = 0.0 : Place mount rot at meridian and set inc0 = inc senseang = 1 : 1=positive -------------------- slew_deg_per_sec = 5.0 -------------------- SIMU INC -> ANG = HA inc = 0.0 : inc is read from encoder rot = 0.0000000 : rot = (inc - inc0) * senseinc / inc_per_deg pierside = 1 : pierside must be given by polar axis ang = 0.0000000 : ang = senseang * rot -------------------- SIMU ANG = HA -> INC ang = 0.0000000 : Next target celestial angle HA pierside = 1 : Next target pier side (+1 or -1) rot = 0.0000000 : rot = -ang / senseang inc = 0.0 : inc = inc0 + rot * inc_per_deg / senseinc -------------------- REAL INC -> ANG = HA inc = 0.0 : inc is read from encoder rot = 0.0000000 : rot = (inc - inc0) * senseinc / inc_per_deg pierside = 1 : pierside must be given by polar axis ang = 0.0000000 : ang = senseang * rot -------------------- REAL ANG = HA -> INC ang = 0.0000000 : Next target celestial angle HA pierside = 1 : Next target pier side (+1 or -1) rot = 0.0000000 : rot = -ang / senseang inc = 0.0 : inc = inc0 + rot * inc_per_deg / senseinc """ if self._axis_type=="DEC" or self._axis_type=="ELEV": msg_rot0 = "pole" msg_pierside_inc2rot = "pierside = sign of rot" if self._latitude<0: msg_rot2ang = "ang = -90 + abs(rot) (Southern hem.)" msg_ang2rot = "rot = (90 + ang) * pierside (Southern hem.)" else: msg_rot2ang = "ang = 90 - abs(rot) (Northern hem.)" msg_ang2rot = "rot = (90 - ang) * pierside (Northern hem.)" else: msg_rot0 = "meridian" msg_pierside_inc2rot = "pierside must be given by polar axis" msg_rot2ang = "ang = senseang * rot" if self._pierside == self.PIERSIDE_POS1: msg_ang2rot = "rot = -ang / senseang" else: msg_ang2rot = "rot = (-ang-180) / senseang" print("{}".format(20*"-")) print("AXIS name = {} ".format(self.name)) print("axis_type = {} ".format(self.axis_type)) print("latitude = {} ".format(self.latitude)) print("real hardware = {} ".format(self.real)) print("{}".format(20*"-")) print("ratio_wheel_puley = {} ".format(self.ratio_wheel_puley)) print("ratio_puley_motor = {} ".format(self.ratio_puley_motor)) print("inc_per_motor_rev = {} ".format(self.inc_per_motor_rev)) print("{}".format(20*"-")) print("inc_per_sky_rev = {} ".format(self.inc_per_sky_rev)) print("inc_per_deg = {} ".format(self.inc_per_deg)) print("{}".format(20*"-")) print("senseinc = {:d} : 1=positive".format(self.senseinc)) print("inc0 = {:12.1f} : Place mount rot at {} and set inc0 = inc".format(self.inc0, msg_rot0)) print("senseang = {:d} : 1=positive".format(self.senseang)) print("{}".format(20*"-")) print("slew_deg_per_sec = {} ".format(self.slew_deg_per_sec)) for disp_real in (False,True): if disp_real==True: inc = self._inc rot = self._rot ang = self._ang pierside = self._pierside msg_simu = "REAL" else: inc = self._incsimu rot = self._rotsimu ang = self._angsimu pierside = self._piersidesimu msg_simu = "SIMU" print("{} {} INC -> ANG = {}".format(20*"-",msg_simu,self.axis_type)) print("inc = {:12.1f} : inc is read from encoder ".format(inc)) print("rot = {:12.7f} : rot = (inc - inc0) * senseinc / inc_per_deg".format(rot)) print("pierside = {:d} : {}".format(pierside, msg_pierside_inc2rot)) print("ang = {:12.7f} : {} ".format(ang, msg_rot2ang)) print("{} {} ANG = {} -> INC".format(20*"-",msg_simu,self.axis_type)) print("ang = {:12.7f} : Next target celestial angle {}".format(ang,self.axis_type)) print("pierside = {:d} : Next target pier side (+1 or -1)".format(pierside)) print("rot = {:12.7f} : {}".format(rot, msg_ang2rot)) print("inc = {:12.1f} : inc = inc0 + rot * inc_per_deg / senseinc ".format(inc))
[docs] def synchro_real2simu(self): """ Synchronisation between simulation value of axis to real values of the axis. Parameters are setted : - _incsimu, - _rotsimu, - _angsimu, - _piersidesimu, Useful for ending slewing movement to prevent difference offset due to calculation time of the simulation mode. Instanciation of the axis are indispensable. However, the mountaxis module when running, have by default axisb et axisp instancied. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") >>> axisp = Mountaxis("DEC", name = "Unknown") :Usage: :: >>> axisb.synchro_real2simu() >>> axisp.synchro_real2simu() :returns: No message returned by the fonction """ inc = self._inc rot, pierside = self.inc2rot(inc) ang = self.rot2ang(rot, pierside) self._incsimu = inc self._rotsimu = rot self._angsimu = ang self._piersidesimu = pierside
[docs] def synchro_simu2real(self): """ Synchronisation between real value of axis to simulation values of the axis. Parameters are setted : - _inc, - _rot, - _ang, - _pierside, Useful for ending slewing movement to prevent difference offset due to calculation time of the simulation mode. Instanciation of the axis are indispensable. However, the mountaxis module when running, have by default axisb et axisp instancied. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") >>> axisp = Mountaxis("DEC", name = "Unknown") :Usage: :: >>> axisb.sydispnchro_simu2real() >>> axisp.synchro_simu2real() :returns: No message returned by the fonction """ inc = self._incsimu rot, pierside = self.inc2rot(inc) ang = self.rot2ang(rot, pierside) self._inc = inc self._rot = rot self._ang = ang self._pierside = pierside
[docs] def update_inc0(self, inc, ang, pierside=PIERSIDE_POS1): """ Update the value of the inc0. Instanciation of the axis are indispensable. However, the mountaxis module when running, have by default axisb et axisp instancied. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") >>> axisp = Mountaxis("DEC", name = "Unknown") :Usage: :: >>> axisb.update_inc0() >>> axisp.update_inc0() :param inc: :param ang: :param pierside: :returns: No message returned by the fonction. """ if self._axis_type=="DEC" or self._axis_type=="ELEV": if ang > 90: ang = 90 if ang < -90: ang = -90 if self._latitude<0: rot = (90 + ang) * pierside else: rot = (90 - ang) * pierside # inc = inc0 + rot * inc_per_deg / senseinc inc0 = inc - rot * self.inc_per_deg / self.senseinc else: rot = ang / self.senseang if self._latitude>0: rot *= -1 rot = math.fmod(rot+1440,360) if (rot>180): rot -= 360 if pierside==self.PIERSIDE_POS1: # inc = inc0 + rot * inc_per_deg / senseinc inc0 = inc - rot * self.inc_per_deg / self.senseinc else: # inc = inc0 + (rot-180) * inc_per_deg / senseinc inc0 = inc - (rot-180) * self.inc_per_deg / self.senseinc self.inc0 = inc0 return inc0
[docs] def inc2rot(self, inc:float, save=SAVE_NONE) -> tuple: """ Calculation of rot and pierside from inc. :param inc: Encoder increments (inc) :type inc: float :param save: Define how the results are stored: * SAVE_NONE (=0) * SAVE_AS_SIMU (=1) * SAVE_AS_REAL (=2) :type save: int :returns: Tuple of (rot, pierside) :rtype: tuple The rot value is computed according inc, _inc0, _senseinc, _inc_per_deg and _axis_type. The save parameter allows to update the real or simu internal values of inc and rot: * SAVE_AS_SIMU: Only _incsimu, _rotsimu and _piersidesimu for simulated values are updated. * SAVE_AS_REAL: Only _inc, _rot and _pierside for real values are updated. * SAVE_NONE: No internal variables are updates. Instanciation of the axis is indispensable. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") :Usage: :: >>> axisb.inc2rot(2000,axisb.SAVE_NONE) """ rot = (inc - self._inc0) * self._senseinc / self._inc_per_deg # --- identify the pierside of the current pointing for polar axis if self._axis_type=="DEC" or self._axis_type=="ELEV": if rot >= 0: pierside = self.PIERSIDE_POS1 else: pierside = self.PIERSIDE_POS2 else: pierside = self.PIERSIDE_POS1 # --- update attributes if save == self.SAVE_AS_SIMU: self._rotsimu = rot self._incsimu = inc self._piersidesimu = pierside elif save == self.SAVE_AS_REAL: self._rot = rot self._inc = inc self._pierside = pierside return rot, pierside
[docs] def rot2ang(self, rot:float, pierside:int, save:int=SAVE_NONE) -> float: """ Calculation of ang from rot and pierside. :param rot: Rotation angle (degrees) :type rot: float :param pierside: Location of the optical tube against the mount pier: * PIERSIDE_POS1 (=1) normal position * PIERSIDE_POS2 (=-1) back flip position :type pierside: int :param save: Define how the results are stored: * SAVE_NONE (=0) * SAVE_AS_SIMU (=1) * SAVE_AS_REAL (=2) :type save: int :returns: ang :rtype: float The ang value is computed according rot, _latitude, _senseang and _axis_type. The save parameter allows to update the real or simu internal values of and, pierside and rot: * SAVE_AS_SIMU: Only _angsimu, _rotsimu and _piersidesimu for simulated values are updated. * SAVE_AS_REAL: Only _ang, _rot and _pierside for real values are updated. * SAVE_NONE: No internal variables are updates. Instanciation of the axis is indispensable. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") :Usage: :: >>> axisb.rot2ang(10, axisb.PIERSIDE_POS1, axisb.SAVE_NONE) """ # compute apparent ang ang = 0 if self._axis_type=="DEC" or self._axis_type=="ELEV": if self._latitude<0: # --- southern hemisphere ang = -90 + abs(rot) else: # --- nothern hemisphere ang = 90 - abs(rot) # --- following lines must be verified if ang<-90: ang +=360; if ang>90: ang -=360; # identify the pierside of the current pointing if self._axis_type=="HA" or self._axis_type=="ROLL": if self._latitude<0: ang = self._senseang * rot else: ang = self._senseang * -rot if pierside == self.PIERSIDE_POS2: ang += 180 ang = math.fmod(ang+720,360) if (ang>180): ang -= 360 if self._axis_type=="AZ": if self._latitude<0: ang = self._senseang * rot else: ang = self._senseang * -rot if pierside == self.PIERSIDE_POS2: ang += 180 ang = math.fmod(ang+720,360) if save == self.SAVE_AS_SIMU: self._angsimu = ang self._piersidesimu = pierside self._rotsimu = rot elif save == self.SAVE_AS_REAL: self._ang = ang self._pierside = pierside self._rot = rot return ang
[docs] def ang2rot(self, ang:float, pierside:int=PIERSIDE_POS1, save:int=SAVE_NONE) -> float: """ Calculation rot from ang and pierside. :param ang: Celestial angle (degrees) :type ang: float :param pierside: Location of the optical tube against the mount pier: * PIERSIDE_POS1 (=1) normal position * PIERSIDE_POS2 (=-1) back flip position :type pierside: int :param save: Define how the results are stored: * SAVE_NONE (=0) * SAVE_AS_SIMU (=1) * SAVE_AS_REAL (=2) :type save: int :returns: rot :rtype: float The rot value is computed according rot, _latitude, _senseang and _axis_type. The save parameter allows to update the real or simu internal values of and, pierside and rot: * SAVE_AS_SIMU: Only _angsimu, _rotsimu and _piersidesimu for simulated values are updated. * SAVE_AS_REAL: Only _ang, _rot and _pierside for real values are updated. * SAVE_NONE: No internal variables are updates. Instanciation of the axis is indispensable. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") :Usage: :: >>> axisb.ang2rot(-10, axisb.PIERSIDE_POS1, axisb.SAVE_NONE) """ # compute apparent rot rot = 0 if self._axis_type=="DEC" or self._axis_type=="ELEV": if self._latitude<0: # --- southern hemisphere rot = (90 + ang) * pierside else: # --- nothern hemisphere rot = (90 - ang) * pierside if self._axis_type=="HA" or self._axis_type=="AZ": if pierside==self.PIERSIDE_POS2: ang -= 180 if self._latitude<0: rot = ang else: rot = -ang if (rot>180): rot -= 360 if (rot<-180): rot += 360 rot /= self._senseang if save == self.SAVE_AS_SIMU: self._angsimu = ang self._piersidesimu = pierside self._rotsimu = rot elif save == self.SAVE_AS_REAL: self._ang = ang self._pierside = pierside self._rot = rot return rot
[docs] def rot2inc(self, rot:float, save:int=SAVE_NONE) -> float : """ Calculation of inc from rot. :param rot: Rotation angle (degrees) :type rot: float :param save: Define how the results are stored: * SAVE_NONE (=0) * SAVE_AS_SIMU (=1) * SAVE_AS_REAL (=2) :type save: int :returns: inc :rtype: float The inc value is computed according rot, _inc0, _senseinc, _inc_per_deg. The inc values are calculated in the interval from -inc_per_sky_rev/2 to +inc_per_sky_rev/2. The save parameter allows to update the real or simu internal values of inc and rot: * SAVE_AS_SIMU: Only _incsimu, _rotsimu for simulated values are updated. * SAVE_AS_REAL: Only _inc, _rot for real values are updated. * SAVE_NONE: No internal variables are updates. Instanciation of the axis is indispensable. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") :Usage: :: >>> axisb.rot2inc(10,axisb.SAVE_NONE) """ inc = self._inc0 + rot * self._inc_per_deg / self._senseinc # --- verify the limits inc_per_sky_rev = self._inc_per_sky_rev limn = -inc_per_sky_rev/2 limp = inc_per_sky_rev/2 if inc>limp: inc -= inc_per_sky_rev if inc<limn: inc += inc_per_sky_rev # --- if save == self.SAVE_AS_SIMU: self._rotsimu = rot self._incsimu = inc elif save == self.SAVE_AS_REAL: self._rot = rot self._inc = inc return inc
# ===================================================================== # ===================================================================== # Motion methods for simulation # ===================================================================== # =====================================================================
[docs] def simu_motion_start(self, *args, **kwargs): """ Start a simulation motion. :param args: First args is a string to define the type of motion to do. :type args: args :param kwargs: Dictionnary of motion parameters: :type kwargs: kwargs :returns: _incsimu :rtype: float Types of motion can be: * SLEW or ABSOLUTE: Absolute position of the target position. * MOVE or CONTINUOUS: Infinite motion. Dictionnary of motion parameters are: * Case motion type = SLEW or ABSOLUTE: * POSITION (inc or ang according the FRAME). * VELOCITY (deg/sec). Can be negative. * DRIFT (deg/sec). Can be negative. * Case motion type = MOVE or CONTINUOUS: * VELOCITY (deg/sec). Can be negative. * DRIFT (deg/sec). Can be negative. * For all cases of motions: * FRAME (str). "inc" (by default) or "ang" Instanciation of the axis is mandatory. :Instanciation Usage: :: >>> axisb = Mountaxis("HA", name = "Unknown") :Usage: :: >>> axisb.simu_motion_start("SLEW", position=1000, velocity=100, frame='inc', drift=0) """ # ========= Definition of motion_types # --- Dico of motion types and their parameters motion_types = {} motion_types["SLEW"] = {"MANDATORY" : {"POSITION":[float,0.0], "VELOCITY":[float,1.0]}, "OPTIONAL" : {"DRIFT":[float,0.0]} } motion_types["MOVE"] = {"MANDATORY" : {"VELOCITY":[float,1.0]}, "OPTIONAL" : {"DRIFT":[float,0.0]} } motion_types["DRIFT"] = {"MANDATORY" : {"DRIFT":[float,0.0]}, "OPTIONAL" : {} } # --- deprecadec motion_types["ABSOLUTE"] = {"MANDATORY" : {"POSITION":[float,0.0], "VELOCITY":[float,1.0]}, "OPTIONAL" : {"DRIFT":[float,0.0]} } motion_types["CONTINUOUS"] = {"MANDATORY" : {"VELOCITY":[float,1.0]}, "OPTIONAL" : {"DRIFT":[float,0.0]} } # --- Dico of optional parameters for all motion types param_optionals = {"FRAME":(str,'inc')} ; # inc or ang # ========= Decode params self._simu_params = self.decode_args_kwargs(0,motion_types, param_optionals, *args, **kwargs) # ========= Decode params self._simu_motion_type = self._simu_params["SELECTED_ARG"] # ========= Update motion_state_simu self.motion_state_simu = self.MOTION_STATE_UNKNOWN if self._simu_motion_type=="SLEW" or self._simu_motion_type=="ABSOLUTE": self.motion_state_simu = self.MOTION_STATE_SLEWING #print("SLEW drift={} ".format(self._simu_params['DRIFT'])) elif self._simu_motion_type=="MOVE" or self._simu_motion_type=="CONTINUOUS": self.motion_state_simu = self.MOTION_STATE_MOVING #print("MOVE drift={} ".format(self._simu_params['DRIFT'])) elif self._simu_motion_type=="DRIFT": self.motion_state_simu = self.MOTION_STATE_DRIFTING # --- Update t0, inct0 self._simu_motion_t0 = time.time() self._simu_motion_inct0 = self._incsimu # --- Update start_t0, start_inct0 self._simu_motion_start_t0 = self._simu_motion_t0 self._simu_motion_start_inct0 = self._simu_motion_inct0 # --- Fille history motion history = [time.time(), 0.0, "MOTION_START", self._simu_motion_type, self._simu_params] self._history.append(history) # --- get the current inc inc = self.simu_update_inc() return inc
[docs] def simu_motion_stop(self): """ Stop a simulation motion. """ # --- get the current inc inc = self.simu_update_inc() # --- we stop self.motion_state_simu = self.MOTION_STATE_NOMOTION t = time.time() dt = t-self._simu_motion_start_t0; history = [t, dt, "MOTION_STOP", self.motion_state_simu , inc] self._history.append(history) # --- obsolete attributes self._simu_signal_move = 0 self._simu_signal_drift = 0 return inc
[docs] def simu_motion_stop_move(self): """ Stop a moving motion. """ if self.motion_state_simu == self.MOTION_STATE_MOVING: # --- get the current inc inc = self.simu_update_inc() # --- We switch to drift self.motion_state_simu = self.MOTION_STATE_DRIFTING # --- get the current inc inc = self.simu_update_inc() return inc
[docs] def simu_update_inc(self): """ Calculate the current position of a simulation motion. A simple rectangular profile is applied to velocity. """ if self.motion_state_simu == self.MOTION_STATE_NOMOTION: return self._incsimu # --- compute the duration from t0 t = time.time() t0 = self._simu_motion_t0 inc = self._incsimu inct0 = self._simu_motion_inct0 # --- get motion parameters vel = 0 if "VELOCITY" in self._simu_params: # --- velocity unit deg/sec vel = float(self._simu_params["VELOCITY"]) drift = 0 if "DRIFT" in self._simu_params: # --- drift unit deg/sec drift = float(self._simu_params["DRIFT"]) frame = 'ang' if "FRAME" in self._simu_params: frame = self._simu_params["FRAME"] if frame == "ang": # --- conversions deg/sec to inc/sec vel *= self.inc_per_deg drift *= self.inc_per_deg # --- moving case if self.motion_state_simu == self.MOTION_STATE_MOVING: # --- So we can compute the inc since t0 inc = inct0 + vel*(t-t0) # --- slewing case if self.motion_state_simu == self.MOTION_STATE_SLEWING: # --- compute the pos to reach inc_end_slew = float(self._simu_params["POSITION"]) if frame == "ang": # --- conversions deg to inc inc_end_slew *= self.inc_per_deg # --- process the sign of vel #if self._axis_type=="HA": # print("inct0={} inc_end_slew={:.1f} vel={}".format(inct0,inc_end_slew,vel)) if inct0 < inc_end_slew: vel = abs(vel) else: vel = -abs(vel) # --- compute the expected t to reach inc_end_slew t1 = (inc_end_slew-inct0) / vel + t0 # print("vel={} t={} t1={}".format(vel,t,t1)) if t < t1: # --- We did not overtake the target. We continue to slew # --- So we can compute the inc since t0 inc = inct0 + vel*(t-t0) #if self._axis_type=="HA": # print("t<t1 {:.0f} = {:.0f} + {:.1f} * {}".format(inc, inct0, vel, (t-t0))) t0 = t else: # --- We overtook the target. We switch to drift at time t1 # --- So we can compute the inc since t0 self.motion_state_simu = self.MOTION_STATE_DRIFTING t0 = t1 inct0 = inc_end_slew inc = inct0 #if self._axis_type=="HA": # print("t>=t1 {:.0f}".format(inc)) # --- drifting case if self.motion_state_simu == self.MOTION_STATE_DRIFTING: #if self._axis_type=="HA": # print("drift drift={} inc/sec".format(drift)) if drift == 0: self.motion_state_simu = self.MOTION_STATE_NOMOTION else: # --- compute the current inc inc = inct0 + drift*(t-t0) #if self._axis_type=="HA": # print("t<t1 {:.0f} = {:.0f} + {:.1f} * {}".format(inc, inct0, drift, (t-t0))) t0 = t # --- We must test if we overtake the inc limits # --- update the inc0 and t0 values self._incsimu = inc self._simu_motion_t0 = t0 self._simu_motion_inct0 = inc # --- deprecated attributes if self.motion_state_simu == self.MOTION_STATE_NOMOTION: self._simu_signal_move = 0 else: self._simu_signal_move = 1 if self.motion_state_simu == self.MOTION_STATE_DRIFTING: self._simu_signal_drift = 1 else: self._simu_signal_drift = 0 # --- update the motion history dt = t-self._simu_motion_start_t0; history = [t, dt, "MOTION_UPDATE", self.motion_state_simu , inc] self._history.append(history) return inc
# ===================================================================== # ===================================================================== # Special methods # ===================================================================== # ===================================================================== def __init__(self, *args, **kwargs): # --- Dico of optional parameters for all axis_types param_optionals = {} param_optionals["RATIO_WHEEL_PULEY"] = (float, 5.250) param_optionals["RATIO_PULEY_MOTOR"] = (float, 100.0) param_optionals["INC_PER_MOTOR_REV"] = (float, 1000.0) param_optionals["INC0"] = (float, 0.0) param_optionals["SENSEINC"] = (float, Mountaxis.POSITIVE) param_optionals["SENSEANG"] = (float, Mountaxis.POSITIVE) param_optionals["REAL"] = (bool, False) param_optionals["DESCRIPTION"] = (str, "No description.") param_optionals["LANGUAGE_PROTOCOL"] = (str, "") # --- Dico of axis_types and their parameters axis_types = {} # --- equatorial axis_types["HA"] = {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Hour Angle"]} } axis_types["DEC"] = {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Declination"]} } # --- altaz axis_types["AZ"] = {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Azimuth"]} } axis_types["ELEV"] = {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Elevation"]} } axis_types["ROT"] = {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Rotator"]} } # --- altalt axis_types["ROLL"] = {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Roll"]} } axis_types["PITCH"]= {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Pitch"]} } axis_types["YAW"] = {"MANDATORY" : {"NAME":[str,"Unknown"]}, "OPTIONAL" : {"LABEL":[str,"Yaw"]} } # N.B. Generally yaw is fixed in EW or NS direction # --- Decode args and kwargs parameters self._axis_params = self.decode_args_kwargs(0, axis_types, param_optionals, *args, **kwargs) # === self.axis_type = self._axis_params["SELECTED_ARG"] # === self._name = self._axis_params["NAME"] self._description = self._axis_params["DESCRIPTION"] self._language_protocol = self._axis_params["LANGUAGE_PROTOCOL"] # === relations mechanics and inc self.ratio_wheel_puley = self._axis_params["RATIO_WHEEL_PULEY"] self.ratio_puley_motor = self._axis_params["RATIO_PULEY_MOTOR"] self.inc_per_motor_rev = self._axis_params["INC_PER_MOTOR_REV"] # === relations angle and inc self.inc0 = self._axis_params["INC0"] self.senseinc = self._axis_params["SENSEINC"] self.senseang = self._axis_params["SENSEANG"] # === simulation self.real = self._axis_params["REAL"] self.simu_signal_move = 0 self._simu_signal_drift = 0 self._simu_param_vel = 0 self._simu_param_drift = 0 self._simu_param_frame = "ang" self._simu_motion_t0 = time.time() self._simu_motion_start_t0 = self._simu_motion_t0 # === velocities self.slewmax_deg_per_sec = 5.0 self.slew_deg_per_sec = 5.0 # === motion states self.motion_state_simu = self.MOTION_STATE_NOMOTION self.motion_state = self.MOTION_STATE_NOMOTION # === history self._history = [] self._simu_start_t0 = time.time()
# ##################################################################### # ##################################################################### # ##################################################################### # Main # ##################################################################### # ##################################################################### # ##################################################################### if __name__ == "__main__": cwd = os.getcwd() example = 1 print("Example = {}".format(example)) if example == 1: # === SCX11 # --- default values ratio_wheel_puley = 5.25 ; # 5.25 ratio_puley_motor = 100.0 ; # harmonic reducer inc_per_motor_rev = 1000.0 ; # IMC parameter. System Confg -> System Parameters - Distance/Revolution # --- SCX11 HA axisp = Mountaxis("DEC", name = "SCX11", ratio_wheel_puley=ratio_wheel_puley, ratio_puley_motor=ratio_puley_motor, inc_per_motor_rev=inc_per_motor_rev, inc0=0, senseinc=1, real=False) axisp.update_inc0(0,0,axisp.PIERSIDE_POS1) inc = axisp.simu_update_inc() print("inc before SLEW={:.0f} state={}".format(inc,axisp.motion_state_simu)) axisp.simu_motion_start("SLEW", position=30, velocity=4.0, frame="ang", drift=0.1) time.sleep(1) inc = axisp.simu_update_inc() print("inc during SLEW={:.0f} state={}".format(inc,axisp.motion_state_simu)) for k in range(10): time.sleep(1) inc = axisp.simu_update_inc() print("inc during SLEW={:.0f} state={}".format(inc,axisp.motion_state_simu)) axisp.simu_motion_start("MOVE", velocity=-1.0, frame="ang", drift=0.1) for k in range(10): time.sleep(1) inc = axisp.simu_update_inc() print("inc during MOVE={:.0f} state={}".format(inc,axisp.motion_state_simu)) axisp.simu_motion_stop_move() # stop move, start drift for k in range(10): time.sleep(1) inc = axisp.simu_update_inc() print("inc after MOVE={:.0f} state={}".format(inc,axisp.motion_state_simu)) inc = axisp.simu_motion_stop() print("inc after STOP={:.0f} state={}".format(inc,axisp.motion_state_simu)) time.sleep(1) inc = axisp.simu_update_inc() print("inc after STOP={:.0f} state={}".format(inc,axisp.motion_state_simu)) inc = axisp.simu_update_inc() print("inc before DRIFT={:.0f} state={}".format(inc,axisp.motion_state_simu)) axisp.simu_motion_start("DRIFT", frame="ang", drift=0.1) for k in range(10): time.sleep(1) inc = axisp.simu_update_inc() print("inc during DRIFT={:.0f} state={}".format(inc,axisp.motion_state_simu)) inc = axisp.simu_motion_stop() print("inc after STOP={:.0f} state={}".format(inc,axisp.motion_state_simu)) if example == 2: # === EQMOD # --- default values to simulate a EQ6 mount a = 9024000 ; # microsteps / 360° : Number of microsteps for one turn over the sky #b = 64935 ; # (microsteps2 / sec) : Velocity parameter (i) = (1|g) * (b) / speedtrack(deg/s) / ((a)/360) d = 8388608 ; # (microsteps) : initial position (j) when the mount is just switched on s = 50133 ; # (microsteps) : Microsteps to a complete turnover of worm inc_per_sky_rev = a ratio_puley_motor = 1 inc_per_motor_rev = s ratio_wheel_puley = inc_per_sky_rev/(ratio_puley_motor*inc_per_motor_rev) # --- EQMOD HA axisb = Mountaxis("HA", name = "EQMOD", ratio_wheel_puley=ratio_wheel_puley, ratio_puley_motor=ratio_puley_motor, inc_per_motor_rev=inc_per_motor_rev, inc0=0, senseinc=1, real=False) axisb.senseinc = 1 axisb.update_inc0(d,-90,axisb.PIERSIDE_POS1) axisb.slew_deg_per_sec = 10 # --- EQMOD DEC axisp = Mountaxis("DEC", name = "EQMOD", ratio_wheel_puley=ratio_wheel_puley, ratio_puley_motor=ratio_puley_motor, inc_per_motor_rev=inc_per_motor_rev, inc0=0, senseinc=1, real=False) axisp.update_inc0(d+a/4,90,axisp.PIERSIDE_POS1) axisp.slew_deg_per_sec = 10 inc = axisp.simu_update_inc() print("inc before CONTINUOUS={:.0f}".format(inc)) axisp.simu_motion_start("CONTINUOUS", frame="ang", drift=0.1) time.sleep(3) inc = axisp.simu_update_inc() print("inc during CONTINUOUS={:.0f}".format(inc)) inc = axisp.simu_motion_stop() print("inc after CONTINUOUS={:.0f}".format(inc)) time.sleep(1) inc = axisp.simu_update_inc() print("inc after CONTINUOUS={:.0f}".format(inc)) inc = axisp.simu_update_inc() print("inc before OFFSET={:.0f}".format(inc)) axisp.simu_motion_start("OFFSET", velocity=2.1, offset=1.0, drift=0) time.sleep(3) inc = axisp.simu_update_inc() print("inc during OFFSET={:.0f}".format(inc)) inc = axisp.simu_motion_stop() print("inc after OFFSET={:.0f}".format(inc)) time.sleep(1) inc = axisp.simu_update_inc() print("inc after OFFSET={:.0f}".format(inc))