DTObs.py 10.9 KB
Newer Older
1
2
3
4
5
#!/usr/bin/env python3

import sys
import csv
import logging
6
from configparser import ConfigParser
7
8
9
import subprocess
import traceback
import time
10
import os.path
11
12
13
14
15
16
17
18
19
import numpy as np

from PyQt5 import QtGui
from PyQt5.QtCore import QRunnable, QObject, pyqtSignal, pyqtSlot, QThreadPool
from PyQt5.QtWidgets import (QFileDialog, QApplication, QMainWindow, QTableWidgetItem)
from ui.dtobswindow import Ui_mainWindow

from astropy import units as u
from astropy.coordinates import SkyCoord
20

marc's avatar
marc committed
21
22
23
24
from telescope import telescope
from measurements import Measurements
from metadata import MetaData

25
'''
marc's avatar
marc committed
26
Setup config ini
27
28
29
'''
config = ConfigParser()
config.readfp(open(os.path.join(os.path.dirname(__file__), 'dtobsgui.ini')))
marc's avatar
marc committed
30
31
32
33
34
35
36
37

'''
Setup logging
'''
logger = logging.getLogger('root')
FORMAT = "[%(asctime)s%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s,filename='dtobsgui.log',filemode='w'"
logging.basicConfig(format=FORMAT)
logger.setLevel(logging.DEBUG)
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

class WorkerSignals(QObject):
    """
    Defines the signals available from a running worker thread.

    Supported signals are:

    finished
        `int` of the setpoint number

    error
        `tuple` (exctype, value, traceback.format_exc() )

    result
        `object` data returned from processing, anything

    progress
        `int` inicating % progress
    """

    finished = pyqtSignal(int, str)
    allfinished = pyqtSignal()
    # error = pyqtSignal(tuple)
    # result = pyqtSignal(object)
    progress = pyqtSignal(int, int, str) # meas_num, percent_complete, remaining

class Worker(QRunnable):
    """ Execute a function asynchronously, connect to signals """

    def __init__(self, function_to_run, *args, **kwargs):
        super(Worker, self).__init__()
        # Store arguments
        self.function_to_run = function_to_run
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()

        self.kwargs['oneSetpointCompleteSignal'] = self.signals.finished
        self.kwargs['progressSignal'] = self.signals.progress

    @pyqtSlot()
    def run(self):
        """ Initialize the runner function with passed args, kwargs """

        self.function_to_run(*self.args, **self.kwargs)
        self.signals.allfinished.emit()

marc's avatar
marc committed
86
87
class DTObservationProgram(Ui_mainWindow):    
  
88
    def __init__(self, mainWindow):
marc's avatar
marc committed
89
        logger.info('Initialization of DTObsGUI')
90
        self.myDT = telescope(setmode='J2000', consoleHost='consoledemo.dmz.camras.nl')
marc's avatar
marc committed
91
92
93
        self.myMeas = Measurements()
        self.myMetaData = MetaData()
        
94
        self.radec = None
95
96
97
98
99
        self.measProgramma = None
        self.integrationTime = None
        self.outputDir = None
        self.outputFile = None

100
101
102
103
104
105
106
        Ui_mainWindow.__init__(self)
        self.setupUi(mainWindow)
        self.actionOpen.triggered.connect(self.openFileNameDialog)
        self.actionClose.triggered.connect(self.closeMeasurement)
        self.actionQuit.triggered.connect(self.quitDTObs)
        self.pushButtonStartMeasurement.clicked.connect(self.startMeasurement)
        self.pushButtonStopMeasurement.clicked.connect(self.stopMeasurement)
107
        self.pushButtonOutputDirectory.clicked.connect(self.selectOutputDirectory)
marc's avatar
marc committed
108
109
        self.homedir = os.environ['HOME']
        self.lineEditOutputDir.setText(self.homedir)
110
        '''
marc's avatar
marc committed
111
        Read the available tools from measurement Class and store them in the 
112
113
114
115
116
        comboBoxProgramma for selection.
        '''
        indexNR=0
        for tool in self.myMeas.getTools():
            self.comboBoxProgramma.setItemText(indexNR, tool)
marc's avatar
marc committed
117
            indexNR+=1
118
119
120
121
122
123
124

        self.threadpool = QThreadPool()

    def openFileNameDialog(self):
        '''
        Clear tableWidget and read pointings from file
        '''
125
        fileName,_ = QFileDialog.getOpenFileName(None, 'Open File', "","Meas Files (*.dat);;All Files (*)")
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
        if fileName:
            reader = csv.reader(open(fileName),delimiter='\t')
            rowPosition = self.tableWidgetPointings.rowCount()

            for meas, ra, dec in reader:
                self.radec = SkyCoord(float(ra)*u.degree, float(dec)*u.degree, frame='icrs')
                (raStr, decStr)  = self.radec.to_string('hmsdms').split()

                self.tableWidgetPointings.insertRow(rowPosition)
                self.tableWidgetPointings.setItem(rowPosition, 0, QTableWidgetItem(raStr))
                self.tableWidgetPointings.setItem(rowPosition, 1, QTableWidgetItem(decStr))
                self.tableWidgetPointings.setItem(rowPosition, 2, QTableWidgetItem(""))

                rowPosition += 1

        self.tableWidgetPointings.resizeColumnsToContents()
marc's avatar
marc committed
142
        logger.info('Imported {} pointings'.format(rowPosition))
143
144
145
146
147
148
        
    def selectOutputDirectory(self):
        '''
        Select the output directory to store the measurements
        '''
        dialog = QFileDialog()
marc's avatar
marc committed
149
        folder_path = dialog.getExistingDirectory(None, "Select Folder",self.homedir)
150
        self.lineEditOutputDir.setText(folder_path)
marc's avatar
marc committed
151
        
152
153
154
155
156
    def closeMeasurement(self):
        for rowPosition in range(self.tableWidgetPointings.rowCount(),-1,-1):
            self.tableWidgetPointings.removeRow(rowPosition)

    def quitDTObs(self):
157
        logging.shutdown()
158
159
160
161
        sys.exit(app.exec_())

    def allCompleted(self):
        """ Callback function when all pointings completed """
marc's avatar
marc committed
162
        logging.info("All pointings completed")
163
164
165
166
167
168
169
170

    def measCompleted(self, meas_num, status):
        """ Make the meas_num-th row of the table green """
        colors = {'Completed': QtGui.QColor('green'),
                  'Slewing'  : QtGui.QColor('yellow'),
                  'Measuring': QtGui.QColor('cyan')}

        self.tableWidgetPointings.item(meas_num, 2).setText(status)
marc's avatar
marc committed
171
172
173
174
175
176
        '''
        TODO
        '''
        #self.tableWidgetPointings.item(meas_num, 3).setText(measFile.name))

        for column in range(4):
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
            self.tableWidgetPointings.item(meas_num,column).setBackground(colors[status])
        self.tableWidgetPointings.resizeColumnsToContents()

    def goToSetpoints(self, setpoints, oneSetpointCompleteSignal=None, progressSignal=None):
        """ Send a list of setpoints to the telescope """
        for setpoint_nr, setpoint in enumerate(setpoints):
            self.myDT.setRaDec(setpoint)
            oneSetpointCompleteSignal.emit(setpoint_nr, 'Slewing')
            time.sleep(3)
            dist = np.sqrt(self.myDT.dist_el**2+self.myDT.dist_az**2)
            firstDist = dist
            while not dist < 0.01*u.deg:
                percentSlew = max(100 - dist/firstDist*100, 0)
                progressSignal.emit(setpoint_nr, percentSlew, "{:.3f}°".format(dist.value))
                #print("{:.3f}".format(dist))
                #print("{:2.0f}".format(percentSlew.value))
                self.myDT.getDistance(waitForUpdate=True)
                dist = np.sqrt(self.myDT.dist_el**2+self.myDT.dist_az**2)

            oneSetpointCompleteSignal.emit(setpoint_nr, 'Measuring')
            self.doMeasurement(setpoint_nr, progressSignal=progressSignal)
            oneSetpointCompleteSignal.emit(setpoint_nr, 'Completed')

    def updateProgress(self, meas_num, progress_percent, remaining_str):
        """
        Update the table with some progress indicator

        meas_num: the row which needs to be updated
        progress_percent: integer giving the percentage complete
        remaining_str: string indicating how much remaining, e.g. "1:30" or "3°"
        """
        #self.tableWidgetPointings.item(meas_num, 2).setText(remaining_str)
        self.tableWidgetPointings.item(meas_num, 2).setText(str(progress_percent)+"%")

marc's avatar
marc committed
211
212
213
214
215
216
217
218
219
    def setMetaData(self):
        '''
        Define metadata
        '''   
        self.myMetaData.description = self.textEditDescription.toPlainText()
        self.myMetaData.startTime = time.strftime("%Y%m%d-%H%M%S")
        self.myMetaData.operator1 = self.lineEditOperator1.text()
        self.myMetaData.operator2 = self.lineEditOperator2.text()
        self.myMetaData.operator3 = self.lineEditOperator3.text()
220
221
222
223
        self.myMetaData.tool = self.measProgramme 
        self.myMetaData.integrationTime = self.integrationTime        
        self.myMetaData.outputDirectory = self.outputDir
        self.myMetaData.outputFile = self.outputFile
marc's avatar
marc committed
224
225
        self.myMetaData.RaDEC = self.radec
        self.myMetaData.refactionEnabled = self.checkBoxRefractionEnabled.checkStateSet()
226
        self.myMetaData.DTModelEnabled = self.checkBoxDTModel.checkStateSet()
marc's avatar
marc committed
227
228

    def writeMetaData(self,file):
229
230
231
        for key, value in self.myMetaData.getMetaData().items():
            file.write('#' + str(key) + '\t' + str(value) + '\n')
            logging.info('#key: {}\t value: {}'.format(key,value))
232
233
234
235

    def doMeasurement(self, measnum, progressSignal=None):
        """ 
        Dumping data into file including meta-data
236
        measProgramme: cli command read from comboBoxProgramma
237
        integrationTime: string read from spinBoxIntTime
238
        measurement: result of measurement of which 
239
        data: is the stdout result which need to be stored line by line
marc's avatar
marc committed
240
        measFile: result file storing the stdout results including meta data
241
        """
242
243
244
245
246
        self.integrationTime = self.spinBoxIntTime.value()
        self.measProgramme = self.comboBoxProgramma.currentText()
        self.outputDir = self.lineEditOutputDir.text()
        self.outputFile = '/DT-' + '{:03d}'.format(measnum) + '-' + time.strftime("%Y%m%d") + '.dat'
        measFile = open(self.outputDir + self.outputFile,'w')
marc's avatar
marc committed
247
        
248
249
        self.setMetaData()
        self.writeMetaData(measFile)
marc's avatar
marc committed
250
                
251
        logging.info("Output to file: {}".format(measFile))
marc's avatar
marc committed
252
        
253
        self.myMeas.startMeasurement(self.measProgramme, self.integrationTime, measFile)
254

255
256
257
258
        for sec in range(self.integrationTime):
            remainstring =  str(int((self.integrationTime-sec)/60)) + ":"
            remainstring += "{:02}".format((self.integrationTime-sec)%60)
            progressSignal.emit(measnum, int(float(sec)/self.integrationTime*100), remainstring)
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
            time.sleep(1)
        measFile.close()

    def startMeasurement(self):
        print("Measurement started")
        setpoints = []
        for meas in range(0,self.tableWidgetPointings.rowCount()):
            ra  = self.tableWidgetPointings.item(meas, 0).text()
            dec = self.tableWidgetPointings.item(meas, 1).text()

            self.tableWidgetPointings.item(meas, 2).setText("Scheduled")
            self.tableWidgetPointings.resizeColumnsToContents()
            setpoint = SkyCoord(ra, dec, frame='icrs')
            setpoints.append(setpoint)

        worker = Worker(self.goToSetpoints, setpoints)
        worker.signals.finished.connect(self.measCompleted)
        worker.signals.allfinished.connect(self.allCompleted)
        worker.signals.progress.connect(self.updateProgress)
        self.threadpool.start(worker)

    def stopMeasurement(self):
        print("Measurement stopped")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWindow = QMainWindow()

    prog = DTObservationProgram(mainWindow)

    mainWindow.show()
    sys.exit(app.exec_())