DTObs.py 13.4 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
from telescope import telescope
from measurements import Measurements
from metadata import MetaData
marc's avatar
marc committed
24
from backend import Backend
marc's avatar
marc committed
25

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

'''
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)
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
86

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
87
88
class DTObservationProgram(Ui_mainWindow):    
  
89
    def __init__(self, mainWindow):
marc's avatar
marc committed
90
        logger.info('Initialization of DTObsGUI')
marc's avatar
marc committed
91
92
93
94
        self.TelescopeEnabled = config.get('TestConfig','myDT')
        if self.TelescopeEnabled == True:
            self.myDT = telescope(setmode='J2000', consoleHost='consoledemo.dmz.camras.nl')
        
marc's avatar
marc committed
95
96
        self.myMeas = Measurements()
        self.myMetaData = MetaData()
marc's avatar
marc committed
97
        self.myBackend = Backend(setmode='Pulsar')
marc's avatar
marc committed
98
        
99
        self.radec = None
100
101
        self.measProgramma = None
        self.integrationTime = None
marc's avatar
marc committed
102
        self.inputFile = None
103
104
        self.outputDir = None
        self.outputFile = None
marc's avatar
marc committed
105
        self.GUIbackendMode = None
106

107
108
        Ui_mainWindow.__init__(self)
        self.setupUi(mainWindow)
marc's avatar
marc committed
109
110
111
112
113
114
        self.homedir = os.environ['HOME']
        self.lineEditOutputDir.setText(self.homedir)
        
        '''
        Define slot and connections for GUI
        '''
115
116
117
118
119
        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)
120
        self.pushButtonOutputDirectory.clicked.connect(self.selectOutputDirectory)
marc's avatar
marc committed
121
        self.tableWidgetPointings.itemDoubleClicked.connect(self.viewResultMeasurement)
marc's avatar
marc committed
122

123
        '''
marc's avatar
marc committed
124
        Read the available tools from measurement Class and store them in the 
125
126
127
128
129
        comboBoxProgramma for selection.
        '''
        indexNR=0
        for tool in self.myMeas.getTools():
            self.comboBoxProgramma.setItemText(indexNR, tool)
marc's avatar
marc committed
130
            indexNR+=1
131
132
133
134
135
136
137

        self.threadpool = QThreadPool()

    def openFileNameDialog(self):
        '''
        Clear tableWidget and read pointings from file
        '''
marc's avatar
marc committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
        self.inputFile,_ = QFileDialog.getOpenFileName(None, 'Open File', "templates","Meas Files (*.dat);;All Files (*)")
        if self.inputFile:
            self.loadPointingsInTable(self.inputFile)
    
    def loadPointingsInTable(self,fileName):
        reader = csv.reader(open(self.inputFile),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(""))
            self.tableWidgetPointings.setItem(rowPosition, 3, QTableWidgetItem(""))

            rowPosition += 1
156
157

        self.tableWidgetPointings.resizeColumnsToContents()
marc's avatar
marc committed
158
        logger.info('Imported {} pointings'.format(rowPosition))
159
160
161
162
163
164
        
    def selectOutputDirectory(self):
        '''
        Select the output directory to store the measurements
        '''
        dialog = QFileDialog()
marc's avatar
marc committed
165
        folder_path = dialog.getExistingDirectory(None, "Select Folder",self.homedir)
166
        self.lineEditOutputDir.setText(folder_path)
marc's avatar
marc committed
167
        
168
169
170
171
172
    def closeMeasurement(self):
        for rowPosition in range(self.tableWidgetPointings.rowCount(),-1,-1):
            self.tableWidgetPointings.removeRow(rowPosition)

    def quitDTObs(self):
173
        logging.shutdown()
174
175
176
177
        sys.exit(app.exec_())

    def allCompleted(self):
        """ Callback function when all pointings completed """
marc's avatar
marc committed
178
        logging.info("All pointings completed")
179
180
181
182
183
184
185

    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
186
187
        self.tableWidgetPointings.setItem(meas_num, 3, QTableWidgetItem(self.outputFile))
        
marc's avatar
marc committed
188
189
190
191
192
        '''
        TODO
        '''
        #self.tableWidgetPointings.item(meas_num, 3).setText(measFile.name))

marc's avatar
marc committed
193
        for column in range(4):
194
195
196
197
198
199
            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):
marc's avatar
marc committed
200
201
202
203
            if self.TelescopeEnabled == True:
                self.myDT.setRaDec(setpoint)
                oneSetpointCompleteSignal.emit(setpoint_nr, 'Slewing')
                time.sleep(3)
204
                dist = np.sqrt(self.myDT.dist_el**2+self.myDT.dist_az**2)
marc's avatar
marc committed
205
206
207
208
209
210
211
212
213
                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)
                    
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
            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
229
230
231
232
233
234
235
236
237
    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()
238
239
240
241
        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
242
        self.myMetaData.RaDEC = self.radec
marc's avatar
marc committed
243
244
245
246
247
        self.myMetaData.refractionEnabled = self.checkBoxRefraction.isChecked()
        logging.debug('checkBoxRefraction Setting is {}:'.format(self.checkBoxRefraction.isChecked()))
        self.myMetaData.DTModelEnabled = self.checkBoxDTModel.isChecked()
        self.myMetaData.mode = self.GUIbackendMode
        self.myMetaData.LSREnabled = self.radioButtonLSR.isChecked()
marc's avatar
marc committed
248
249

    def writeMetaData(self,file):
marc's avatar
marc committed
250
251
        myMetaData = self.myMetaData.getMetaData()
        for data in myMetaData:
marc's avatar
marc committed
252
            logging.info('#{:20}: {:40}'.format(data[0],str(data[1])))
marc's avatar
marc committed
253
            file.write('#{:20}: {:40}\n'.format(data[0],str(data[1])))      
marc's avatar
marc committed
254
 
255
256
257
    def doMeasurement(self, measnum, progressSignal=None):
        """ 
        Dumping data into file including meta-data
258
        measProgramme: cli command read from comboBoxProgramma
259
        integrationTime: string read from spinBoxIntTime
260
        measurement: result of measurement of which 
261
        data: is the stdout result which need to be stored line by line
marc's avatar
marc committed
262
        measFile: result file storing the stdout results including meta data
263
        """
264
        self.integrationTime = self.spinBoxIntTime.value()
marc's avatar
marc committed
265
        self.centralFrequency = self.spinBoxCentralFrequency.value()
266
        self.measProgramme = self.comboBoxProgramma.currentText()
marc's avatar
marc committed
267
268
        self.outputDir = self.lineEditOutputDir.text() + '/'
        self.outputFile = 'DT-' + '{:03d}'.format(measnum) + '-' + time.strftime("%Y%m%d") + '.dat'
269
        measFile = open(self.outputDir + self.outputFile,'w')
marc's avatar
marc committed
270
        
271
272
        self.setMetaData()
        self.writeMetaData(measFile)
marc's avatar
marc committed
273
                
274
        logging.info("Output to file: {}".format(measFile))
marc's avatar
marc committed
275
        
marc's avatar
marc committed
276
        self.myMeas.startMeasurement(self.measProgramme, self.centralFrequency, self.integrationTime, measFile)
277

278
279
280
281
        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)
282
            time.sleep(1)
marc's avatar
marc committed
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
        measFile.close()      
    
    def readGUIBackendMode(self):
        '''
        Read the selected Backend mode from the GUI backend checkbox group
        '''
        if self.checkBoxPulsar.isChecked():
            self.GUIbackendMode = 'Pulsar'
        if self.checkBoxHI.isChecked():
            self.GUIbackendMode = 'Hydrogen'
        if self.checkBoxSDR.isChecked():
            self.GUIbackendMode = 'SDR'
        if self.checkBoxRaw.isChecked():
            self.GUIbackendMode = 'Raw'
                    
298
    def startMeasurement(self):
marc's avatar
marc committed
299
300
301
302
303
304
305
        '''
        Put backend in proper mode
        '''
        logging.info('Start Measurement')
        self.readGUIBackendMode()
        self.myBackend.setMode(self.GUIbackendMode)       
        
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
        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):
marc's avatar
marc committed
323
        worker.signals.allfinished.connect(self.allCompleted)
marc's avatar
marc committed
324
        logging.info("Measurement stopped")
325

marc's avatar
marc committed
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
    def viewResultMeasurement(self):
        currentRow = self.tableWidgetPointings.currentRow()
        resultFile = self.tableWidgetPointings.item(currentRow,3).text()
        logging.debug('Selected row is: {}'.format(currentRow))
        logging.info('Show result in gnuplot for file: {}'.format(resultFile))
        '''
        TODO:
        Create a plot file:
        marc@gamma2:~> cat temp.plg 
        set title datafile
        set grid
        plot datafile w l
        pause -1

        and call gnuplot as:
        gnuplot -e "datafile='DT-002-20171029.dat'" temp.plg
        '''
               
344
345
346
347
348
349
350
351
if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWindow = QMainWindow()

    prog = DTObservationProgram(mainWindow)

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