Skip to content
Snippets Groups Projects
Commit ae0cf270 authored by Jan David Mol's avatar Jan David Mol
Browse files

L2SS-1471: Moved points available in both user and factory image to...

parent be65f8ec
No related branches found
No related tags found
1 merge request!698L2SS-1471: Moved points available in both user and factory image to...
Showing
with 182 additions and 120 deletions
......@@ -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_*",
......
......@@ -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!)
......
......@@ -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(
......
......@@ -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):
......
......@@ -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()
......
......@@ -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)]
......
......@@ -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)
......
......@@ -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,
......
......@@ -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
# ----------
......
......@@ -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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment