Commit 61ad69e3 authored by Tammo Jan Dijkema's avatar Tammo Jan Dijkema
Browse files

Merge branch refactor

parents 75826d0f 336a023b
...@@ -106,30 +106,12 @@ venv.bak/ ...@@ -106,30 +106,12 @@ venv.bak/
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff: .idea
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake # CMake
cmake-build-debug/ cmake-build-debug/
cmake-build-release/ cmake-build-release/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format: ## File-based project format:
*.iws *.iws
...@@ -144,13 +126,9 @@ out/ ...@@ -144,13 +126,9 @@ out/
# JIRA plugin # JIRA plugin
atlassian-ide-plugin.xml atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ) # Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml com_crashlytics_export_strings.xml
crashlytics.properties crashlytics.properties
crashlytics-build.properties crashlytics-build.properties
fabric.properties fabric.properties
#!/usr/bin/env python #!/usr/bin/env python
from setuptools import setup, find_packages from setuptools import setup
meta = dict(name='telescope', meta = dict(name = 'telescope',
version='1.1.1', version = '1.1.1',
author='Tammo Jan Dijkema', author = 'Tammo Jan Dijkema',
author_email='T.J.Dijkema@camras.nl', author_email = 'T.J.Dijkema@camras.nl',
url='https://gitlab.camras.nl/dijkema/telescope', url = 'https://gitlab.camras.nl/dijkema/telescope',
description='Software for the CAMRAS Dwingeloo Radio Telescope', description = 'Software for the CAMRAS Dwingeloo Radio Telescope',
platforms='Linux, Mac OSX', platforms = 'Linux, Mac OSX',
py_modules=['telescope'], py_modules = ['telescope'],
include_package_data=True, include_package_data = True,
data_files=[('', ['telescope.ini',]),], data_files=[('', ['telescope.ini',]), ],
install_requires=['astropy','configparser','numpy'] install_requires=['astropy', 'configparser', 'numpy']
) )
setup(**meta) setup(**meta)
...@@ -2,17 +2,16 @@ from __future__ import print_function ...@@ -2,17 +2,16 @@ from __future__ import print_function
import socket import socket
import logging import logging
from astropy.coordinates import SkyCoord, AltAz, RangeError from astropy.coordinates import SkyCoord, RangeError
from astropy import units as u
from configparser import ConfigParser from configparser import ConfigParser
import astropy.units as u
import os.path import os.path
import numpy as np import numpy as np
import threading import threading
import select import select
import time
config = ConfigParser() config = ConfigParser()
config.readfp(open(os.path.join(os.path.dirname(__file__), 'telescope.ini'))) config.read_file(open(os.path.join(os.path.dirname(__file__), 'telescope.ini')))
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
...@@ -20,22 +19,25 @@ _telescope__num_instances = 0 ...@@ -20,22 +19,25 @@ _telescope__num_instances = 0
__version__ = "1.1.1" __version__ = "1.1.1"
class telescope():
def __init__(self, setmode=None, consoleHost=None):
'''
Initializes a telescope instance. The mode for writing can be
'J2000' or 'AZEL'.
'''
global __num_instances
if setmode not in ('J2000', 'AZEL', None): class Telescope:
raise ValueError("Mode must be None, 'J2000' or 'AZEL', not " + setmode) def __init__(self, setMode=None, consoleHost=None):
self.setmode = setmode """
Initializes a telescope instance
:param setMode: The mode for writing. Can be 'J2000' or 'AZEL'
:param consoleHost: Address of the console. E.g. 'console' or 'consoledemo.dmz.camras.nl'
"""
global _telescope__num_instances
if setMode not in ('J2000', 'AZEL', None):
raise ValueError("Mode must be None, 'J2000' or 'AZEL', not " + setMode)
self.setmode = setMode
if consoleHost is None: if consoleHost is None:
consoleHost = config.get('Console','HostName') consoleHost = config.get('Console', 'HostName')
if consoleHost == 'console': if consoleHost == 'console':
if socket.gethostname()=="mercurius": if setMode is not None and socket.gethostname() == "mercurius":
logging.warning("You are using the actual console, not a demo!") logging.warning("You are using the actual console, not a demo!")
else: else:
raise ValueError("Talking to the actual console can only be done from mercurius") raise ValueError("Talking to the actual console can only be done from mercurius")
...@@ -52,22 +54,22 @@ class telescope(): ...@@ -52,22 +54,22 @@ class telescope():
self.refraction_enabled = None self.refraction_enabled = None
self.model_enabled = None self.model_enabled = None
__num_instances += 1 _telescope__num_instances += 1
if __num_instances != 1: if _telescope__num_instances != 1:
raise RuntimeError("Do not make more than one telescope instance.") raise RuntimeError("Do not make more than one telescope instance.")
self._outsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._outsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if self.setmode == 'J2000': if self.setmode == 'J2000':
try: try:
self._outsocket.connect((consoleHost, self._outsocket.connect((consoleHost,
config.getint('Console','Port_Write_J2000'))) config.getint('Console', 'Port_Write_J2000')))
except socket.gaierror as e: except socket.gaierror as e:
raise IOError("Could not connect to DT J2000 port: " + str(e)) raise IOError("Could not connect to DT J2000 port: " + str(e))
elif self.setmode == 'AZEL': elif self.setmode == 'AZEL':
try: try:
self._outsocket.connect((consoleHost, self._outsocket.connect((consoleHost,
config.getint('Console','Port_Write_AzEl'))) config.getint('Console', 'Port_Write_AzEl')))
except socket.gaierror as e: except socket.gaierror as e:
raise IOError("Could not connect to DT AZEL port: " + str(e)) raise IOError("Could not connect to DT AZEL port: " + str(e))
...@@ -76,38 +78,41 @@ class telescope(): ...@@ -76,38 +78,41 @@ class telescope():
self._offsetsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._offsetsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: try:
self._offsetsocket.connect((consoleHost, self._offsetsocket.connect((consoleHost,
config.getint('Console','Port_Write_Offset'))) config.getint('Console', 'Port_Write_Offset')))
except socket.gaierror as e: except socket.gaierror as e:
raise IOError("Could not connect to DT Offset port: " + str(e)) raise IOError("Could not connect to DT Offset port: " + str(e))
self._event_j2000 = threading.Event() self._event_j2000 = threading.Event()
thread_read_j2000 = threading.Thread(target=self._readj2000, args=( thread_read_j2000 = threading.Thread(target=self._readj2000, args=(
consoleHost, consoleHost,
config.getint('Console','Port_Read_J2000'))) config.getint('Console', 'Port_Read_J2000')))
thread_read_j2000.daemon = True thread_read_j2000.daemon = True
thread_read_j2000.start() thread_read_j2000.start()
self._event_traces = threading.Event() self._event_traces = threading.Event()
thread_read_traces = threading.Thread(target=self._readtraces, args=( thread_read_traces = threading.Thread(target=self._readtraces, args=(
consoleHost, consoleHost,
config.getint('Console', 'Port_Trace2Port'))) config.getint('Console', 'Port_Trace2Port')))
thread_read_traces.daemon = True thread_read_traces.daemon = True
thread_read_traces.start() thread_read_traces.start()
def _readj2000(self, consolehost, j2000_read_port): def _readj2000(self, consoleHost, j2000_read_port):
''' """
Poll the socket with the j2000 info, store its values in class members. Poll the socket with the J2000 info, store its values in class members
''' :param consoleHost: Hostname
:param j2000_read_port: Port with J200 info
:return: None
"""
try: try:
self._j2000socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._j2000socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._j2000socket.connect((consolehost, j2000_read_port)) self._j2000socket.connect((consoleHost, j2000_read_port))
except socket.error: except socket.error:
raise IOError("Cannot connect to j2000 status server") raise IOError("Cannot connect to j2000 status server")
while True: while True:
readable, writable, exceptional = select.select( readable, writable, exceptional = select.select(
[self._j2000socket], [], []) [self._j2000socket], [], [])
if len(exceptional)>0: if len(exceptional) > 0:
raise IOError("Error with j2000 read socket") raise IOError("Error with j2000 read socket")
msg = readable[0].recv(4096).decode("UTF-8") msg = readable[0].recv(4096).decode("UTF-8")
vals = msg.strip().split() vals = msg.strip().split()
...@@ -131,6 +136,13 @@ class telescope(): ...@@ -131,6 +136,13 @@ class telescope():
self._event_j2000.set() self._event_j2000.set()
def _readtraces(self, tracehost, traceport): def _readtraces(self, tracehost, traceport):
"""
Poll the socket with the traces (a running 'trace2port' instance), store the trace values
in class members
:param tracehost: Hostname
:param traceport: Port at which trace2port is running
:return: None
"""
''' '''
Poll the socket with the traces, store their values in class members Poll the socket with the traces, store their values in class members
''' '''
...@@ -142,21 +154,27 @@ class telescope(): ...@@ -142,21 +154,27 @@ class telescope():
while True: while True:
readable, writable, exceptional = select.select( readable, writable, exceptional = select.select(
[self._tracesocket], [], []) [self._tracesocket], [], [])
if len(exceptional)>0: if len(exceptional) > 0:
raise IOError("Error with trace read socket") raise IOError("Error with trace read socket")
msg = readable[0].recv(4096).decode("UTF-8") msg = readable[0].recv(4096).decode("UTF-8")
vals = msg.strip().split() vals = msg.strip().split()
self.az = (float(vals[0])*u.rad).to(u.deg) self.az = (float(vals[0]) * u.rad).to(u.deg)
self.el = (float(vals[1])*u.rad).to(u.deg) self.el = (float(vals[1]) * u.rad).to(u.deg)
self.dist_az = (float(vals[2])*u.rad).to(u.deg) self.dist_az = (float(vals[2]) * u.rad).to(u.deg)
self.dist_el = (float(vals[3])*u.rad).to(u.deg) self.dist_el = (float(vals[3]) * u.rad).to(u.deg)
self.speed_az = float(vals[4]) self.speed_az = float(vals[4])
self.speed_el = float(vals[5]) self.speed_el = float(vals[5])
self.focusbox_pos = float(vals[6]) self.focusbox_pos = float(vals[6])
self._event_traces.set() self._event_traces.set()
def setRaDec(self, setpoint): def setRaDec(self, setpoint):
"""
Set the J2000 setpoint of the telescope.
Only works if the telescope is in J2000 mode.
:param setpoint: New setpoint. An astropy SkyCoord or a tuple (ra, dec) in radians
:return: None
"""
''' '''
Set the J2000 setpoint of the telescope. Set the J2000 setpoint of the telescope.
Only works if the setmode is 'J2000'. Only works if the setmode is 'J2000'.
...@@ -167,22 +185,23 @@ class telescope(): ...@@ -167,22 +185,23 @@ class telescope():
raise ValueError("Cannot set Ra/Dec if mode is not J2000") raise ValueError("Cannot set Ra/Dec if mode is not J2000")
if isinstance(setpoint, tuple): if isinstance(setpoint, tuple):
coord = SkyCoord(ra =setpoint[0] * u.radian, setpoint = SkyCoord(ra=setpoint[0] * u.radian,
dec=setpoint[1] * u.radian) dec=setpoint[1] * u.radian)
(ra, dec) = setpoint.to_string('hmsdms').split() (ra, dec) = setpoint.to_string('hmsdms').split()
cmd = '{:s}\t{:s}\n'.format(ra, dec.strip('+')).encode('UTF-8') cmd = '{:s}\t{:s}\n'.format(ra, dec.strip('+')).encode('UTF-8')
logging.info('J2000 setpoint sent to DT: {}'.format(cmd)) logging.info('J2000 setpoint sent to DT: {}'.format(cmd))
self._outsocket.send(cmd) self._outsocket.send(cmd)
def setAzEl(self, setpoint): def setAzEl(self, setpoint):
''' """
Set the AzEl setpoint of the telescope. Set the AzEl setpoint of the telescope.
Only works if the setmode is 'AZEL' Only works if the setmode is 'AZEL'
:param setpoint: New setpoint. A tuple of floats (interpreted as degrees), or
setpoint should be a tuple of degrees or of astropy Quantity a tuple of astropy Quantity
''' :return: None
"""
if self.setmode != 'AZEL': if self.setmode != 'AZEL':
raise ValueError("Cannot set Az/El if mode is not AZEL") raise ValueError("Cannot set Az/El if mode is not AZEL")
...@@ -197,11 +216,12 @@ class telescope(): ...@@ -197,11 +216,12 @@ class telescope():
self._outsocket.send(cmd) self._outsocket.send(cmd)
def setOffset(self, offset): def setOffset(self, offset):
''' """
Set the offset of the telescope in Az / El. Set the offset of the telescope in Az / El.
:param offset: New setpoint. A tuple of floats (interpreted as radians), or
offset should be a tuple of degrees or of astropy Quantity a tuple of astropy Quantity
''' :return: None
"""
if self.setmode is None: if self.setmode is None:
raise ValueError("Cannot set offset if mode is None") raise ValueError("Cannot set offset if mode is None")
...@@ -220,103 +240,109 @@ class telescope(): ...@@ -220,103 +240,109 @@ class telescope():
self._offsetsocket.send(cmd) self._offsetsocket.send(cmd)
def getFocusboxPosition(self, waitForUpdate=False): def getFocusboxPosition(self, waitForUpdate=False):
''' """
Get the focusbox position. If waitForUpdate=True, it waits for the Get the focusbox position.
next signal from the socket. :param waitForUpdate: wait for the next signal from the socket
Returns an astropy quantity :return: Astropy quantity with the focusbox position
''' """
if waitForUpdate or self.focusbox_pos is None: if waitForUpdate or self.focusbox_pos is None:
self._event_traces.clear() self._event_traces.clear()
self._event_traces.wait() self._event_traces.wait()
return self.focusbox_pos * 0.1 * u.m return self.focusbox_pos * 0.1 * u.m
def getAzEl(self, waitForUpdate=False): def getAzEl(self, waitForUpdate=False):
''' """
Get azimut and elevation. If waitForUpdate=True, it waits for the Get azimut and elevation. If waitForUpdate=True, it waits for the
next signal from the socket. next signal from the socket.
Returns a tuple (az, el) as astropy quantity Returns a tuple (az, el) as astropy quantity
''' """
if waitForUpdate or self.az is None or self.radec is None: if waitForUpdate or self.az is None or self.radec is None:
self._event_traces.clear() self._event_traces.clear()
self._event_traces.wait() self._event_traces.wait()
return (self.az, self.el) return (self.az, self.el)
def getDistance(self, waitForUpdate=False): def getDistance(self, waitForUpdate=False):
''' """
Get distance in azimut and elevation. If waitForUpdate=True, it waits Get distance in azimut and elevation. If waitForUpdate=True, it waits
for the next signal from the socket. for the next signal from the socket.
Returns a tuple (dist_az, dist_el) as astropy quantity Returns a tuple (dist_az, dist_el) as astropy quantity
''' """
if waitForUpdate or self.dist_az is None or self.dist_el is None: if waitForUpdate or self.dist_az is None or self.dist_el is None:
self._event_traces.clear() self._event_traces.clear()
self._event_traces.wait() self._event_traces.wait()
return (self.dist_az, self.dist_el) return (self.dist_az, self.dist_el)
def getRaDec(self, waitForUpdate=False): def getRaDec(self, waitForUpdate=False):
''' """
Get current Ra/Dec. If waitForUpdate=True, it waits for the Get current Ra/Dec. If waitForUpdate=True, it waits for the
next signal from the socket. next signal from the socket.
Returns an astropy SkyCoord Returns an astropy SkyCoord
''' """
if waitForUpdate or self.radec is None: if waitForUpdate or self.radec is None:
self._event_traces.clear() self._event_traces.clear()
self._event_traces.wait() self._event_traces.wait()
return self.radec return self.radec
def getSetpoint_RaDec(self, waitForUpdate=False): def getSetpoint_RaDec(self, waitForUpdate=False):
''' """
Get current Ra/Dec setpoint. If waitForUpdate=True, it waits for the Get current Ra/Dec setpoint. If waitForUpdate=True, it waits for the
next signal from the socket. next signal from the socket.
Returns an astropy SkyCoord Returns an astropy SkyCoord
''' """
if waitForUpdate or self.radec is None: if waitForUpdate or self.radec is None:
self._event_traces.clear() self._event_traces.clear()
self._event_traces.wait() self._event_traces.wait()
return self.setpoint_radec return self.setpoint_radec
def waitUntilThere(self, tolerance=0.01*u.deg): def waitUntilThere(self, tolerance=0.01 * u.deg):
''' """
Wait until distance to the setpoint gets within tolerance Wait until distance to the setpoint gets within tolerance
''' """
logging.info("Waiting to reach setpoint") logging.info("Waiting to reach setpoint")
distIsSmall = False distIsSmall = False
while not distIsSmall: while not distIsSmall:
diff = self.getDistance(waitForUpdate=True) diff = self.getDistance(waitForUpdate=True)
distIsSmall = abs(self.dist_el)<tolerance and abs(self.dist_az)<tolerance distIsSmall = abs(self.dist_el) < tolerance and abs(self.dist_az) < tolerance
def waitUntilMoving(self, tolerance=0.01*u.deg): def waitUntilMoving(self, tolerance=0.01 * u.deg):
''' """
Wait until the telescope reckognizes that it is not at its setpoint Wait until the telescope reckognizes that it is not at its setpoint
''' """
logging.info("Waiting to set setpoint") logging.info("Waiting to set setpoint")
distIsBig = False distIsBig = False
while not distIsBig: while not distIsBig:
dist = self.getDistance(waitForUpdate=True) dist = self.getDistance(waitForUpdate=True)
distIsBig = dist[0]>tolerance or dist[1]>tolerance distIsBig = dist[0] > tolerance or dist[1] > tolerance
def getJ2000(self): def getJ2000(self):
''' """
Generates dicts with 'radec' and 'setpoint_radec', Generates dicts with 'radec' and 'setpoint_radec',
containing radec and setpoint_radec, at whichever containing radec and setpoint_radec, at whichever
speed the console is outputting them (probably once a second). speed the console is outputting them (probably once a second).
''' """
self._event_traces.clear() self._event_traces.clear()
while True: while True:
self._event_j2000.wait() self._event_j2000.wait()
self._event_j2000.clear() self._event_j2000.clear()
yield({'radec': self.radec, yield ({'radec': self.radec,
'setpoint_radec': self.setpoint_radec}) 'setpoint_radec': self.setpoint_radec})
def getTraces(self): def getTraces(self):
''' """
Generates a dict of all traces from trace2port at whichever Generates a dict of all traces from trace2port at whichever
rate trace2port generates them (probably once a second). rate trace2port generates them (probably once a second).
''' """
self._event_traces.clear() self._event_traces.clear()
while True: while True:
self._event_traces.wait() self._event_traces.wait()
self._event_traces.clear() self._event_traces.clear()
yield({'position_azel': (self.az, self.el), yield ({'position_azel': (self.az, self.el),
'distance': (self.dist_az, self.dist_el), 'distance': (self.dist_az, self.dist_el),
'speed': (self.speed_az, self.speed_el)}) 'speed': (self.speed_az, self.speed_el)})
class telescope(Telescope):
def __init__(self, *args, **kwargs):
super(Telescope, self).__init__(*args, **kwargs)
logging.warning("Please use the new class name Telescope (with capital T)")
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment