From f65306d37cb88a3cae0e606e0bb6ded10cd5fd9e Mon Sep 17 00:00:00 2001
From: thijs snijder <snijder@astron.nl>
Date: Thu, 17 Mar 2022 16:56:39 +0100
Subject: [PATCH] started DigitalBeam branch by copying Beamlet device

---
 docker-compose/device-digitalbeam.yml         |  47 ++++++
 .../startup/01-devices.py                     |   3 +-
 .../docs/source/devices/DigitalBeam.rst       |   4 +
 tangostationcontrol/docs/source/index.rst     |   1 +
 tangostationcontrol/setup.cfg                 |   1 +
 .../tangostationcontrol/devices/boot.py       |   1 +
 .../devices/sdp/DigitalBeam.py                | 136 ++++++++++++++++++
 7 files changed, 192 insertions(+), 1 deletion(-)
 create mode 100644 docker-compose/device-digitalbeam.yml
 create mode 100644 tangostationcontrol/docs/source/devices/DigitalBeam.rst
 create mode 100644 tangostationcontrol/tangostationcontrol/devices/sdp/DigitalBeam.py

diff --git a/docker-compose/device-digitalbeam.yml b/docker-compose/device-digitalbeam.yml
new file mode 100644
index 000000000..891c7fc8a
--- /dev/null
+++ b/docker-compose/device-digitalbeam.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-digitalbeam:
+    image: device-digitalbeam
+    # 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-digitalbeam
+    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-digitalbeam DigitalBeam 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 82afee40e..9a64688d7 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
@@ -9,7 +9,8 @@ unb2 = DeviceProxy("STAT/UNB2/1")
 boot = DeviceProxy("STAT/Boot/1")
 tilebeam = DeviceProxy("STAT/TileBeam/1")
 beamlet = DeviceProxy("STAT/Beamlet/1")
+beamlet = DeviceProxy("STAT/DigitalBeam/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, tilebeam, beamlet, docker]
+devices = [apsct, apspu, recv, sdp, sst, xst, unb2, boot, tilebeam, beamlet, DigitalBeam, docker]
diff --git a/tangostationcontrol/docs/source/devices/DigitalBeam.rst b/tangostationcontrol/docs/source/devices/DigitalBeam.rst
new file mode 100644
index 000000000..90d9a75d4
--- /dev/null
+++ b/tangostationcontrol/docs/source/devices/DigitalBeam.rst
@@ -0,0 +1,4 @@
+DigitalBeam
+====================
+
+``DigitalBeam == DeviceProxy("STAT/DigitalBeam/1")``
diff --git a/tangostationcontrol/docs/source/index.rst b/tangostationcontrol/docs/source/index.rst
index 0fd199ad8..ab4b696c0 100644
--- a/tangostationcontrol/docs/source/index.rst
+++ b/tangostationcontrol/docs/source/index.rst
@@ -21,6 +21,7 @@ Even without having access to any LOFAR2.0 hardware, you can install the full st
    devices/using
    devices/tilebeam
    devices/beamlet
+   devices/DigitalBeam
    devices/boot
    devices/docker
    devices/recv
diff --git a/tangostationcontrol/setup.cfg b/tangostationcontrol/setup.cfg
index cfc0a249b..6a438752e 100644
--- a/tangostationcontrol/setup.cfg
+++ b/tangostationcontrol/setup.cfg
@@ -37,6 +37,7 @@ console_scripts =
     l2ss-apspu = tangostationcontrol.devices.apspu:main
     l2ss-tilebeam = tangostationcontrol.devices.tilebeam:main
     l2ss-beamlet = tangostationcontrol.devices.sdp.beamlet:main
+    l2ss-DigitalBeam = tangostationcontrol.devices.sdp.DigitalBeam: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/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py
index ba9279092..0b496636f 100644
--- a/tangostationcontrol/tangostationcontrol/devices/boot.py
+++ b/tangostationcontrol/tangostationcontrol/devices/boot.py
@@ -242,6 +242,7 @@ class Boot(lofar_device):
                        "STAT/XST/1",
                        "STAT/Beamlet/1",
                        "STAT/TileBeam/1",   # Accesses RECV and Beamlet
+                       "STAT/DigitalBeam/1",
                       ],
     )
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/DigitalBeam.py b/tangostationcontrol/tangostationcontrol/devices/sdp/DigitalBeam.py
new file mode 100644
index 000000000..e226ec094
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/DigitalBeam.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+""" DigitalBeam 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__ = ["DigitalBeam", "main"]
+
+
+class DigitalBeam(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(DigitalBeam, **kwargs)
-- 
GitLab