diff --git a/docker-compose/tango-prometheus-exporter/lofar2-policy.json b/docker-compose/tango-prometheus-exporter/lofar2-policy.json index 01d574dc8517357935174f080d4df1fcd030271d..313d9a05c9f170d9bbf1e01216d02715d5b3548c 100644 --- a/docker-compose/tango-prometheus-exporter/lofar2-policy.json +++ b/docker-compose/tango-prometheus-exporter/lofar2-policy.json @@ -92,6 +92,9 @@ ] }, "stat/sdpfirmware/*": { + "exclude": [ + "FPGA_scrap_*" + ], "include": [ "TR_fpga_mask_RW" ] @@ -101,7 +104,6 @@ "FPGA_subband_weights_*", "FPGA_signal_input_samples_delay_*", "FPGA_jesd204b_*", - "FPGA_scrap_*", "FPGA_wg_amplitude_*", "FPGA_wg_frequency_*", "FPGA_wg_phase_*", diff --git a/tangostationcontrol/integration_test/default/devices/test_device_antennafield.py b/tangostationcontrol/integration_test/default/devices/test_device_antennafield.py index 1dd6c9e23a1d1bf62f26443f516909d7932ba734..74a59f50724330cce2ac45d3cc14bbbaebc7559e 100644 --- a/tangostationcontrol/integration_test/default/devices/test_device_antennafield.py +++ b/tangostationcontrol/integration_test/default/devices/test_device_antennafield.py @@ -43,7 +43,7 @@ class TestAntennaFieldDevice(AbstractTestBases.TestDeviceBase): # configure the frequencies, which allows access # to the calibration attributes and commands - self.sdp_proxy.clock_RW = CLK_200_MHZ + self.sdpfirmware_proxy.clock_RW = CLK_200_MHZ self.recv_proxy.RCU_band_select_RW = [[1] * N_rcu_inp] * N_rcu def restore_antennafield(self): @@ -369,7 +369,7 @@ class TestAntennaFieldDevice(AbstractTestBases.TestDeviceBase): # test whether clock propagated correctly numpy.testing.assert_equal( - band.clock, self.sdp_proxy.clock_RW, err_msg=f"{band.name}" + band.clock, self.sdpfirmware_proxy.clock_RW, err_msg=f"{band.name}" ) # test whether nyquist zone propagated correctly (for both polarisations!) diff --git a/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py b/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py index 1eecddd87c21c609726862679ee8f7cc1593a73e..213e6a2c6d9c063947d815158b247e7c7920e760 100644 --- a/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py +++ b/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py @@ -27,6 +27,7 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): def test_device_read_all_attributes(self): # We need to connect to SDP first to read some of our attributes self.sdp_proxy = self.setup_sdp() + self.sdpfirmware_proxy = self.setup_sdpfirmware() super().test_device_read_all_attributes() @@ -40,13 +41,25 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): # setup the frequencies as expected in the test sdp_proxy.antenna_type_RW = [[antenna_type] * S_pn] * N_pn sdp_proxy.nyquist_zone_RW = [[2] * S_pn] * N_pn - sdp_proxy.clock_RW = clock return sdp_proxy + def setup_sdpfirmware(self, antenna_type="HBA", clock=CLK_200_MHZ): + # setup SDP, on which this device depends + sdpfirmware_proxy = TestDeviceProxy("STAT/SDPFirmware/HBA") + sdpfirmware_proxy.off() + sdpfirmware_proxy.boot() + sdpfirmware_proxy.set_defaults() + + # setup the frequencies as expected in the test + sdpfirmware_proxy.clock_RW = clock + + return sdpfirmware_proxy + def test_pointing_to_zenith(self): # Setup configuration sdp_proxy = self.setup_sdp() + sdpfirmware_proxy = self.setup_sdpfirmware() self.proxy.initialise() self.proxy.on() @@ -70,6 +83,7 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): def test_subband_select_change(self): # Setup configuration sdp_proxy = self.setup_sdp() + sdpfirmware_proxy = self.setup_sdpfirmware() # Change subband self.proxy.off() @@ -97,6 +111,7 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): def test_sdp_clock_change(self): # Setup configuration sdp_proxy = self.setup_sdp() + sdpfirmware_proxy = self.setup_sdpfirmware() self.proxy.initialise() self.proxy.subband_select_RW = numpy.array( @@ -108,15 +123,15 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): # any non-zero delay should result in different weights for different clocks delays = numpy.array([[[2.5e-9] * N_pn] * A_pn] * N_beamlets_ctrl) - sdp_proxy.clock_RW = CLK_200_MHZ + sdpfirmware_proxy.clock_RW = CLK_200_MHZ time.sleep(1) # wait for beamlet device to process change event calculated_bf_weights_200 = self.proxy.calculate_bf_weights(delays.flatten()) - sdp_proxy.clock_RW = CLK_160_MHZ + sdpfirmware_proxy.clock_RW = CLK_160_MHZ time.sleep(1) # wait for beamlet device to process change event calculated_bf_weights_160 = self.proxy.calculate_bf_weights(delays.flatten()) - sdp_proxy.clock_RW = CLK_200_MHZ + sdpfirmware_proxy.clock_RW = CLK_200_MHZ time.sleep(1) # wait for beamlet device to process change event calculated_bf_weights_200_v2 = self.proxy.calculate_bf_weights(delays.flatten()) @@ -136,7 +151,7 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): (calculated_bf_weights_200_v2 != calculated_bf_weights_200_v3).all() ) - sdp_proxy.clock_RW = CLK_160_MHZ + sdpfirmware_proxy.clock_RW = CLK_160_MHZ time.sleep(1) # wait for beamlet device to process change event calculated_bf_weights_160_v2 = self.proxy.calculate_bf_weights(delays.flatten()) self.assertTrue( diff --git a/tangostationcontrol/integration_test/default/devices/test_device_calibration.py b/tangostationcontrol/integration_test/default/devices/test_device_calibration.py index e664942be6a2ee6ef484289c3d0c4b81bed2d7b2..f6f835992e03356d1c431912fd908eb510cc8f5e 100644 --- a/tangostationcontrol/integration_test/default/devices/test_device_calibration.py +++ b/tangostationcontrol/integration_test/default/devices/test_device_calibration.py @@ -88,7 +88,7 @@ class TestCalibrationDevice(AbstractTestBases.TestDeviceBase): # configure the frequencies, which allows access # to the calibration attributes and commands - self.sdp_proxy.clock_RW = CLK_200_MHZ + self.sdpfirmware_proxy.clock_RW = CLK_200_MHZ self.recv_proxy.RCU_band_select_RW = [[1] * N_rcu_inp] * N_rcu def restore_antennafield(self): diff --git a/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py b/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py index 42974a60e8198ec4bf558b8b497f753862532479..248c06aa1bfdf2c228a6189c6f19ccce2ce89fd7 100644 --- a/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py +++ b/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py @@ -126,7 +126,7 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): self.beamlet_proxy.on() # Set first (default) clock configuration - self.sdp_proxy.clock_RW = CLK_200_MHZ + self.sdpfirmware_proxy.clock_RW = CLK_200_MHZ time.sleep(1) self.proxy.initialise() @@ -148,7 +148,7 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): self.beamlet_proxy.on() # Change clock configuration - self.sdp_proxy.clock_RW = CLK_160_MHZ + self.sdpfirmware_proxy.clock_RW = CLK_160_MHZ time.sleep(1) FPGA_bf_weights_pp_clock160 = self.beamlet_proxy.FPGA_bf_weights_pp_RW.flatten() diff --git a/tangostationcontrol/tangostationcontrol/common/calibration.py b/tangostationcontrol/tangostationcontrol/common/calibration.py index b6f759887ca3f165d928421d7604ab570527410f..1c98b7dbce3e5a4584dae2bfd5b4151af03741ba 100644 --- a/tangostationcontrol/tangostationcontrol/common/calibration.py +++ b/tangostationcontrol/tangostationcontrol/common/calibration.py @@ -207,7 +207,9 @@ def dB_to_factor(dB: numpy.ndarray) -> numpy.ndarray: # SDP input delay calibration -def calibrate_input_samples_delay(antenna_field: DeviceProxy, sdp: DeviceProxy): +def calibrate_input_samples_delay( + antenna_field: DeviceProxy, sdpfirmware: DeviceProxy, sdp: DeviceProxy +): # Mapping [antenna] -> [fpga][input] antenna_to_sdp_mapping = antenna_field.Antenna_to_SDP_Mapping_R @@ -220,7 +222,7 @@ def calibrate_input_samples_delay(antenna_field: DeviceProxy, sdp: DeviceProxy): signal_delay_seconds = antenna_field.Antenna_Cables_Delay_R # compute the required compensation - clock = sdp.clock_RW + clock = sdpfirmware.clock_RW input_samples_delay, _ = delay_compensation(signal_delay_seconds, clock) # read-modify-write on [fpga][(input, polarisation)] diff --git a/tangostationcontrol/tangostationcontrol/devices/antennafield.py b/tangostationcontrol/tangostationcontrol/devices/antennafield.py index fda4e626fb9f5fa3bdbf7add121dbf4cc10815ac..e919715b6ce9d16b765990cc2742ce5f801551ca 100644 --- a/tangostationcontrol/tangostationcontrol/devices/antennafield.py +++ b/tangostationcontrol/tangostationcontrol/devices/antennafield.py @@ -695,7 +695,7 @@ class AntennaField(LOFARDevice): rcu_band_select = self.read_attribute("RCU_band_select_RW") # fetch settings from SDP - clock = self.sdp_proxy.clock_RW + clock = self.sdpfirmware_proxy.clock_RW def lookup_band(rcu_band): try: @@ -738,7 +738,7 @@ class AntennaField(LOFARDevice): )(value) # apply settings on SDP - self.sdp_proxy.clock_RW = bands[value[0]].clock + self.sdpfirmware_proxy.clock_RW = bands[value[0]].clock # read-modify-write on [fpga][(input, polarisation)] sdp_nyquist_zone = numpy.full((N_pn, A_pn * N_pol), None) diff --git a/tangostationcontrol/tangostationcontrol/devices/calibration.py b/tangostationcontrol/tangostationcontrol/devices/calibration.py index 115facc8fbf2f0441f1b9605a768996babbe42f4..5b4360808e1fcc0c8cbe56c4a0084e26b49ca092 100644 --- a/tangostationcontrol/tangostationcontrol/devices/calibration.py +++ b/tangostationcontrol/tangostationcontrol/devices/calibration.py @@ -137,7 +137,7 @@ class Calibration(LOFARDevice): """Calibrate RECV for our antennas. Run whenever the following changes: - sdp.clock_RW + sdpfirmware.clock_RW antennafield.RCU_band_select_RW """ @@ -161,7 +161,7 @@ class Calibration(LOFARDevice): """Calibrate SDP for our antennas. Run whenever the following changes: - sdp.clock_RW + sdpfirmware.clock_RW antennafield.RCU_band_select_RW """ @@ -189,7 +189,7 @@ class Calibration(LOFARDevice): sdp_proxy = self.sdp_proxies[sdp_device] - calibrate_input_samples_delay(ant_proxy, sdp_proxy) + calibrate_input_samples_delay(ant_proxy, sdpfirmware_proxy, sdp_proxy) self._calibration_manager.calibrate_subband_weights(ant_proxy, sdp_proxy) @@ -236,7 +236,7 @@ class Calibration(LOFARDevice): ), ) ) - for prx in self.sdp_proxies.values(): + for prx in self.sdpfirmware_proxies.values(): self.event_subscriptions.append( ( prx, diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py index 4783e0c0111ab2a589b6390c04af57d3f5b460ae..6250fb9eef5345a7eefd0b6bcbfb8c71f4daec22 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py @@ -14,7 +14,13 @@ from tango import AttrWriteType from tango.server import device_property, attribute # Additional import -from tangostationcontrol.common.constants import N_pn +from tangostationcontrol.common.constants import ( + N_pn, + CLK_200_MHZ, + CLK_160_MHZ, + N_subbands, + DEFAULT_POLLING_PERIOD, +) from tangostationcontrol.common.entrypoint import entry from tangostationcontrol.common.lofar_logging import device_logging_to_python @@ -38,6 +44,9 @@ class SDPFirmware(OPCUADevice): TR_fpga_mask_RW_default = device_property( dtype="DevVarBooleanArray", mandatory=False, default_value=[True] * N_pn ) + clock_RW_default = device_property( + dtype="DevULong", mandatory=False, default_value=CLK_200_MHZ + ) TRANSLATOR_DEFAULT_SETTINGS = ["TR_fpga_mask_RW"] @@ -45,6 +54,11 @@ class SDPFirmware(OPCUADevice): # Attributes # ---------- + # The SDP Firmware device offers all monitoring & control points + # available in the Board Support Package (BSP) that is supported + # by both the user and factory images. + + # Translator TR_fpga_mask_R = AttributeWrapper( comms_annotation=["TR_fpga_mask_R"], datatype=bool, dims=(N_pn,) ) @@ -57,6 +71,37 @@ class SDPFirmware(OPCUADevice): TR_fpga_communication_error_R = AttributeWrapper( comms_annotation=["TR_fpga_communication_error_R"], datatype=bool, dims=(N_pn,) ) + TR_sdp_config_first_fpga_nr_R = AttributeWrapper( + comms_annotation=["TR_sdp_config_first_fpga_nr_R"], datatype=numpy.uint32 + ) + TR_sdp_config_nof_beamsets_R = AttributeWrapper( + comms_annotation=["TR_sdp_config_nof_beamsets_R"], datatype=numpy.uint32 + ) + TR_sdp_config_nof_fpgas_R = AttributeWrapper( + comms_annotation=["TR_sdp_config_nof_fpgas_R"], datatype=numpy.uint32 + ) + TR_software_version_R = AttributeWrapper( + comms_annotation=["TR_software_version_R"], datatype=str + ) + TR_start_time_R = AttributeWrapper( + comms_annotation=["TR_start_time_R"], datatype=numpy.int64 + ) + TR_tod_R = AttributeWrapper( + doc="Time-of-day of SDPTR", + comms_annotation=["TR_tod_R"], + datatype=numpy.int64, + dims=(2,), + ) # struct of (time_t, int64) + TR_tod_sync_status_R = AttributeWrapper( + doc="Time-of-day synchronisation status with PTP/NTP (True=synchronised)", + comms_annotation=["TR_tod_sync_status_R"], + datatype=bool, + ) + TR_tod_pps_delta_R = AttributeWrapper( + comms_annotation=["TR_tod_pps_delta_R"], datatype=numpy.double + ) + + # Firmware & flash FPGA_boot_image_R = AttributeWrapper( comms_annotation=["FPGA_boot_image_R"], datatype=numpy.int32, @@ -69,9 +114,74 @@ class SDPFirmware(OPCUADevice): dims=(N_pn,), access=AttrWriteType.READ_WRITE, ) + FPGA_flash_protect_R = AttributeWrapper( + comms_annotation=["FPGA_flash_protect_R"], + datatype=numpy.uint32, + dims=(N_pn,), + doc="Protect (0) or unprotect (1) writing the factory image", + ) + FPGA_flash_protect_RW = AttributeWrapper( + comms_annotation=["FPGA_flash_protect_RW"], + datatype=numpy.uint32, + dims=(N_pn,), + access=AttrWriteType.READ_WRITE, + doc="Protect (0) or unprotect (1) writing the factory image", + ) + FPGA_scrap_R = AttributeWrapper( + comms_annotation=["FPGA_scrap_R"], + datatype=numpy.int32, + dims=(N_pn, N_subbands), + doc="FPGA scratch memory", + ) + FPGA_scrap_RW = AttributeWrapper( + comms_annotation=["FPGA_scrap_RW"], + datatype=numpy.int32, + dims=(N_pn, N_subbands), + access=AttrWriteType.READ_WRITE, + doc="FPGA scratch memory", + ) + + # Configuration FPGA_firmware_version_R = AttributeWrapper( comms_annotation=["FPGA_firmware_version_R"], datatype=str, dims=(N_pn,) ) + FPGA_hardware_version_R = AttributeWrapper( + comms_annotation=["FPGA_hardware_version_R"], datatype=str, dims=(N_pn,) + ) + FPGA_global_node_index_R = AttributeWrapper( + comms_annotation=["FPGA_global_node_index_R"], + datatype=numpy.uint32, + dims=(N_pn,), + ) + + # PPS + FPGA_pps_present_R = AttributeWrapper( + comms_annotation=["FPGA_pps_present_R"], datatype=bool, dims=(N_pn,) + ) + FPGA_pps_capture_cnt_R = AttributeWrapper( + comms_annotation=["FPGA_pps_capture_cnt_R"], datatype=numpy.uint32, dims=(N_pn,) + ) + FPGA_pps_expected_cnt_R = AttributeWrapper( + comms_annotation=["FPGA_pps_expected_cnt_R"], + datatype=numpy.uint32, + dims=(N_pn,), + ) + FPGA_pps_expected_cnt_RW = AttributeWrapper( + comms_annotation=["FPGA_pps_expected_cnt_RW"], + datatype=numpy.uint32, + dims=(N_pn,), + access=AttrWriteType.READ_WRITE, + ) + FPGA_time_since_last_pps_R = AttributeWrapper( + comms_annotation=["FPGA_time_since_last_pps_R"], + datatype=numpy.float_, + dims=(N_pn,), + ) + + # Monitoring + FPGA_temp_R = AttributeWrapper( + comms_annotation=["FPGA_temp_R"], datatype=numpy.float_, dims=(N_pn,) + ) @attribute( access=AttrWriteType.READ, @@ -80,6 +190,38 @@ class SDPFirmware(OPCUADevice): def SDP_device_R(self): return self.control.child(DeviceTypes.SDP).dev_name() + clock_RW = attribute( + doc="Configured sampling clock (Hz)", + unit="Hz", + dtype=numpy.uint32, + access=AttrWriteType.READ_WRITE, + fisallowed="is_attribute_access_allowed", + polling_period=DEFAULT_POLLING_PERIOD, + abs_change=1, + ) + + def read_clock_RW(self): + # We can only return a single value, so we assume the FPGA is configured coherently. + # Which is something that is to be checked by an independent monitoring system anyway. + mask = self.read_attribute( + "TR_fpga_mask_RW", + ) + clocks = self.read_attribute("FPGA_pps_expected_cnt_RW") + clocks_in_mask = [clock for idx, clock in enumerate(clocks) if mask[idx]] + + # We return first setting within the mask. + # If there are no FPGAs selected at all, just return a sane default. + return ( + numpy.uint32(clocks_in_mask[0]) if clocks_in_mask else self.clock_RW_default + ) + + def write_clock_RW(self, clock): + if clock not in (CLK_160_MHZ, CLK_200_MHZ): + raise ValueError(f"Unsupported clock frequency: {clock}") + + # Tell all FPGAs to use this clock + self.proxy.FPGA_pps_expected_cnt_RW = [clock] * N_pn + # ---------- # Summarising Attributes # ---------- diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py index c18dfc89da979924844b92c5622d030d355847cd..dff46058d014385a80bb428f462a65994fd57e08 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py @@ -17,7 +17,6 @@ from tangostationcontrol.common.constants import ( S_pn, N_pn, CLK_200_MHZ, - CLK_160_MHZ, N_subband_res, N_subbands, DEFAULT_SUBBAND, @@ -105,39 +104,10 @@ class SDP(OPCUADevice): default_value=[[0] * S_pn * N_subbands] * N_pn, ) - clock_RW_default = device_property( - dtype="DevULong", mandatory=False, default_value=CLK_200_MHZ - ) - # ---------- # Attributes # ---------- - FPGA_global_node_index_R = AttributeWrapper( - comms_annotation=["FPGA_global_node_index_R"], - datatype=numpy.uint32, - dims=(N_pn,), - ) - FPGA_hardware_version_R = AttributeWrapper( - comms_annotation=["FPGA_hardware_version_R"], datatype=str, dims=(N_pn,) - ) - FPGA_pps_present_R = AttributeWrapper( - comms_annotation=["FPGA_pps_present_R"], datatype=bool, dims=(N_pn,) - ) - FPGA_pps_capture_cnt_R = AttributeWrapper( - comms_annotation=["FPGA_pps_capture_cnt_R"], datatype=numpy.uint32, dims=(N_pn,) - ) - FPGA_pps_expected_cnt_R = AttributeWrapper( - comms_annotation=["FPGA_pps_expected_cnt_R"], - datatype=numpy.uint32, - dims=(N_pn,), - ) - FPGA_pps_expected_cnt_RW = AttributeWrapper( - comms_annotation=["FPGA_pps_expected_cnt_RW"], - datatype=numpy.uint32, - dims=(N_pn,), - access=AttrWriteType.READ_WRITE, - ) FPGA_processing_enable_R = AttributeWrapper( comms_annotation=["FPGA_processing_enable_R"], datatype=bool, dims=(N_pn,) ) @@ -189,15 +159,6 @@ class SDP(OPCUADevice): dims=(N_pn,), access=AttrWriteType.READ_WRITE, ) - FPGA_scrap_R = AttributeWrapper( - comms_annotation=["FPGA_scrap_R"], datatype=numpy.int32, dims=(N_pn, N_subbands) - ) - FPGA_scrap_RW = AttributeWrapper( - comms_annotation=["FPGA_scrap_RW"], - datatype=numpy.int32, - dims=(N_pn, N_subbands), - access=AttrWriteType.READ_WRITE, - ) FPGA_sdp_info_antenna_band_index_R = AttributeWrapper( comms_annotation=["FPGA_sdp_info_antenna_band_index_R"], datatype=numpy.uint32, @@ -288,14 +249,6 @@ class SDP(OPCUADevice): dims=(N_pn, S_pn, N_subbands), access=AttrWriteType.READ_WRITE, ) - FPGA_time_since_last_pps_R = AttributeWrapper( - comms_annotation=["FPGA_time_since_last_pps_R"], - datatype=numpy.float_, - dims=(N_pn,), - ) - FPGA_temp_R = AttributeWrapper( - comms_annotation=["FPGA_temp_R"], datatype=numpy.float_, dims=(N_pn,) - ) FPGA_wg_amplitude_R = AttributeWrapper( comms_annotation=["FPGA_wg_amplitude_R"], @@ -337,27 +290,6 @@ class SDP(OPCUADevice): dims=(N_pn, S_pn), access=AttrWriteType.READ_WRITE, ) - TR_sdp_config_first_fpga_nr_R = AttributeWrapper( - comms_annotation=["TR_sdp_config_first_fpga_nr_R"], datatype=numpy.uint32 - ) - TR_sdp_config_nof_beamsets_R = AttributeWrapper( - comms_annotation=["TR_sdp_config_nof_beamsets_R"], datatype=numpy.uint32 - ) - TR_sdp_config_nof_fpgas_R = AttributeWrapper( - comms_annotation=["TR_sdp_config_nof_fpgas_R"], datatype=numpy.uint32 - ) - TR_software_version_R = AttributeWrapper( - comms_annotation=["TR_software_version_R"], datatype=str - ) - TR_start_time_R = AttributeWrapper( - comms_annotation=["TR_start_time_R"], datatype=numpy.int64 - ) - TR_tod_R = AttributeWrapper( - comms_annotation=["TR_tod_R"], datatype=numpy.int64, dims=(2,) - ) # struct of (time_t, int64) - TR_tod_pps_delta_R = AttributeWrapper( - comms_annotation=["TR_tod_pps_delta_R"], datatype=numpy.double - ) # OPC-UA MP only points for AIT FPGA_signal_input_mean_R = AttributeWrapper( @@ -446,15 +378,6 @@ class SDP(OPCUADevice): polling_period=DEFAULT_POLLING_PERIOD, abs_change=1, ) - clock_RW = attribute( - doc="Configured sampling clock (Hz)", - unit="Hz", - dtype=numpy.uint32, - access=AttrWriteType.READ_WRITE, - fisallowed="is_attribute_access_allowed", - polling_period=DEFAULT_POLLING_PERIOD, - abs_change=1, - ) subband_frequency_R = attribute( doc="Frequency of each subband for each input", unit="Hz", @@ -537,31 +460,9 @@ class SDP(OPCUADevice): # to make sure the subbands are ascending in frequency self.proxy.FPGA_spectral_inversion_RW = self._nyquist_zone % 2 - def read_clock_RW(self): - # We can only return a single value, so we assume the FPGA is configured coherently. - # Which is something that is to be checked by an independent monitoring system anyway. - mask = self.control.read_parent_attribute( - "TR_fpga_mask_RW", - ) - clocks = self.read_attribute("FPGA_pps_expected_cnt_RW") - clocks_in_mask = [clock for idx, clock in enumerate(clocks) if mask[idx]] - - # We return first setting within the mask. - # If there are no FPGAs selected at all, just return a sane default. - return ( - numpy.uint32(clocks_in_mask[0]) if clocks_in_mask else self.clock_RW_default - ) - - def write_clock_RW(self, clock): - if clock not in (CLK_160_MHZ, CLK_200_MHZ): - raise ValueError(f"Unsupported clock frequency: {clock}") - - # Tell all FPGAs to use this clock - self.proxy.FPGA_pps_expected_cnt_RW = [clock] * N_pn - @TimeIt() def read_subband_frequency_R(self): - clock = self.read_attribute("clock_RW") # scalar + clock = self.control.read_parent_attribute("clock_RW") # scalar nyquist_zone = numpy.array( self.read_attribute("nyquist_zone_RW") ) # N_pn x S_pn