From 45d0012b5e0b8c87673ed902f29f6a79a01141c5 Mon Sep 17 00:00:00 2001 From: Jan David Mol <mol@astron.nl> Date: Thu, 3 Mar 2022 14:15:05 +0100 Subject: [PATCH] L2SS-433: Add beamlet device, which takes over responsibility of all beamlet and bf_weight attributes from SDP --- .gitlab-ci.yml | 24 ++++ CDB/LOFAR_ConfigDb.json | 28 ++++ CDB/stations/DTS_ConfigDb.json | 55 +++++++ CDB/stations/LTS_ConfigDb.json | 55 +++++++ CDB/stations/simulators_ConfigDb.json | 55 +++++++ docker-compose/device-beamlet.yml | 47 ++++++ .../startup/01-devices.py | 3 +- sbin/run_integration_test.sh | 6 +- .../docs/source/devices/beamlet.rst | 8 ++ tangostationcontrol/docs/source/index.rst | 1 + tangostationcontrol/setup.cfg | 1 + .../tangostationcontrol/devices/README.md | 3 +- .../tangostationcontrol/devices/boot.py | 5 +- .../devices/sdp/beamlet.py | 136 ++++++++++++++++++ .../tangostationcontrol/devices/sdp/sdp.py | 46 ------ .../default/devices/test_device_beamlet.py | 32 +++++ 16 files changed, 452 insertions(+), 53 deletions(-) create mode 100644 docker-compose/device-beamlet.yml create mode 100644 tangostationcontrol/docs/source/devices/beamlet.rst create mode 100644 tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py create mode 100644 tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 24be19a0d..4b4dd9211 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -97,6 +97,8 @@ docker_build_image_all: - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh unb2-sim latest - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-apsct latest - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-apspu latest + - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-beam latest + - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-beamlet latest - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-boot latest - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-docker latest - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-observation_control latest @@ -250,6 +252,28 @@ docker_build_image_device_apspu: script: # Do not remove 'bash' or statement will be ignored by primitive docker shell - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-apspu $tag +docker_build_image_device_beam: + extends: .base_docker_images_except + only: + refs: + - merge_requests + changes: + - docker-compose/device-beam.yml + - docker-compose/lofar-device-base/* + script: +# Do not remove 'bash' or statement will be ignored by primitive docker shell + - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-beam $tag +docker_build_image_device_beamlet: + extends: .base_docker_images_except + only: + refs: + - merge_requests + changes: + - docker-compose/device-beamlet.yml + - docker-compose/lofar-device-base/* + script: +# Do not remove 'bash' or statement will be ignored by primitive docker shell + - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-beamlet $tag docker_build_image_device_boot: extends: .base_docker_images_except only: diff --git a/CDB/LOFAR_ConfigDb.json b/CDB/LOFAR_ConfigDb.json index c7573e87d..621c84589 100644 --- a/CDB/LOFAR_ConfigDb.json +++ b/CDB/LOFAR_ConfigDb.json @@ -21,6 +21,34 @@ } } }, + "Beamlet": { + "STAT": { + "Beamlet": { + "STAT/Beamlet/1": { + "properties": { + "FPGA_beamlet_output_hdr_udp_destination_port_RW_default": [ + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001" + ] + } + } + } + } + }, "boot": { "STAT": { "Boot": { diff --git a/CDB/stations/DTS_ConfigDb.json b/CDB/stations/DTS_ConfigDb.json index bd797a7ea..7017e28e7 100644 --- a/CDB/stations/DTS_ConfigDb.json +++ b/CDB/stations/DTS_ConfigDb.json @@ -51,6 +51,61 @@ } } }, + "Beamlet": { + "STAT": { + "Beamlet": { + "STAT/Beamlet/1": { + "properties": { + "OPC_Server_Name": [ + "10.99.0.252" + ], + "OPC_Server_Port": [ + "4840" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_beamlet_output_hdr_eth_destination_mac_RW_default": [ + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1" + ], + "FPGA_beamlet_output_hdr_ip_destination_address_RW_default": [ + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250" + ] + } + } + } + } + }, "RECV": { "STAT": { "RECV": { diff --git a/CDB/stations/LTS_ConfigDb.json b/CDB/stations/LTS_ConfigDb.json index faa429379..63b16a78b 100644 --- a/CDB/stations/LTS_ConfigDb.json +++ b/CDB/stations/LTS_ConfigDb.json @@ -13,6 +13,61 @@ } } }, + "Beamlet": { + "STAT": { + "Beamlet": { + "STAT/Beamlet/1": { + "properties": { + "OPC_Server_Name": [ + "dop369.astron.nl" + ], + "OPC_Server_Port": [ + "4840" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_beamlet_output_hdr_eth_destination_mac_RW_default": [ + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00", + "00:07:43:06:c7:00" + ], + "FPGA_beamlet_output_hdr_ip_destination_address_RW_default": [ + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254", + "192.168.0.254" + ] + } + } + } + } + }, "RECV": { "STAT": { "RECV": { diff --git a/CDB/stations/simulators_ConfigDb.json b/CDB/stations/simulators_ConfigDb.json index c9c1b8135..1418898b7 100644 --- a/CDB/stations/simulators_ConfigDb.json +++ b/CDB/stations/simulators_ConfigDb.json @@ -51,6 +51,61 @@ } } }, + "Beamlet": { + "STAT": { + "Beamlet": { + "STAT/Beamlet/1": { + "properties": { + "OPC_Server_Name": [ + "sdptr-sim" + ], + "OPC_Server_Port": [ + "4840" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_beamlet_output_hdr_eth_destination_mac_RW_default": [ + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB", + "01:23:45:67:89:AB" + ], + "FPGA_beamlet_output_hdr_ip_destination_address_RW_default": [ + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1" + ] + } + } + } + } + }, "RECV": { "STAT": { "RECV": { diff --git a/docker-compose/device-beamlet.yml b/docker-compose/device-beamlet.yml new file mode 100644 index 000000000..d54366ccf --- /dev/null +++ b/docker-compose/device-beamlet.yml @@ -0,0 +1,47 @@ +# +# Docker compose file that launches an interactive iTango session. +# +# Connect to the interactive session with 'docker attach itango'. +# Disconnect with the Docker deattach sequence: <CTRL>+<P> <CTRL>+<Q> +# +# Defines: +# - itango: iTango interactive session +# +# Requires: +# - lofar-device-base.yml +# +version: '2' + +services: + device-beamlet: + image: device-beamlet + # build explicitly, as docker-compose does not understand a local image + # being shared among services. + build: + context: .. + dockerfile: docker-compose/lofar-device-base/Dockerfile + args: + SOURCE_IMAGE: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-itango:${TANGO_ITANGO_VERSION} + container_name: ${CONTAINER_NAME_PREFIX}device-beamlet + logging: + driver: "json-file" + options: + max-size: "100m" + max-file: "10" + networks: + - control + ports: + - "5712:5712" # unique port for this DS + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ..:/opt/lofar/tango:rw + environment: + - TANGO_HOST=${TANGO_HOST} + working_dir: /opt/lofar/tango + entrypoint: + - bin/start-ds.sh + # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA + # can't know about our Docker port forwarding + - l2ss-beamlet Beamlet STAT -v -ORBendPoint giop:tcp:0:5712 -ORBendPointPublish giop:tcp:${HOSTNAME}:5712 + restart: unless-stopped diff --git a/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py b/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py index 03e7b75b4..394979833 100644 --- a/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py +++ b/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py @@ -8,7 +8,8 @@ xst = DeviceProxy("STAT/XST/1") unb2 = DeviceProxy("STAT/UNB2/1") boot = DeviceProxy("STAT/Boot/1") beam = DeviceProxy("STAT/Beam/1") +beamlet = DeviceProxy("STAT/Beamlet/1") docker = DeviceProxy("STAT/Docker/1") # Put them in a list in case one wants to iterate -devices = [apsct, apspu, recv, sdp, sst, xst, unb2, boot, beam, docker] +devices = [apsct, apspu, recv, sdp, sst, xst, unb2, boot, beam, beamlet, docker] diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh index c6078a890..17fa54803 100755 --- a/sbin/run_integration_test.sh +++ b/sbin/run_integration_test.sh @@ -14,13 +14,13 @@ cd "$LOFAR20_DIR/docker-compose" || exit 1 # Build only the required images, please do not build everything that makes CI # take really long to finish, especially grafana / jupyter / prometheus. # jupyter is physically large > 2.5gb and overlayfs is really slow. -make build device-sdp device-recv device-sst device-unb2 device-xst +make build device-sdp device-recv device-sst device-unb2 device-xst device-beamlet device-beam make build sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim make build databaseds dsconfig elk integration-test make build archiver-timescale hdbppts-cm hdbppts-es # Start and stop sequence -make stop device-boot device-docker device-apsct device-apspu device-sdp device-recv device-sst device-unb2 device-xst device-beam sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim hdbppts-es hdbppts-cm archiver-timescale +make stop device-boot device-docker device-apsct device-apspu device-sdp device-recv device-sst device-unb2 device-xst device-beamlet device-beam sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim hdbppts-es hdbppts-cm archiver-timescale make start databaseds dsconfig elk # Give dsconfig and databaseds time to start @@ -38,7 +38,7 @@ make start sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim # Give the simulators time to start sleep 5 -make start device-boot device-apsct device-apspu device-sdp device-recv device-sst device-unb2 device-xst device-beam +make start device-boot device-apsct device-apspu device-sdp device-recv device-sst device-unb2 device-xst device-beam device-beamlet # Archive devices -> starting order is important make start archiver-timescale hdbppts-cm hdbppts-es diff --git a/tangostationcontrol/docs/source/devices/beamlet.rst b/tangostationcontrol/docs/source/devices/beamlet.rst new file mode 100644 index 000000000..39e2a5887 --- /dev/null +++ b/tangostationcontrol/docs/source/devices/beamlet.rst @@ -0,0 +1,8 @@ +Beamlet +==================== + +The ``beamlet == DeviceProxy("STAT/Beamlet/1")`` device controls the creation and emission of beamlets. Each beamlet is a signal stream characterised by: + +- The set of antennas to use as input, +- The pointing towards which to beamform these antennas, +- A single subband (frequency) selected from the PPF. diff --git a/tangostationcontrol/docs/source/index.rst b/tangostationcontrol/docs/source/index.rst index 25c71f9f7..f72e468dd 100644 --- a/tangostationcontrol/docs/source/index.rst +++ b/tangostationcontrol/docs/source/index.rst @@ -20,6 +20,7 @@ Even without having access to any LOFAR2.0 hardware, you can install the full st interfaces/overview devices/using devices/beam + devices/beamlet devices/boot devices/docker devices/recv diff --git a/tangostationcontrol/setup.cfg b/tangostationcontrol/setup.cfg index 99960c1ba..44e21e97a 100644 --- a/tangostationcontrol/setup.cfg +++ b/tangostationcontrol/setup.cfg @@ -36,6 +36,7 @@ console_scripts = l2ss-apsct = tangostationcontrol.devices.apsct:main l2ss-apspu = tangostationcontrol.devices.apspu:main l2ss-beam = tangostationcontrol.devices.beam:main + l2ss-beamlet = tangostationcontrol.devices.sdp.beamlet:main l2ss-boot = tangostationcontrol.devices.boot:main l2ss-docker-device = tangostationcontrol.devices.docker_device:main l2ss-observation = tangostationcontrol.devices.observation:main diff --git a/tangostationcontrol/tangostationcontrol/devices/README.md b/tangostationcontrol/tangostationcontrol/devices/README.md index f546fb339..08eaf1f67 100644 --- a/tangostationcontrol/tangostationcontrol/devices/README.md +++ b/tangostationcontrol/tangostationcontrol/devices/README.md @@ -11,8 +11,9 @@ If a new device is added, it will (likely) need to be referenced in several plac - Adjust `tangostationcontrol/tangostationcontrol/devices/boot.py` to add the device to the station initialisation sequence, - Add to `docker-compose/` to create a YaML file to start the device in a docker container. NOTE: it needs a unique 57xx port assigned, - Adjust `tangostationcontrol/setup.cfg` to add an entry point for the device in the package installation, -- Add to `tangostationcontrol/tangostationcontrol/integration_test/devices/` to add an integration test, +- Add to `tangostationcontrol/tangostationcontrol/integration_test/default/devices/` to add an integration test, - Adjust `sbin/run_integration_test.sh` to have the device started when running the integration tests, +- Adjust `.gitlab-ci.yml` to add the device to the `docker_build_image_all` step and to create a `docker_build_image_device_XXX` step, - Add to `docs/source/devices/` to mention the device in the end-user documentation. - Adjust `docs/source/index.rst` to include the newly created file in `docs/source/devices/`. diff --git a/tangostationcontrol/tangostationcontrol/devices/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py index 0bee84c2e..3ae76d7f8 100644 --- a/tangostationcontrol/tangostationcontrol/devices/boot.py +++ b/tangostationcontrol/tangostationcontrol/devices/boot.py @@ -237,10 +237,11 @@ class Boot(lofar_device): "STAT/APSCT/1", "STAT/RECV/1", # RCUs are input for SDP, so initialise them first "STAT/UNB2/1", # Uniboards host SDP, so initialise them first - "STAT/SDP/1", # SDP controls the mask for SST/XST/BST, so initialise it first + "STAT/SDP/1", # SDP controls the mask for SST/XST/BST/Beamlet, so initialise it first "STAT/SST/1", "STAT/XST/1", - "STAT/Beam/1", # Accesses RECV and SDP + "STAT/Beamlet/1", + "STAT/Beam/1", # Accesses RECV and Beamlet ], ) diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py new file mode 100644 index 000000000..36ac75f4e --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" Beamlet Device Server for LOFAR2.0 + +""" + +# PyTango imports +from tango.server import device_property +from tango import AttrWriteType +# Additional import + +from tangostationcontrol.common.entrypoint import entry +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.devices.opcua_device import opcua_device +from tangostationcontrol.devices.sdp.sdp import SDP + +import numpy + +__all__ = ["Beamlet", "main"] + + +class Beamlet(opcua_device): + + # ----------------- + # Device Properties + # ----------------- + + FPGA_beamlet_output_hdr_eth_destination_mac_RW_default = device_property( + dtype='DevVarStringArray', + mandatory=True + ) + + FPGA_beamlet_output_hdr_ip_destination_address_RW_default = device_property( + dtype='DevVarStringArray', + mandatory=True + ) + + FPGA_beamlet_output_hdr_udp_destination_port_RW_default = device_property( + dtype='DevVarUShortArray', + mandatory=True + ) + + FPGA_beamlet_output_enable_RW_default = device_property( + dtype='DevVarBooleanArray', + mandatory=False, + default_value=[False] * 16 + ) + + FPGA_beamlet_output_scale_RW_default = device_property( + dtype='DevVarDoubleArray', + mandatory=False, + default_value=[1.0] * 16 + ) + + first_default_settings = [ + 'FPGA_beamlet_output_hdr_eth_destination_mac_RW', + 'FPGA_beamlet_output_hdr_ip_destination_address_RW', + 'FPGA_beamlet_output_hdr_udp_destination_port_RW', + + 'FPGA_beamlet_output_enable_RW' + ] + + # ---------- + # Attributes + # ---------- + + FPGA_beamlet_output_enable_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_enable_R"], datatype=numpy.bool_, dims=(16,)) + FPGA_beamlet_output_enable_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_beamlet_output_hdr_eth_destination_mac_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_eth_destination_mac_R"], datatype=numpy.str, dims=(16,)) + FPGA_beamlet_output_hdr_eth_destination_mac_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_eth_destination_mac_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_beamlet_output_hdr_ip_destination_address_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_ip_destination_address_R"], datatype=numpy.str, dims=(16,)) + FPGA_beamlet_output_hdr_ip_destination_address_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_ip_destination_address_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_beamlet_output_hdr_udp_destination_port_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_udp_destination_port_R"], datatype=numpy.uint16, dims=(16,)) + FPGA_beamlet_output_hdr_udp_destination_port_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_udp_destination_port_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_beamlet_output_scale_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_scale_R"], datatype=numpy.double, dims=(16,)) + FPGA_beamlet_output_scale_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_scale_RW"], datatype=numpy.double, dims=(16,), access=AttrWriteType.READ_WRITE) + + # List of OPC-UA CP for BF beamlets + S_pn = SDP.S_pn + N_pn = SDP.N_pn + A_pn = 6 + N_pol = 2 + N_beamlets_ctrl = 488 + N_pol_bf = 2 + + # cint16[N_pn][A_pn][N_pol][N_beamlets_ctrl] + # Co-polarization BF weights. The N_pol = 2 parameter index is: + # 0 for antenna polarization X in beamlet polarization X, + # 1 for antenna polarization Y in beamlet polarization Y. + FPGA_bf_weights_xx_yy_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_yy_R"], datatype=numpy.int16, dims=(A_pn * N_pol * N_beamlets_ctrl, N_pn)) + FPGA_bf_weights_xx_yy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_yy_RW"], datatype=numpy.int16, dims=(A_pn * N_pol * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) + + # cint16[N_pn][A_pn][N_pol][N_beamlets_ctrl] + # Cross-polarization BF weights. The N_pol = 2 parameter index is (note that index pol in range 0:N_pol-1 is the antenna polarization, so index !pol is the beamlet polarization): + # 0 for antenna polarization X in beamlet polarization Y, + # 1 for antenna polarization Y in beamlet polarization X. + FPGA_bf_weights_xy_yx_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xy_yx_R"], datatype=numpy.int16, 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.int16, 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] + # 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.int16, dims=(N_pol_bf * A_pn * N_pol * N_beamlets_ctrl, N_pn)) + FPGA_bf_weights_xx_xy_yx_yy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_xy_yx_yy_RW"], datatype=numpy.int16, dims=(N_pol_bf * A_pn * N_pol * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) + + # cint16[N_pn][A_pn][N_beamlets_ctrl] + # BF weights for separate access to respectively w_xx, w_xy, w_yx, and w_yy. + FPGA_bf_weights_xx_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn)) + FPGA_bf_weights_xx_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) + FPGA_bf_weights_xy_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xy_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn)) + FPGA_bf_weights_xy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xy_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) + FPGA_bf_weights_yx_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yx_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn)) + FPGA_bf_weights_yx_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yx_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) + FPGA_bf_weights_yy_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yy_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn)) + FPGA_bf_weights_yy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yy_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) + + # ---------- + # Summarising Attributes + # ---------- + + # -------- + # Overloaded functions + # -------- + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- +def main(**kwargs): + """Main function of the SST Device module.""" + return entry(Beamlet, **kwargs) diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py index a483aec43..9c5d157a0 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py @@ -97,16 +97,6 @@ class SDP(opcua_device): # Attributes # ---------- - FPGA_beamlet_output_enable_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_enable_R"], datatype=numpy.bool_, dims=(16,)) - FPGA_beamlet_output_enable_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) - FPGA_beamlet_output_hdr_eth_destination_mac_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_eth_destination_mac_R"], datatype=numpy.str, dims=(16,)) - FPGA_beamlet_output_hdr_eth_destination_mac_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_eth_destination_mac_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) - FPGA_beamlet_output_hdr_ip_destination_address_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_ip_destination_address_R"], datatype=numpy.str, dims=(16,)) - FPGA_beamlet_output_hdr_ip_destination_address_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_ip_destination_address_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) - FPGA_beamlet_output_hdr_udp_destination_port_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_udp_destination_port_R"], datatype=numpy.uint16, dims=(16,)) - FPGA_beamlet_output_hdr_udp_destination_port_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_hdr_udp_destination_port_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) - FPGA_beamlet_output_scale_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_scale_R"], datatype=numpy.uint32, dims=(16,)) - FPGA_beamlet_output_scale_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_scale_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_firmware_version_R = attribute_wrapper(comms_annotation=["FPGA_firmware_version_R"], datatype=numpy.str, dims=(16,)) FPGA_boot_image_R = attribute_wrapper(comms_annotation=["FPGA_boot_image_R"], datatype=numpy.uint32, dims=(16,), doc="Active FPGA image (0=factory, 1=user)") FPGA_boot_image_RW = attribute_wrapper(comms_annotation=["FPGA_boot_image_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) @@ -151,10 +141,6 @@ class SDP(opcua_device): # TODO: needs to not be statically declared as this can change depending on the station and configuration S_pn = 12 # Number of ADC signal inputs per Processing Node (PN) FPGA. N_pn = 16 # Number of FPGAs per antenna band that is controlled via the SC - SDP interface. - A_pn = 6 - N_pol = 2 - N_beamlets_ctrl = 488 - N_pol_bf = 2 # OPC-UA MP only points for AIT FPGA_signal_input_mean_R = attribute_wrapper(comms_annotation=["FPGA_signal_input_mean_R"], datatype=numpy.double , dims=(S_pn, N_pn)) @@ -172,38 +158,6 @@ class SDP(opcua_device): FPGA_signal_input_samples_delay_R = attribute_wrapper(comms_annotation=["FPGA_signal_input_samples_delay_R"], datatype=numpy.uint32, dims=(S_pn, N_pn)) FPGA_signal_input_samples_delay_RW = attribute_wrapper(comms_annotation=["FPGA_signal_input_samples_delay_RW"], datatype=numpy.uint32, dims=(S_pn, N_pn), access=AttrWriteType.READ_WRITE) - # List of OPC-UA CP for BF beamlets - - # cint16[N_pn][A_pn][N_pol][N_beamlets_ctrl] - # Co-polarization BF weights. The N_pol = 2 parameter index is: - # 0 for antenna polarization X in beamlet polarization X, - # 1 for antenna polarization Y in beamlet polarization Y. - FPGA_bf_weights_xx_yy_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_yy_R"], datatype=numpy.int16, dims=(A_pn * N_pol * N_beamlets_ctrl, N_pn)) - FPGA_bf_weights_xx_yy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_yy_RW"], datatype=numpy.int16, dims=(A_pn * N_pol * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) - - # cint16[N_pn][A_pn][N_pol][N_beamlets_ctrl] - # Cross-polarization BF weights. The N_pol = 2 parameter index is (note that index pol in range 0:N_pol-1 is the antenna polarization, so index !pol is the beamlet polarization): - # 0 for antenna polarization X in beamlet polarization Y, - # 1 for antenna polarization Y in beamlet polarization X. - FPGA_bf_weights_xy_yx_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xy_yx_R"], datatype=numpy.int16, 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.int16, 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] - # 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.int16, dims=(N_pol_bf * A_pn * N_pol * N_beamlets_ctrl, N_pn)) - FPGA_bf_weights_xx_xy_yx_yy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_xy_yx_yy_RW"], datatype=numpy.int16, dims=(N_pol_bf * A_pn * N_pol * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) - - # cint16[N_pn][A_pn][N_beamlets_ctrl] - # BF weights for separate access to respectively w_xx, w_xy, w_yx, and w_yy. - FPGA_bf_weights_xx_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn)) - FPGA_bf_weights_xx_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xx_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) - FPGA_bf_weights_xy_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xy_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn)) - FPGA_bf_weights_xy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_xy_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) - FPGA_bf_weights_yx_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yx_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn)) - FPGA_bf_weights_yx_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yx_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) - FPGA_bf_weights_yy_R = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yy_R"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn)) - FPGA_bf_weights_yy_RW = attribute_wrapper(comms_annotation=["FPGA_bf_weights_yy_RW"], datatype=numpy.int16, dims=(A_pn * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) - # ---------- # Summarising Attributes # ---------- diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py new file mode 100644 index 000000000..dcabcafc1 --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beamlet.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 TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): + + def setUp(self): + """Intentionally recreate the device object in each test""" + super().setUp("STAT/Beamlet/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 -- GitLab