39 Widget to simulate a closed loop
42 widget_ao.py [<parameters_filename>] [options]
44 with 'parameters_filename' the path to the parameters file
47 -h --help Show this help message and exit
48 --cacao Distribute data with cacao
49 --expert Display expert panel
50 -d, --devices devices Specify the devices
51 -i, --interactive keep the script interactive
59 import pyqtgraph
as pg
65 from PyQt5
import QtWidgets
66 from PyQt5.QtCore
import Qt
67 except ModuleNotFoundError
as e:
69 from PySide2
import QtWidgets
70 from PySide2.QtCore
import Qt
71 except ModuleNotFoundError
as e:
72 raise ModuleNotFoundError(
"No module named 'PyQt5' or PySide2', please install one of them\nException raised: "+e.msg)
74 from typing
import Any, Dict, Tuple
76 from docopt
import docopt
77 from collections
import deque
81 AOWindowTemplate, AOClassTemplate = uiLoader(
'widget_ao')
93 def __init__(self, config_file: Any =
None, cacao: bool =
False,
94 expert: bool =
False, devices: str =
None,
95 hide_histograms: bool =
False, twoStages: bool =
False) ->
None:
96 WidgetBase.__init__(self, hide_histograms=hide_histograms)
97 AOClassTemplate.__init__(self)
108 self.
uiAOuiAO.setupUi(self)
118 self.
uiAOuiAO.wao_nbiters.setValue(1000)
132 self.
uiAOuiAO.wao_run.setCheckable(
True)
134 self.
uiAOuiAO.wao_open_loop.setCheckable(
True)
135 self.
uiAOuiAO.wao_open_loop.clicked[bool].connect(self.
aoLoopOpenaoLoopOpen)
136 self.
uiAOuiAO.wao_next.clicked.connect(self.
loop_onceloop_once)
137 self.
uiAOuiAO.wao_resetSR.clicked.connect(self.
resetSRresetSR)
138 self.
uiAOuiAO.wao_resetCoro.clicked.connect(self.
resetCororesetCoro)
145 self.
uiAOuiAO.wao_atmosphere.clicked[bool].connect(self.
enable_atmosenable_atmos)
147 self.
uiAOuiAO.wao_clearSR.clicked.connect(self.
clearSRclearSR)
150 self.
uiAOuiAO.wao_run.setDisabled(
True)
151 self.
uiAOuiAO.wao_next.setDisabled(
True)
152 self.
uiAOuiAO.wao_unzoom.setDisabled(
True)
153 self.
uiAOuiAO.wao_resetSR.setDisabled(
True)
154 self.
uiAOuiAO.wao_resetCoro.setDisabled(
True)
155 self.
uiAOuiAO.wao_allCoro.setDisabled(
True)
157 p1 = self.
uiAOuiAO.wao_SRPlotWindow.addPlot(title=
'SR evolution')
158 self.
curveSRSEcurveSRSE = p1.plot(pen=(255, 0, 0), symbolBrush=(255, 0, 0), name=
"SR SE")
159 self.
curveSRLEcurveSRLE = p1.plot(pen=(0, 0, 255), symbolBrush=(0, 0, 255), name=
"SR LE")
173 self.
uiAOuiAO.wao_SRDock.setVisible(
False)
175 self.addDockWidget(Qt.DockWidgetArea(1), self.
uiBaseuiBase.wao_ConfigDock)
176 self.addDockWidget(Qt.DockWidgetArea(1), self.
uiBaseuiBase.wao_DisplayDock)
177 self.
uiBaseuiBase.wao_ConfigDock.setFloating(
False)
178 self.
uiBaseuiBase.wao_DisplayDock.setFloating(
False)
185 Qt.DockWidgetArea(1), self.
expertWidgetexpertWidget.uiExpert.wao_expertDock)
186 self.
expertWidgetexpertWidget.uiExpert.wao_expertDock.setFloating(
False)
190 if config_file
is not None:
191 self.
uiBaseuiBase.wao_selectConfig.clear()
192 self.
uiBaseuiBase.wao_selectConfig.addItem(config_file)
214 self.
uiAOuiAO.wao_resetSR_tarNum.setDisabled(state)
217 self.
uiAOuiAO.wao_resetCoro_coroNum.setDisabled(state)
220 self.
uiAOuiAO.wao_nbiters.setDisabled(state)
223 self.
supervisorsupervisor.atmos.enable_atmos(atmos)
226 if self.
uiAOuiAO.wao_allTarget.isChecked():
227 for t
in range(len(self.
configconfig.p_targets)):
228 self.
supervisorsupervisor.target.reset_strehl(t)
230 tarnum = self.
uiAOuiAO.wao_resetSR_tarNum.value()
231 print(
"Reset SR on target %d" % tarnum)
232 self.
supervisorsupervisor.target.reset_strehl(tarnum)
236 if self.
uiAOuiAO.wao_allCoro.isChecked():
237 for c
in range(self.
ncoroncoro):
240 coroNum = self.
uiAOuiAO.wao_resetCoro_coroNum.value()
241 print(
"Reset Coro %d" % coroNum)
244 def add_dispDock(self, name: str, parent, type: str =
"pg_image") ->
None:
245 d = WidgetBase.add_dispDock(self, name, parent, type)
247 d.addWidget(self.
uiAOuiAO.wao_Strehl)
249 def load_config(self, *args, config_file=None, supervisor=None, **kwargs) -> None:
251 Callback when 'LOAD' button is hit
252 * required to catch positionals, as by default
253 if a positional is allowed the QPushButton will send a boolean value
254 and hence overwrite supervisor...
257 WidgetBase.load_config(self)
258 for key, pgpl
in self.
SRcirclesSRcircles.items():
259 self.
viewboxesviewboxes[key].removeItem(pgpl)
261 for key, pgpl
in self.
SRCrossXSRCrossX.items():
262 self.
viewboxesviewboxes[key].removeItem(pgpl)
264 for key, pgpl
in self.
SRCrossYSRCrossY.items():
265 self.
viewboxesviewboxes[key].removeItem(pgpl)
267 for key, pgpl
in self.
PyrEdgeXPyrEdgeX.items():
268 self.
viewboxesviewboxes[key].removeItem(pgpl)
270 for key, pgpl
in self.
PyrEdgeYPyrEdgeY.items():
271 self.
viewboxesviewboxes[key].removeItem(pgpl)
273 if config_file
is None:
274 config_file = str(self.
uiBaseuiBase.wao_selectConfig.currentText())
277 if supervisor
is None:
280 self.
configconfig = supervisor.get_config()
283 self.
configconfig.p_loop.set_devices([
284 int(device)
for device
in self.
devicesdevices.
split(
",")
298 for ctrl
in range(self.
nctrlnctrl):
301 self.
natmnatm = len(self.
configconfig.p_atmos.alt)
302 for atm
in range(self.
natmnatm):
303 name =
'atm_%d' % atm
307 for wfs
in range(self.
nwfsnwfs):
308 name =
'wfs_%d' % wfs
310 name =
'slpComp_%d' % wfs
312 name =
'slpGeom_%d' % wfs
314 if self.
configconfig.p_wfss[wfs].type == scons.WFSType.SH:
317 elif self.
configconfig.p_wfss[
318 wfs].type == scons.WFSType.PYRHR
or self.
configconfig.p_wfss[
319 wfs].type == scons.WFSType.PYRLR:
320 name =
'pyrFocalPlane_%d' % wfs
322 name =
'pyrHR_%d' % wfs
324 name =
'pyrLR_%d' % wfs
327 raise "Analyser unknown"
329 self.
ndmndm = len(self.
configconfig.p_dms)
330 for dm
in range(self.
ndmndm):
334 self.
ntarntar = len(self.
configconfig.p_targets)
335 for tar
in range(self.
ntarntar):
336 name =
'tar_%d' % tar
338 for tar
in range(self.
ntarntar):
339 name =
'psfSE_%d' % tar
341 for tar
in range(self.
ntarntar):
342 name =
'psfLE_%d' % tar
344 if(self.
configconfig.p_coronos)
is not None:
348 for coro
in range(self.
ncoroncoro):
349 name =
'coroImageLE_%d' % coro
351 for coro
in range(self.
ncoroncoro):
352 name =
'coroImageSE_%d' % coro
354 for coro
in range(self.
ncoroncoro):
355 name =
'coroPSFLE_%d' % coro
357 for coro
in range(self.
ncoroncoro):
358 name =
'coroPSFSE_%d' % coro
362 for coro
in range(self.
ncoroncoro):
366 self.
uiAOuiAO.wao_resetSR_tarNum.setValue(0)
367 self.
uiAOuiAO.wao_resetSR_tarNum.setMaximum(len(self.
configconfig.p_targets) - 1)
369 self.
uiAOuiAO.wao_resetCoro_coroNum.setValue(0)
370 self.
uiAOuiAO.wao_resetCoro_coroNum.setMaximum(self.
ncoroncoro - 1)
372 self.
uiAOuiAO.wao_dispSR_tar.setValue(0)
373 self.
uiAOuiAO.wao_dispSR_tar.setMaximum(len(self.
configconfig.p_targets) - 1)
375 self.
uiAOuiAO.wao_run.setDisabled(
True)
376 self.
uiAOuiAO.wao_next.setDisabled(
True)
377 self.
uiAOuiAO.wao_unzoom.setDisabled(
True)
378 self.
uiAOuiAO.wao_resetSR.setDisabled(
True)
379 self.
uiAOuiAO.wao_resetCoro.setDisabled(
True)
380 self.
uiAOuiAO.wao_allCoro.setDisabled(
True)
382 self.
uiBaseuiBase.wao_init.setDisabled(
False)
388 if (hasattr(self.
configconfig._config,
"layout")):
390 self.
loadArealoadArea(filename=area_filename)
396 self.
stopstop =
False
398 self.
nbiternbiter = self.
uiAOuiAO.wao_nbiters.value()
400 if self.
uiAOuiAO.wao_forever.isChecked():
401 print(
"LOOP STARTED")
403 print(
"LOOP STARTED FOR %d iterations" % self.
nbiternbiter)
411 self.
uiAOuiAO.wao_open_loop.setText(
"Open Loop")
414 self.
uiAOuiAO.wao_open_loop.setText(
"Close Loop")
422 WidgetBase.init_config(self)
425 self.
uiAOuiAO.wao_deviceNumber.setDisabled(
True)
431 for i
in range(self.
natmnatm):
433 data = self.
supervisorsupervisor.atmos.get_atmos_layer(i)
435 data.shape[0], data.shape[1])
436 self.
SRcirclesSRcircles[key] = pg.ScatterPlotItem(cx, cy, pen=
'r', size=1)
438 self.
SRcirclesSRcircles[key].setData(cx, cy)
440 for i
in range(self.
nwfsnwfs):
442 data = self.
supervisorsupervisor.wfs.get_wfs_phase(i)
444 data.shape[0], data.shape[1])
445 self.
SRcirclesSRcircles[key] = pg.ScatterPlotItem(cx, cy, pen=
'r', size=1)
447 self.
SRcirclesSRcircles[key].setData(cx, cy)
448 key =
'slpComp_%d' % i
449 key =
'slpGeom_%d' % i
456 for i
in range(self.
ndmndm):
458 dm_type = self.
configconfig.p_dms[i].type
459 alt = self.
configconfig.p_dms[i].alt
460 data = self.
supervisorsupervisor.dms.get_dm_shape(i)
462 data.shape[0], data.shape[1])
463 self.
SRcirclesSRcircles[key] = pg.ScatterPlotItem(cx, cy, pen=
'r', size=1)
465 self.
SRcirclesSRcircles[key].setData(cx, cy)
467 for i
in range(len(self.
configconfig.p_targets)):
469 data = self.
supervisorsupervisor.target.get_tar_phase(i)
471 data.shape[0], data.shape[1])
472 self.
SRcirclesSRcircles[key] = pg.ScatterPlotItem(cx, cy, pen=
'r', size=1)
474 self.
SRcirclesSRcircles[key].setData(cx, cy)
476 data = self.
supervisorsupervisor.target.get_tar_image(i)
477 for psf
in [
"psfSE_",
"psfLE_"]:
480 self.
SRCrossXSRCrossX[key] = pg.PlotCurveItem(
482 data.shape[0] / 2 + 0.5 - Delta,
483 data.shape[0] / 2 + 0.5 + Delta
484 ]), np.array([data.shape[1] / 2 + 0.5, data.shape[1] / 2 + 0.5]),
486 self.
SRCrossYSRCrossY[key] = pg.PlotCurveItem(
487 np.array([data.shape[0] / 2 + 0.5, data.shape[0] / 2 + 0.5]),
489 data.shape[1] / 2 + 0.5 - Delta,
490 data.shape[1] / 2 + 0.5 + Delta
498 for i
in range(self.
ncoroncoro):
499 data = self.
supervisorsupervisor.corono.get_image(i, expo_type=
"se")
500 for psf
in [
"coroImageSE_",
"coroImageLE_",
"coroPSFSE_",
"coroPSFLE_"]:
507 self.
SRCrossXSRCrossX[key] = pg.PlotCurveItem(
509 data.shape[0] / 2 + center - Delta,
510 data.shape[0] / 2 + center + Delta
511 ]), np.array([data.shape[1] / 2 + center, data.shape[1] / 2 + center]),
513 self.
SRCrossYSRCrossY[key] = pg.PlotCurveItem(
514 np.array([data.shape[0] / 2 + center, data.shape[0] / 2 + center]),
516 data.shape[1] / 2 + center - Delta,
517 data.shape[1] / 2 + center + Delta
526 for i
in range(len(self.
configconfig.p_wfss)):
527 if (self.
configconfig.p_wfss[i].type == scons.WFSType.PYRHR
or
528 self.
configconfig.p_wfss[i].type == scons.WFSType.PYRLR):
529 key =
"pyrFocalPlane_%d" % i
530 data = self.
supervisorsupervisor.wfs.get_pyr_focal_plane(i)
531 Delta = len(data) / 2
532 self.
PyrEdgeXPyrEdgeX[key] = pg.PlotCurveItem(
534 data.shape[0] / 2 + 0.5 - Delta,
535 data.shape[0] / 2 + 0.5 + Delta
536 ]), np.array([data.shape[1] / 2 + 0.5, data.shape[1] / 2 + 0.5]),
538 self.
PyrEdgeYPyrEdgeY[key] = pg.PlotCurveItem(
539 np.array([data.shape[0] / 2 + 0.5, data.shape[0] / 2 + 0.5]),
541 data.shape[1] / 2 + 0.5 - Delta,
542 data.shape[1] / 2 + 0.5 + Delta
556 self.
uiAOuiAO.wao_run.setDisabled(
False)
557 self.
uiAOuiAO.wao_next.setDisabled(
False)
558 self.
uiAOuiAO.wao_open_loop.setDisabled(
False)
559 self.
uiAOuiAO.wao_unzoom.setDisabled(
False)
560 self.
uiAOuiAO.wao_resetSR.setDisabled(
False)
562 self.
uiAOuiAO.wao_resetCoro.setDisabled(
False)
563 self.
uiAOuiAO.wao_allCoro.setDisabled(
False)
565 WidgetBase.init_configFinished(self)
568 datashape1: int) -> Tuple[float, float]:
569 cx = ampli * np.sin((np.arange(npts) + 1) * 2. * np.pi / npts) + datashape0 / 2
570 cy = ampli * np.cos((np.arange(npts) + 1) * 2. * np.pi / npts) + datashape1 / 2
574 self.
SRLESRLE = deque(maxlen=20)
575 self.
SRSESRSE = deque(maxlen=20)
576 self.
numiternumiter = deque(maxlen=20)
579 self.
SRLESRLE.append(SRLE)
580 self.
SRSESRSE.append(SRSE)
581 self.
numiternumiter.append(numiter)
589 if not self.
loopLockloopLock.acquire(
False):
593 for key, dock
in self.
docksdocks.items():
594 if key
in [
"Strehl"]:
596 elif dock.isVisible():
597 index = int(key.split(
"_")[-1])
600 data = self.
supervisorsupervisor.atmos.get_atmos_layer(index)
602 data = self.
supervisorsupervisor.wfs.get_wfs_phase(index)
604 dm_type = self.
configconfig.p_dms[index].type
605 alt = self.
configconfig.p_dms[index].alt
606 data = self.
supervisorsupervisor.dms.get_dm_shape(index)
608 data = self.
supervisorsupervisor.target.get_tar_phase(index)
610 data = self.
supervisorsupervisor.target.get_tar_image(
611 index, expo_type=
"le")
613 data = self.
supervisorsupervisor.target.get_tar_image(
614 index, expo_type=
"se")
615 if "coroImageLE" in key:
616 data = self.
supervisorsupervisor.corono.get_image(index, expo_type=
"le")
617 if "coroImageSE" in key:
618 data = self.
supervisorsupervisor.corono.get_image(index, expo_type=
"se")
619 if "coroPSFLE" in key:
620 data = self.
supervisorsupervisor.corono.get_psf(index, expo_type=
"le")
621 if "coroPSFSE" in key:
622 data = self.
supervisorsupervisor.corono.get_psf(index, expo_type=
"se")
624 if "psf" in key
or "coro" in key:
625 if (self.
uiAOuiAO.actionPSF_Log_Scale.isChecked()):
626 if np.any(data <= 0):
628 data[data <= 0] = np.min(data[data > 0])
629 data = np.log10(data)
630 if (self.
supervisorsupervisor.get_frame_counter() < 10):
632 xRange=(data.shape[0] / 2 + 0.5 - self.
PSFzoomPSFzoom,
633 data.shape[0] / 2 + 0.5 + self.
PSFzoomPSFzoom),
634 yRange=(data.shape[1] / 2 + 0.5 - self.
PSFzoomPSFzoom,
635 data.shape[1] / 2 + 0.5 + self.
PSFzoomPSFzoom),
639 data = self.
supervisorsupervisor.wfs.get_wfs_image(index)
641 data = self.
supervisorsupervisor.wfs.get_wfs_image(index)
643 data = self.
supervisorsupervisor.wfs.get_pyrhr_image(index)
644 if "pyrFocalPlane" in key:
645 data = self.
supervisorsupervisor.wfs.get_pyr_focal_plane(index)
647 if (data
is not None):
652 self.
imgsimgs[key].setImage(data, autoLevels=autoscale)
655 self.
imgsimgs[key].canvas.axes.clear()
657 slopes = self.
supervisorsupervisor.rtc.get_slopes_geom()
659 slopes, self.
configconfig.p_wfss[index].nxsub,
660 self.
configconfig.p_tel.cobs, returnquiver=
True
663 centroids = self.
supervisorsupervisor.rtc.get_slopes(index)
665 2 * p_wfs._nvalid
for p_wfs
in self.
configconfig.p_wfss
667 first_ind = np.sum(nmes[:index], dtype=np.int32)
668 if (self.
configconfig.p_wfss[index].type == scons.WFSType.PYRHR
669 or self.
configconfig.p_wfss[index].type == scons.
673 centroids[first_ind:first_ind + nmes[index]],
675 self.
configconfig.p_wfss[index]._validsubsx,
676 self.
configconfig.p_wfss[index]._validsubsy
680 centroids[first_ind:first_ind + nmes[index]],
681 self.
configconfig.p_wfss[index].nxsub,
682 self.
configconfig.p_tel.cobs, returnquiver=
True
684 self.
imgsimgs[key].canvas.axes.quiver(x, y, vx, vy)
685 self.
imgsimgs[key].canvas.draw()
686 elif "modalGains" in key:
687 data = self.
supervisorsupervisor.rtc.get_modal_gains(index)
688 self.
imgsimgs[key].canvas.axes.clear()
689 self.
imgsimgs[key].canvas.axes.plot(data)
690 self.
imgsimgs[key].canvas.draw()
691 elif "ContrastLE" in key:
692 distances, mean, std, mini, maxi = self.
supervisorsupervisor.corono.get_contrast(index, expo_type=
'le')
694 self.
imgsimgs[key].canvas.axes.clear()
695 self.
imgsimgs[key].canvas.axes.plot(distances, mean)
696 self.
imgsimgs[key].canvas.axes.set_yscale(
'log')
697 self.
imgsimgs[key].canvas.axes.set_xlabel(
"angular distance (Lambda/D)")
698 self.
imgsimgs[key].canvas.axes.set_ylabel(
"Raw contrast")
700 self.
imgsimgs[key].canvas.axes.grid()
701 self.
imgsimgs[key].canvas.draw()
702 elif "ContrastSE" in key:
703 distances, mean, std, mini, maxi = self.
supervisorsupervisor.corono.get_contrast(index, expo_type=
'se')
705 self.
imgsimgs[key].canvas.axes.clear()
706 self.
imgsimgs[key].canvas.axes.plot(distances, mean)
707 self.
imgsimgs[key].canvas.axes.set_yscale(
'log')
708 self.
imgsimgs[key].canvas.axes.set_xlabel(
"angular distance (Lambda/D)")
709 self.
imgsimgs[key].canvas.axes.set_ylabel(
"Raw contrast")
710 self.
imgsimgs[key].canvas.axes.grid()
711 self.
imgsimgs[key].canvas.draw()
718 self.
uiAOuiAO.wao_strehlSE.setText(SRSE)
721 self.
uiAOuiAO.wao_strehlLE.setText(SRLE)
724 self.
uiAOuiAO.wao_currentFreq.setValue(freq)
727 if not self.
loopLockloopLock.acquire(
False):
728 print(
"Display locked")
734 for t
in range(len(self.
supervisorsupervisor.config.p_targets)):
735 self.
supervisorsupervisor.target.comp_tar_image(t)
736 loopTime = time.time() - start
738 refreshDisplayTime = 1. / self.
uiBaseuiBase.wao_frameRate.value()
740 if (time.time() - self.
refreshTimerefreshTime > refreshDisplayTime):
743 for t
in range(len(self.
configconfig.p_targets)):
744 SR = self.
supervisorsupervisor.target.get_strehl(t)
746 if (t == self.
uiAOuiAO.wao_dispSR_tar.value()
749 self.
supervisorsupervisor.get_frame_counter())
750 signal_se +=
"%1.2f " % SR[0]
751 signal_le +=
"%1.2f " % SR[1]
753 currentFreq = 1 / loopTime
754 refreshFreq = 1 / (time.time() - self.
refreshTimerefreshTime)
762 "iter #%d SR: (L.E, S.E.)= (%s, %s) running at %4.1fHz (real %4.1fHz)"
763 % (self.
supervisorsupervisor.get_frame_counter(), signal_le,
764 signal_se, refreshFreq, currentFreq))
767 except Exception
as e:
776 if not self.
uiAOuiAO.wao_forever.isChecked():
779 if self.
nbiternbiter <= 0:
781 self.
uiAOuiAO.wao_run.setChecked(
False)
788 display_env = os.environ[
'DISPLAY']
791 display_host, display_num = display_env.split(
':')
792 display_num_major = display_num.split(
'.')[0]
795 display_port = 6000 + int(display_num_major)
798 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
800 sock.connect((display_host, display_port))
807 if __name__ ==
'__main__':
811 arguments = docopt(__doc__)
812 app = QtWidgets.QApplication(sys.argv)
813 app.setStyle(
'cleanlooks')
814 wao =
widgetAOWindow(arguments[
"<parameters_filename>"], cacao=arguments[
"--cacao"],
815 expert=arguments[
"--expert"], devices=arguments[
"--devices"])
819 print(
"If the GUI is black, you can:")
820 print(
" type %gui qt5 to unlock GUI")
821 print(
" or launch ipython with the option '--gui=qt' or '--matplotlib=qt'")
822 print(
" or edit ~/.ipython/profile_default/ipython_config.py to set c.TerminalIPythonApp.matplotlib = 'qt'")
824 if arguments[
"--interactive"]:
826 embed(os.path.basename(__file__), locals())
Shesha parameters configuration class.
This class implements generic supervisor to handle compass simulation.
void split(std::vector< std::string > &tokens, const std::string &text, char sep)
Parameter classes for COMPASS.
Initialization and execution of a COMPASS supervisor.
Initialization and execution of a single stage supervisor for cascaded AO systems.