diff --git a/tangostationcontrol/tangostationcontrol/clients/statistics_client.py b/tangostationcontrol/tangostationcontrol/clients/statistics_client.py index e602b410ab202906f2953ec1a722b74a2ff64734..08d2889d0b61c5bd9c9ce5468e35ac6bf82fdffb 100644 --- a/tangostationcontrol/tangostationcontrol/clients/statistics_client.py +++ b/tangostationcontrol/tangostationcontrol/clients/statistics_client.py @@ -99,10 +99,10 @@ class StatisticsClient(AsyncCommClient): def read_function(): if annotation.get("reshape", False): # force array into the shape of the attribute - if attribute.max_dim_y > 1: - return self.collector.parameters[parameter].reshape(attribute.max_dim_y, attribute.max_dim_x) + if attribute.dim_y > 1: + return self.collector.parameters[parameter].reshape(attribute.dim_y, attribute.dim_x) else: - return self.collector.parameters[parameter].reshape(attribute.max_dim_x) + return self.collector.parameters[parameter].reshape(attribute.dim_x) else: return self.collector.parameters[parameter] elif annotation["type"] == "udp": diff --git a/tangostationcontrol/tangostationcontrol/devices/apsct.py b/tangostationcontrol/tangostationcontrol/devices/apsct.py index 20b161e97b16f99d352340246770d5db9f99f3e3..6a3ef5ff8a609eecbc86242d745c7fcb97f61b1a 100644 --- a/tangostationcontrol/tangostationcontrol/devices/apsct.py +++ b/tangostationcontrol/tangostationcontrol/devices/apsct.py @@ -79,12 +79,12 @@ class APSCT(opcua_device): def read_APSCT_error_R(self): return ((self.proxy.APSCTTR_I2C_error_R > 0) - | self.alarm_val("APSCT_PCB_ID_R") - | ~self.proxy.APSCT_INPUT_10MHz_good_R - | (~self.proxy.APSCT_INPUT_PPS_good_R & ~self.proxy.ASPCT_PPS_ignore_R) - | (~self.proxy.APSCT_PLL_160MHz_locked_R & ~self.proxy.APSCT_PLL_200MHz_locked_R) - | (self.proxy.APSCT_PLL_200MHz_locked_R & self.proxy.APSCT_PLL_200MHz_error_R) - | (self.proxy.APSCT_PLL_160MHz_locked_R & self.proxy.APSCT_PLL_160MHz_error_R) + or self.alarm_val("APSCT_PCB_ID_R") + or (not self.proxy.APSCT_INPUT_10MHz_good_R) + or (not self.proxy.APSCT_INPUT_PPS_good_R and not self.proxy.APSCT_PPS_ignore_R) + or (not self.proxy.APSCT_PLL_160MHz_locked_R and not self.proxy.APSCT_PLL_200MHz_locked_R) + or (self.proxy.APSCT_PLL_200MHz_locked_R and self.proxy.APSCT_PLL_200MHz_error_R) + or (self.proxy.APSCT_PLL_160MHz_locked_R and self.proxy.APSCT_PLL_160MHz_error_R) ) APSCT_TEMP_error_R = attribute(dtype=bool) @@ -95,13 +95,13 @@ class APSCT(opcua_device): def read_APSCT_VOUT_error_R(self): return ( self.alarm_val("APSCT_PWR_PPSDIST_3V3_R") - | self.alarm_val("APSCT_PWR_CLKDIST1_3V3_R") - | self.alarm_val("APSCT_PWR_CLKDIST2_3V3_R") - | self.alarm_val("APSCT_PWR_CTRL_3V3_R") - | self.alarm_val("APSCT_PWR_INPUT_3V3_R") - | (self.proxy.APSCT_PWR_PLL_160MHz_on_R & self.alarm_val("APSCT_PWR_PLL_160MHz_3V3_R")) - | (self.proxy.APSCT_PWR_PLL_200MHz_on_R & self.alarm_val("APSCT_PWR_PLL_200MHz_3V3_R")) - | ~self.proxy.APSCT_PWR_on_R + or self.alarm_val("APSCT_PWR_CLKDIST1_3V3_R") + or self.alarm_val("APSCT_PWR_CLKDIST2_3V3_R") + or self.alarm_val("APSCT_PWR_CTRL_3V3_R") + or self.alarm_val("APSCT_PWR_INPUT_3V3_R") + or (self.proxy.APSCT_PWR_PLL_160MHz_on_R and self.alarm_val("APSCT_PWR_PLL_160MHz_3V3_R")) + or (self.proxy.APSCT_PWR_PLL_200MHz_on_R and self.alarm_val("APSCT_PWR_PLL_200MHz_3V3_R")) + or (not self.proxy.APSCT_PWR_on_R) ) # -------- diff --git a/tangostationcontrol/tangostationcontrol/devices/apspu.py b/tangostationcontrol/tangostationcontrol/devices/apspu.py index d014a0eae151d89e77c71a9f0048dfb5e01bcd6b..6be213696c16efc69ca8f20319efc03cfc0f4fe7 100644 --- a/tangostationcontrol/tangostationcontrol/devices/apspu.py +++ b/tangostationcontrol/tangostationcontrol/devices/apspu.py @@ -67,10 +67,10 @@ class APSPU(opcua_device): def read_APSPU_error_R(self): return ((self.proxy.APSPUTR_I2C_error_R > 0) - | self.alarm_val("APSPU_PCB_ID_R") - | self.alarm_val("APSPU_FAN1_RPM_R") - | self.alarm_val("APSPU_FAN2_RPM_R") - | self.alarm_val("APSPU_FAN3_RPM_R")) + or self.alarm_val("APSPU_PCB_ID_R") + or self.alarm_val("APSPU_FAN1_RPM_R") + or self.alarm_val("APSPU_FAN2_RPM_R") + or self.alarm_val("APSPU_FAN3_RPM_R")) APSPU_IOUT_error_R = attribute(dtype=bool) APSPU_TEMP_error_R = attribute(dtype=bool) @@ -78,20 +78,20 @@ class APSPU(opcua_device): def read_APSPU_IOUT_error_R(self): return ( self.alarm_val("APSPU_LBA_IOUT_R") - | self.alarm_val("APSPU_RCU2A_IOUT_R") - | self.alarm_val("APSPU_RCU2D_IOUT_R") + or self.alarm_val("APSPU_RCU2A_IOUT_R") + or self.alarm_val("APSPU_RCU2D_IOUT_R") ) def read_APSPU_TEMP_error_R(self): return ( self.alarm_val("APSPU_LBA_TEMP_R") - | self.alarm_val("APSPU_RCU2A_TEMP_R") - | self.alarm_val("APSPU_RCU2D_TEMP_R") + or self.alarm_val("APSPU_RCU2A_TEMP_R") + or self.alarm_val("APSPU_RCU2D_TEMP_R") ) def read_APSPU_VOUT_error_R(self): return ( self.alarm_val("APSPU_LBA_VOUT_R") - | self.alarm_val("APSPU_RCU2A_VOUT_R") - | self.alarm_val("APSPU_RCU2D_VOUT_R") + or self.alarm_val("APSPU_RCU2A_VOUT_R") + or self.alarm_val("APSPU_RCU2D_VOUT_R") ) # -------- diff --git a/tangostationcontrol/tangostationcontrol/devices/beam.py b/tangostationcontrol/tangostationcontrol/devices/beam.py index 9540560a9537eca57d16e208defd44dffa8d9814..3709bc5602dda655892b6d318e59c2eef77de716 100644 --- a/tangostationcontrol/tangostationcontrol/devices/beam.py +++ b/tangostationcontrol/tangostationcontrol/devices/beam.py @@ -12,7 +12,7 @@ import datetime from json import loads from tango.server import attribute, command, device_property -from tango import AttrWriteType, DebugIt, DevState, DeviceProxy, DevVarStringArray, DevVarDoubleArray, DevString +from tango import AttrWriteType, DebugIt, DevState, DeviceProxy, DevVarStringArray, DevVarDoubleArray, DevString, DevSource from threading import Thread, Lock, Condition # Additional import @@ -67,6 +67,16 @@ class Beam(lofar_device): dtype=(numpy.double,), max_dim_x=96, fget=lambda self: self._hbat_pointing_timestamp_r) + HBAT_tracking_enabled_R = attribute(access=AttrWriteType.READ, + doc="Whether the HBAT tile beam is updated periodically", + dtype=numpy.bool, + fget=lambda self: self.HBAT_beam_tracker.is_alive()) + + HBAT_tracking_enabled_RW = attribute(access=AttrWriteType.READ_WRITE, + doc="Whether the HBAT tile beam should be updated periodically", + dtype=numpy.bool, + fget=lambda self: self._hbat_tracking_enabled_rw) + # Directory where the casacore measures that we use, reside. We configure ~/.casarc to # use the symlink /opt/IERS/current, which we switch to the actual set of files to use. measures_directory_R = attribute(dtype=str, access=AttrWriteType.READ, fget = lambda self: get_measures_directory()) @@ -86,8 +96,12 @@ class Beam(lofar_device): self._hbat_pointing_direction_r = numpy.zeros((96,3), dtype="<U32") self._hbat_pointing_direction_rw = numpy.array([["AZELGEO","0deg","90deg"]] * 96, dtype="<U32") + # Initialise tracking control + self._hbat_tracking_enabled_rw = True + # Set a reference of RECV device self.recv_proxy = DeviceProxy("STAT/RECV/1") + self.recv_proxy.set_source(DevSource.DEV) # Retrieve positions from RECV device HBAT_reference_itrf = self.recv_proxy.HBAT_reference_itrf_R @@ -107,15 +121,15 @@ class Beam(lofar_device): super().configure_for_on() # Start beam tracking thread - self.HBAT_beam_tracker.start() + if self._hbat_tracking_enabled_rw: + self.HBAT_beam_tracker.start() - @log_exceptions + @log_exceptions() def configure_for_off(self): - super().configure_for_off() - # Stop thread object self.HBAT_beam_tracker.stop() - self.HBAT_beam_tracker = None + + super().configure_for_off() # -------- # internal functions @@ -132,6 +146,15 @@ class Beam(lofar_device): # force update across tiles if pointing changes self.HBAT_beam_tracker.force_update() + logger.info("Pointing direction update requested") + + def write_HBAT_tracking_enabled_RW(self, value): + self._hbat_tracking_enabled_rw = value + + if value: + self.HBAT_beam_tracker.start() + else: + self.HBAT_beam_tracker.stop() def _HBAT_delays(self, pointing_direction: numpy.array, timestamp: datetime.datetime = datetime.datetime.now()): """ @@ -166,7 +189,7 @@ class Beam(lofar_device): # Record where we now point to, now that we've updated the weights. # Only the entries within the mask have been updated - mask = self.recv_proxy.Ant_mask_RW.flatten() + mask = self.recv_proxy.ANT_mask_RW.flatten() for rcu in range(96): if mask[rcu]: self._hbat_pointing_direction_r[rcu] = pointing_direction[rcu] @@ -225,6 +248,7 @@ class Beam(lofar_device): @command(dtype_in=DevVarStringArray) @DebugIt() + @log_exceptions() @only_in_states([DevState.ON]) def HBAT_set_pointing(self, pointing_direction: list, timestamp: datetime.datetime = datetime.datetime.now()): """ @@ -275,25 +299,39 @@ class BeamTracker(): """ Object that encapsulates a Thread, resposible for beam tracking operations """ def __init__(self, device: lofar_device): - self.thread = Thread(target=self._update_HBAT_pointing_direction) + self.thread = None self.device = device # Condition to trigger a forced update or early abort self.update_lock = Lock() self.update_condition = Condition(self.update_lock) + + # Whether the pointing has to be forced updated + self.stale_pointing = True def start(self): """ Starts the Beam Tracking thread """ + if self.thread: + # already started + return + self.done = False + self.thread = Thread(target=self._update_HBAT_pointing_direction, name=f"BeamTracker of {self.device.get_name()}") self.thread.start() + + logger.info("BeamTracking thread started") def is_alive(self): """ Returns True just before the Thread run() method starts until just after the Thread run() method terminates. """ - return self.thread.is_alive() + return self.thread and self.thread.is_alive() def force_update(self): """ Force the pointing to be updated. """ + self.stale_pointing = True + self.unlock_thread() + + def unlock_thread(self): # inform the thread to stop waiting with self.update_lock: self.update_condition.notify() @@ -301,6 +339,11 @@ class BeamTracker(): def stop(self): """ Stops the Beam Tracking loop """ + if not self.thread: + return + + logger.info("BeamTracking thread stopping") + self.done = True self.force_update() @@ -309,6 +352,10 @@ class BeamTracker(): if self.is_alive(): logger.error("BeamTracking Thread did not properly terminate") + + self.thread = None + + logger.info("BeamTracking thread stopped") def _get_sleep_time(self): """ Computes the sleep time (in seconds) that needs to be waited for the next beam tracking update """ @@ -324,14 +371,22 @@ class BeamTracker(): return sleep_time + self.device.HBAT_beam_tracking_interval else: return sleep_time - + + # @fault_on_error routes errors here. we forward them to our device + def Fault(self, msg): + self.device.Fault(msg) + + @log_exceptions() + @fault_on_error() def _update_HBAT_pointing_direction(self): """ Updates the beam weights using a fixed interval of time """ # Check if flag beamtracking is true with self.update_lock: while not self.done: - self.device.HBAT_set_pointing(numpy.array(self.device.proxy.HBAT_pointing_direction_RW).flatten()) + self.stale_pointing = False + self.device._HBAT_set_pointing(self.device._hbat_pointing_direction_rw, datetime.datetime.now()) # sleep until the next update, or when interrupted (this releases the lock, allowing for notification) - self.update_condition.wait(self._get_sleep_time()) + # note that we need wait_for as conditions can be triggered multiple times in succession + self.update_condition.wait_for(lambda: self.done or self.stale_pointing, self._get_sleep_time()) diff --git a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py index bb9bf90099aa3b96bdc83011678481dca720191f..6943f86d4bde44010662cbcea45cb350ca271cea 100644 --- a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py @@ -430,5 +430,8 @@ class lofar_device(Device, metaclass=DeviceMeta): alarm_state |= value <= value.dtype.type(alarms.min_alarm) # return alarm state, with the same dimensions as the attribute - return alarm_state + if is_scalar: + return alarm_state.item() + else: + return alarm_state diff --git a/tangostationcontrol/tangostationcontrol/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py index db29fe62c791a6b75744954f1f45090b7f777414..b853b60406511930ae262458564bb52eff111fe1 100644 --- a/tangostationcontrol/tangostationcontrol/devices/recv.py +++ b/tangostationcontrol/tangostationcontrol/devices/recv.py @@ -196,9 +196,9 @@ class RECV(opcua_device): RECV_VOUT_error_R = attribute(dtype=(bool,), max_dim_x=32) def read_RECV_IOUT_error_R(self): - return self.proxy.ANT_mask_RW & ( + return (self.proxy.ANT_mask_RW & ( self.alarm_val("RCU_PWR_ANT_IOUT_R") - ).any(axis=1) + )).any(axis=1) def read_RECV_TEMP_error_R(self): # Don't apply the mask here --- we always want to know if things get too hot! @@ -210,13 +210,13 @@ class RECV(opcua_device): return (self.proxy.ANT_mask_RW & ( self.alarm_val("RCU_PWR_ANT_VIN_R") | self.alarm_val("RCU_PWR_ANT_VOUT_R") - ).any(axis=1) | (self.proxy.RCU_mask_RW & ( + )).any(axis=1) | (self.proxy.RCU_mask_RW & ( self.alarm_val("RCU_PWR_1V8_R") | self.alarm_val("RCU_PWR_2V5_R") | self.alarm_val("RCU_PWR_3V3_R") | ~self.proxy.RCU_PWR_DIGITAL_on_R | ~self.proxy.RCU_PWR_good_R - ))) + )) # -------- # overloaded functions diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py index eb9d1c5d41d917a98f9cdd8687630eae0ff9067a..a483aec43eacf571a6344ee1b1823cf62af96a92 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py @@ -129,8 +129,6 @@ class SDP(opcua_device): FPGA_subband_weights_R = attribute_wrapper(comms_annotation=["FPGA_subband_weights_R"], datatype=numpy.uint32, dims=(12 * 512, 16)) FPGA_subband_weights_RW = attribute_wrapper(comms_annotation=["FPGA_subband_weights_RW"], datatype=numpy.uint32, dims=(12 * 512, 16), access=AttrWriteType.READ_WRITE) FPGA_temp_R = attribute_wrapper(comms_annotation=["FPGA_temp_R"], datatype=numpy.float_, dims=(16,)) - FPGA_weights_R = attribute_wrapper(comms_annotation=["FPGA_weights_R"], datatype=numpy.int16, dims=(12 * 488 * 2, 16)) - FPGA_weights_RW = attribute_wrapper(comms_annotation=["FPGA_weights_RW"], datatype=numpy.int16, dims=(12 * 488 * 2, 16), access=AttrWriteType.READ_WRITE) FPGA_wg_amplitude_R = attribute_wrapper(comms_annotation=["FPGA_wg_amplitude_R"], datatype=numpy.float_, dims=(12, 16)) FPGA_wg_amplitude_RW = attribute_wrapper(comms_annotation=["FPGA_wg_amplitude_RW"], datatype=numpy.float_, dims=(12, 16), access=AttrWriteType.READ_WRITE) FPGA_wg_enable_R = attribute_wrapper(comms_annotation=["FPGA_wg_enable_R"], datatype=numpy.bool_, dims=(12, 16)) @@ -211,11 +209,12 @@ class SDP(opcua_device): # ---------- FPGA_error_R = attribute(dtype=(bool,), max_dim_x=16) FPGA_processing_error_R = attribute(dtype=(bool,), max_dim_x=16) + FPGA_input_error_R = attribute(dtype=(bool,), max_dim_x=16) def read_FPGA_error_R(self): return self.proxy.TR_fpga_mask_RW & ( self.proxy.TR_fpga_communication_error_R - | self.proxy.FPGA_firmware_version_R != "" + | (self.proxy.FPGA_firmware_version_R != "") | (self.proxy.FPGA_jesd204b_csr_dev_syncn_R == 0).any(axis=1) ) @@ -223,8 +222,12 @@ class SDP(opcua_device): return self.proxy.TR_fpga_mask_RW & ( ~self.proxy.FPGA_processing_enable_R | (self.proxy.FPGA_boot_image_R == 0) - | self.proxy.FPGA_wg_enable_R.any(axis=1) - | (self.proxy.FPGA_signal_input_rms_R == 0) + ) + + def read_FPGA_input_error_R(self): + return self.proxy.TR_fpga_mask_RW & ( + self.proxy.FPGA_wg_enable_R.any(axis=1) + | (self.proxy.FPGA_signal_input_rms_R == 0).any(axis=1) ) # -------- @@ -237,7 +240,7 @@ class SDP(opcua_device): # wait for the firmware to be loaded (ignoring masked out elements) mask = self.proxy.TR_fpga_mask_RW - self.wait_attribute("FPGA_boot_image_R", lambda attr: (attr == 1) | ~mask, 10) + self.wait_attribute("FPGA_boot_image_R", lambda attr: ((attr == 1) | ~mask).all(), 10) # -------- # Commands diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py index 0e96366bcaa2758921ef4431a82e01ac653d19ad..338c5506458f0f63bf65fd33dd34f4d3e6c4122e 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py @@ -13,7 +13,7 @@ # PyTango imports from tango.server import device_property -from tango import DeviceProxy +from tango import DeviceProxy, DevSource # Additional import import asyncio @@ -126,6 +126,7 @@ class Statistics(opcua_device): # proxy the SDP device in case we need the FPGA mask self.sdp_proxy = DeviceProxy("STAT/SDP/1") + self.sdp_proxy.set_source(DevSource.DEV) async def _connect_statistics(self): # map an access helper class diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py index 05a76d9b39d0245251c1ddeb2ee67eb9f74870a9..8c88a3a638eafdd152e0ee39be5db3dfc8e1063f 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py @@ -151,7 +151,7 @@ class XSTCollector(StatisticsCollector): # When the youngest data for each subband was received "xst_timestamps": numpy.zeros((self.MAX_PARALLEL_SUBBANDS,), dtype=numpy.float64), "xst_subbands": numpy.zeros((self.MAX_PARALLEL_SUBBANDS,), dtype=numpy.uint16), - "integration_intervals": numpy.zeros((self.MAX_PARALLEL_SUBBANDS,), dtype=numpy.float32), + "xst_integration_intervals": numpy.zeros((self.MAX_PARALLEL_SUBBANDS,), dtype=numpy.float32), }) return defaults @@ -234,7 +234,7 @@ class XSTCollector(StatisticsCollector): self.parameters["xst_timestamps"][subband_slot] = numpy.float64(fields.timestamp().timestamp()) self.parameters["xst_conjugated"][subband_slot, block_index] = conjugated self.parameters["xst_subbands"][subband_slot] = numpy.uint16(fields.subband_index) - self.parameters["integration_intervals"][subband_slot] = fields.integration_interval() + self.parameters["xst_integration_intervals"][subband_slot] = fields.integration_interval() def xst_values(self, subband_indices=range(MAX_PARALLEL_SUBBANDS)): """ xst_blocks, but as a matrix[len(subband_indices)][MAX_INPUTS][MAX_INPUTS] of complex values. @@ -246,7 +246,7 @@ class XSTCollector(StatisticsCollector): xst_blocks = self.parameters["xst_blocks"] xst_conjugated = self.parameters["xst_conjugated"] - for subband_index in subband_indices: + for matrix_idx, subband_index in enumerate(subband_indices): for block_index in range(self.MAX_BLOCKS): # convert real/imag int to complex float values. this works as real/imag come in pairs block = xst_blocks[subband_index][block_index].astype(numpy.float32).view(numpy.complex64) @@ -263,7 +263,7 @@ class XSTCollector(StatisticsCollector): first_baseline = (first_baseline[0] * self.BLOCK_LENGTH, first_baseline[1] * self.BLOCK_LENGTH) # copy block into matrix - matrix[subband_index][first_baseline[0]:first_baseline[0]+self.BLOCK_LENGTH, first_baseline[1]:first_baseline[1]+self.BLOCK_LENGTH] = block + matrix[matrix_idx][first_baseline[0]:first_baseline[0]+self.BLOCK_LENGTH, first_baseline[1]:first_baseline[1]+self.BLOCK_LENGTH] = block return matrix diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py index 38c9f76562530ff7735c54214b8bef3d023bbc9c..8be9cdb483ef4f21818791009a19e8fbc2b91cb9 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py @@ -116,15 +116,15 @@ class XST(Statistics): # number of packets with invalid payloads nof_payload_errors_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_payload_errors"}, dims=(XSTCollector.MAX_FPGAS,), datatype=numpy.uint64) # latest XSTs - xst_blocks_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_blocks", "reshape": True}, dims=(XSTCollector.BLOCK_LENGTH * XSTCollector.BLOCK_LENGTH * XSTCollector.VALUES_PER_COMPLEX, XSTCollector.MAX_BLOCKS), datatype=numpy.int64) + xst_blocks_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_blocks", "reshape": True}, dims=(XSTCollector.MAX_BLOCKS * XSTCollector.BLOCK_LENGTH * XSTCollector.BLOCK_LENGTH * XSTCollector.VALUES_PER_COMPLEX, XSTCollector.MAX_PARALLEL_SUBBANDS), datatype=numpy.int64) # whether the values in the block are conjugated and transposed - xst_conjugated_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_conjugated", "reshape": True}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.bool_) + xst_conjugated_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_conjugated", "reshape": True}, dims=(XSTCollector.MAX_BLOCKS, XSTCollector.MAX_PARALLEL_SUBBANDS), datatype=numpy.bool_) # reported timestamp for each subband in the latest XSTs xst_timestamp_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_timestamps"}, dims=(XSTCollector.MAX_PARALLEL_SUBBANDS,), datatype=numpy.uint64) # which subband the XSTs describe xst_subbands_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_subbands"}, dims=(XSTCollector.MAX_PARALLEL_SUBBANDS,), datatype=numpy.uint16) # integration interval for each subband in the latest XSTs - integration_interval_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "integration_intervals"}, dims=(XSTCollector.MAX_PARALLEL_SUBBANDS,), datatype=numpy.float32) + xst_integration_interval_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_integration_intervals"}, dims=(XSTCollector.MAX_PARALLEL_SUBBANDS,), datatype=numpy.float32) # xst_R, but as a matrix of subband x (input x input) xst_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS * XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_PARALLEL_SUBBANDS, dtype=((numpy.float32,),)) @@ -145,57 +145,57 @@ class XST(Statistics): return numpy.angle(self.statistics_client.collector.xst_values()).reshape(XSTCollector.MAX_PARALLEL_SUBBANDS, XSTCollector.MAX_INPUTS * XSTCollector.MAX_INPUTS) # xst_R, but as a matrix of input x input, for each specific subband index - xst_0_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(0)) - xst_0_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(0)) - xst_0_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(0)) - xst_0_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(0)) - - xst_1_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(1)) - xst_1_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(1)) - xst_1_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(1)) - xst_1_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(1)) - - xst_2_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(2)) - xst_2_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(2)) - xst_2_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(2)) - xst_2_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(2)) - - xst_3_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(3)) - xst_3_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(3)) - xst_3_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(3)) - xst_3_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(3)) - - xst_4_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(4)) - xst_4_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(4)) - xst_4_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(4)) - xst_4_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(4)) - - xst_5_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(5)) - xst_5_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(5)) - xst_5_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(5)) - xst_5_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(5)) - - xst_6_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(6)) - xst_6_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(6)) - xst_6_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(6)) - xst_6_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(6)) - - xst_7_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(7)) - xst_7_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(7)) - xst_7_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(7)) - xst_7_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(7)) + xst_0_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(0)) + xst_0_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(0)) + xst_0_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(0)) + xst_0_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(0)) + + xst_1_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(1)) + xst_1_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(1)) + xst_1_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(1)) + xst_1_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(1)) + + xst_2_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(2)) + xst_2_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(2)) + xst_2_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(2)) + xst_2_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(2)) + + xst_3_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(3)) + xst_3_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(3)) + xst_3_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(3)) + xst_3_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(3)) + + xst_4_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(4)) + xst_4_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(4)) + xst_4_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(4)) + xst_4_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(4)) + + xst_5_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(5)) + xst_5_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(5)) + xst_5_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(5)) + xst_5_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(5)) + + xst_6_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(6)) + xst_6_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(6)) + xst_6_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(6)) + xst_6_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(6)) + + xst_7_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(7)) + xst_7_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(7)) + xst_7_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(7)) + xst_7_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(7)) def read_xst_N_real_R(self, subband_idx): - return numpy.real(self.statistics_client.collector.xst_values(subband_idx)[0]) + return numpy.real(self.statistics_client.collector.xst_values([subband_idx])[0]) def read_xst_N_imag_R(self, subband_idx): - return numpy.imag(self.statistics_client.collector.xst_values(subband_idx)[0]) + return numpy.imag(self.statistics_client.collector.xst_values([subband_idx])[0]) def read_xst_N_power_R(self, subband_idx): - return numpy.abs(self.statistics_client.collector.xst_values(subband_idx)[0]) + return numpy.abs(self.statistics_client.collector.xst_values([subband_idx])[0]) def read_xst_N_phase_R(self, subband_idx): - return numpy.angle(self.statistics_client.collector.xst_values(subband_idx)[0]) + return numpy.angle(self.statistics_client.collector.xst_values([subband_idx])[0]) # ---------- # Summarising Attributes diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/base.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/base.py index db9f51eb7b82d081c5b83ebe04dc998a8e5f1506..7a2609e811432a8c772a239186e16f2f442ba7a3 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/base.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/base.py @@ -61,9 +61,30 @@ class AbstractTestBases: self.self.assertListEqual([], self.proxy.opcua_missing_attributes_R) def test_device_on(self): - """Test if we can transition to on""" + """Test if we can transition off -> standby -> on""" self.proxy.initialise() self.proxy.on() self.assertEqual(DevState.ON, self.proxy.state()) + + def test_device_warm_boot(self): + """Test if we can transition off -> on using a warm boot""" + + self.proxy.warm_boot() + + self.assertEqual(DevState.ON, self.proxy.state()) + + def test_device_read_all_attributes(self): + """Test if we can read all of the exposed attributes in the ON state. + + This test covers the reading logic of all attributes. """ + + self.proxy.initialise() + self.proxy.on() + + for attribute_name in self.proxy.get_attribute_list(): + try: + _ = self.proxy.read_attribute(attribute_name).value + except Exception as e: + raise AssertionError(f"Could not read attribute {attribute_name} from device {self.name}") from e diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beam.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beam.py index 21c93eab4f434350fa091523a421837513aadf9c..381678118b123793767aff753e4461b7916fe5bd 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beam.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beam.py @@ -12,7 +12,6 @@ import numpy import datetime import json -from tango import DevState from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy from .base import AbstractTestBases @@ -36,11 +35,8 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase): # setup RECV recv_proxy = TestDeviceProxy("STAT/RECV/1") recv_proxy.off() - recv_proxy.initialise() - self.assertEqual(DevState.STANDBY, recv_proxy.state()) + recv_proxy.warm_boot() recv_proxy.set_defaults() - recv_proxy.on() - self.assertEqual(DevState.ON, recv_proxy.state()) return recv_proxy def test_HBAT_delays_dims(self): @@ -48,12 +44,7 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase): self.setup_recv_proxy() # setup BEAM - self.proxy.init() - self.proxy.Initialise() - self.assertEqual(DevState.STANDBY, self.proxy.state()) - self.proxy.set_defaults() - self.proxy.on() - self.assertEqual(DevState.ON, self.proxy.state()) + self.proxy.warm_boot() # verify HBAT_delays method returns the correct dimensions HBAT_delays = self.proxy.HBAT_delays(self.pointing_direction) @@ -64,12 +55,8 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase): recv_proxy = self.setup_recv_proxy() # setup BEAM - self.proxy.init() - self.proxy.Initialise() - self.assertEqual(DevState.STANDBY, self.proxy.state()) - self.proxy.set_defaults() - self.proxy.on() - self.assertEqual(DevState.ON, self.proxy.state()) + self.proxy.warm_boot() + self.proxy.HBAT_tracking_enabled_RW = False # Verify attribute is present (all zeros if never used before) HBAT_delays_r1 = numpy.array(recv_proxy.read_attribute('HBAT_BF_delay_steps_RW').value) @@ -89,8 +76,8 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase): # setup RECV as well recv_proxy = self.setup_recv_proxy() - self.proxy.initialise() - self.proxy.on() + self.proxy.warm_boot() + self.proxy.HBAT_tracking_enabled_RW = False # Point to Zenith self.proxy.HBAT_set_pointing(numpy.array([["AZELGEO","0deg","90deg"]] * 96).flatten()) @@ -101,12 +88,39 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase): numpy.testing.assert_equal(calculated_HBAT_delay_steps, expected_HBAT_delay_steps) + def test_pointing_across_horizon(self): + # setup RECV as well + recv_proxy = self.setup_recv_proxy() + + self.proxy.warm_boot() + self.proxy.HBAT_tracking_enabled_RW = False + + # point at north on the horizon + self.proxy.HBAT_set_pointing(["AZELGEO","0deg","0deg"] * 96) + + # obtain delays of the X polarisation of all the elements of the first tile + north_beam_delay_steps = recv_proxy.HBAT_BF_delay_steps_RW[0].reshape(2,4,4)[0] + + # delays must differ under rotation, or our test will give a false positive + self.assertNotEqual(north_beam_delay_steps.tolist(), numpy.rot90(north_beam_delay_steps).tolist()) + + for angle in (90,180,270): + # point at angle degrees (90=E, 180=S, 270=W) + self.proxy.HBAT_set_pointing(["AZELGEO",f"{angle}deg","0deg"] * 96) + + # obtain delays of the X polarisation of all the elements of the first tile + angled_beam_delay_steps = recv_proxy.HBAT_BF_delay_steps_RW[0].reshape(2,4,4)[0] + + expected_delay_steps = numpy.rot90(north_beam_delay_steps, k=-(angle/90)) + + self.assertListEqual(expected_delay_steps.tolist(), angled_beam_delay_steps.tolist(), msg=f"angle={angle}") + def test_delays_same_as_LOFAR_ref_pointing(self): # setup RECV as well recv_proxy = self.setup_recv_proxy() - self.proxy.initialise() - self.proxy.on() + self.proxy.warm_boot() + self.proxy.HBAT_tracking_enabled_RW = False # Point to LOFAR 1 ref pointing (0.929342, 0.952579, J2000) pointings = numpy.array([["J2000", "0.929342rad", "0.952579rad"]] * 96).flatten() @@ -131,3 +145,20 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase): expected_HBAT_delay_steps = numpy.array([24, 25, 27, 29, 17, 18, 20, 21, 10, 11, 13, 14, 3, 4, 5, 7] * 2, dtype=numpy.int64) numpy.testing.assert_equal(calculated_HBAT_delay_steps[0], expected_HBAT_delay_steps) numpy.testing.assert_equal(calculated_HBAT_delay_steps[48], expected_HBAT_delay_steps) + + def test_beam_tracking(self): + # setup RECV as well + recv_proxy = self.setup_recv_proxy() + + self.proxy.warm_boot() + + # check if we're really tracking + self.assertTrue(self.proxy.HBAT_tracking_enabled_R) + + # point somewhere + new_pointings = [("J2000",f"{tile}deg","0deg") for tile in range(96)] + self.proxy.HBAT_pointing_direction_RW = new_pointings + + # check pointing + self.assertListEqual(new_pointings, list(self.proxy.HBAT_pointing_direction_R)) + diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sdp.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sdp.py index 3a075e697c473c9800257b4796f21f7b79e5e430..570d8ce6cee1bc2f1ab833c9d93acdd11cc270f7 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sdp.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sdp.py @@ -19,7 +19,6 @@ class TestDeviceSDP(AbstractTestBases.TestDeviceBase): def test_device_sdp_read_attribute(self): """Test if we can read an attribute obtained over OPC-UA""" - self.proxy.initialise() - self.proxy.on() + self.proxy.warm_boot() self.assertListEqual([True]*16, list(self.proxy.TR_fpga_communication_error_R)) diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sst.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sst.py index 60675e121364b52fb692b2f8461001bfdc78b50a..2398206083533a5fcc40051b6a80e42923a38347 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sst.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sst.py @@ -12,6 +12,7 @@ import sys import time from tango._tango import DevState +from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy from .base import AbstractTestBases @@ -22,14 +23,24 @@ class TestDeviceSST(AbstractTestBases.TestDeviceBase): """Intentionally recreate the device object in each test""" super().setUp("STAT/SST/1") + 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() + + super().test_device_read_all_attributes() + + def setup_sdp(self): + # setup SDP, on which this device depends + sdp_proxy = TestDeviceProxy("STAT/SDP/1") + sdp_proxy.off() + sdp_proxy.warm_boot() + sdp_proxy.set_defaults() + return sdp_proxy + def test_device_sst_send_udp(self): port_property = {"Statistics_Client_TCP_Port": "4998"} self.proxy.put_property(port_property) - self.proxy.initialise() - - self.assertEqual(DevState.STANDBY, self.proxy.state()) - - self.proxy.on() + self.proxy.warm_boot() self.assertEqual(DevState.ON, self.proxy.state()) @@ -44,13 +55,7 @@ class TestDeviceSST(AbstractTestBases.TestDeviceBase): def test_device_sst_connect_tcp_receive(self): port_property = {"Statistics_Client_TCP_Port": "5101"} self.proxy.put_property(port_property) - self.proxy.initialise() - - self.assertEqual(DevState.STANDBY, self.proxy.state()) - - self.proxy.on() - - self.assertEqual(DevState.ON, self.proxy.state()) + self.proxy.warm_boot() time.sleep(2) diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_xst.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_xst.py new file mode 100644 index 0000000000000000000000000000000000000000..071108ee57e025c30fc423d7ce2312b66d5012a7 --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_xst.py @@ -0,0 +1,32 @@ + +# -*- coding: utf-8 -*- +# +# This file is part of the LOFAR 2.0 Station Software +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. +from .base import AbstractTestBases + +from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy + +class TestDeviceSST(AbstractTestBases.TestDeviceBase): + + def setUp(self): + """Intentionally recreate the device object in each test""" + super().setUp("STAT/XST/1") + + 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() + + super().test_device_read_all_attributes() + + def setup_sdp(self): + # setup SDP, on which this device depends + sdp_proxy = TestDeviceProxy("STAT/SDP/1") + sdp_proxy.off() + sdp_proxy.warm_boot() + sdp_proxy.set_defaults() + return sdp_proxy