From cf8a39ed0ad83c3b94a3194debc24c9e44d23ed6 Mon Sep 17 00:00:00 2001 From: Jan David Mol <mol@astron.nl> Date: Wed, 2 Aug 2023 09:06:17 +0000 Subject: [PATCH] L2SS-1465: Add digital pointing to BST files, and move subband selection from header to BST matrix --- README.md | 1 + VERSION | 2 +- .../statistics/statistics_data.py | 8 +- .../statistics/writer/hdf5.py | 29 ++++--- tests/statistics/test_writer.py | 81 +++++++++++-------- tests/test_devices.py | 24 +++++- 6 files changed, 97 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 442afc5..7218d99 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ tox -e debug tests.requests.test_prometheus ``` ## Releasenotes +- 0.15.3 - BSTs: added digital pointing direction, and subband selection - 0.15.2 - Statistics filenames: added antennafield, moved subband to the end - 0.15.1 - Infer device names and default port from the specified antenna-field name - 0.15.0 - Add all_connected method to MultiStationObservation diff --git a/VERSION b/VERSION index 4312e0d..1985d91 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.15.2 +0.15.3 diff --git a/lofar_station_client/statistics/statistics_data.py b/lofar_station_client/statistics/statistics_data.py index aee8de5..80b15c2 100644 --- a/lofar_station_client/statistics/statistics_data.py +++ b/lofar_station_client/statistics/statistics_data.py @@ -138,6 +138,12 @@ class StatisticsData(ndarray): tile_beam_tracking_enabled: bool = attribute(optional=True) """ Whether the tile beam is tracking """ + digital_beam_pointing_direction: str = attribute(optional=True) + """ Direction of the digital beam """ + + digital_beam_tracking_enabled: bool = attribute(optional=True) + """ Whether the digital beam is tracking """ + hbat_pwr_on: str = attribute(optional=True) """ Elements per hba tile """ @@ -203,8 +209,6 @@ class StatisticsFileHeader: frequency_band: str = attribute(optional=True) """ filter selection """ - subbands: int = attribute(optional=True) - fpga_firmware_version: str = attribute(optional=True) fpga_hardware_version: str = attribute(optional=True) diff --git a/lofar_station_client/statistics/writer/hdf5.py b/lofar_station_client/statistics/writer/hdf5.py index 21c1137..ff609b5 100644 --- a/lofar_station_client/statistics/writer/hdf5.py +++ b/lofar_station_client/statistics/writer/hdf5.py @@ -246,14 +246,6 @@ class HDF5Writer(ABC): except DevFailed: logger.exception("Failed to read from %s", self.sdp_device.name()) - if self.digitalbeam_device: - try: - self.file.subbands = self.digitalbeam_device.subband_select_RW - except DevFailed: - logger.exception( - "Failed to read from %s", self.digitalbeam_device.name() - ) - def hdf5_matrix_header(self, statistics_packet_header: dict) -> dict: """Returns the header fields per statistics matrix.""" @@ -442,7 +434,7 @@ class HDF5Writer(ABC): if self.tilebeam_device: try: matrix.tile_beam_pointing_direction = ( - self.tilebeam_device.Pointing_direction_str + self.tilebeam_device.Pointing_direction_str_R ) matrix.tile_beam_tracking_enabled = ( self.tilebeam_device.Tracking_enabled_R @@ -588,6 +580,25 @@ class BstHdf5Writer(HDF5Writer): def new_collector(self): return BSTCollector() + def hdf5_matrix_header(self, statistics_packet_header: dict) -> dict: + header = super().hdf5_matrix_header(statistics_packet_header) + + if self.digitalbeam_device: + try: + header[ + "digital_beam_pointing_direction" + ] = self.digitalbeam_device.Pointing_direction_str_R + header[ + "digital_beam_tracking_enabled" + ] = self.digitalbeam_device.Tracking_enabled_R + header["subbands"] = self.digitalbeam_device.subband_select_RW + except DevFailed: + logger.exception( + "Failed to read from %s", self.digitalbeam_device.name() + ) + + return header + def get_values_matrix(self) -> StatisticsData: return ( self.current_matrix.parameters["bst_values"] diff --git a/tests/statistics/test_writer.py b/tests/statistics/test_writer.py index b20654e..308fffd 100644 --- a/tests/statistics/test_writer.py +++ b/tests/statistics/test_writer.py @@ -20,6 +20,7 @@ from tests.test_devices import ( FakeAntennaFieldDeviceProxy, FakeOffAntennaFieldDeviceProxy, FakeDigitalBeamDeviceProxy, + FakeTileBeamDeviceProxy, FakeSDPDeviceProxy, FakeStationManagerDeviceProxy, ) @@ -31,13 +32,15 @@ class TestStatisticsReaderWriter(base.TestCase): def _mock_get_tango_device(self, tango_disabled, host, device_name): """Return our mocked DeviceProxies""" - if device_name == "STAT/AntennaField/LBA": + if device_name == "STAT/AntennaField/HBA": return FakeAntennaFieldDeviceProxy(device_name) - if device_name == "STAT/DigitalBeam/LBA": + if device_name == "STAT/DigitalBeam/HBA": return FakeDigitalBeamDeviceProxy(device_name) + if device_name == "STAT/TileBeam/HBA": + return FakeTileBeamDeviceProxy(device_name) if device_name == "STAT/StationManager/1": return FakeStationManagerDeviceProxy - if device_name == "STAT/SDP/LBA": + if device_name == "STAT/SDP/HBA": return FakeSDPDeviceProxy(device_name) raise ValueError( f"Device not mocked, and thus not available in this test: {device_name}" @@ -45,13 +48,15 @@ class TestStatisticsReaderWriter(base.TestCase): def _mock_get_tango_device_off(self, tango_disabled, host, device_name): """Return our mocked DeviceProxies that simulate a device that is off""" - if device_name == "STAT/AntennaField/LBA": + if device_name == "STAT/AntennaField/HBA": return FakeOffAntennaFieldDeviceProxy(device_name) - if device_name == "STAT/DigitalBeam/LBA": + if device_name == "STAT/DigitalBeam/HBA": return FakeDigitalBeamDeviceProxy(device_name) + if device_name == "STAT/TileBeam/HBA": + return FakeTileBeamDeviceProxy(device_name) if device_name == "STAT/StationManager/1": return FakeStationManagerDeviceProxy - if device_name == "STAT/SDP/LBA": + if device_name == "STAT/SDP/HBA": return FakeSDPDeviceProxy(device_name) raise ValueError( f"Device not mocked, and thus not available in this test: {device_name}" @@ -62,7 +67,7 @@ class TestStatisticsReaderWriterSST(TestStatisticsReaderWriter): """TestStatistics class for SST-mode""" def _run_writer_reader( - self, tmpdir: str, writer_argv: list, antennafield: str = "LBA" + self, tmpdir: str, writer_argv: list, antennafield: str = "HBA" ) -> Tuple[StatisticsData, StatisticsFileHeader]: """Run the statistics writer with the given arguments, and read and return the output.""" @@ -77,6 +82,9 @@ class TestStatisticsReaderWriterSST(TestStatisticsReaderWriter): tmpdir, ] + if antennafield != "unknown": + default_writer_sys_argv += ["--antennafield", antennafield] + with mock.patch.object( entry.sys, "argv", default_writer_sys_argv + writer_argv ): @@ -122,10 +130,7 @@ class TestStatisticsReaderWriterSST(TestStatisticsReaderWriter): def test_insert_tango_SST_statistics(self): with TemporaryDirectory() as tmpdir: - writer_argv = [ - "--antennafield", - "LBA", - ] + writer_argv = [] with mock.patch.object( entry, "_get_tango_device", self._mock_get_tango_device @@ -145,15 +150,11 @@ class TestStatisticsReaderWriterSST(TestStatisticsReaderWriter): "--no-tango", ] - _ = self._run_writer_reader(tmpdir, writer_argv, antennafield="unknown") + _ = self._run_writer_reader(tmpdir, writer_argv, "unknown") def test_SST_statistics_with_device_in_off(self): with TemporaryDirectory() as tmpdir: - writer_argv = [ - "--no-tango", - "--antennafield", - "LBA", - ] + writer_argv = [] with mock.patch.object( entry, "_get_tango_device", self._mock_get_tango_device_off @@ -185,6 +186,8 @@ class TestStatisticsReaderWriterBST(TestStatisticsReaderWriter): sys.argv[0], "--mode", "BST", + "--antennafield", + "HBA", "--file", dirname(__file__) + "/SDP_BST_statistics_packets.bin", "--output_dir", @@ -198,13 +201,13 @@ class TestStatisticsReaderWriterBST(TestStatisticsReaderWriter): entry.main() # check if file was written - self.assertTrue(isfile(f"{tmpdir}/BST_2022-05-20-11-08-44_LBA.h5")) + self.assertTrue(isfile(f"{tmpdir}/BST_2022-05-20-11-08-44_HBA.h5")) # default arguments for statistics reader default_reader_sys_argv = [ sys.argv[0], "--files", - f"{tmpdir}/BST_2022-05-20-11-08-44_LBA.h5", + f"{tmpdir}/BST_2022-05-20-11-08-44_HBA.h5", "--start_time", "2021-09-20#07:40:08.937+00:00", "--end_time", @@ -222,18 +225,22 @@ class TestStatisticsReaderWriterBST(TestStatisticsReaderWriter): def test_insert_tango_BST_statistics(self): with TemporaryDirectory() as tmpdir: - writer_argv = [ - "--antennafield", - "LBA", - ] + writer_argv = [] with mock.patch.object( entry, "_get_tango_device", self._mock_get_tango_device ): - file_header = self._run_writer_reader(tmpdir, writer_argv) + _ = self._run_writer_reader(tmpdir, writer_argv) - # Test some AntennField attributes, whether they match our mock - self.assertListEqual(list(range(0, 488)), file_header.subbands.tolist()) + # validate HDF5 content + with h5py.File(f"{tmpdir}/BST_2022-05-20-11-08-44_HBA.h5") as f: + # validate header + self.assertIn("station_version", dict(f.attrs)) + self.assertIn("writer_version", dict(f.attrs)) + self.assertIn("mode", dict(f.attrs)) + + # check for the datasets present in our input data + self.assertIn("BST_2022-05-20T11:08:44.999+00:00", dict(f.items())) def test_bst(self): with mock.patch.object(entry, "_get_tango_device", self._mock_get_tango_device): @@ -242,6 +249,8 @@ class TestStatisticsReaderWriterBST(TestStatisticsReaderWriter): sys.argv[0], "--mode", "BST", + "--antennafield", + "HBA", "--file", dirname(__file__) + "/SDP_BST_statistics_packets.bin", "--output_dir", @@ -252,7 +261,7 @@ class TestStatisticsReaderWriterBST(TestStatisticsReaderWriter): entry.main() # check if file was written - self.assertTrue(isfile(f"{tmpdir}/BST_2022-05-20-11-08-44_LBA.h5")) + self.assertTrue(isfile(f"{tmpdir}/BST_2022-05-20-11-08-44_HBA.h5")) class TestStatisticsWriterXST(TestStatisticsReaderWriter): @@ -265,6 +274,8 @@ class TestStatisticsWriterXST(TestStatisticsReaderWriter): sys.argv[0], "--mode", "XST", + "--antennafield", + "HBA", "--file", dirname(__file__) + "/SDP_XST_statistics_packets.bin", "--output_dir", @@ -276,11 +287,11 @@ class TestStatisticsWriterXST(TestStatisticsReaderWriter): # check if file was written self.assertTrue( - isfile(f"{tmpdir}/XST_2021-09-13-13-21-32_LBA_SB102.h5") + isfile(f"{tmpdir}/XST_2021-09-13-13-21-32_HBA_SB102.h5") ) # validate HDF5 content - with h5py.File(f"{tmpdir}/XST_2021-09-13-13-21-32_LBA_SB102.h5") as f: + with h5py.File(f"{tmpdir}/XST_2021-09-13-13-21-32_HBA_SB102.h5") as f: # validate header self.assertIn("station_version", dict(f.attrs)) self.assertIn("writer_version", dict(f.attrs)) @@ -315,6 +326,8 @@ class TestStatisticsWriterXST(TestStatisticsReaderWriter): sys.argv[0], "--mode", "XST", + "--antennafield", + "HBA", "--file", dirname(__file__) + "/SDP_XST_statistics_packets_multiple_subbands.bin", @@ -327,10 +340,10 @@ class TestStatisticsWriterXST(TestStatisticsReaderWriter): # check if files were written self.assertTrue( - isfile(f"{tmpdir}/XST_2021-09-13-13-21-32_LBA_SB102.h5") + isfile(f"{tmpdir}/XST_2021-09-13-13-21-32_HBA_SB102.h5") ) self.assertTrue( - isfile(f"{tmpdir}/XST_2021-09-13-13-21-32_LBA_SB103.h5") + isfile(f"{tmpdir}/XST_2021-09-13-13-21-32_HBA_SB103.h5") ) def test_xst_with_antennafield(self): @@ -343,7 +356,7 @@ class TestStatisticsWriterXST(TestStatisticsReaderWriter): "--mode", "XST", "--antennafield", - "LBA", + "HBA", "--file", dirname(__file__) + "/SDP_XST_statistics_packets.bin", "--output_dir", @@ -355,11 +368,11 @@ class TestStatisticsWriterXST(TestStatisticsReaderWriter): # check if file was written self.assertTrue( - isfile(f"{tmpdir}/XST_2021-09-13-13-21-32_LBA_SB102.h5") + isfile(f"{tmpdir}/XST_2021-09-13-13-21-32_HBA_SB102.h5") ) # validate HDF5 content - with h5py.File(f"{tmpdir}/XST_2021-09-13-13-21-32_LBA_SB102.h5") as f: + with h5py.File(f"{tmpdir}/XST_2021-09-13-13-21-32_HBA_SB102.h5") as f: # check extra header fields provided by the AntennaField self.assertIn("antenna_names", dict(f.attrs)) self.assertIn("antenna_quality", dict(f.attrs)) diff --git a/tests/test_devices.py b/tests/test_devices.py index e11cc9b..fc75572 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -288,9 +288,9 @@ class FakeAntennaFieldDeviceProxy: Antenna_Names_R = ["Aap", "Noot", "Mies"] Antenna_Reference_ITRF_R = [[0, 0, 0]] * nr_antennas_R Antenna_Quality_str_R = ["OK"] * nr_antennas_R - Antenna_Type_R = "LBA" + Antenna_Type_R = "HBA" Antenna_Usage_Mask_R = [True] * nr_antennas_R - Frequency_Band_RW = ["LBA_10_90"] * nr_antennas_R + Frequency_Band_RW = ["HBA_110_190"] * nr_antennas_R RCU_attenuator_dB_R = [0, 1, 2] RCU_band_select_R = [1] * nr_antennas_R RCU_DTH_on_R = [False] * nr_antennas_R @@ -348,12 +348,32 @@ class FakeSDPDeviceProxy: return getattr(self, attrname) +class FakeTileBeamDeviceProxy: + """DeviceProxy that mocks access to a TileBeam device.""" + + N_tiles = 48 + + Pointing_direction_str_R = ["J2000 (0deg, 0deg)"] * N_tiles + Tracking_enabled_R = [True] * N_tiles + + def __init__(self, name): + self._name = name + + def name(self): + return self._name + + def __getattr__(self, attrname): + return getattr(self, attrname) + + class FakeDigitalBeamDeviceProxy: """DeviceProxy that mocks access to an DigitalBeam device.""" N_beamlets_ctrl = 488 subband_select_RW = list(range(0, N_beamlets_ctrl)) + Pointing_direction_str_R = ["J2000 (0deg, 0deg)"] * N_beamlets_ctrl + Tracking_enabled_R = [True] * N_beamlets_ctrl def __init__(self, name): self._name = name -- GitLab