diff --git a/tangostationcontrol/tangostationcontrol/clients/statistics/client.py b/tangostationcontrol/tangostationcontrol/clients/statistics/client.py index 9ea336ad9e81df319f8596ab76d3b6080db7f567..a774e2a56fc74a0e0310ba7ce5e42484e6ad6219 100644 --- a/tangostationcontrol/tangostationcontrol/clients/statistics/client.py +++ b/tangostationcontrol/tangostationcontrol/clients/statistics/client.py @@ -21,12 +21,16 @@ logger = logging.getLogger() class StatisticsClient(AsyncCommClient): + + # the maximum amount of packets unhandled packets the statistics client thread will queue + STATISTICS_CLIENT_QUEUE_SIZE = 1024 + """ Collects statistics packets over UDP, forwards them to a StatisticsCollector, and provides a CommClient interface to expose points to a Device Server. """ - def __init__(self, collector, udp_options, tcp_options, fault_func, event_loop=None, queuesize=constants.STATISTICS_CLIENT_QUEUE_SIZE): + def __init__(self, collector, udp_options, tcp_options, fault_func, event_loop=None, queuesize=STATISTICS_CLIENT_QUEUE_SIZE): """ Create the statistics client and connect() to it and get the object node. diff --git a/tangostationcontrol/tangostationcontrol/clients/udp_receiver.py b/tangostationcontrol/tangostationcontrol/clients/udp_receiver.py index 585655172530fa7efbacc32c576f76362cb4785f..04e24366e193567f4b04c97716a77a5546bcadae 100644 --- a/tangostationcontrol/tangostationcontrol/clients/udp_receiver.py +++ b/tangostationcontrol/tangostationcontrol/clients/udp_receiver.py @@ -52,7 +52,7 @@ class UDPReceiver(Thread, StatisticsClientThread): # Number of packets we had to drop due to a full queue "nof_packets_dropped": numpy.uint64(0), # Packets are at most 9000 bytes, the largest payload (well, MTU) of an Ethernet Jumbo frame - "last_packet": numpy.zeros((constants.MAX_PACKET_SIZE,), dtype=numpy.uint8), + "last_packet": numpy.zeros((constants.MAX_ETH_FRAME_SIZE,), dtype=numpy.uint8), # Timestamp of when the last packet was received "last_packet_timestamp": numpy.uint64(0), } @@ -90,7 +90,7 @@ class UDPReceiver(Thread, StatisticsClientThread): while self.stream_on: try: - packet, _, _, _ = self.sock.recvmsg(constants.MAX_PACKET_SIZE) + packet, _, _, _ = self.sock.recvmsg(constants.MAX_ETH_FRAME_SIZE) self.parameters["nof_packets_received"] += numpy.uint64(1) self.parameters["nof_bytes_received"] += numpy.uint64(len(packet)) diff --git a/tangostationcontrol/tangostationcontrol/common/constants.py b/tangostationcontrol/tangostationcontrol/common/constants.py index 6b8db920e66e9bde7700db48357b90f691169f1b..001bb089d9f9d7a7a11f2cfbea82b83e521001fd 100644 --- a/tangostationcontrol/tangostationcontrol/common/constants.py +++ b/tangostationcontrol/tangostationcontrol/common/constants.py @@ -1,13 +1,5 @@ class constants: - """ - TODO: support configurations - - A good source for a good chunk of the constants can be found here: https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+SC+-+SDP+OPC-UA+interface - """ - - # number of dual polarization antennas in the band. - N_ant = 48 # number of FPGA processing nodes N_pn = 16 @@ -15,24 +7,20 @@ class constants: # number of polarisations per antenna (X and y polarisations) N_pol = 2 - # Single polarization signal inputs ( N_ant * N_pol) - S_ant = 96 - - # number of polarisations for beamforming - N_pol_bf = 2 - # antennas per FPGA A_pn = 6 # signal inputs per FPGA ( A_pn * N_pol ) S_pn = A_pn * N_pol - # TODO verify whether this is the same as S_ant or not - # Highest number of HBA tiles we support per AntennaField - MAX_NUMBER_OF_HBAT = 96 + # Highest number antennas we support + MAX_ANTENNA = 96 + + # Maximum number of antenna inputs we support (used to determine array sizes) + MAX_INPUTS = 192 # Number of tile elements (antenna dipoles) in each HBA tile - N_te = 16 + N_elements = 16 # number of RCU's per subrack N_rcu = 32 @@ -58,10 +46,7 @@ class constants: # Maximum number of subbands we support N_subbands = 512 # Number of points per subband (the resolution) - N_subband_pts = 1024 - - # Maximum number of antenna inputs we support (used to determine array sizes) - MAX_INPUTS = 192 + N_subband_res = 1024 # main clock frequency's are 200MHz and 160MHz @@ -79,10 +64,6 @@ class constants: BST_MAX_BLOCKS = 2 - # number of sockets the psoc has - PSOC_SOCKETS = 8 - - # UNB2 constants # number of uniboards in a subrack N_unb = 2 @@ -93,77 +74,23 @@ class constants: # number of QSFP tranceivers per uniboard N_qsfp = 24 - # the three spatial dimensions XYZ used a lot for pointing directions and ITRF coordinates. + + # the three spatial dimensions XYZ used a lot for PQR and ITRF coordinates. N_xyz = 3 + # amount of parameters needed for a pointing + N_point_prop = 3 # number of values for latitude/longitude coordinates - N_coord = 2 + N_latlong = 2 # default subband we use because of its low RFI DEFAULT_SUBBAND = 102 # Maximum array size to allocate for beam_device pointings, - MAX_POINTINGS = 1024 + MAX_POINTINGS = N_beamlets_max # max size for a statistic packet - MAX_PACKET_SIZE = 9000 - - # Maximum number of supported simultaneous replicator clients - MAX_STATISTICS_CLIENTS = 128 - - # the maximum amount of packets unhandled packets the statistics client thread will queue - STATISTICS_CLIENT_QUEUE_SIZE = 1024 - - # maximum number of devices boot.py supports - MAX_BOOT_DEVICES = 128 + MAX_ETH_FRAME_SIZE = 9000 # The default polling period for polled attributes DEFAULT_POLLING_PERIOD = 1000 - - -""" -These classes for now exist for future use - -class LB: - S_ant = 192 - N_ant = 96 - N_pn = 16 - N_beamsets_sdp = 2 - N_beamsets_ctrl = 1 - N_beamlets_sdp = 976 - N_beamlets_ctrl = 488 - N_beamlets_out = 488 - - -class HB_Core: - S_ant = 96 - N_ant = 48 - N_pn = 8 - N_beamsets_sdp = 2 - N_beamsets_ctrl = 2 - N_beamlets_sdp = 976 - N_beamlets_ctrl = 976 - N_beamlets_out = 976 - - -class HB_Remote: - S_ant = 96 - N_ant = 48 - N_pn = 8 - N_beamsets_sdp = 2 - N_beamsets_ctrl = 1 - N_beamlets_sdp = 976 - N_beamlets_ctrl = 488 - N_beamlets_out = 488 - - -class HB_International: - S_ant = 192 - N_ant = 96 - N_pn = 16 - N_beamsets_sdp = 2 - N_beamsets_ctrl = 1 - N_beamlets_sdp = 976 - N_beamlets_ctrl = 488 - N_beamlets_out = 488 -""" diff --git a/tangostationcontrol/tangostationcontrol/devices/antennafield.py b/tangostationcontrol/tangostationcontrol/devices/antennafield.py index 52a4ccd359867f329e3ae18ad889ce52c920c7e7..21e5d00831bda8c81fd16e839679c7f44509b3f0 100644 --- a/tangostationcontrol/tangostationcontrol/devices/antennafield.py +++ b/tangostationcontrol/tangostationcontrol/devices/antennafield.py @@ -91,8 +91,8 @@ class AntennaField(lofar_device): calculated, as well as the geohash. """ - MAX_NUMBER_OF_HBAT = constants.MAX_NUMBER_OF_HBAT - N_te = constants.N_te + MAX_NUMBER_OF_HBAT = constants.MAX_ANTENNA + N_te = constants.N_elements # ----- Antenna names @@ -292,7 +292,7 @@ class AntennaField(lofar_device): Antenna_Field_Reference_GEO_R = attribute(access=AttrWriteType.READ, doc='Absolute reference position of antenna field, in latitude/longitude (degrees)', - dtype=(numpy.float64,), max_dim_x=constants.N_coord) + dtype=(numpy.float64,), max_dim_x=constants.N_latlong) Antenna_Field_Reference_GEOHASH_R = attribute(access=AttrWriteType.READ, doc='Absolute reference position of antenna field, as a geohash string', @@ -309,7 +309,7 @@ class AntennaField(lofar_device): Antenna_Reference_GEO_R = attribute(access=AttrWriteType.READ, doc='Absolute reference position of each tile, in latitude/longitude (degrees)', - dtype=((numpy.float64,),), max_dim_x=constants.N_coord, max_dim_y=MAX_NUMBER_OF_HBAT) + dtype=((numpy.float64,),), max_dim_x=constants.N_latlong, max_dim_y=MAX_NUMBER_OF_HBAT) Antenna_Reference_GEOHASH_R = attribute(access=AttrWriteType.READ, doc='Absolute reference position of each tile, as geohash strings', @@ -392,7 +392,7 @@ class AntennaField(lofar_device): tiles lie on the same plane in ITRF. """ # the relative offsets between the elements is fixed in HBAT_base_antenna_offsets - base_antenna_offsets = numpy.array(self.HBAT_base_antenna_offsets).reshape(constants.N_te, constants.N_xyz) + base_antenna_offsets = numpy.array(self.HBAT_base_antenna_offsets).reshape(constants.N_elements, constants.N_xyz) PQR_to_ETRS_rotation_matrix = numpy.array(self.PQR_to_ETRS_rotation_matrix).reshape(constants.N_xyz, constants.N_xyz) @@ -404,7 +404,7 @@ class AntennaField(lofar_device): PQR_to_ETRS_rotation_matrix) for angle_deg in self.HBAT_PQR_rotation_angles_deg]) - return all_offsets.reshape(-1, constants.N_te * constants.N_xyz) + return all_offsets.reshape(-1, constants.N_elements * constants.N_xyz) def read_Antenna_Reference_ITRF_R(self): # provide ITRF coordinates if they were configured @@ -521,10 +521,10 @@ class AntennaField(lofar_device): def calculate_HBAT_bf_delay_steps(self, delays: numpy.ndarray): num_tiles = self.read_nr_antennas_R() - delays = delays.reshape(num_tiles, constants.N_te) + delays = delays.reshape(num_tiles, constants.N_elements) - result_values = numpy.zeros((num_tiles, constants.N_te * constants.N_pol_bf), dtype=numpy.int64) - control_mapping = numpy.reshape(self.Control_to_RECV_mapping, (-1, constants.N_pol_bf)) + result_values = numpy.zeros((num_tiles, constants.N_elements * constants.N_pol), dtype=numpy.int64) + control_mapping = numpy.reshape(self.Control_to_RECV_mapping, (-1, constants.N_pol)) for recv_idx, recv_proxy in enumerate(self.recv_proxies): # collect all delays for this recv_proxy @@ -538,7 +538,7 @@ class AntennaField(lofar_device): # convert them into delay steps flatten_delay_steps = numpy.array(recv_proxy.calculate_HBAT_bf_delay_steps(recv_delays.flatten()), dtype=numpy.int64) - delay_steps = numpy.reshape(flatten_delay_steps, (-1, constants.N_te * constants.N_pol)) + delay_steps = numpy.reshape(flatten_delay_steps, (-1, constants.N_elements * constants.N_pol)) # write back into same positions we collected them from result_values[recv_result_indices] = delay_steps @@ -548,8 +548,8 @@ class AntennaField(lofar_device): class AntennaToRecvMapper(object): - _VALUE_MAP_NONE_96 = numpy.full(constants.MAX_NUMBER_OF_HBAT, None) - _VALUE_MAP_NONE_96_32 = numpy.full((constants.MAX_NUMBER_OF_HBAT, constants.N_rcu), None) + _VALUE_MAP_NONE_96 = numpy.full(constants.MAX_ANTENNA, None) + _VALUE_MAP_NONE_96_32 = numpy.full((constants.MAX_ANTENNA, constants.N_rcu), None) def __init__(self, control_to_recv_mapping, power_to_recv_mapping, number_of_receivers): number_of_antennas = len(control_to_recv_mapping) @@ -587,13 +587,13 @@ class AntennaToRecvMapper(object): "RCU_band_select_RW": AntennaToRecvMapper._VALUE_MAP_NONE_96, } self._reshape_attributes_in = { - "HBAT_BF_delay_steps_RW": (constants.MAX_NUMBER_OF_HBAT, constants.N_rcu), - "RCU_PWR_ANT_on_R": (constants.MAX_NUMBER_OF_HBAT,), - "RCU_PWR_ANT_on_RW": (constants.MAX_NUMBER_OF_HBAT,), - "RCU_band_select_RW": (constants.MAX_NUMBER_OF_HBAT,), + "HBAT_BF_delay_steps_RW": (constants.MAX_ANTENNA, constants.N_rcu), + "RCU_PWR_ANT_on_R": (constants.MAX_ANTENNA,), + "RCU_PWR_ANT_on_RW": (constants.MAX_ANTENNA,), + "RCU_band_select_RW": (constants.MAX_ANTENNA,), } self._reshape_attributes_out = { - "HBAT_BF_delay_steps_RW": (constants.MAX_NUMBER_OF_HBAT, constants.N_rcu), + "HBAT_BF_delay_steps_RW": (constants.MAX_ANTENNA, constants.N_rcu), "RCU_PWR_ANT_on_R": (constants.N_rcu, constants.N_rcu_inp), "RCU_PWR_ANT_on_RW": (constants.N_rcu, constants.N_rcu_inp), "RCU_band_select_RW": (constants.N_rcu, constants.N_rcu_inp), diff --git a/tangostationcontrol/tangostationcontrol/devices/beam_device.py b/tangostationcontrol/tangostationcontrol/devices/beam_device.py index 4f64b44044ba2724aac3667c47eee33c10a5b13b..f0b66e2155965de8cf4f523e8bbca36ce749a56f 100644 --- a/tangostationcontrol/tangostationcontrol/devices/beam_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/beam_device.py @@ -74,11 +74,11 @@ class beam_device(lofar_device): # will be stored as self._num_pointings. Pointing_direction_R = attribute(access=AttrWriteType.READ, - dtype=((str,),), max_dim_x=constants.N_xyz, max_dim_y=constants.MAX_POINTINGS, + dtype=((str,),), max_dim_x=constants.N_point_prop, max_dim_y=constants.MAX_POINTINGS, fget=lambda self: self._pointing_direction_r) Pointing_direction_RW = attribute(access=AttrWriteType.READ_WRITE, - dtype=((str,),), max_dim_x=constants.N_xyz, max_dim_y=constants.MAX_POINTINGS, + dtype=((str,),), max_dim_x=constants.N_point_prop, max_dim_y=constants.MAX_POINTINGS, fget=lambda self: self._pointing_direction_rw) Pointing_direction_str_R = attribute(access=AttrWriteType.READ, @@ -230,7 +230,7 @@ class beam_device(lofar_device): # Initialise tracking control self._num_pointings = num_pointings self._pointing_timestamp_r = numpy.zeros(num_pointings, dtype=numpy.double) - self._pointing_direction_r = numpy.zeros((num_pointings, constants.N_xyz), dtype="<U32") + self._pointing_direction_r = numpy.zeros((num_pointings, constants.N_point_prop), dtype="<U32") self._pointing_direction_rw = numpy.array([["AZELGEO","0deg","90deg"]] * num_pointings, dtype="<U32") self._tracking_enabled_rw = self.Tracking_enabled_RW_default diff --git a/tangostationcontrol/tangostationcontrol/devices/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py index 338d88cd2e8bca397c6fdfba9c258dc85f668a2f..2f44875c6538015e9e92b3bc6d0220b36d5ecb60 100644 --- a/tangostationcontrol/tangostationcontrol/devices/boot.py +++ b/tangostationcontrol/tangostationcontrol/devices/boot.py @@ -53,6 +53,7 @@ class DevicesInitialiser(object): the start() method, and progress can be followed by inspecting the members progress (0-100), status (string), and is_running() (bool). """ + def __init__(self, device_names, reboot=False, initialise_hardware=True, proxy_timeout=60.0): self.reboot = reboot self.initialise_hardware = initialise_hardware @@ -214,6 +215,10 @@ class DevicesInitialiser(object): @device_logging_to_python() class Boot(lofar_device): + + # maximum number of devices boot.py supports + MAX_BOOT_DEVICES = 128 + # ----------------- # Device Properties # ----------------- @@ -261,8 +266,8 @@ class Boot(lofar_device): booting_R = attribute(dtype=bool, access=AttrWriteType.READ, fget=lambda self: self.initialiser.is_running(), doc="Whether booting is in progress.") progress_R = attribute(dtype=numpy.int32, access=AttrWriteType.READ, fget=lambda self: numpy.int32(self.initialiser.progress), doc="Percentage of devices that was initialised") status_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: self.initialiser.status, doc="Description of current boot activity") - initialised_devices_R = attribute(dtype=(str,), max_dim_x=constants.MAX_BOOT_DEVICES, access=AttrWriteType.READ, fget=lambda self: [name for name, initialised in self.initialiser.device_initialised.items() if initialised], doc="Which devices were initialised succesfully") - uninitialised_devices_R = attribute(dtype=(str,), max_dim_x=constants.MAX_BOOT_DEVICES, access=AttrWriteType.READ, fget=lambda self: [name for name, initialised in self.initialiser.device_initialised.items() if not initialised], doc="Which devices have not been initialised or failed to initialise") + initialised_devices_R = attribute(dtype=(str,), max_dim_x=MAX_BOOT_DEVICES, access=AttrWriteType.READ, fget=lambda self: [name for name, initialised in self.initialiser.device_initialised.items() if initialised], doc="Which devices were initialised succesfully") + uninitialised_devices_R = attribute(dtype=(str,), max_dim_x=MAX_BOOT_DEVICES, access=AttrWriteType.READ, fget=lambda self: [name for name, initialised in self.initialiser.device_initialised.items() if not initialised], doc="Which devices have not been initialised or failed to initialise") # -------- # overloaded functions diff --git a/tangostationcontrol/tangostationcontrol/devices/observation.py b/tangostationcontrol/tangostationcontrol/devices/observation.py index 1d506f92f4fcffb472d57cf809ca602dffa6ec96..90b786e43f3be7b2344691d8fd9e1abb1edeecfb 100644 --- a/tangostationcontrol/tangostationcontrol/devices/observation.py +++ b/tangostationcontrol/tangostationcontrol/devices/observation.py @@ -47,11 +47,11 @@ class Observation(lofar_device): rel_change="1.0") observation_id_R = attribute(dtype=numpy.int64, access=AttrWriteType.READ) stop_time_R = attribute(dtype=numpy.float64, access=AttrWriteType.READ) - antenna_mask_R = attribute(dtype=(numpy.int64,), max_dim_x=constants.MAX_NUMBER_OF_HBAT, access=AttrWriteType.READ) + antenna_mask_R = attribute(dtype=(numpy.int64,), max_dim_x=constants.MAX_ANTENNA, access=AttrWriteType.READ) filter_R = attribute(dtype=numpy.str, access=AttrWriteType.READ) - saps_subband_R = attribute(dtype=((numpy.uint32,),), max_dim_x=constants.S_ant, max_dim_y=constants.N_beamlets_ctrl, access=AttrWriteType.READ) - saps_pointing_R = attribute(dtype=((numpy.str,),), max_dim_x=3, max_dim_y=constants.N_beamlets_ctrl, access=AttrWriteType.READ) - tile_beam_R = attribute(dtype=(numpy.str,), max_dim_x=3, access=AttrWriteType.READ) + saps_subband_R = attribute(dtype=((numpy.uint32,),), max_dim_x=constants.N_beamlets_ctrl, max_dim_y=constants.N_beamlets_ctrl, access=AttrWriteType.READ) + saps_pointing_R = attribute(dtype=((numpy.str,),), max_dim_x=constants.N_point_prop, max_dim_y=constants.N_beamlets_ctrl, access=AttrWriteType.READ) + tile_beam_R = attribute(dtype=(numpy.str,), max_dim_x=constants.N_point_prop, access=AttrWriteType.READ) first_beamlet_R = attribute(dtype=numpy.int64, access=AttrWriteType.READ) observation_settings_RW = attribute(dtype=str, access=AttrWriteType.READ_WRITE) @@ -243,8 +243,8 @@ class Observation(lofar_device): retrieve the RCU band from filter name, returning the correct format for AntennaField device """ - ANT_mask_RW = [False] * constants.MAX_NUMBER_OF_HBAT - RCU_band_select_RW = [0] * constants.MAX_NUMBER_OF_HBAT + ANT_mask_RW = [False] * constants.MAX_ANTENNA + RCU_band_select_RW = [0] * constants.MAX_ANTENNA rcu_band = self.recv_proxy.get_rcu_band_from_filter(filter_name) for a in antenna_mask: ANT_mask_RW[a] = True diff --git a/tangostationcontrol/tangostationcontrol/devices/psoc.py b/tangostationcontrol/tangostationcontrol/devices/psoc.py index 73010d8cb9118a63ae929fd8572f5d5051c151d1..31e1393cec0c5ab09633fa8566fc8deef956b13a 100644 --- a/tangostationcontrol/tangostationcontrol/devices/psoc.py +++ b/tangostationcontrol/tangostationcontrol/devices/psoc.py @@ -10,7 +10,6 @@ # Additional import from tangostationcontrol.common.entrypoint import entry from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions -from tangostationcontrol.common.constants import constants from tango.server import device_property, command @@ -33,6 +32,10 @@ __all__ = ["PSOC", "main"] @device_logging_to_python() class PSOC(snmp_device): + + # number of sockets the psoc has + PSOC_SOCKETS = 8 + # ----------------- # Device Properties # ----------------- @@ -44,7 +47,7 @@ class PSOC(snmp_device): # ---------- # Attributes # ---------- - sockets_state_R = attribute_wrapper(comms_annotation={"mib": "PowerNet-MIB", "name": "sPDUOutletCtl", "index": 1}, dims=(constants.PSOC_SOCKETS,), datatype=str) + sockets_state_R = attribute_wrapper(comms_annotation={"mib": "PowerNet-MIB", "name": "sPDUOutletCtl", "index": 1}, dims=(PSOC_SOCKETS,), datatype=str) master_state_R = attribute_wrapper(comms_annotation={"mib": "PowerNet-MIB", "name": "sPDUMasterState"}, datatype=str) current_load_R = attribute_wrapper(comms_annotation={"mib": "PowerNet-MIB", "name": "rPDULoadStatusLoad", "index": 1}, datatype=numpy.int64) uptime_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "sysUpTime"}, datatype=numpy.int64) @@ -58,7 +61,7 @@ class PSOC(snmp_device): """ user code here. is called when the state is set to STANDBY """ # make sure all sockets are named - if len(self.PSOC_sockets) != constants.PSOC_SOCKETS: + if len(self.PSOC_sockets) != self.PSOC_SOCKETS: raise Exception( f"At least {self.PSOC_NOF_SOCKETS} names are required to be given. You can simply leave any unused sockets as empty strings") else: diff --git a/tangostationcontrol/tangostationcontrol/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py index a08abce8656750e05e4fe7f67ba2217ca8a466ce..503daeb6a7d086f21cf740a7e2dbfb3d20c916cf 100644 --- a/tangostationcontrol/tangostationcontrol/devices/recv.py +++ b/tangostationcontrol/tangostationcontrol/devices/recv.py @@ -38,9 +38,8 @@ __all__ = ["RECV", "main"] @device_logging_to_python() class RECV(opcua_device): - S_ant = constants.S_ant N_rcu = constants.N_rcu - N_te = constants.N_te + N_te = constants.N_elements N_pol = constants.N_pol N_rcu_inp = constants.N_rcu_inp @@ -63,7 +62,7 @@ class RECV(opcua_device): ANT_mask_RW_default = device_property( dtype='DevVarBooleanArray', mandatory=False, - default_value=[True] * S_ant + default_value=[True] * N_rcu * N_rcu_inp ) RCU_mask_RW_default = device_property( @@ -75,19 +74,19 @@ class RECV(opcua_device): RCU_attenuator_dB_RW_default = device_property( dtype='DevVarLong64Array', mandatory=False, - default_value=[0] * S_ant + default_value=[0] * N_rcu * N_rcu_inp ) RCU_band_select_RW_default = device_property( dtype='DevVarLong64Array', mandatory=False, - default_value=[0] * S_ant + default_value=[0] * N_rcu * N_rcu_inp ) RCU_PWR_ANT_on_RW_default = device_property( dtype='DevVarBooleanArray', mandatory=False, - default_value=[False] * S_ant + default_value=[False] * N_rcu * N_rcu_inp # turn power off by default in test setups, f.e. to prevent blowing up the noise sources ) @@ -146,23 +145,23 @@ class RECV(opcua_device): # Attributes # ---------- - ANT_mask_RW = attribute_wrapper(comms_annotation=["ANT_mask_RW"], datatype=bool, dims=(S_ant,), + ANT_mask_RW = attribute_wrapper(comms_annotation=["ANT_mask_RW"], datatype=bool, dims=(N_rcu * N_rcu_inp,), access=AttrWriteType.READ_WRITE) # The HBAT beamformer delays represent 32 delays for each of the 96 inputs. # The 32 delays deconstruct as delays[polarisation][dipole], and each delay is the number of 'delay steps' to apply (0.5ns for HBAT1). HBAT_BF_delay_steps_R = attribute_wrapper(comms_annotation=["HBAT_BF_delay_steps_R"], datatype=numpy.int64, - dims=(S_ant, N_te, N_pol)) + dims=(N_rcu * N_rcu_inp, N_te, N_pol)) HBAT_BF_delay_steps_RW = attribute_wrapper(comms_annotation=["HBAT_BF_delay_steps_RW"], datatype=numpy.int64, - dims=(S_ant, N_te, N_pol), access=AttrWriteType.READ_WRITE) - HBAT_LED_on_R = attribute_wrapper(comms_annotation=["HBAT_LED_on_R"], datatype=bool, dims=(S_ant, N_te, N_pol)) - HBAT_LED_on_RW = attribute_wrapper(comms_annotation=["HBAT_LED_on_RW"], datatype=bool, dims=(S_ant, N_te, N_pol), + dims=(N_rcu * N_rcu_inp, N_te, N_pol), access=AttrWriteType.READ_WRITE) + HBAT_LED_on_R = attribute_wrapper(comms_annotation=["HBAT_LED_on_R"], datatype=bool, dims=(N_rcu * N_rcu_inp, N_te, N_pol)) + HBAT_LED_on_RW = attribute_wrapper(comms_annotation=["HBAT_LED_on_RW"], datatype=bool, dims=(N_rcu * N_rcu_inp, N_te, N_pol), access=AttrWriteType.READ_WRITE) - HBAT_PWR_LNA_on_R = attribute_wrapper(comms_annotation=["HBAT_PWR_LNA_on_R"], datatype=bool, dims=(S_ant, N_te, N_pol)) - HBAT_PWR_LNA_on_RW = attribute_wrapper(comms_annotation=["HBAT_PWR_LNA_on_RW"], datatype=bool, dims=(S_ant, N_te, N_pol), + HBAT_PWR_LNA_on_R = attribute_wrapper(comms_annotation=["HBAT_PWR_LNA_on_R"], datatype=bool, dims=(N_rcu * N_rcu_inp, N_te, N_pol)) + HBAT_PWR_LNA_on_RW = attribute_wrapper(comms_annotation=["HBAT_PWR_LNA_on_RW"], datatype=bool, dims=(N_rcu * N_rcu_inp, N_te, N_pol), access=AttrWriteType.READ_WRITE) - HBAT_PWR_on_R = attribute_wrapper(comms_annotation=["HBAT_PWR_on_R"], datatype=bool, dims=(S_ant, N_te, N_pol)) - HBAT_PWR_on_RW = attribute_wrapper(comms_annotation=["HBAT_PWR_on_RW"], datatype=bool, dims=(S_ant, N_te, N_pol), + HBAT_PWR_on_R = attribute_wrapper(comms_annotation=["HBAT_PWR_on_R"], datatype=bool, dims=(N_rcu * N_rcu_inp, N_te, N_pol)) + HBAT_PWR_on_RW = attribute_wrapper(comms_annotation=["HBAT_PWR_on_RW"], datatype=bool, dims=(N_rcu * N_rcu_inp, N_te, N_pol), access=AttrWriteType.READ_WRITE) RCU_ADC_locked_R = attribute_wrapper(comms_annotation=["RCU_ADC_locked_R"], datatype=bool, dims=(N_rcu, N_rcu_inp)) RCU_attenuator_dB_R = attribute_wrapper(comms_annotation=["RCU_attenuator_dB_R"], datatype=numpy.int64, @@ -217,7 +216,7 @@ class RECV(opcua_device): numpy.uint32) RCU_error_R = attribute(dtype=(bool,), max_dim_x=N_rcu, fisallowed="is_attribute_access_allowed") - ANT_error_R = attribute(dtype=(bool,), max_dim_x=S_ant, fisallowed="is_attribute_access_allowed") + ANT_error_R = attribute(dtype=(bool,), max_dim_x=N_rcu * N_rcu_inp, fisallowed="is_attribute_access_allowed") def read_RCU_error_R(self): return self.read_attribute("RCU_mask_RW") & ( @@ -230,7 +229,7 @@ class RECV(opcua_device): ~self.read_attribute("RCU_ADC_locked_R").flatten() ) - RECV_IOUT_error_R = attribute(dtype=(bool,), max_dim_x=S_ant, fisallowed="is_attribute_access_allowed") + RECV_IOUT_error_R = attribute(dtype=(bool,), max_dim_x=N_rcu * N_rcu_inp, fisallowed="is_attribute_access_allowed") RECV_TEMP_error_R = attribute(dtype=(bool,), max_dim_x=N_rcu, fisallowed="is_attribute_access_allowed", polling_period=constants.DEFAULT_POLLING_PERIOD) RECV_VOUT_error_R = attribute(dtype=(bool,), max_dim_x=N_rcu, fisallowed="is_attribute_access_allowed") @@ -326,7 +325,7 @@ class RECV(opcua_device): """ converts a signal path delay (in seconds) to an analog beam weight """ # Reshape the flattened input array, into whatever how many tiles we get - delays = numpy.array(delays).reshape(-1, constants.N_te) + delays = numpy.array(delays).reshape(-1, constants.N_elements) # Calculate the beam weight array HBAT_bf_delay_steps = self._calculate_HBAT_bf_delay_steps(delays) diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py index 062db14920b2e1d23317d02d25d7e76a0b0c4fb9..2db9f70dd45a9d2a11b53fe9243373851c0b117f 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py @@ -35,7 +35,7 @@ class Beamlet(opcua_device): N_pol = constants.N_pol N_beamlets_ctrl = constants.N_beamlets_ctrl N_beamsets_ctrl = constants.N_beamsets_ctrl - N_pol_bf = constants.N_pol_bf + N_pol_bf = constants.N_pol P_sum = constants.P_sum # ----------------- @@ -165,7 +165,7 @@ class Beamlet(opcua_device): FPGA_bf_weights_xy_yx_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xy_yx_R"], datatype=numpy.uint32, dims=(A_pn * N_pol * N_beamlets_ctrl, N_pn)) FPGA_bf_weights_xy_yx_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xy_yx_RW"], datatype=numpy.uint32, dims=(A_pn * N_pol * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) - # cint16[N_pn][N_pol_bf][A_pn][N_pol][N_beamlets_ctrl] + # cint16[N_pn][N_pol][A_pn][N_pol][N_beamlets_ctrl] # Full Jones matrix of BF weights. FPGA_bf_weights_xx_xy_yx_yy_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_xy_yx_yy_R"], datatype=numpy.uint32, dims=(N_pn, N_pol_bf, A_pn, N_pol, N_beamlets_ctrl)) FPGA_bf_weights_xx_xy_yx_yy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_xy_yx_yy_RW"], datatype=numpy.uint32, dims=(N_pn, N_pol_bf, A_pn, N_pol, N_beamlets_ctrl), access=AttrWriteType.READ_WRITE) @@ -320,7 +320,7 @@ class Beamlet(opcua_device): def _subband_frequencies(subbands: numpy.ndarray, clock: int, nyquist_zones: numpy.ndarray) -> numpy.ndarray: """ Obtain the frequencies of each subband, given a clock and an antenna type. """ - subband_width = clock / constants.N_subband_pts + subband_width = clock / constants.N_subband_res base_subbands = nyquist_zones * constants.N_subbands # broadcast clock across frequencies diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py index 6770f738a0a43e23919f8a73cf49f012f3827a21..b7ae61b8f7fd3d6720a4561b8c8eae1181933b98 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py @@ -73,10 +73,10 @@ class DigitalBeam(beam_device): dtype=numpy.float64, fget=lambda self: self._delays.statistics["last"] or 0) input_select_RW = attribute(doc='Selection of inputs to use for forming each beamlet. Allows selecting broken antennas.', - dtype=((bool,),), max_dim_x=constants.N_beamlets_ctrl, max_dim_y=constants.S_ant, access=AttrWriteType.READ_WRITE, fisallowed="is_attribute_access_allowed") + dtype=((bool,),), max_dim_x=constants.N_beamlets_ctrl, max_dim_y=constants.MAX_ANTENNA, access=AttrWriteType.READ_WRITE, fisallowed="is_attribute_access_allowed") antenna_select_RW = attribute(doc='Selection of antennas desired to use for forming each beamlet (= a subset of input_select of the configured antennas). Unselects broken antennas.', - dtype=((bool,),), max_dim_x=constants.N_beamlets_ctrl, max_dim_y=constants.S_ant, access=AttrWriteType.READ_WRITE, fisallowed="is_attribute_access_allowed") + dtype=((bool,),), max_dim_x=constants.N_beamlets_ctrl, max_dim_y=constants.MAX_ANTENNA, access=AttrWriteType.READ_WRITE, fisallowed="is_attribute_access_allowed") nr_inputs_R = attribute(doc='Number of configured inputs from the associated antenna field.', dtype=numpy.uint32, fget="nr_inputs") @@ -144,7 +144,7 @@ class DigitalBeam(beam_device): # Generate positions for all FPGA inputs. # Use reference position for any missing antennas so they always get a delay of 0 - input_itrf = numpy.array([reference_itrf] * constants.S_ant) + input_itrf = numpy.array([reference_itrf] * constants.MAX_ANTENNA) for antenna_nr, (fpga_nr, input_nr) in enumerate(self.antennafield_proxy.Antenna_to_SDP_Mapping_R): if input_nr >= 0: input_itrf[fpga_nr * constants.A_pn + input_nr] = antenna_itrf[antenna_nr] @@ -157,7 +157,7 @@ class DigitalBeam(beam_device): self.relative_input_positions = input_itrf - reference_itrf # use all antennas in the mapping for all beamlets, unless specified otherwise - self.write_input_select_RW(numpy.zeros((constants.S_ant, constants.N_beamlets_ctrl), dtype=bool)) + self.write_input_select_RW(numpy.zeros((constants.MAX_ANTENNA, constants.N_beamlets_ctrl), dtype=bool)) self.write_antenna_select_RW(numpy.ones((self.nr_inputs(), constants.N_beamlets_ctrl), dtype=bool)) # -------- @@ -172,7 +172,7 @@ class DigitalBeam(beam_device): Returns delays[antenna][beamlet] """ - delays = numpy.zeros((constants.S_ant, constants.N_beamlets_ctrl), dtype=numpy.float64) + delays = numpy.zeros((constants.MAX_ANTENNA, constants.N_beamlets_ctrl), dtype=numpy.float64) d = self.delay_calculator d.set_measure_time(timestamp) @@ -186,7 +186,7 @@ class DigitalBeam(beam_device): """ Converts an array with dimensions [antenna][beamlet] -> [fpga_nr][input_nr][pol_nr][beamlet] by repeating the values for both polarisations. """ - assert arr.shape == (constants.S_ant, constants.N_beamlets_ctrl) + assert arr.shape == (constants.MAX_ANTENNA, constants.N_beamlets_ctrl) # Each antenna maps on [fpga_nr][input_nr][0] and [fpga_nr][input_nr][1], and we work # with [antenna][beamlet], so we have to interleave copies of the input array per beamlet diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py index 2ff880cf4e4a6a586f39a058a0dd71e878a79ff5..1e1b24f233444b1a6214344078d55a1f8ec4d682 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py @@ -69,7 +69,7 @@ class SDP(opcua_device): dtype='DevVarDoubleArray', mandatory=False, # Emit a signal on subband 102 - default_value=[[constants.DEFAULT_SUBBAND * float(constants.CLK_200_MHZ) / constants.N_subband_pts] * S_pn] * N_pn + default_value=[[constants.DEFAULT_SUBBAND * float(constants.CLK_200_MHZ) / constants.N_subband_res] * S_pn] * N_pn ) FPGA_wg_phase_RW_default = device_property( diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py index 117742d5fe4ac5acf6846664b556f6903f8b9c0c..4f1d62947356e90173fcdd4095a8088dcb29576d 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py @@ -40,6 +40,9 @@ class Statistics(opcua_device): def STATISTICS_COLLECTOR_CLASS(self): raise NotImplementedError + # Maximum number of supported simultaneous replicator clients + MAX_STATISTICS_CLIENTS = 128 + # ----------------- # Device Properties # ----------------- @@ -64,7 +67,7 @@ class Statistics(opcua_device): # number of UDP packets that were dropped because we couldn't keep up with processing nof_packets_dropped_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "nof_packets_dropped"}, datatype=numpy.uint64) # last packet we processed - last_packet_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "last_packet"}, dims=(constants.MAX_PACKET_SIZE,), datatype=numpy.uint8) + last_packet_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "last_packet"}, dims=(constants.MAX_ETH_FRAME_SIZE,), datatype=numpy.uint8) # when last packet was received last_packet_timestamp_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "last_packet_timestamp"}, datatype=numpy.uint64) @@ -73,7 +76,7 @@ class Statistics(opcua_device): queue_collector_fill_percentage_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "queue", "parameter": "collector_fill_percentage"}, datatype=numpy.uint64) queue_replicator_fill_percentage_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "queue", "parameter": "replicator_fill_percentage"}, datatype=numpy.uint64) - replicator_clients_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "clients"}, dims=(constants.MAX_STATISTICS_CLIENTS,), datatype=str) + replicator_clients_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "clients"}, dims=(MAX_STATISTICS_CLIENTS,), datatype=str) replicator_nof_bytes_sent_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "nof_bytes_sent"}, datatype=numpy.uint64) replicator_nof_packets_sent_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "nof_packets_sent"}, datatype=numpy.uint64) @@ -84,7 +87,7 @@ class Statistics(opcua_device): # number of invalid (non-SST) packets received nof_invalid_packets_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_invalid_packets"}, datatype=numpy.uint64) # last packet that could not be parsed - last_invalid_packet_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "last_invalid_packet"}, dims=(constants.MAX_PACKET_SIZE,), datatype=numpy.uint8) + last_invalid_packet_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "last_invalid_packet"}, dims=(constants.MAX_ETH_FRAME_SIZE,), datatype=numpy.uint8) # what the last exception was last_invalid_packet_exception_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "last_invalid_packet_exception"}, datatype=str) diff --git a/tangostationcontrol/tangostationcontrol/devices/tilebeam.py b/tangostationcontrol/tangostationcontrol/devices/tilebeam.py index c175963a3533163e888119d8d7ac811b410dab37..1207eaab54560684d89dbe9e8c2726fd960b8c85 100644 --- a/tangostationcontrol/tangostationcontrol/devices/tilebeam.py +++ b/tangostationcontrol/tangostationcontrol/devices/tilebeam.py @@ -58,7 +58,7 @@ class TileBeam(beam_device): # Retrieve positions from AntennaField device Antenna_Reference_itrf = self.antennafield_proxy.Antenna_Reference_itrf_R - HBAT_antenna_itrf_offsets = self.antennafield_proxy.HBAT_antenna_itrf_offsets_R.reshape(self._nr_tiles, constants.N_te, constants.N_xyz) + HBAT_antenna_itrf_offsets = self.antennafield_proxy.HBAT_antenna_itrf_offsets_R.reshape(self._nr_tiles, constants.N_elements, constants.N_xyz) # a delay calculator for each tile self.HBAT_delay_calculators = [Delays(reference_itrf) for reference_itrf in Antenna_Reference_itrf] @@ -77,7 +77,7 @@ class TileBeam(beam_device): Returns delays[tile][element] """ - delays = numpy.zeros((self._nr_tiles, constants.N_te), dtype=numpy.float64) + delays = numpy.zeros((self._nr_tiles, constants.N_elements), dtype=numpy.float64) for tile in range(self._nr_tiles): # initialise delay calculator @@ -107,7 +107,7 @@ class TileBeam(beam_device): @TimeIt() def _apply_weights(self, pointing_direction: numpy.array, timestamp: datetime.datetime, bf_delay_steps: numpy.array): # Write weights to RECV through the AntennaToRecvMapper - self.antennafield_proxy.HBAT_bf_delay_steps_RW = bf_delay_steps.reshape(self._nr_tiles, constants.N_te * constants.N_pol) + self.antennafield_proxy.HBAT_bf_delay_steps_RW = bf_delay_steps.reshape(self._nr_tiles, constants.N_elements * constants.N_pol) # Record where we now point to, now that we've updated the weights. # Only the entries within the mask have been updated