# -*- 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))