diff --git a/.gitignore b/.gitignore
index af7bbfbee9775dc73c6cb0081ccc537a3213be74..7489d2a10dba8069c8cc10309b20ea61e5397aef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,5 +12,7 @@
 **/.project
 **/.pydevproject
 **/.settings/org.eclipse.core.resources.prefs
-**/dist
+tangostationcontrol/dist
+tangostationcontrol/build
 **/.ipynb_checkpoints
+**/pending_log_messages.db
diff --git a/devices/.stestr.conf b/devices/.stestr.conf
deleted file mode 100644
index 07147c8697683f270e9388da8b914c20cb8e4c45..0000000000000000000000000000000000000000
--- a/devices/.stestr.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-[DEFAULT]
-test_path=${TESTS_DIR:-./test}
-top_dir=./
diff --git a/devices/LICENSE.txt b/devices/LICENSE.txt
deleted file mode 100644
index 8a0eaeb196094a651006f51fd99c0c05cb16ccd6..0000000000000000000000000000000000000000
--- a/devices/LICENSE.txt
+++ /dev/null
@@ -1,201 +0,0 @@
-                              Apache License
-                        Version 2.0, January 2004
-                     http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-   "License" shall mean the terms and conditions for use, reproduction,
-   and distribution as defined by Sections 1 through 9 of this document.
-
-   "Licensor" shall mean the copyright owner or entity authorized by
-   the copyright owner that is granting the License.
-
-   "Legal Entity" shall mean the union of the acting entity and all
-   other entities that control, are controlled by, or are under common
-   control with that entity. For the purposes of this definition,
-   "control" means (i) the power, direct or indirect, to cause the
-   direction or management of such entity, whether by contract or
-   otherwise, or (ii) ownership of fifty percent (50%) or more of the
-   outstanding shares, or (iii) beneficial ownership of such entity.
-
-   "You" (or "Your") shall mean an individual or Legal Entity
-   exercising permissions granted by this License.
-
-   "Source" form shall mean the preferred form for making modifications,
-   including but not limited to software source code, documentation
-   source, and configuration files.
-
-   "Object" form shall mean any form resulting from mechanical
-   transformation or translation of a Source form, including but
-   not limited to compiled object code, generated documentation,
-   and conversions to other media types.
-
-   "Work" shall mean the work of authorship, whether in Source or
-   Object form, made available under the License, as indicated by a
-   copyright notice that is included in or attached to the work
-   (an example is provided in the Appendix below).
-
-   "Derivative Works" shall mean any work, whether in Source or Object
-   form, that is based on (or derived from) the Work and for which the
-   editorial revisions, annotations, elaborations, or other modifications
-   represent, as a whole, an original work of authorship. For the purposes
-   of this License, Derivative Works shall not include works that remain
-   separable from, or merely link (or bind by name) to the interfaces of,
-   the Work and Derivative Works thereof.
-
-   "Contribution" shall mean any work of authorship, including
-   the original version of the Work and any modifications or additions
-   to that Work or Derivative Works thereof, that is intentionally
-   submitted to Licensor for inclusion in the Work by the copyright owner
-   or by an individual or Legal Entity authorized to submit on behalf of
-   the copyright owner. For the purposes of this definition, "submitted"
-   means any form of electronic, verbal, or written communication sent
-   to the Licensor or its representatives, including but not limited to
-   communication on electronic mailing lists, source code control systems,
-   and issue tracking systems that are managed by, or on behalf of, the
-   Licensor for the purpose of discussing and improving the Work, but
-   excluding communication that is conspicuously marked or otherwise
-   designated in writing by the copyright owner as "Not a Contribution."
-
-   "Contributor" shall mean Licensor and any individual or Legal Entity
-   on behalf of whom a Contribution has been received by Licensor and
-   subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
-   this License, each Contributor hereby grants to You a perpetual,
-   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-   copyright license to reproduce, prepare Derivative Works of,
-   publicly display, publicly perform, sublicense, and distribute the
-   Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
-   this License, each Contributor hereby grants to You a perpetual,
-   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-   (except as stated in this section) patent license to make, have made,
-   use, offer to sell, sell, import, and otherwise transfer the Work,
-   where such license applies only to those patent claims licensable
-   by such Contributor that are necessarily infringed by their
-   Contribution(s) alone or by combination of their Contribution(s)
-   with the Work to which such Contribution(s) was submitted. If You
-   institute patent litigation against any entity (including a
-   cross-claim or counterclaim in a lawsuit) alleging that the Work
-   or a Contribution incorporated within the Work constitutes direct
-   or contributory patent infringement, then any patent licenses
-   granted to You under this License for that Work shall terminate
-   as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
-   Work or Derivative Works thereof in any medium, with or without
-   modifications, and in Source or Object form, provided that You
-   meet the following conditions:
-
-   (a) You must give any other recipients of the Work or
-       Derivative Works a copy of this License; and
-
-   (b) You must cause any modified files to carry prominent notices
-       stating that You changed the files; and
-
-   (c) You must retain, in the Source form of any Derivative Works
-       that You distribute, all copyright, patent, trademark, and
-       attribution notices from the Source form of the Work,
-       excluding those notices that do not pertain to any part of
-       the Derivative Works; and
-
-   (d) If the Work includes a "NOTICE" text file as part of its
-       distribution, then any Derivative Works that You distribute must
-       include a readable copy of the attribution notices contained
-       within such NOTICE file, excluding those notices that do not
-       pertain to any part of the Derivative Works, in at least one
-       of the following places: within a NOTICE text file distributed
-       as part of the Derivative Works; within the Source form or
-       documentation, if provided along with the Derivative Works; or,
-       within a display generated by the Derivative Works, if and
-       wherever such third-party notices normally appear. The contents
-       of the NOTICE file are for informational purposes only and
-       do not modify the License. You may add Your own attribution
-       notices within Derivative Works that You distribute, alongside
-       or as an addendum to the NOTICE text from the Work, provided
-       that such additional attribution notices cannot be construed
-       as modifying the License.
-
-   You may add Your own copyright statement to Your modifications and
-   may provide additional or different license terms and conditions
-   for use, reproduction, or distribution of Your modifications, or
-   for any such Derivative Works as a whole, provided Your use,
-   reproduction, and distribution of the Work otherwise complies with
-   the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
-   any Contribution intentionally submitted for inclusion in the Work
-   by You to the Licensor shall be under the terms and conditions of
-   this License, without any additional terms or conditions.
-   Notwithstanding the above, nothing herein shall supersede or modify
-   the terms of any separate license agreement you may have executed
-   with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
-   names, trademarks, service marks, or product names of the Licensor,
-   except as required for reasonable and customary use in describing the
-   origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
-   agreed to in writing, Licensor provides the Work (and each
-   Contributor provides its Contributions) on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-   implied, including, without limitation, any warranties or conditions
-   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-   PARTICULAR PURPOSE. You are solely responsible for determining the
-   appropriateness of using or redistributing the Work and assume any
-   risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
-   whether in tort (including negligence), contract, or otherwise,
-   unless required by applicable law (such as deliberate and grossly
-   negligent acts) or agreed to in writing, shall any Contributor be
-   liable to You for damages, including any direct, indirect, special,
-   incidental, or consequential damages of any character arising as a
-   result of this License or out of the use or inability to use the
-   Work (including but not limited to damages for loss of goodwill,
-   work stoppage, computer failure or malfunction, or any and all
-   other commercial damages or losses), even if such Contributor
-   has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
-   the Work or Derivative Works thereof, You may choose to offer,
-   and charge a fee for, acceptance of support, warranty, indemnity,
-   or other liability obligations and/or rights consistent with this
-   License. However, in accepting such obligations, You may act only
-   on Your own behalf and on Your sole responsibility, not on behalf
-   of any other Contributor, and only if You agree to indemnify,
-   defend, and hold each Contributor harmless for any liability
-   incurred by, or claims asserted against, such Contributor by reason
-   of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
-   To apply the Apache License to your work, attach the following
-   boilerplate notice, with the fields enclosed by brackets "[]"
-   replaced with your own identifying information. (Don't include
-   the brackets!)  The text should be enclosed in the appropriate
-   comment syntax for the file format. We also recommend that a
-   file or class name and description of purpose be included on the
-   same "printed page" as the copyright notice for easier
-   identification within third-party archives.
-
-Copyright 2021 ASTRON Netherlands Institute for Radio Astronomy
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/devices/__init__.py b/devices/__init__.py
deleted file mode 100644
index 329b61fa54fd8d6afeee2937b3f6cd2c12efb711..0000000000000000000000000000000000000000
--- a/devices/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from util.lofar_git import get_version
-
-__version__ = get_version()
diff --git a/devices/setup.cfg b/devices/setup.cfg
deleted file mode 100644
index 26fb9e4fbb77178f32cfd9589904f8315bb17597..0000000000000000000000000000000000000000
--- a/devices/setup.cfg
+++ /dev/null
@@ -1,45 +0,0 @@
-[metadata]
-name = TangoStationControl
-summary = LOFAR 2.0 Station Control
-description_file =
-    README.md
-description_content_type = text/x-rst; charset=UTF-8
-author = ASTRON
-home_page = https://astron.nl
-project_urls =
-    Bug Tracker = https://support.astron.nl/jira/projects/L2SS/issues/
-    Source Code = https://git.astron.nl/lofar2.0/tango
-license = Apache-2
-classifier =
-    Environment :: Console
-    License :: Apache Software License
-    Operating System :: POSIX :: Linux
-    Programming Language :: Python
-    Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.6
-    Programming Language :: Python :: 3.7
-    Programming Language :: Python :: 3.8
-    Programming Language :: Python :: 3.9
-
-[files]
-package_dir=./
-
-[entry_points]
-console_scripts =
-    l2ss-docker-device = devices.docker_device:main
-    l2ss-observation = devices.observation:main
-    l2ss-observation-control = devices.observation_control:main
-    l2ss-receiver = devices.recv:main
-    l2ss-sdp = devices.sdp.sdp:main
-    l2ss-sst = devices.sdp.sst:main
-    l2ss-unb2 = devices.unb2:main
-    l2ss-xst = devices.sdp.xst:main
-
-# The following entry points should eventually be removed / replaced
-    l2ss-cold-start = toolkit.lts_cold_start:main
-    l2ss-hardware-device-template = examples.HW_device_template:main
-    l2ss-ini-device = examples.load_from_disc.ini_device:main
-    l2ss-parse-statistics-packet = devices.sdp.statistics_packet:main
-    l2ss-random-data = test.devices.random_data:main
-    l2ss-snmp = examples.snmp.snmp:main
-    l2ss-version = common.lofar_git:main
diff --git a/docker-compose/integration-test.yml b/docker-compose/integration-test.yml
index e0d1c6baf58948cdbee5a71ff2f859ab429dcd4b..23b114b08916f5850bc6a89ec26dcc1c45935cfd 100644
--- a/docker-compose/integration-test.yml
+++ b/docker-compose/integration-test.yml
@@ -19,7 +19,7 @@ services:
         - ..:/opt/lofar/tango:rw
     environment:
       - TANGO_HOST=${TANGO_HOST}
-    working_dir: /opt/lofar/tango/devices
+    working_dir: /opt/lofar/tango/tangostationcontrol
     entrypoint:
       - /usr/local/bin/wait-for-it.sh
       - ${TANGO_HOST}
diff --git a/tangostationcontrol/.stestr.conf b/tangostationcontrol/.stestr.conf
new file mode 100644
index 0000000000000000000000000000000000000000..59b161cddad8a253059cf201b8872bc946f78c64
--- /dev/null
+++ b/tangostationcontrol/.stestr.conf
@@ -0,0 +1,3 @@
+[DEFAULT]
+test_path=${TESTS_DIR:-./tangostationcontrol/test}
+top_dir=./
diff --git a/devices/README.md b/tangostationcontrol/README.md
similarity index 100%
rename from devices/README.md
rename to tangostationcontrol/README.md
diff --git a/devices/requirements.txt b/tangostationcontrol/requirements.txt
similarity index 100%
rename from devices/requirements.txt
rename to tangostationcontrol/requirements.txt
diff --git a/tangostationcontrol/setup.cfg b/tangostationcontrol/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..fa8fefb94c391064d9d461d0b0b51a53251f7847
--- /dev/null
+++ b/tangostationcontrol/setup.cfg
@@ -0,0 +1,45 @@
+[metadata]
+name = TangoStationControl
+summary = LOFAR 2.0 Station Control
+description_file =
+    README.md
+description_content_type = text/x-rst; charset=UTF-8
+author = ASTRON
+home_page = https://astron.nl
+project_urls =
+    Bug Tracker = https://support.astron.nl/jira/projects/L2SS/issues/
+    Source Code = https://git.astron.nl/lofar2.0/tango
+license = Apache-2
+classifier =
+    Environment :: Console
+    License :: Apache Software License
+    Operating System :: POSIX :: Linux
+    Programming Language :: Python
+    Programming Language :: Python :: 3
+    Programming Language :: Python :: 3.6
+    Programming Language :: Python :: 3.7
+    Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
+
+[files]
+packages=tangostationcontrol
+
+[entry_points]
+console_scripts =
+    l2ss-docker-device = tangostationcontrol.devices.docker_device:main
+    l2ss-observation = tangostationcontrol.devices.observation:main
+    l2ss-observation-control = tangostationcontrol.devices.observation_control:main
+    l2ss-receiver = tangostationcontrol.devices.recv:main
+    l2ss-sdp = tangostationcontrol.devices.sdp.sdp:main
+    l2ss-sst = tangostationcontrol.devices.sdp.sst:main
+    l2ss-unb2 = tangostationcontrol.devices.unb2:main
+    l2ss-xst = tangostationcontrol.devices.sdp.xst:main
+
+# The following entry points should eventually be removed / replaced
+    l2ss-cold-start = tangostationcontrol.toolkit.lts_cold_start:main
+    l2ss-hardware-device-template = tangostationcontrol.examples.HW_device_template:main
+    l2ss-ini-device = tangostationcontrol.examples.load_from_disk.ini_device:main
+    l2ss-parse-statistics-packet = tangostationcontrol.devices.sdp.statistics_packet:main
+    l2ss-random-data = tangostationcontrol.test.devices.random_data:main
+    l2ss-snmp = tangostationcontrol.examples.snmp.snmp:main
+    l2ss-version = tangostationcontrol.common.lofar_git:main
diff --git a/devices/setup.py b/tangostationcontrol/setup.py
similarity index 100%
rename from devices/setup.py
rename to tangostationcontrol/setup.py
diff --git a/tangostationcontrol/tangostationcontrol/__init__.py b/tangostationcontrol/tangostationcontrol/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3cd94bd233d6a03b0a7b22fc878b1ec2d466ad5
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/__init__.py
@@ -0,0 +1,5 @@
+import pbr.version
+# from util.lofar_git import get_version
+
+# __version__ = get_version()
+__version__ = pbr.version.VersionInfo('TangoStationControl').version_string()
diff --git a/devices/clients/README.md b/tangostationcontrol/tangostationcontrol/clients/README.md
similarity index 100%
rename from devices/clients/README.md
rename to tangostationcontrol/tangostationcontrol/clients/README.md
diff --git a/devices/devices/__init__.py b/tangostationcontrol/tangostationcontrol/clients/__init__.py
similarity index 100%
rename from devices/devices/__init__.py
rename to tangostationcontrol/tangostationcontrol/clients/__init__.py
diff --git a/devices/clients/attribute_wrapper.py b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
similarity index 98%
rename from devices/clients/attribute_wrapper.py
rename to tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
index e55a662142cb89f62775fb7ac2189c063593df37..007b9ff86455d41a752168ad06558a3b220b8d79 100644
--- a/devices/clients/attribute_wrapper.py
+++ b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
@@ -2,7 +2,7 @@ from tango.server import attribute
 from tango import AttrWriteType
 import numpy
 
-from devices.device_decorators import only_when_on, fault_on_error
+from tangostationcontrol.devices.device_decorators import only_when_on, fault_on_error
 import logging
 
 logger = logging.getLogger()
diff --git a/devices/clients/comms_client.py b/tangostationcontrol/tangostationcontrol/clients/comms_client.py
similarity index 100%
rename from devices/clients/comms_client.py
rename to tangostationcontrol/tangostationcontrol/clients/comms_client.py
diff --git a/devices/clients/docker_client.py b/tangostationcontrol/tangostationcontrol/clients/docker_client.py
similarity index 100%
rename from devices/clients/docker_client.py
rename to tangostationcontrol/tangostationcontrol/clients/docker_client.py
diff --git a/devices/clients/opcua_client.py b/tangostationcontrol/tangostationcontrol/clients/opcua_client.py
similarity index 99%
rename from devices/clients/opcua_client.py
rename to tangostationcontrol/tangostationcontrol/clients/opcua_client.py
index 2bde56237f8f61dcdc3abc4bdb4d03f7741746fb..f06a9ca85d2f02fafd4ada9e576393ae18f783f9 100644
--- a/devices/clients/opcua_client.py
+++ b/tangostationcontrol/tangostationcontrol/clients/opcua_client.py
@@ -1,11 +1,10 @@
-from threading import Thread
 import socket
 import numpy
 import asyncua
 import asyncio
 from asyncua import Client
 
-from clients.comms_client import AsyncCommClient
+from tangostationcontrol.clients.comms_client import AsyncCommClient
 
 import logging
 logger = logging.getLogger()
diff --git a/devices/clients/statistics_client.py b/tangostationcontrol/tangostationcontrol/clients/statistics_client.py
similarity index 98%
rename from devices/clients/statistics_client.py
rename to tangostationcontrol/tangostationcontrol/clients/statistics_client.py
index 3fd470fbf0319e45242abbc3a79362584628f844..2790197bdb90f2483d872f0dfd5978fa088d4980 100644
--- a/devices/clients/statistics_client.py
+++ b/tangostationcontrol/tangostationcontrol/clients/statistics_client.py
@@ -6,7 +6,7 @@ from .comms_client import AsyncCommClient
 from .tcp_replicator import TCPReplicator
 from .udp_receiver import UDPReceiver
 
-from devices.sdp.statistics_collector import StatisticsConsumer
+from tangostationcontrol.devices.sdp.statistics_collector import StatisticsConsumer
 
 logger = logging.getLogger()
 
diff --git a/devices/clients/statistics_client_thread.py b/tangostationcontrol/tangostationcontrol/clients/statistics_client_thread.py
similarity index 100%
rename from devices/clients/statistics_client_thread.py
rename to tangostationcontrol/tangostationcontrol/clients/statistics_client_thread.py
diff --git a/devices/clients/tcp_replicator.py b/tangostationcontrol/tangostationcontrol/clients/tcp_replicator.py
similarity index 99%
rename from devices/clients/tcp_replicator.py
rename to tangostationcontrol/tangostationcontrol/clients/tcp_replicator.py
index 5ac6e492d977cf14452d4f97bd213c0d12af7cbb..8b820cc765daa193fe142f4a3f49ef7a3f643c76 100644
--- a/devices/clients/tcp_replicator.py
+++ b/tangostationcontrol/tangostationcontrol/clients/tcp_replicator.py
@@ -6,7 +6,7 @@ from threading import Thread
 import asyncio
 import logging
 
-from clients.statistics_client_thread import StatisticsClientThread
+from tangostationcontrol.clients.statistics_client_thread import StatisticsClientThread
 
 logger = logging.getLogger()
 
diff --git a/devices/clients/udp_receiver.py b/tangostationcontrol/tangostationcontrol/clients/udp_receiver.py
similarity index 98%
rename from devices/clients/udp_receiver.py
rename to tangostationcontrol/tangostationcontrol/clients/udp_receiver.py
index 8a9d1429945cdd5c41c47bf45edc5034c1cafa0c..2bda038a1fb0646af2d2e14082e10ca3d7866816 100644
--- a/devices/clients/udp_receiver.py
+++ b/tangostationcontrol/tangostationcontrol/clients/udp_receiver.py
@@ -7,7 +7,7 @@ import socket
 import time
 from typing import List # not needed for python3.9+, where we can use the type "list[Queue]" directly
 
-from clients.statistics_client_thread import StatisticsClientThread
+from tangostationcontrol.clients.statistics_client_thread import StatisticsClientThread
 
 logger = logging.getLogger()
 
diff --git a/devices/devices/sdp/__init__.py b/tangostationcontrol/tangostationcontrol/common/__init__.py
similarity index 100%
rename from devices/devices/sdp/__init__.py
rename to tangostationcontrol/tangostationcontrol/common/__init__.py
diff --git a/devices/common/baselines.py b/tangostationcontrol/tangostationcontrol/common/baselines.py
similarity index 100%
rename from devices/common/baselines.py
rename to tangostationcontrol/tangostationcontrol/common/baselines.py
diff --git a/devices/common/lofar_git.py b/tangostationcontrol/tangostationcontrol/common/lofar_git.py
similarity index 100%
rename from devices/common/lofar_git.py
rename to tangostationcontrol/tangostationcontrol/common/lofar_git.py
diff --git a/devices/common/lofar_logging.py b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py
similarity index 98%
rename from devices/common/lofar_logging.py
rename to tangostationcontrol/tangostationcontrol/common/lofar_logging.py
index 07f7f64e95819d2278ca0feb29ee2a28900b3012..18e07423d3f86f5de80513c24b0e626e14242c82 100644
--- a/devices/common/lofar_logging.py
+++ b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py
@@ -1,7 +1,6 @@
 import logging
 from functools import wraps
 from tango.server import Device
-import sys
 import traceback
 import socket
 import time
@@ -100,7 +99,7 @@ class LogAnnotator(logging.Formatter):
         # we just annotate, we don't filter
         return True
 
-def configure_logger(logger: logging.Logger=None, log_extra=None, debug=False):
+def configure_logger(logger: logging.Logger=None, log_extra=None):
     """
        Configure the given logger (or root if None) to:
          - send logs to the ELK stack
@@ -123,26 +122,6 @@ def configure_logger(logger: logging.Logger=None, log_extra=None, debug=False):
     # don't spam errors for git, as we use it in our log handler, which would result in an infinite loop
     logging.getLogger("git").setLevel(logging.ERROR)
 
-    # for now, also log to stderr
-    # Set up logging in a way that it can be understood by a human reader, be
-    # easily grep'ed, be parsed with a couple of shell commands and
-    # easily fed into an Kibana/Elastic search system.
-    handler = logging.StreamHandler()
-
-    # Always also log the hostname because it makes the origin of the log clear.
-    hostname = socket.gethostname()
-
-    formatter = logging.Formatter(fmt = '%(asctime)s.%(msecs)d %(levelname)s - HOST="{}" DEVICE="%(tango_device)s" PID="%(process)d" TNAME="%(threadName)s" FILE="%(pathname)s" LINE="%(lineno)d" FUNC="%(funcName)s" MSG="%(message)s"'.format(hostname), datefmt = '%Y-%m-%dT%H:%M:%S')
-    handler.setFormatter(formatter)
-    handler.addFilter(LogSuppressErrorSpam())
-    handler.addFilter(LogAnnotator())
-
-    logger.addHandler(handler)
-
-    # If configuring for debug; exit early
-    if debug:
-        return logger
-
     # Log to ELK stack
     try:
         from logstash_async.handler import AsynchronousLogstashHandler, LogstashFormatter
@@ -172,6 +151,23 @@ def configure_logger(logger: logging.Logger=None, log_extra=None, debug=False):
     except Exception:
         logger.exception("Cannot forward logs to Tango.")
 
+
+    # for now, also log to stderr
+    # Set up logging in a way that it can be understood by a human reader, be
+    # easily grep'ed, be parsed with a couple of shell commands and
+    # easily fed into an Kibana/Elastic search system.
+    handler = logging.StreamHandler()
+
+    # Always also log the hostname because it makes the origin of the log clear.
+    hostname = socket.gethostname()
+
+    formatter = logging.Formatter(fmt = '%(asctime)s.%(msecs)d %(levelname)s - HOST="{}" DEVICE="%(tango_device)s" PID="%(process)d" TNAME="%(threadName)s" FILE="%(pathname)s" LINE="%(lineno)d" FUNC="%(funcName)s" MSG="%(message)s"'.format(hostname), datefmt = '%Y-%m-%dT%H:%M:%S')
+    handler.setFormatter(formatter)
+    handler.addFilter(LogSuppressErrorSpam())
+    handler.addFilter(LogAnnotator())
+
+    logger.addHandler(handler)
+
     return logger
 
 def device_logging_to_python():
diff --git a/devices/examples/__init__.py b/tangostationcontrol/tangostationcontrol/devices/__init__.py
similarity index 100%
rename from devices/examples/__init__.py
rename to tangostationcontrol/tangostationcontrol/devices/__init__.py
diff --git a/devices/devices/abstract_device.py b/tangostationcontrol/tangostationcontrol/devices/abstract_device.py
similarity index 100%
rename from devices/devices/abstract_device.py
rename to tangostationcontrol/tangostationcontrol/devices/abstract_device.py
diff --git a/devices/devices/device_decorators.py b/tangostationcontrol/tangostationcontrol/devices/device_decorators.py
similarity index 100%
rename from devices/devices/device_decorators.py
rename to tangostationcontrol/tangostationcontrol/devices/device_decorators.py
diff --git a/devices/devices/docker_device.py b/tangostationcontrol/tangostationcontrol/devices/docker_device.py
similarity index 90%
rename from devices/devices/docker_device.py
rename to tangostationcontrol/tangostationcontrol/devices/docker_device.py
index 5e786db6fbeda01b6a2fcf9b619e0df3e793d3cb..1f1fd245ecbf250d4729d5bb76941f9f4893b45b 100644
--- a/devices/devices/docker_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/docker_device.py
@@ -12,7 +12,6 @@
 """
 
 # PyTango imports
-from tango import DebugIt
 from tango.server import run, command
 from tango.server import device_property, attribute
 from tango import AttrWriteType
@@ -22,10 +21,12 @@ import asyncio
 
 from device_decorators import *
 
-from clients.docker_client import DockerClient
-from clients.attribute_wrapper import attribute_wrapper
-from devices.hardware_device import hardware_device
-from common.lofar_logging import device_logging_to_python, log_exceptions
+# Additional import
+from tangostationcontrol.clients.docker_client import DockerClient
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.devices.hardware_device import hardware_device
+from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions
+from tangostationcontrol.common.lofar_git import get_version
 
 __all__ = ["Docker", "main"]
 
@@ -44,6 +45,7 @@ class Docker(hardware_device):
     # ----------
     # Attributes
     # ----------
+    version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version())
     archiver_maria_db_R = attribute_wrapper(comms_annotation={"container": "archiver-maria-db"}, datatype=numpy.bool_)
     archiver_maria_db_RW = attribute_wrapper(comms_annotation={"container": "archiver-maria-db"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE)
     databaseds_R = attribute_wrapper(comms_annotation={"container": "databaseds"}, datatype=numpy.bool_)
@@ -93,7 +95,7 @@ class Docker(hardware_device):
         try:
             self.docker_client.sync_stop()
         except Exception as e:
-            self.warn_stream("Exception while stopping docker client in configure_for_off function: {}. Exception ignored".format(e))
+            self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e))
 
     @log_exceptions()
     def configure_for_initialise(self):
@@ -124,7 +126,7 @@ class Docker(hardware_device):
 def main(args=None, **kwargs):
     """Main function of the Docker module."""
 
-    from common.lofar_logging import configure_logger
+    from tangostationcontrol.common.lofar_logging import configure_logger
     configure_logger()
 
     return run((Docker,), args=args, **kwargs)
diff --git a/devices/devices/hardware_device.py b/tangostationcontrol/tangostationcontrol/devices/hardware_device.py
similarity index 92%
rename from devices/devices/hardware_device.py
rename to tangostationcontrol/tangostationcontrol/devices/hardware_device.py
index c812f432bb6249e2bc84d08ea0b004ac68e0cee8..7669cc15111619d3bfc05360406172a5bb4c796e 100644
--- a/devices/devices/hardware_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/hardware_device.py
@@ -15,17 +15,19 @@ from abc import abstractmethod
 
 # PyTango imports
 from tango.server import Device, command, DeviceMeta, attribute
-from tango import DevState, DebugIt, Attribute, DeviceProxy, AttrWriteType
-# Additional import
+from tango import DevState, DebugIt, Attribute, DeviceProxy
 
-from clients.attribute_wrapper import attribute_wrapper
-from common.lofar_logging import log_exceptions
-from common.lofar_git import get_version
-from devices.abstract_device import AbstractDeviceMetas
-from devices.device_decorators import only_in_states, fault_on_error
 import time
 import math
 
+# Additional import
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.common.lofar_logging import log_exceptions
+from tangostationcontrol.common.lofar_git import get_version
+from tangostationcontrol.devices.abstract_device import AbstractDeviceMetas
+from tangostationcontrol.devices.device_decorators import only_in_states, fault_on_error
+
+
 __all__ = ["hardware_device"]
 
 import logging
@@ -75,7 +77,6 @@ class hardware_device(Device, metaclass=AbstractDeviceMetas):
 
         self.value_dict = {i: i.initial_value() for i in self.attr_list()}
 
-    @log_exceptions()
     def init_device(self):
         """ Instantiates the device in the OFF state. """
 
@@ -84,19 +85,6 @@ class hardware_device(Device, metaclass=AbstractDeviceMetas):
 
         self.set_state(DevState.OFF)
 
-    @log_exceptions()
-    def delete_device(self):
-        """Hook to delete resources allocated in init_device.
-
-        This method allows for any memory or other resources allocated in the
-        init_device method to be released.  This method is called by the device
-        destructor and by the device Init command (a Tango built-in).
-        """
-        logger.info("Shutting down...")
-
-        self.Off()
-        logger.info("Shut down.  Good bye.")
-
     # --------
     # Commands
     # --------
@@ -203,6 +191,18 @@ class hardware_device(Device, metaclass=AbstractDeviceMetas):
         """Method always executed before any TANGO command is executed."""
         pass
 
+    def delete_device(self):
+        """Hook to delete resources allocated in init_device.
+
+        This method allows for any memory or other resources allocated in the
+        init_device method to be released.  This method is called by the device
+        destructor and by the device Init command (a Tango built-in).
+        """
+        self.debug_stream("Shutting down...")
+
+        self.Off()
+        self.debug_stream("Shut down.  Good bye.")
+
     @command()
     @only_in_states([DevState.STANDBY, DevState.ON])
     @DebugIt()
@@ -212,11 +212,6 @@ class hardware_device(Device, metaclass=AbstractDeviceMetas):
 
             A hardware point XXX is set to the value of the object member named XXX_default, if it exists.
             XXX_default can be f.e. a constant, or a device_property.
-
-            The points are set in the following order:
-                1) The python class property 'first_default_settings' is read, as an array of strings denoting property names. Each property
-                   is set in that order.
-                2) Any remaining default properties are set.
         """
 
         # we cannot write directly to our attribute, as that would not
diff --git a/devices/devices/observation.py b/tangostationcontrol/tangostationcontrol/devices/observation.py
similarity index 95%
rename from devices/devices/observation.py
rename to tangostationcontrol/tangostationcontrol/devices/observation.py
index 2297c5304abf9861ac820d186ebd5faeb9089d75..0dbec128dac155fb5243a8ead8f0de0be12dbda4 100644
--- a/devices/devices/observation.py
+++ b/tangostationcontrol/tangostationcontrol/devices/observation.py
@@ -11,9 +11,9 @@ from tango.server import Device, run, command, attribute
 import numpy
 from time import time
 
-from devices.device_decorators import *
-from common.lofar_logging import device_logging_to_python, log_exceptions
-from common.lofar_git import get_version
+from tangostationcontrol.devices.device_decorators import *
+from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions
+from tangostationcontrol.common.lofar_git import get_version
 
 from json import loads
 
diff --git a/devices/devices/observation_control.py b/tangostationcontrol/tangostationcontrol/devices/observation_control.py
similarity index 98%
rename from devices/devices/observation_control.py
rename to tangostationcontrol/tangostationcontrol/devices/observation_control.py
index 0cba0af6aa6990c0f8a4ec837a92a115eb6f28d9..20bd9f546647833287c239bb5015661cb5850cda 100644
--- a/devices/devices/observation_control.py
+++ b/tangostationcontrol/tangostationcontrol/devices/observation_control.py
@@ -14,11 +14,10 @@ import numpy
 import time
 from json import loads
 
-from devices.device_decorators import *
-from common.lofar_logging import device_logging_to_python, log_exceptions
-from common.lofar_git import get_version
-
-from observation import Observation
+from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions
+from tangostationcontrol.common.lofar_git import get_version
+from tangostationcontrol.devices.device_decorators import *
+from tangostationcontrol.devices.observation import Observation
 
 
 __all__ = ["ObservationControl", "main"]
diff --git a/devices/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py
similarity index 75%
rename from devices/devices/recv.py
rename to tangostationcontrol/tangostationcontrol/devices/recv.py
index f63d1c4eda811ea2eaabbb46098e8a33cf4b3548..aa0d098cfdcaae3e62d3d5df3ef5ee88d844e50c 100644
--- a/devices/devices/recv.py
+++ b/tangostationcontrol/tangostationcontrol/devices/recv.py
@@ -17,43 +17,59 @@ from tango.server import run, command
 from tango.server import device_property, attribute
 from tango import AttrWriteType
 import numpy
-# Additional import
-
-from device_decorators import *
 
-from clients.attribute_wrapper import attribute_wrapper
-from devices.opcua_device import opcua_device
-from common.lofar_logging import device_logging_to_python, log_exceptions
+# Additional import
+from tangostationcontrol.clients.opcua_client import OPCUAConnection
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions
+from tangostationcontrol.common.lofar_git import get_version
+from tangostationcontrol.devices.device_decorators import *
+from tangostationcontrol.devices.hardware_device import hardware_device
 
 __all__ = ["RECV", "main"]
 
 @device_logging_to_python()
-class RECV(opcua_device):
+class RECV(hardware_device):
+    """
+
+    **Properties:**
+
+    - Device Property
+        OPC_Server_Name
+            - Type:'DevString'
+        OPC_Server_Port
+            - Type:'DevULong'
+        OPC_Time_Out
+            - Type:'DevDouble'
+    """
+
     # -----------------
     # Device Properties
     # -----------------
 
-    Ant_mask_RW_default = device_property(
-        dtype='DevVarBooleanArray',
-        mandatory=False,
-        default_value=[[True] * 3] * 32
+    OPC_Server_Name = device_property(
+        dtype='DevString',
+        mandatory=True
     )
 
-    RCU_mask_RW_default = device_property(
-        dtype='DevVarBooleanArray',
-        mandatory=False,
-        default_value=[True] * 32
+    OPC_Server_Port = device_property(
+        dtype='DevULong',
+        mandatory=True
     )
 
-    first_default_settings = [
-        # set the masks first, as those filter any subsequent settings
-        'Ant_mask_RW',
-        'RCU_mask_RW'
-    ]
+    OPC_Time_Out = device_property(
+        dtype='DevDouble',
+        mandatory=True
+    )
+    OPC_namespace = device_property(
+        dtype='DevString',
+        mandatory=False
+    )
 
     # ----------
     # Attributes
     # ----------
+    version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version())
     Ant_mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:Ant_mask_RW"], datatype=numpy.bool_, dims=(3, 32), access=AttrWriteType.READ_WRITE)
     Ant_status_R = attribute(dtype=str, max_dim_x=3, max_dim_y=32)
     CLK_Enable_PWR_R = attribute_wrapper(comms_annotation=["2:PCC", "2:CLK_Enable_PWR_R"], datatype=numpy.bool_)
@@ -89,9 +105,55 @@ class RECV(opcua_device):
     RCU_translator_busy_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_translator_busy_R"], datatype=numpy.bool_)
     RCU_version_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_version_R"], datatype=numpy.str, dims=(32,))
 
+    @log_exceptions()
+    def delete_device(self):
+        """Hook to delete resources allocated in init_device.
+
+        This method allows for any memory or other resources allocated in the
+        init_device method to be released.  This method is called by the device
+        destructor and by the device Init command (a Tango built-in).
+        """
+        self.debug_stream("Shutting down...")
+
+        self.Off()
+        self.debug_stream("Shut down.  Good bye.")
+
     # --------
     # overloaded functions
     # --------
+    @log_exceptions()
+    def configure_for_off(self):
+        """ user code here. is called when the state is set to OFF """
+        # Stop keep-alive
+        try:
+            self.opcua_connection.stop()
+        except Exception as e:
+            self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e))
+
+    @log_exceptions()
+    def configure_for_initialise(self):
+        """ user code here. is called when the state is set to INIT """
+
+        # Init the dict that contains function to OPC-UA function mappings.
+        self.function_mapping = {}
+        self.function_mapping["RCU_on"] = {}
+        self.function_mapping["RCU_off"] = {}
+        self.function_mapping["CLK_on"] = {}
+        self.function_mapping["CLK_off"] = {}
+
+        # set up the OPC ua client
+        self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self)
+
+        # map an access helper class
+        for i in self.attr_list():
+            try:
+                i.set_comm_client(self.OPCua_client)
+            except Exception as e:
+                # use the pass function instead of setting read/write fails
+                i.set_pass_func()
+                self.warn_stream("error while setting the RECV attribute {} read/write function. {}".format(i, e))
+
+        self.OPCua_client.start()
 
     # --------
     # Commands
@@ -261,7 +323,7 @@ class RECV(opcua_device):
 def main(args=None, **kwargs):
     """Main function of the RECV module."""
 
-    from common.lofar_logging import configure_logger
+    from tangostationcontrol.common.lofar_logging import configure_logger
     configure_logger()
 
     return run((RECV,), args=args, **kwargs)
diff --git a/devices/integration_test/__init__.py b/tangostationcontrol/tangostationcontrol/devices/sdp/__init__.py
similarity index 100%
rename from devices/integration_test/__init__.py
rename to tangostationcontrol/tangostationcontrol/devices/sdp/__init__.py
diff --git a/devices/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
similarity index 82%
rename from devices/devices/sdp/sdp.py
rename to tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
index 1365aba7782502d9418297101368b6e9de975e79..dd755853d3cd151fa0db4e6fc36c2c200b980784 100644
--- a/devices/devices/sdp/sdp.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
@@ -15,27 +15,51 @@
 from tango.server import run
 from tango.server import device_property, attribute
 from tango import AttrWriteType
-# Additional import
 
-from clients.attribute_wrapper import attribute_wrapper
-from devices.opcua_device import opcua_device
+# Additional import
+from tangostationcontrol.clients.opcua_client import OPCUAConnection
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.devices.hardware_device import hardware_device
 
-from common.lofar_logging import device_logging_to_python, log_exceptions
+from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions
+from tangostationcontrol.common.lofar_git import get_version
 
 import numpy
 
 __all__ = ["SDP", "main"]
 
 @device_logging_to_python()
-class SDP(opcua_device):
+class SDP(hardware_device):
+    """
+
+    **Properties:**
+
+    - Device Property
+        OPC_Server_Name
+            - Type:'DevString'
+        OPC_Server_Port
+            - Type:'DevULong'
+        OPC_Time_Out
+            - Type:'DevDouble'
+    """
+
     # -----------------
     # Device Properties
     # -----------------
 
-    TR_fpga_mask_RW_default = device_property(
-        dtype='DevVarBooleanArray',
-        mandatory=False,
-        default_value=[True] * 16
+    OPC_Server_Name = device_property(
+        dtype='DevString',
+        mandatory=True
+    )
+
+    OPC_Server_Port = device_property(
+        dtype='DevULong',
+        mandatory=True
+    )
+
+    OPC_Time_Out = device_property(
+        dtype='DevDouble',
+        mandatory=True
     )
 
     FPGA_processing_enable_RW_default = device_property(
@@ -49,27 +73,6 @@ class SDP(opcua_device):
         mandatory=False,
         default_value=[[False] * 12] * 16
     )
-
-    # If we enable the waveform generator, we want some sane defaults.
-
-    FPGA_wg_amplitude_RW = device_property(
-        dtype='DevVarDoubleArray',
-        mandatory=False,
-        default_value=[[0.1] * 12] * 16
-    )
-
-    FPGA_wg_frequency_RW = device_property(
-        dtype='DevVarDoubleArray',
-        mandatory=False,
-        # Emit a signal on subband 102
-        default_value=[[102 * 200e6/1024] * 12] * 16
-    )
-
-    FPGA_wg_phase_RW = device_property(
-        dtype='DevVarDoubleArray',
-        mandatory=False,
-        default_value=[[0.0] * 12] * 16
-    )
     
     FPGA_sdp_info_station_id_RW_default = device_property(
         dtype='DevVarULongArray',
@@ -82,15 +85,12 @@ class SDP(opcua_device):
         default_value=[[8192] * 12 * 512] * 16
     )
 
-    first_default_settings = [
-        # set the masks first, as those filter any subsequent settings
-        'TR_fpga_mask_RW'
-    ]
-
     # ----------
     # Attributes
     # ----------
 
+    version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version())
+
     FPGA_beamlet_output_enable_R = attribute_wrapper(comms_annotation=["2:FPGA_beamlet_output_enable_R"], datatype=numpy.bool_, dims=(16,))
     FPGA_beamlet_output_enable_RW = attribute_wrapper(comms_annotation=["2: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=["2:FPGA_beamlet_output_hdr_eth_destination_mac_R"], datatype=numpy.str, dims=(16,))
@@ -161,10 +161,38 @@ class SDP(opcua_device):
     FPGA_bsn_monitor_input_nof_valid_R = attribute_wrapper(comms_annotation=["2:FPGA_bsn_monitor_input_nof_valid_R"], datatype=numpy.int32, dims=(N_pn,))
     FPGA_bsn_monitor_input_nof_err_R = attribute_wrapper(comms_annotation=["2:FPGA_bsn_monitor_input_nof_err_R"], datatype=numpy.int32, dims=(N_pn,))
 
-
     # --------
     # overloaded functions
     # --------
+    @log_exceptions()
+    def configure_for_off(self):
+        """ user code here. is called when the state is set to OFF """
+
+        # Stop keep-alive
+        try:
+            self.OPCua_client.stop()
+        except Exception as e:
+            self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e))
+
+    @log_exceptions()
+    def configure_for_initialise(self):
+        """ user code here. is called when the sate is set to INIT """
+        """Initialises the attributes and properties of the SDP."""
+
+        # set up the OPC ua client
+        self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self)
+
+        # map an access helper class
+        for i in self.attr_list():
+            try:
+                i.set_comm_client(self.OPCua_client)
+            except Exception as e:
+                # use the pass function instead of setting read/write fails
+                i.set_pass_func()
+                self.warn_stream("error while setting the SDP attribute {} read/write function. {}".format(i, e))
+                pass
+
+        self.OPCua_client.start()
 
     # --------
     # Commands
@@ -176,7 +204,7 @@ class SDP(opcua_device):
 def main(args=None, **kwargs):
     """Main function of the SDP module."""
 
-    from common.lofar_logging import configure_logger
+    from tangostationcontrol.common.lofar_logging import configure_logger
     configure_logger()
 
     return run((SDP,), args=args, **kwargs)
diff --git a/devices/devices/sdp/sst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py
similarity index 85%
rename from devices/devices/sdp/sst.py
rename to tangostationcontrol/tangostationcontrol/devices/sdp/sst.py
index 8118656494e79106775dbdec90cecfc4179d248a..1c12330391bd05c28971a3f9ed67cca7e8e66ae9 100644
--- a/devices/devices/sdp/sst.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py
@@ -17,11 +17,11 @@ from tango.server import device_property, attribute
 from tango import AttrWriteType
 # Additional import
 
-from clients.attribute_wrapper import attribute_wrapper
-from clients.statistics_client import StatisticsClient
-from clients.opcua_client import OPCUAConnection
-from devices.sdp.statistics import Statistics
-from devices.sdp.statistics_collector import SSTCollector
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.clients.opcua_client import OPCUAConnection
+from tangostationcontrol.clients.statistics_client import StatisticsClient
+from tangostationcontrol.devices.sdp.statistics import Statistics
+from tangostationcontrol.devices.sdp.statistics_collector import SSTCollector
 
 import numpy
 
@@ -50,29 +50,12 @@ class SST(Statistics):
         mandatory=True
     )
 
-    FPGA_sst_offload_enable_RW_default = device_property(
-        dtype='DevVarBooleanArray',
-        mandatory=False,
-        default_value=[True] * 16
-    )
-
     FPGA_sst_offload_weighted_subbands_RW_default = device_property(
         dtype='DevVarBooleanArray',
         mandatory=False,
         default_value=[True] * 16
     )
 
-    first_default_settings = [
-        'FPGA_sst_offload_hdr_eth_destination_mac_RW',
-        'FPGA_sst_offload_hdr_ip_destination_address_RW',
-        'FPGA_sst_offload_hdr_udp_destination_port_RW',
-
-        'FPGA_sst_offload_weighted_subbands_RW',
-
-        # enable only after the offloading is configured correctly
-        'FPGA_sst_offload_enable_RW'
-    ]
-
     # ----------
     # Attributes
     # ----------
@@ -116,7 +99,7 @@ class SST(Statistics):
 def main(args=None, **kwargs):
     """Main function of the SST Device module."""
 
-    from common.lofar_logging import configure_logger
+    from tangostationcontrol.common.lofar_logging import configure_logger
     configure_logger()
 
     return run((SST,), args=args, **kwargs)
diff --git a/devices/devices/sdp/statistics.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
similarity index 84%
rename from devices/devices/sdp/statistics.py
rename to tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
index 0884e5ca7f523af5d56d6a67dd29fd03f9927b5f..abb9bddc91873c6fb793fa3be57fac0127981fbb 100644
--- a/devices/devices/sdp/statistics.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
@@ -16,15 +16,17 @@ from abc import ABCMeta, abstractmethod
 # PyTango imports
 from tango.server import device_property, attribute
 from tango import AttrWriteType
+
 # Additional import
 import asyncio
 
-from clients.statistics_client import StatisticsClient
-from clients.attribute_wrapper import attribute_wrapper
-
-from devices.opcua_device import opcua_device
+from tangostationcontrol.clients.statistics_client import StatisticsClient
+from tangostationcontrol.clients.opcua_client import OPCUAConnection
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.devices.hardware_device import hardware_device
+from tangostationcontrol.common.lofar_git import get_version
+from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions
 
-from common.lofar_logging import device_logging_to_python, log_exceptions
 import logging
 
 logger = logging.getLogger()
@@ -33,7 +35,7 @@ import numpy
 
 __all__ = ["Statistics"]
 
-class Statistics(opcua_device, metaclass=ABCMeta):
+class Statistics(hardware_device, metaclass=ABCMeta):
 
     # In derived classes, set this to a subclass of StatisticsCollector
     @property
@@ -45,6 +47,21 @@ class Statistics(opcua_device, metaclass=ABCMeta):
     # Device Properties
     # -----------------
 
+    OPC_Server_Name = device_property(
+        dtype='DevString',
+        mandatory=True
+    )
+
+    OPC_Server_Port = device_property(
+        dtype='DevULong',
+        mandatory=True
+    )
+
+    OPC_Time_Out = device_property(
+        dtype='DevDouble',
+        mandatory=True
+    )
+
     Statistics_Client_UDP_Port = device_property(
         dtype='DevUShort',
         mandatory=True
@@ -59,6 +76,8 @@ class Statistics(opcua_device, metaclass=ABCMeta):
     # Attributes
     # ----------
 
+    version_R = attribute(dtype = str, access = AttrWriteType.READ, fget = lambda self: get_version())
+
     # number of UDP packets and bytes that were received
     nof_packets_received_R  = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "nof_packets_received"}, datatype=numpy.uint64)
     nof_bytes_received_R  = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "nof_bytes_received"}, datatype=numpy.uint64)
@@ -98,15 +117,16 @@ class Statistics(opcua_device, metaclass=ABCMeta):
         except Exception as e:
             logger.exception("Exception while stopping statistics_client in configure_for_off. Exception ignored")
 
-        super().configure_for_off()
+        try:
+            self.OPCUA_client.stop()
+        except Exception as e:
+            logger.exception("Exception while stopping OPC UA connection in configure_for_off. Exception ignored")
 
     @log_exceptions()
     def configure_for_initialise(self):
         """ user code here. is called when the sate is set to INIT """
         """Initialises the attributes and properties of the statistics device."""
 
-        super().configure_for_initialise()
-
         # Options for UDPReceiver
         udp_options = {
             "udp_port": self.Statistics_Client_UDP_Port,
diff --git a/devices/devices/sdp/statistics_collector.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py
similarity index 88%
rename from devices/devices/sdp/statistics_collector.py
rename to tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py
index d9e5668b7e9b3db288a4b2360f4fa298594bbc1c..a073fbd945ce96701d2a19f673141b45a839f449 100644
--- a/devices/devices/sdp/statistics_collector.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py
@@ -4,8 +4,8 @@ import logging
 import numpy
 
 from .statistics_packet import SSTPacket, XSTPacket
-from common.baselines import nr_baselines, baseline_index, baseline_from_index
-from clients.statistics_client_thread import StatisticsClientThread
+from tangostationcontrol.common.baselines import nr_baselines, baseline_index, baseline_from_index
+from tangostationcontrol.clients.statistics_client_thread import StatisticsClientThread
 
 logger = logging.getLogger()
 
@@ -131,8 +131,6 @@ class XSTCollector(StatisticsCollector):
 
             # Last value array we've constructed out of the packets
             "xst_blocks":            numpy.zeros((self.MAX_BLOCKS, self.BLOCK_LENGTH * self.BLOCK_LENGTH * self.VALUES_PER_COMPLEX), dtype=numpy.int64),
-            # Whether the values are actually conjugated and transposed
-            "xst_conjugated":        numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.bool_),
             "xst_timestamps":        numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.float64),
             "xst_subbands":          numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.uint16),
             "integration_intervals": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.float32),
@@ -164,29 +162,20 @@ class XSTCollector(StatisticsCollector):
             if fields.first_baseline[antenna] % self.BLOCK_LENGTH != 0:
                 raise ValueError("Packet describes baselines starting at %s, but we require a multiple of BLOCK_LENGTH=%d" % (fields.first_baseline, self.MAX_INPUTS))
 
-        # Make sure we always have a baseline (a,b) with a>=b. If not, we swap the indices and mark that the data must be conjugated and transposed when processed.
-        first_baseline = fields.first_baseline
-        if first_baseline[0] < first_baseline[1]:
-            conjugated = True
-            first_baseline = (first_baseline[1], first_baseline[0])
-        else:
-            conjugated = False
-
         # the payload contains complex values for the block of baselines of size BLOCK_LENGTH x BLOCK_LENGTH
         # starting at baseline first_baseline.
         #
         # we honour this format, as we want to keep the metadata together with these blocks. we do need to put the blocks in a linear
         # and tight order, however, so we calculate a block index.
-        block_index = baseline_index(first_baseline[0] // self.BLOCK_LENGTH, first_baseline[1] // self.BLOCK_LENGTH)
-
-        # We did enough checks on first_baseline for this to be a logic error in our code
-        assert 0 <= block_index < self.MAX_BLOCKS, f"Received block {block_index}, but have only room for {self.MAX_BLOCKS}. Block starts at baseline {first_baseline}."
+        block_index = baseline_index(fields.first_baseline[0] // self.BLOCK_LENGTH, fields.first_baseline[1] // self.BLOCK_LENGTH)
 
         # process the packet
         self.parameters["nof_valid_payloads"][fields.gn_index] += numpy.uint64(1)
+
+        block_index = baseline_index(fields.first_baseline[0], fields.first_baseline[1])
+
         self.parameters["xst_blocks"][block_index][:fields.nof_statistics_per_packet] = fields.payload
         self.parameters["xst_timestamps"][block_index]        = numpy.float64(fields.timestamp().timestamp())
-        self.parameters["xst_conjugated"][block_index]        = conjugated
         self.parameters["xst_subbands"][block_index]          = numpy.uint16(fields.subband_index)
         self.parameters["integration_intervals"][block_index] = fields.integration_interval()
 
@@ -195,16 +184,11 @@ class XSTCollector(StatisticsCollector):
 
         matrix = numpy.zeros((self.MAX_INPUTS, self.MAX_INPUTS), dtype=numpy.complex64)
         xst_blocks = self.parameters["xst_blocks"]
-        xst_conjugated = self.parameters["xst_conjugated"]
 
         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[block_index].astype(numpy.float32).view(numpy.complex64)
 
-            if xst_conjugated[block_index]:
-                # block is conjugated and transposed. process.
-                block = block.conjugate().transpose()
-
             # reshape into [a][b]
             block = block.reshape(self.BLOCK_LENGTH, self.BLOCK_LENGTH)
 
diff --git a/devices/devices/sdp/statistics_packet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_packet.py
similarity index 99%
rename from devices/devices/sdp/statistics_packet.py
rename to tangostationcontrol/tangostationcontrol/devices/sdp/statistics_packet.py
index b68525039c49fdd14b2c47ab8eaadefc97154395..c98ae9b5bdc604e8a55480cc5473e658b10cefa1 100644
--- a/devices/devices/sdp/statistics_packet.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_packet.py
@@ -1,6 +1,5 @@
 import struct
 from datetime import datetime, timezone
-from typing import Tuple
 import numpy
 
 __all__ = ["StatisticsPacket", "SSTPacket", "XSTPacket", "BSTPacket"]
diff --git a/devices/devices/sdp/xst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
similarity index 82%
rename from devices/devices/sdp/xst.py
rename to tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
index 8f3402063b6ada84a0e12b3b09106127d9aa8201..55cd535fe7fdcb9a360f1974c9f5c24cb59066c4 100644
--- a/devices/devices/sdp/xst.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
@@ -15,16 +15,14 @@
 from tango.server import run
 from tango.server import device_property, attribute
 from tango import AttrWriteType
-# Additional import
-
-from clients.attribute_wrapper import attribute_wrapper
-from clients.statistics_client import StatisticsClient
-from clients.opcua_client import OPCUAConnection
 
-from common.lofar_logging import device_logging_to_python, log_exceptions
+# Additional import
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.clients.opcua_client import OPCUAConnection
+from tangostationcontrol.clients.statistics_client import StatisticsClient
 
-from devices.sdp.statistics import Statistics
-from devices.sdp.statistics_collector import XSTCollector
+from tangostationcontrol.devices.sdp.statistics import Statistics
+from tangostationcontrol.devices.sdp.statistics_collector import XSTCollector
 
 import numpy
 
@@ -65,37 +63,13 @@ class XST(Statistics):
         default_value=[[0,102,0,0,0,0,0,0]] * 16
     )
 
-    FPGA_xst_integration_interval_RW_default = device_property(
-        dtype='DevVarDoubleArray',
-        mandatory=False,
-        default_value=[1.0] * 16
-    )
-
-    FPGA_xst_offload_enable_RW_default = device_property(
-        dtype='DevVarBooleanArray',
-        mandatory=False,
-        default_value=[True] * 16
-    )
-
-    first_default_settings = [
-        'FPGA_xst_offload_hdr_eth_destination_mac_RW',
-        'FPGA_xst_offload_hdr_ip_destination_address_RW',
-        'FPGA_xst_offload_hdr_udp_destination_port_RW',
-
-        'FPGA_xst_subband_select_RW',
-        'FPGA_xst_integration_interval_RW',
-
-        # enable only after the offloading is configured correctly
-        'FPGA_xst_offload_enable_RW'
-    ]
-
     # ----------
     # Attributes
     # ----------
 
     # FPGA control points for XSTs
-    FPGA_xst_integration_interval_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_RW"], datatype=numpy.double, dims=(16,), access=AttrWriteType.READ_WRITE)
-    FPGA_xst_integration_interval_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_R"], datatype=numpy.double, dims=(16,))
+    FPGA_xst_integration_interval_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_RW"], datatype=numpy.double, dims=(8,16), access=AttrWriteType.READ_WRITE)
+    FPGA_xst_integration_interval_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_R"], datatype=numpy.double, dims=(8,16))
     FPGA_xst_offload_enable_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE)
     FPGA_xst_offload_enable_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_enable_R"], datatype=numpy.bool_, dims=(16,))
     FPGA_xst_offload_hdr_eth_destination_mac_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_eth_destination_mac_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE)
@@ -115,8 +89,6 @@ class XST(Statistics):
     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"}, dims=(XSTCollector.BLOCK_LENGTH * XSTCollector.BLOCK_LENGTH * XSTCollector.VALUES_PER_COMPLEX, XSTCollector.MAX_BLOCKS), 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"}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.bool_)
     # reported timestamp for each row in the latest XSTs
     xst_timestamp_R         = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_timestamps"}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.uint64)
     # which subband the XSTs describe
@@ -156,7 +128,7 @@ class XST(Statistics):
 def main(args=None, **kwargs):
     """Main function of the XST Device module."""
 
-    from common.lofar_logging import configure_logger
+    from tangostationcontrol.common.lofar_logging import configure_logger
     configure_logger()
 
     return run((XST,), args=args, **kwargs)
diff --git a/devices/devices/unb2.py b/tangostationcontrol/tangostationcontrol/devices/unb2.py
similarity index 82%
rename from devices/devices/unb2.py
rename to tangostationcontrol/tangostationcontrol/devices/unb2.py
index 7ecd96e6f0de4a3d92e9fdf3e66d2fb915aa2303..4216374e913daa3e5d3083753ebb10651ddef61e 100644
--- a/devices/devices/unb2.py
+++ b/tangostationcontrol/tangostationcontrol/devices/unb2.py
@@ -17,17 +17,32 @@ from tango.server import device_property, attribute
 from tango import AttrWriteType
 # Additional import
 
-from clients.attribute_wrapper import attribute_wrapper
-from devices.opcua_device import opcua_device
+from tangostationcontrol.clients.opcua_client import OPCUAConnection
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.devices.hardware_device import hardware_device
 
-from common.lofar_logging import device_logging_to_python, log_exceptions
+from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions
+from tangostationcontrol.common.lofar_git import get_version
 
 import numpy
 
 __all__ = ["UNB2", "main"]
 
 @device_logging_to_python()
-class UNB2(opcua_device):
+class UNB2(hardware_device):
+    """
+
+    **Properties:**
+
+    - Device Property
+        OPC_Server_Name
+            - Type:'DevString'
+        OPC_Server_Port
+            - Type:'DevULong'
+        OPC_Time_Out
+            - Type:'DevDouble'
+    """
+
     # -----------------
     # Device Properties
     # -----------------
@@ -42,6 +57,8 @@ class UNB2(opcua_device):
     # Attributes
     # ----------
 
+    version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version())
+
     N_unb = 2
     N_fpga = 4
     N_ddr = 2
@@ -133,10 +150,49 @@ class UNB2(opcua_device):
 
     # QualifiedName(2: UNB2_on)
     # QualifiedName(2: UNB2_off)
+    @log_exceptions()
+    def delete_device(self):
+        """Hook to delete resources allocated in init_device.
+
+        This method allows for any memory or other resources allocated in the
+        init_device method to be released.  This method is called by the device
+        destructor and by the device Init command (a Tango built-in).
+        """
+        self.debug_stream("Shutting down...")
+
+        self.Off()
+        self.debug_stream("Shut down.  Good bye.")
 
     # --------
     # overloaded functions
     # --------
+    @log_exceptions()
+    def configure_for_off(self):
+        """ user code here. is called when the state is set to OFF """
+        # Stop keep-alive
+        try:
+            self.opcua_connection.stop()
+        except Exception as e:
+            self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e))
+
+    @log_exceptions()
+    def configure_for_initialise(self):
+        """ user code here. is called when the sate is set to INIT """
+        """Initialises the attributes and properties of theRECV."""
+
+        # set up the OPC ua client
+        self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self)
+
+        # map an access helper class
+        for i in self.attr_list():
+            try:
+                i.set_comm_client(self.OPCua_client)
+            except Exception as e:
+                # use the pass function instead of setting read/write fails
+                i.set_pass_func()
+                self.warn_stream("error while setting the UNB2 attribute {} read/write function. {}".format(i, e))
+
+        self.OPCua_client.start()
 
     # --------
     # Commands
@@ -148,7 +204,7 @@ class UNB2(opcua_device):
 def main(args=None, **kwargs):
     """Main function of the UNB2 module."""
 
-    from common.lofar_logging import configure_logger
+    from tangostationcontrol.common.lofar_logging import configure_logger
     configure_logger()
 
     return run((UNB2,), args=args, **kwargs)
diff --git a/devices/examples/HW_device_template.py b/tangostationcontrol/tangostationcontrol/examples/HW_device_template.py
similarity index 94%
rename from devices/examples/HW_device_template.py
rename to tangostationcontrol/tangostationcontrol/examples/HW_device_template.py
index c51538c9db16b0894a74a9f149a715ede94a8acb..4645488405ac1dc23acde36a7659e3b9182c57a7 100644
--- a/devices/examples/HW_device_template.py
+++ b/tangostationcontrol/tangostationcontrol/examples/HW_device_template.py
@@ -11,11 +11,9 @@
 
 # PyTango imports
 from tango.server import run
-from tango import AttrWriteType
 # Additional import
 
-from clients.attribute_wrapper import attribute_wrapper
-from devices.hardware_device import hardware_device
+from tangostationcontrol.devices.hardware_device import hardware_device
 
 
 __all__ = ["HW_dev"]
diff --git a/devices/integration_test/client/__init__.py b/tangostationcontrol/tangostationcontrol/examples/__init__.py
similarity index 100%
rename from devices/integration_test/client/__init__.py
rename to tangostationcontrol/tangostationcontrol/examples/__init__.py
diff --git a/devices/integration_test/devices/__init__.py b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/__init__.py
similarity index 100%
rename from devices/integration_test/devices/__init__.py
rename to tangostationcontrol/tangostationcontrol/examples/load_from_disk/__init__.py
diff --git a/devices/examples/load_from_disk/ini_client.py b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_client.py
similarity index 98%
rename from devices/examples/load_from_disk/ini_client.py
rename to tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_client.py
index cd227f23458c672c08b3acf08ba65fa9a48b581d..82c57a685e105527625c7dd3c0acdb77e59e1c8c 100644
--- a/devices/examples/load_from_disk/ini_client.py
+++ b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_client.py
@@ -1,4 +1,4 @@
-from clients.comms_client import CommClient
+from tangostationcontrol.clients.comms_client import CommClient
 import configparser
 import numpy
 
diff --git a/devices/examples/load_from_disk/ini_device.py b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_device.py
similarity index 96%
rename from devices/examples/load_from_disk/ini_device.py
rename to tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_device.py
index 8391d2823b68be53e56bd6f8b650b0f46362f429..1f65d3ccd5f0827305a2d1a91e958b6e6241bc85 100644
--- a/devices/examples/load_from_disk/ini_device.py
+++ b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_device.py
@@ -12,16 +12,14 @@
 # PyTango imports
 from tango.server import run
 from tango import AttrWriteType
-# Additional import
-from clients.attribute_wrapper import attribute_wrapper
-from devices.hardware_device import hardware_device
-
 
 import configparser
 import numpy
 
-from ini_client import *
-
+# Additional import
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.devices.hardware_device import hardware_device
+from tangostationcontrol.examples.load_from_disk.ini_client import *
 
 __all__ = ["ini_device"]
 
diff --git a/devices/test/__init__.py b/tangostationcontrol/tangostationcontrol/examples/snmp/__init__.py
similarity index 100%
rename from devices/test/__init__.py
rename to tangostationcontrol/tangostationcontrol/examples/snmp/__init__.py
diff --git a/devices/examples/snmp/snmp.py b/tangostationcontrol/tangostationcontrol/examples/snmp/snmp.py
similarity index 91%
rename from devices/examples/snmp/snmp.py
rename to tangostationcontrol/tangostationcontrol/examples/snmp/snmp.py
index 9ef61ada5a88ae8b9a19447c5cdcf2b42c21149e..845ff0937ccaaf3ad120fe6c96acfce581417f0f 100644
--- a/devices/examples/snmp/snmp.py
+++ b/tangostationcontrol/tangostationcontrol/examples/snmp/snmp.py
@@ -17,9 +17,9 @@ from tango.server import device_property
 from tango import AttrWriteType
 
 # Additional import
-from examples.snmp.snmp_client import SNMP_client
-from clients.attribute_wrapper import attribute_wrapper
-from devices.hardware_device import hardware_device
+from tangostationcontrol.examples.snmp.snmp_client import SNMP_client
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+from tangostationcontrol.devices.hardware_device import hardware_device
 
 import numpy
 
@@ -111,8 +111,7 @@ class SNMP(hardware_device):
 def main(args=None, **kwargs):
     """Main function of the module."""
 
-    from common.lofar_logging import configure_logger
-    import logging
-    configure_logger(logging.getLogger())
+    from tangostationcontrol.common.lofar_logging import configure_logger
+    configure_logger()
 
     return run((SNMP,), args=args, **kwargs)
diff --git a/devices/examples/snmp/snmp_client.py b/tangostationcontrol/tangostationcontrol/examples/snmp/snmp_client.py
similarity index 98%
rename from devices/examples/snmp/snmp_client.py
rename to tangostationcontrol/tangostationcontrol/examples/snmp/snmp_client.py
index 96ac67140b9bdbdba7ab4d4fb8651b5e9674c219..00464f37d92800207fec524279126e494886d01a 100644
--- a/devices/examples/snmp/snmp_client.py
+++ b/tangostationcontrol/tangostationcontrol/examples/snmp/snmp_client.py
@@ -1,5 +1,5 @@
 
-from clients.comms_client import CommClient
+from tangostationcontrol.clients.comms_client import CommClient
 
 import snmp
 
diff --git a/devices/integration_test/README.md b/tangostationcontrol/tangostationcontrol/integration_test/README.md
similarity index 100%
rename from devices/integration_test/README.md
rename to tangostationcontrol/tangostationcontrol/integration_test/README.md
diff --git a/devices/test/clients/__init__.py b/tangostationcontrol/tangostationcontrol/integration_test/__init__.py
similarity index 100%
rename from devices/test/clients/__init__.py
rename to tangostationcontrol/tangostationcontrol/integration_test/__init__.py
diff --git a/devices/integration_test/base.py b/tangostationcontrol/tangostationcontrol/integration_test/base.py
similarity index 85%
rename from devices/integration_test/base.py
rename to tangostationcontrol/tangostationcontrol/integration_test/base.py
index 241f0ecd409fd16484d81e31f1e1f83dc1b9d81b..3583d1901a3ae7cecfefee1ac6ad698f0c098456 100644
--- a/devices/integration_test/base.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/base.py
@@ -7,15 +7,10 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-from common.lofar_logging import configure_logger
-
 import unittest
 import asynctest
 import testscenarios
 
-"""Setup logging for integration tests"""
-configure_logger(debug=True)
-
 
 class BaseIntegrationTestCase(testscenarios.WithScenarios, unittest.TestCase):
     """Integration test base class."""
diff --git a/devices/test/common/__init__.py b/tangostationcontrol/tangostationcontrol/integration_test/client/__init__.py
similarity index 100%
rename from devices/test/common/__init__.py
rename to tangostationcontrol/tangostationcontrol/integration_test/client/__init__.py
diff --git a/devices/integration_test/client/test_sdptr_sim.py b/tangostationcontrol/tangostationcontrol/integration_test/client/test_sdptr_sim.py
similarity index 92%
rename from devices/integration_test/client/test_sdptr_sim.py
rename to tangostationcontrol/tangostationcontrol/integration_test/client/test_sdptr_sim.py
index ab9288b727e515c19b07c99d1fe8a233d7032055..a09f407e2982d7b873021f03b1eb9e78fe336e44 100644
--- a/devices/integration_test/client/test_sdptr_sim.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/client/test_sdptr_sim.py
@@ -9,7 +9,7 @@
 
 from asyncua import Client
 
-from integration_test import base
+from tangostationcontrol.integration_test import base
 
 
 class TestSDPTRSim(base.IntegrationAsyncTestCase):
diff --git a/devices/integration_test/client/test_tcp_replicator.py b/tangostationcontrol/tangostationcontrol/integration_test/client/test_tcp_replicator.py
similarity index 97%
rename from devices/integration_test/client/test_tcp_replicator.py
rename to tangostationcontrol/tangostationcontrol/integration_test/client/test_tcp_replicator.py
index ca45c4c52ab7f5e379c484b964a05225950fc9e1..2467fd6dd8a7b24cc786c5b2cc10a25a610b88f1 100644
--- a/devices/integration_test/client/test_tcp_replicator.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/client/test_tcp_replicator.py
@@ -7,18 +7,16 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-from asyncio import Queue
-
 import logging
 import time
 import socket
 import sys
 
-from clients.tcp_replicator import TCPReplicator
+import timeout_decorator
 
-from integration_test import base
+from tangostationcontrol.clients.tcp_replicator import TCPReplicator
 
-import timeout_decorator
+from tangostationcontrol.integration_test import base
 
 logger = logging.getLogger()
 
diff --git a/devices/integration_test/client/test_unb2_sim.py b/tangostationcontrol/tangostationcontrol/integration_test/client/test_unb2_sim.py
similarity index 92%
rename from devices/integration_test/client/test_unb2_sim.py
rename to tangostationcontrol/tangostationcontrol/integration_test/client/test_unb2_sim.py
index 1eb9972400f2121a6365b2fcb875ecbc2190cdff..5d2e1016e4edfc1f7869b20dafbb5d0a01a46bf2 100644
--- a/devices/integration_test/client/test_unb2_sim.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/client/test_unb2_sim.py
@@ -9,7 +9,7 @@
 
 from asyncua import Client
 
-from integration_test import base
+from tangostationcontrol.integration_test import base
 
 
 class TestUNB2Sim(base.IntegrationAsyncTestCase):
diff --git a/devices/test/devices/__init__.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/__init__.py
similarity index 100%
rename from devices/test/devices/__init__.py
rename to tangostationcontrol/tangostationcontrol/integration_test/devices/__init__.py
diff --git a/devices/integration_test/devices/test_device_recv.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py
similarity index 96%
rename from devices/integration_test/devices/test_device_recv.py
rename to tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py
index 3a010a000c03d3c039f8f93a68c0f6437bc30db1..285f02244d490f97d36be678c5628447e64d96c4 100644
--- a/devices/integration_test/devices/test_device_recv.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py
@@ -7,12 +7,10 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-import time
-
 from tango import DeviceProxy
 from tango._tango import DevState
 
-from integration_test import base
+from tangostationcontrol.integration_test import base
 
 
 class TestDeviceRECV(base.IntegrationTestCase):
diff --git a/devices/integration_test/devices/test_device_sdp.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py
similarity index 96%
rename from devices/integration_test/devices/test_device_sdp.py
rename to tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py
index 5f064128f858e0bd2c44768a4f13057e5dc20266..b4dbb02c0f6d25f4f37cddf687f2b6da47a3738c 100644
--- a/devices/integration_test/devices/test_device_sdp.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py
@@ -7,12 +7,10 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-import time
-
 from tango import DeviceProxy
 from tango._tango import DevState
 
-from integration_test import base
+from tangostationcontrol.integration_test import base
 
 
 class TestDeviceSDP(base.IntegrationTestCase):
diff --git a/devices/integration_test/devices/test_device_sst.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py
similarity index 98%
rename from devices/integration_test/devices/test_device_sst.py
rename to tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py
index a6b71d328305f2dafed46f9e4f3ea9209df9601d..7c20dc1cc96b5c87c6a1f2dc6e3cc5ef10fd2c13 100644
--- a/devices/integration_test/devices/test_device_sst.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py
@@ -13,7 +13,7 @@ import time
 from tango import DeviceProxy
 from tango._tango import DevState
 
-from integration_test import base
+from tangostationcontrol.integration_test import base
 
 
 class TestDeviceSST(base.IntegrationTestCase):
diff --git a/devices/integration_test/devices/test_device_unb2.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py
similarity index 92%
rename from devices/integration_test/devices/test_device_unb2.py
rename to tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py
index 97f31ab6ee162f8183db963e92f4f03b9ee7f617..e0196230b38782ba5003dd0ff46d1fd51ede531b 100644
--- a/devices/integration_test/devices/test_device_unb2.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py
@@ -7,12 +7,10 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-import time
-
 from tango import DeviceProxy
 from tango._tango import DevState
 
-from integration_test import base
+from tangostationcontrol.integration_test import base
 
 
 class TestDeviceUNB2(base.IntegrationTestCase):
diff --git a/devices/statistics_writer/README.md b/tangostationcontrol/tangostationcontrol/statistics_writer/README.md
similarity index 100%
rename from devices/statistics_writer/README.md
rename to tangostationcontrol/tangostationcontrol/statistics_writer/README.md
diff --git a/tangostationcontrol/tangostationcontrol/statistics_writer/__init__.py b/tangostationcontrol/tangostationcontrol/statistics_writer/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/statistics_writer/hdf5_writer.py b/tangostationcontrol/tangostationcontrol/statistics_writer/hdf5_writer.py
similarity index 94%
rename from devices/statistics_writer/hdf5_writer.py
rename to tangostationcontrol/tangostationcontrol/statistics_writer/hdf5_writer.py
index 6715dd870608a0202610ea52c417695844f0d1c9..87fb3d28d7ad991d9ff8e6442656835e6a636fb9 100644
--- a/devices/statistics_writer/hdf5_writer.py
+++ b/tangostationcontrol/tangostationcontrol/statistics_writer/hdf5_writer.py
@@ -11,8 +11,8 @@ import logging
 # import statistics classes with workaround
 import sys
 sys.path.append("..")
-from devices.sdp.statistics_packet import SSTPacket, XSTPacket, BSTPacket, StatisticsPacket
-import devices.sdp.statistics_collector as statistics_collector
+from tangostationcontrol.devices.sdp.statistics_packet import SSTPacket, XSTPacket, BSTPacket, StatisticsPacket
+import tangostationcontrol.devices.sdp.statistics_collector as statistics_collector
 
 
 logger = logging.getLogger("statistics_writer")
@@ -133,7 +133,7 @@ class hdf5_writer:
         """
 
         # create the new hdf5 group based on the timestamp of packets
-        current_group = self.file.create_group("{}_{}".format(self.mode, self.current_timestamp.isoformat(timespec="milliseconds")))
+        current_group = self.file.create_group("{}_{}".format(self.mode, self.current_timestamp.strftime("%Y-%m-%d-%H-%M-%S-%f")[:-3]))
 
         # store the statistics values for the current group
         self.store_function(current_group)
@@ -158,11 +158,11 @@ class hdf5_writer:
 
     def write_sst_matrix(self, current_group):
         # store the SST values
-        current_group.create_dataset(name="values", data=self.current_matrix.parameters["sst_values"].astype(numpy.float32), compression="gzip")
+        current_group.create_dataset(name="sst_values", data=self.current_matrix.parameters["sst_values"].astype(numpy.float32), compression="gzip")
 
     def write_xst_matrix(self, current_group):
         # requires a function call to transform the xst_blocks in to the right structure
-        current_group.create_dataset(name="values", data=self.current_matrix.xst_values().astype(numpy.cfloat), compression="gzip")
+        current_group.create_dataset(name="xst_values", data=self.current_matrix.xst_values().astype(numpy.cfloat), compression="gzip")
 
     def write_bst_matrix(self, current_group):
         raise NotImplementedError("BST values not implemented")
diff --git a/devices/statistics_writer/receiver.py b/tangostationcontrol/tangostationcontrol/statistics_writer/receiver.py
similarity index 95%
rename from devices/statistics_writer/receiver.py
rename to tangostationcontrol/tangostationcontrol/statistics_writer/receiver.py
index 919357764a2196cb7955e4ec77f2487b81d24d59..e719eef9ae4ec888a99a7a1dc5b831e5dba74915 100644
--- a/devices/statistics_writer/receiver.py
+++ b/tangostationcontrol/tangostationcontrol/statistics_writer/receiver.py
@@ -2,7 +2,7 @@ import socket
 
 import sys
 sys.path.append("..")
-from devices.sdp.statistics_packet import StatisticsPacket
+from tangostationcontrol.devices.sdp.statistics_packet import StatisticsPacket
 import os
 
 class receiver:
diff --git a/devices/statistics_writer/statistics_writer.py b/tangostationcontrol/tangostationcontrol/statistics_writer/statistics_writer.py
similarity index 99%
rename from devices/statistics_writer/statistics_writer.py
rename to tangostationcontrol/tangostationcontrol/statistics_writer/statistics_writer.py
index 594e261c6d1e00e0ea7882c595449813c305c8ce..e2d4666fd581b01cdb99e9ad717fbccd32cfa33c 100644
--- a/devices/statistics_writer/statistics_writer.py
+++ b/tangostationcontrol/tangostationcontrol/statistics_writer/statistics_writer.py
@@ -70,3 +70,5 @@ if __name__ == "__main__":
         logger.info("End of input.")
     finally:
         writer.close_writer()
+
+
diff --git a/tangostationcontrol/tangostationcontrol/statistics_writer/test/__init__.py b/tangostationcontrol/tangostationcontrol/statistics_writer/test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/statistics_writer/test/devices_test_SDP_SST_statistics_packets.bin b/tangostationcontrol/tangostationcontrol/statistics_writer/test/devices_test_SDP_SST_statistics_packets.bin
similarity index 100%
rename from devices/statistics_writer/test/devices_test_SDP_SST_statistics_packets.bin
rename to tangostationcontrol/tangostationcontrol/statistics_writer/test/devices_test_SDP_SST_statistics_packets.bin
diff --git a/tangostationcontrol/tangostationcontrol/statistics_writer/test/hdf5_explorer.py b/tangostationcontrol/tangostationcontrol/statistics_writer/test/hdf5_explorer.py
new file mode 100644
index 0000000000000000000000000000000000000000..102c36b79f7beeb6a34ffba9b95a495a85a76f6e
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/statistics_writer/test/hdf5_explorer.py
@@ -0,0 +1,95 @@
+import h5py
+import numpy
+
+import argparse
+
+parser = argparse.ArgumentParser(description='Select a file to explore')
+parser.add_argument('--file', type=str, help='the name and path of the file')
+
+import logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger("hdf5_explorer")
+logger.setLevel(logging.DEBUG)
+
+
+class statistics_data:
+    """
+    Example class not used by anything
+    This class takes the file and the statistics name as its __init__ arguments and then stores the
+    the datasets in them.
+    """
+
+class explorer:
+    """
+    This class serves both as a tool to test and verify the content of HDF5 files as well as provide an example
+    of how you can go through HDF5 files.
+    """
+
+
+    def __init__(self, filename):
+        self.file = h5py.File(filename, 'r')
+
+    def print_all_statistics_full(self):
+        """
+        Explores the file with knowledge of the file structure. assumes all top level groups are statistics
+        and that all statistics groups are made up of datasets.
+        Prints the groups, the datasets and the content of the datasets.
+
+        Can easily be modified to instead of just logging all the data, store it in whatever structure is needed.
+        """
+
+        for group_key in self.file.keys():
+            dataset = list(self.file[group_key])
+
+            #print group name
+            logger.debug(f" \n\ngroup: {group_key}")
+
+            # Go through all the datasets
+            for i in dataset:
+                data = self.file.get(f"{group_key}/{i}")
+                logger.debug(f" dataset: {i}")
+                logger.debug(f" Data: {numpy.array(data)}")
+
+            # go through all the attributes in the group (This is the header info)
+            attr_keys = self.file[group_key].attrs.keys()
+            for i in attr_keys:
+                attr = self.file[group_key].attrs[i]
+
+                logger.debug(f" {i}: {attr}")
+
+    def print_all_statistics_top_level(self):
+        """
+        Explores the file with knowledge of the file structure. assumes all top level groups are statistics
+        and that all statistics groups are made up of datasets.
+        This function prints only the top level groups, AKA all the statistics collected. Useful when dealing with
+        potentially hundreds of statistics.
+        """
+        # List all groups
+        logger.debug("Listing all statistics stored in this file:")
+
+        for group_key in self.file.keys():
+            logger.debug(group_key)
+
+
+# create a data dumper that creates a new file every 10s (for testing)
+if __name__ == "__main__":
+    args = parser.parse_args()
+    Explorer = explorer(args.file)
+
+    """
+    Print the entire files content
+    """
+    Explorer.print_all_statistics_full()
+
+    """
+    Print only the names of all the statistics in this file
+    """
+    logger.debug("--------------Top level groups--------------")
+    Explorer.print_all_statistics_top_level()
+
+
+
+
+
+
+
diff --git a/devices/statistics_writer/test/test_server.py b/tangostationcontrol/tangostationcontrol/statistics_writer/test/test_server.py
similarity index 100%
rename from devices/statistics_writer/test/test_server.py
rename to tangostationcontrol/tangostationcontrol/statistics_writer/test/test_server.py
diff --git a/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/__init__.py b/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/statistics_writer/udp_dev/udp_client.py b/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_client.py
similarity index 100%
rename from devices/statistics_writer/udp_dev/udp_client.py
rename to tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_client.py
diff --git a/devices/statistics_writer/udp_dev/udp_server.py b/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_server.py
similarity index 100%
rename from devices/statistics_writer/udp_dev/udp_server.py
rename to tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_server.py
diff --git a/devices/statistics_writer/udp_dev/udp_write_manager.py b/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_write_manager.py
similarity index 100%
rename from devices/statistics_writer/udp_dev/udp_write_manager.py
rename to tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_write_manager.py
diff --git a/devices/test/README.md b/tangostationcontrol/tangostationcontrol/test/README.md
similarity index 100%
rename from devices/test/README.md
rename to tangostationcontrol/tangostationcontrol/test/README.md
diff --git a/devices/test/SDP_SST_statistics_packet.bin b/tangostationcontrol/tangostationcontrol/test/SDP_SST_statistics_packet.bin
similarity index 100%
rename from devices/test/SDP_SST_statistics_packet.bin
rename to tangostationcontrol/tangostationcontrol/test/SDP_SST_statistics_packet.bin
diff --git a/devices/test/SDP_SST_statistics_packets.bin b/tangostationcontrol/tangostationcontrol/test/SDP_SST_statistics_packets.bin
similarity index 100%
rename from devices/test/SDP_SST_statistics_packets.bin
rename to tangostationcontrol/tangostationcontrol/test/SDP_SST_statistics_packets.bin
diff --git a/devices/test/SDP_XST_statistics_packets.bin b/tangostationcontrol/tangostationcontrol/test/SDP_XST_statistics_packets.bin
similarity index 100%
rename from devices/test/SDP_XST_statistics_packets.bin
rename to tangostationcontrol/tangostationcontrol/test/SDP_XST_statistics_packets.bin
diff --git a/tangostationcontrol/tangostationcontrol/test/__init__.py b/tangostationcontrol/tangostationcontrol/test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/test/base.py b/tangostationcontrol/tangostationcontrol/test/base.py
similarity index 84%
rename from devices/test/base.py
rename to tangostationcontrol/tangostationcontrol/test/base.py
index 66e64ea9a8669713f672db2088344d96a17f6e7c..81a76c46e843dd7af91f19e1c142f612916157c7 100644
--- a/devices/test/base.py
+++ b/tangostationcontrol/tangostationcontrol/test/base.py
@@ -7,15 +7,10 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-from common.lofar_logging import configure_logger
-
 import unittest
 import testscenarios
 import asynctest
 
-"""Setup logging for unit tests"""
-configure_logger(debug=True)
-
 
 class BaseTestCase(testscenarios.WithScenarios, unittest.TestCase):
     """Test base class."""
diff --git a/tangostationcontrol/tangostationcontrol/test/clients/__init__.py b/tangostationcontrol/tangostationcontrol/test/clients/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/test/clients/test_attr_wrapper.py b/tangostationcontrol/tangostationcontrol/test/clients/test_attr_wrapper.py
similarity index 99%
rename from devices/test/clients/test_attr_wrapper.py
rename to tangostationcontrol/tangostationcontrol/test/clients/test_attr_wrapper.py
index 8711d989a67730667c10aed91de7c9929c500fcb..f1c5b652c545b31f897388ddce0c8f66df37912e 100644
--- a/devices/test/clients/test_attr_wrapper.py
+++ b/tangostationcontrol/tangostationcontrol/test/clients/test_attr_wrapper.py
@@ -10,13 +10,13 @@
 from tango import DevState
 
 # Internal imports
-from test.clients.test_client import test_client
-from clients.attribute_wrapper import *
-from devices.hardware_device import *
+from tangostationcontrol.test.clients.test_client import test_client
+from tangostationcontrol.clients.attribute_wrapper import *
+from tangostationcontrol.devices.hardware_device import *
 
 # Test imports
 from tango.test_context import DeviceTestContext
-from test import base
+from tangostationcontrol.test import base
 
 import asyncio
 
diff --git a/devices/test/clients/test_client.py b/tangostationcontrol/tangostationcontrol/test/clients/test_client.py
similarity index 98%
rename from devices/test/clients/test_client.py
rename to tangostationcontrol/tangostationcontrol/test/clients/test_client.py
index 039974a1e34ae1a0c9779fd29c2c87f545bc38b7..a17561d77b7c6d05a06683a8b7a1cf023a289dcf 100644
--- a/devices/test/clients/test_client.py
+++ b/tangostationcontrol/tangostationcontrol/test/clients/test_client.py
@@ -3,7 +3,7 @@ import numpy
 
 
 # Test imports
-from clients.comms_client import CommClient
+from tangostationcontrol.clients.comms_client import CommClient
 
 
 class test_client(CommClient):
diff --git a/devices/test/clients/test_opcua_client.py b/tangostationcontrol/tangostationcontrol/test/clients/test_opcua_client.py
similarity index 98%
rename from devices/test/clients/test_opcua_client.py
rename to tangostationcontrol/tangostationcontrol/test/clients/test_opcua_client.py
index 6315abb20547cb35233b9b47f8f8c32414b1159e..b1e73001328da7bd4681529c408b1514a143262b 100644
--- a/devices/test/clients/test_opcua_client.py
+++ b/tangostationcontrol/tangostationcontrol/test/clients/test_opcua_client.py
@@ -1,16 +1,16 @@
 import numpy
-from clients.opcua_client import OPCUAConnection
-from clients import opcua_client
-
 import asyncua
 import io
 import asyncio
 
 from unittest import mock
-import unittest
 
 from test import base
 import asynctest
+from tangostationcontrol.clients.opcua_client import OPCUAConnection
+from tangostationcontrol.clients import opcua_client
+
+from tangostationcontrol.test import base
 
 
 class attr_props:
diff --git a/devices/test/clients/test_statistics_client_thread.py b/tangostationcontrol/tangostationcontrol/test/clients/test_statistics_client_thread.py
similarity index 86%
rename from devices/test/clients/test_statistics_client_thread.py
rename to tangostationcontrol/tangostationcontrol/test/clients/test_statistics_client_thread.py
index fd7ce0701f9d792863909b9f8ee4a9d39a2b1fd1..17f866871bd682b3f289364c16a55e5ee2010a7c 100644
--- a/devices/test/clients/test_statistics_client_thread.py
+++ b/tangostationcontrol/tangostationcontrol/test/clients/test_statistics_client_thread.py
@@ -10,9 +10,10 @@
 import logging
 from unittest import mock
 
-from clients.statistics_client_thread import StatisticsClientThread
+from tangostationcontrol.clients.statistics_client_thread import \
+    StatisticsClientThread
 
-from test import base
+from tangostationcontrol.test import base
 
 logger = logging.getLogger()
 
diff --git a/devices/test/clients/test_tcp_replicator.py b/tangostationcontrol/tangostationcontrol/test/clients/test_tcp_replicator.py
similarity index 97%
rename from devices/test/clients/test_tcp_replicator.py
rename to tangostationcontrol/tangostationcontrol/test/clients/test_tcp_replicator.py
index a9babed0eb7af7a58544b3ff7535c3113ed12ca3..04c87f1d5a15705f7d0b8ce1460141255cb02b27 100644
--- a/devices/test/clients/test_tcp_replicator.py
+++ b/tangostationcontrol/tangostationcontrol/test/clients/test_tcp_replicator.py
@@ -9,15 +9,14 @@
 
 import logging
 import time
-from queue import Queue
 from unittest import mock
 
-from clients.tcp_replicator import TCPReplicator
-from clients import tcp_replicator
+import timeout_decorator
 
-from test import base
+from tangostationcontrol.clients.tcp_replicator import TCPReplicator
+from tangostationcontrol.clients import tcp_replicator
 
-import timeout_decorator
+from tangostationcontrol.test import base
 
 logger = logging.getLogger()
 
diff --git a/tangostationcontrol/tangostationcontrol/test/common/__init__.py b/tangostationcontrol/tangostationcontrol/test/common/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/test/common/test_baselines.py b/tangostationcontrol/tangostationcontrol/test/common/test_baselines.py
similarity index 91%
rename from devices/test/common/test_baselines.py
rename to tangostationcontrol/tangostationcontrol/test/common/test_baselines.py
index 206b4ca0eccefe1012519c8236d158e52f1cdc38..25eb5d1dfffa2fca8748d74020893edfb17c2037 100644
--- a/devices/test/common/test_baselines.py
+++ b/tangostationcontrol/tangostationcontrol/test/common/test_baselines.py
@@ -7,9 +7,9 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-from common import baselines
+from tangostationcontrol.common import baselines
 
-from test import base
+from tangostationcontrol.test import base
 
 
 class TestBaselines(base.TestCase):
diff --git a/devices/test/common/test_lofar_git.py b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_git.py
similarity index 97%
rename from devices/test/common/test_lofar_git.py
rename to tangostationcontrol/tangostationcontrol/test/common/test_lofar_git.py
index 52a1c7d876fc2827757f082e0f44a0a64b1ffc78..295cf0141064a6d23eec9aa101234d87160c4937 100644
--- a/devices/test/common/test_lofar_git.py
+++ b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_git.py
@@ -10,9 +10,9 @@
 import git
 from unittest import mock
 
-from common import lofar_git
+from tangostationcontrol.common import lofar_git
 
-from test import base
+from tangostationcontrol.test import base
 
 
 class TestLofarGit(base.TestCase):
diff --git a/devices/test/common/test_lofar_logging.py b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_logging.py
similarity index 97%
rename from devices/test/common/test_lofar_logging.py
rename to tangostationcontrol/tangostationcontrol/test/common/test_lofar_logging.py
index 534b2650c8d432ef7c7dae9e32448b01cc5913f3..30a4faa603b4e2550c8c26a13958119a225de8c3 100644
--- a/devices/test/common/test_lofar_logging.py
+++ b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_logging.py
@@ -7,15 +7,15 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-import git
 from unittest import mock
-
-from common import lofar_logging
 import logging
+
 from tango.server import Device
 from tango import device_server
 
-from test import base
+from tangostationcontrol.common import lofar_logging
+
+from tangostationcontrol.test import base
 
 
 class TestLofarLogging(base.TestCase):
diff --git a/tangostationcontrol/tangostationcontrol/test/devices/__init__.py b/tangostationcontrol/tangostationcontrol/test/devices/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/test/devices/random_data.py b/tangostationcontrol/tangostationcontrol/test/devices/random_data.py
similarity index 100%
rename from devices/test/devices/random_data.py
rename to tangostationcontrol/tangostationcontrol/test/devices/random_data.py
diff --git a/devices/test/devices/test_abstract_device.py b/tangostationcontrol/tangostationcontrol/test/devices/test_abstract_device.py
similarity index 95%
rename from devices/test/devices/test_abstract_device.py
rename to tangostationcontrol/tangostationcontrol/test/devices/test_abstract_device.py
index f54383c9a1b85f5c9e442f51d7f04d061951f772..469ea19fa970cf48c2de9c4c6b5648bd7b756040 100644
--- a/devices/test/devices/test_abstract_device.py
+++ b/tangostationcontrol/tangostationcontrol/test/devices/test_abstract_device.py
@@ -16,9 +16,9 @@ from tango.server import attribute
 
 from tango.test_context import DeviceTestContext
 
-from devices.abstract_device import AbstractDeviceMetas
+from tangostationcontrol.devices.abstract_device import AbstractDeviceMetas
 
-from test import base
+from tangostationcontrol.test import base
 
 
 class TestAbstractDevice(base.TestCase):
diff --git a/devices/test/devices/test_statistics_collector.py b/tangostationcontrol/tangostationcontrol/test/devices/test_statistics_collector.py
similarity index 58%
rename from devices/test/devices/test_statistics_collector.py
rename to tangostationcontrol/tangostationcontrol/test/devices/test_statistics_collector.py
index 5fe4e24dabbf169664b19250cba13f19b8020327..3266dbd148bb4872ec1f2eec8f9c555414fde16c 100644
--- a/devices/test/devices/test_statistics_collector.py
+++ b/tangostationcontrol/tangostationcontrol/test/devices/test_statistics_collector.py
@@ -1,22 +1,19 @@
-from devices.sdp.statistics_collector import XSTCollector
-from devices.sdp.statistics_packet import XSTPacket
+from tangostationcontrol.devices.sdp.statistics_collector import XSTCollector
+from tangostationcontrol.devices.sdp.statistics_packet import XSTPacket
 
-from test import base
+from tangostationcontrol.test import base
 
 class TestXSTCollector(base.TestCase):
     def test_valid_packet(self):
         collector = XSTCollector()
 
-        # a valid packet as obtained from SDP, with 64-bit BE 1+1j as payload at (12,0)
-        packet =  b'X\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x0c\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        # a valid packet as obtained from SDP, with 64-bit BE 1+1j as payload
+        packet =  b'X\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01'
 
         # parse it ourselves to extract info nicely
         fields = XSTPacket(packet)
         fpga_index = fields.gn_index
 
-        # baseline indeed should be (12,0)
-        self.assertEqual((12,0), fields.first_baseline)
-
         # this should not throw
         collector.process_packet(packet)
 
@@ -44,51 +41,10 @@ class TestXSTCollector(base.TestCase):
                 else:
                     self.assertEqual(0+0j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] was not in packet, but was written to the XST matrix.')
 
-    def test_conjugated_packet(self):
-        """ Test whether a packet with a baseline (a,b) with a<b will get its payload conjugated. """
-
-        collector = XSTCollector()
-
-        # a valid packet as obtained from SDP, with 64-bit BE 1+1j as payload, at baseline (0,12)
-        #                                                                       VV  VV
-        packet =  b'X\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x0c\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01'
-
-        # parse it ourselves to extract info nicely
-        fields = XSTPacket(packet)
-
-        # baseline indeed should be (0,12)
-        self.assertEqual((0,12), fields.first_baseline)
-
-        # this should not throw
-        collector.process_packet(packet)
-
-        # counters should now be updated
-        self.assertEqual(1, collector.parameters["nof_packets"])
-        self.assertEqual(0, collector.parameters["nof_invalid_packets"])
-
-        # check whether the data ended up in the right block, and the rest is still zero
-        xst_values = collector.xst_values()
-
-        for baseline_a in range(collector.MAX_INPUTS):
-            for baseline_b in range(collector.MAX_INPUTS):
-                if baseline_b > baseline_a:
-                    # only scan top-left triangle
-                    continue
-
-                # use swapped indices!
-                baseline_a_was_in_packet = (fields.first_baseline[1] <= baseline_a < fields.first_baseline[1] + fields.nof_signal_inputs)
-                baseline_b_was_in_packet = (fields.first_baseline[0] <= baseline_b < fields.first_baseline[0] + fields.nof_signal_inputs)
-
-                if baseline_a_was_in_packet and baseline_b_was_in_packet:
-                    self.assertEqual(1-1j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] did not end up conjugated in XST matrix.')
-                else:
-                    self.assertEqual(0+0j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] was not in packet, but was written to the XST matrix.')
-
     def test_invalid_packet(self):
         collector = XSTCollector()
 
         # an invalid packet
-        #           V
         packet =  b'S\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01'
 
         # this should throw
@@ -106,7 +62,6 @@ class TestXSTCollector(base.TestCase):
         collector = XSTCollector()
 
         # an valid packet with a payload error
-        #                                           V
         packet =  b'X\x05\x00\x00\x00\x00\x00\x00\x14\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01'
 
         # parse it ourselves to extract info nicely
diff --git a/devices/toolkit/README.md b/tangostationcontrol/tangostationcontrol/toolkit/README.md
similarity index 100%
rename from devices/toolkit/README.md
rename to tangostationcontrol/tangostationcontrol/toolkit/README.md
diff --git a/tangostationcontrol/tangostationcontrol/toolkit/__init__.py b/tangostationcontrol/tangostationcontrol/toolkit/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/toolkit/archiver.py b/tangostationcontrol/tangostationcontrol/toolkit/archiver.py
similarity index 99%
rename from devices/toolkit/archiver.py
rename to tangostationcontrol/tangostationcontrol/toolkit/archiver.py
index 0991b05c6fac220a786b90bc922032f130785c12..a21c48268eac7d76ea772c579b2c87697f5b7995 100644
--- a/devices/toolkit/archiver.py
+++ b/tangostationcontrol/tangostationcontrol/toolkit/archiver.py
@@ -3,7 +3,9 @@
 #from logging import raiseExceptions
 import logging
 import traceback
-from clients.attribute_wrapper import attribute_wrapper
+
+from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
+
 from tango import DeviceProxy, AttributeProxy
 from datetime import datetime, timedelta
 
diff --git a/devices/toolkit/archiver_base.py b/tangostationcontrol/tangostationcontrol/toolkit/archiver_base.py
similarity index 100%
rename from devices/toolkit/archiver_base.py
rename to tangostationcontrol/tangostationcontrol/toolkit/archiver_base.py
diff --git a/devices/toolkit/archiver_config/lofar2.json b/tangostationcontrol/tangostationcontrol/toolkit/archiver_config/lofar2.json
similarity index 100%
rename from devices/toolkit/archiver_config/lofar2.json
rename to tangostationcontrol/tangostationcontrol/toolkit/archiver_config/lofar2.json
diff --git a/devices/toolkit/get_internal_attribute_history.py b/tangostationcontrol/tangostationcontrol/toolkit/get_internal_attribute_history.py
similarity index 99%
rename from devices/toolkit/get_internal_attribute_history.py
rename to tangostationcontrol/tangostationcontrol/toolkit/get_internal_attribute_history.py
index 735be01613611d73f39678f6b0ed5fb1f1c56a70..8b6b4254f6c10dc8207232b16fd5e22d44e7c556 100644
--- a/devices/toolkit/get_internal_attribute_history.py
+++ b/tangostationcontrol/tangostationcontrol/toolkit/get_internal_attribute_history.py
@@ -1,10 +1,8 @@
 #! /usr/bin/env python3
 
-
 from tango import DeviceProxy
 from numpy import array, transpose
 
-
 def get_internal_attribute_history(device: DeviceProxy, attribute_name: str, depth: int = 10):
     try:
         history = array(device.attribute_history(attr_name = attribute_name, depth = depth))
diff --git a/devices/toolkit/lofar2_config.py b/tangostationcontrol/tangostationcontrol/toolkit/lofar2_config.py
similarity index 99%
rename from devices/toolkit/lofar2_config.py
rename to tangostationcontrol/tangostationcontrol/toolkit/lofar2_config.py
index 581eea4f73a4d276613123ec9bf86bdb7e97a0ea..811f4f27abbbac832c6de6811a223a27229e9032 100644
--- a/devices/toolkit/lofar2_config.py
+++ b/tangostationcontrol/tangostationcontrol/toolkit/lofar2_config.py
@@ -2,7 +2,6 @@
 
 import logging
 
-
 def configure_logging():
     # Always also log the hostname because it makes the origin of the log clear.
     import socket
diff --git a/tangostationcontrol/tangostationcontrol/toolkit/lts_cold_start.py b/tangostationcontrol/tangostationcontrol/toolkit/lts_cold_start.py
new file mode 100644
index 0000000000000000000000000000000000000000..b97e6d6fe9ef5a75aa37ac398b5dda328002d6e8
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/toolkit/lts_cold_start.py
@@ -0,0 +1,222 @@
+#! /usr/bin/env python3
+import logging
+from time import sleep
+
+from tangostationcontrol.toolkit.startup import startup
+from tangostationcontrol.toolkit.lofar2_config import configure_logging
+
+
+def start_device(device: str):
+    '''
+    Start a Tango device with the help of the startup function.
+    The device will not be forced to got through
+    OFF/INIT/STANDBY/ON but it is assumed that the device is in OFF
+    state.  If the device is not in OFF state, then an exception
+    will be raised.
+    '''
+    dev = startup(device = device, force_restart = False)
+    state = device.state()
+    if state is not tango._tango.DevState.ON:
+        raise Exception("Device \"{}\" is unexpectedly in \"{}\" state but it is expected to be in \"{}\" state.  Please check the reason for the unexpected device state.  Aborting the start-up procedure.".format(device, state, tango._tango.DevState.ON))
+    return device
+
+
+def lts_cold_start():
+    '''
+    What is this?
+    This is the LTS (LOFAR Test - and I forgot what S stands for) cold start
+    procedure cast into source code.  The procedure can be found there:
+    https://support.astron.nl/confluence/display/L2M/LTS+startup+procedure
+
+    Paulus wrote already a script that - illegally ;) - makes direct use of the
+    OPC-UA servers to accomplish the same thing that we are doing here.
+    Paulus' script can be found there:
+    https://git.astron.nl/lofar2.0/pypcc/-/blob/master/scripts/Startup.py
+    Thanks, Paulus!  You made it very easy for me to cobble together this
+    script.
+
+    For obvious reasons is our script much better though.  :)
+    First, it is bigger.  And bigger is always better.
+    Then it is better documented but that does not count in the HW world.
+    But it also raises exceptions with error messages that make an attempt to
+    help the user reading them and shuts down the respective Tango device(s) if
+    something goes south.
+    And that is where we try to do it really right:  there is no reason to be
+    excessively verbatim when things work like they are expected to work.  But
+    tell the user when something goes wrong, give an indication of what could
+    have gone wrong and where to look for the problem.
+
+    Again, Paulus' script contains already very good indications where problems
+    might lie and made my job very easy.
+
+    No parameters, parameters are for wimps.  :)
+    '''
+    # Define the LOFAR2.0 specific log format
+    configure_logging()
+
+    # Get a reference to the RECV device, do not
+    # force a restart of the already running Tango
+    # device.
+    recv = startup("LTS/RECV/1")
+
+    # Getting CLK, RCU & RCU ADCs into proper shape for use by real people.
+    #
+    # The start-up needs to happen in this sequence due to HW dependencies
+    # that can introduce issues which are then becoming very complicated to
+    # handle in SW.  Therefore to keep it as simple as possible, let's stick
+    # to the rule recommended by Paulus:
+    # 1 CLK
+    # 2 RCU
+    # 3 RCU ADCs
+    #
+    #
+    # First take the CLK board through the motions.
+    # 1.1 Switch off CLK
+    # 1.2 Wait for CLK_translator_busy_R == True, throw an exception in timeout
+    # 1.3 Switch on CLK
+    # 1.4 Wait for CLK_translator_busy_R == True, throw an exception in timeout
+    # 1.5 Check if CLK_PLL_locked_R == True
+    # 1.6 Done
+    #
+    #
+    # Steps 1.1 & 1.2
+    recv.CLK_off()
+    # 2021-04-30, Thomas
+    # This should be refactored into a function.
+    timeout = 10.0
+    while recv.CLK_translator_busy_R is True:
+        logging.debug("Waiting on \"CLK_translator_busy_R\" to become \"True\"...")
+        timeout = timeout - 1.0
+        if timeout < 1.0:
+            # Switching the RECV clock off should never take longer than
+            # 10 seconds.  Here we ran into a timeout.
+            # Clean up and raise an exception.
+            recv.off()
+            raise Exception("After calling \"CLK_off\" a timeout occured while waiting for \"CLK_translator_busy_R\" to become \"True\".  Please investigate the reason why the RECV translator never set \"CLK_translator_busy_R\" to \"True\".  Aborting start-up procedure.")
+        sleep(1.0)
+
+    # Steps 1.3 & 1.4
+    recv.CLK_on()
+    # Per Paulus this should never take longer than 2 seconds.
+    # 2021-04-30, Thomas
+    # This should be refactored into a function.
+    timeout = 2.0
+    while recv.CLK_translator_busy_R is True:
+        logging.debug("After calling \"CLK_on()\"  Waiting on \"CLK_translator_busy_R\" to become \"True\"...")
+        timeout = timeout - 1.0
+        if timeout < 1.0:
+            # Switching theRECV clock on should never take longer than
+            # a couple of seconds.  Here we ran into a timeout.
+            # Clean up and raise an exception.
+            recv.off()
+            raise Exception("After calling \"CLK_on\" a timeout occured while waiting for \"CLK_translator_busy_R\" to become \"True\".  Please investigate the reason why the RECV translator never set \"CLK_translator_busy_R\" to \"True\".  Aborting start-up procedure.")
+        sleep(1.0)
+
+    # 1.5 Check if CLK_PLL_locked_R == True
+    # 2021-04-30, Thomas
+    # This should be refactored into a function.
+    clk_locked = recv.CLK_PLL_locked_R
+    if clk_locked is True:
+       logging.info("CLK signal is locked.")
+    else:
+        # CLK signal is not locked
+        clk_i2c_status = recv.CLK_I2C_STATUS_R
+        exception_text = "CLK I2C is not working.  Please investigate!  Maybe power cycle subrack to restart CLK board and translator.  Aborting start-up procedure."
+        if i2c_status <= 0:
+            exception_text = "CLK signal is not locked.  Please investigate!  The subrack probably do not receive clock input or the CLK PCB is broken.  Aborting start-up procedure."
+        recv.off()
+        raise Exception(exception_text)
+    # Step 1.6
+    # Done.
+
+    # 2 RCUs
+    # If we reach this point in the start-up procedure, then the CLK board setup
+    # is done.  We can proceed with the RCUs.
+    #
+    # Now take the RCUs through the motions.
+    # 2.1 Set RCU mask to all available RCUs
+    # 2.2 Switch off all RCUs
+    # 2.3 Wait for RCU_translator_busy_R = True, throw an exception in timeout
+    # 2.4 Switch on RCUs
+    # 2.5 Wait for RCU_translator_busy_R = True, throw an exception in timeout
+    # 2.6 Done
+    #
+    #
+    # Step 2.1
+    # We have only 8 RCUs in LTS.
+    recv.RCU_mask_RW = [True, ] * 8
+    # Steps 2.2 & 2.3
+    recv.RCU_off()
+    # 2021-04-30, Thomas
+    # This should be refactored into a function.
+    timeout = 10.0
+    while recv.RCU_translator_busy_R is True:
+        logging.debug("Waiting on \"RCU_translator_busy_R\" to become \"True\"...")
+        timeout = timeout - 1.0
+        if timeout < 1.0:
+            # Switching the RCUs off should never take longer than
+            # 10 seconds.  Here we ran into a timeout.
+            # Clean up and raise an exception.
+            recv.off()
+            raise Exception("After calling \"RCU_off\" a timeout occured while waiting for \"RCU_translator_busy_R\" to become \"True\".  Please investigate the reason why the RECV translator never set \"RCU_translator_busy_R\" to \"True\".  Aborting start-up procedure.")
+        sleep(1.0)
+
+    # Steps 2.4 & 2.5
+    # We leave the RCU mask as it is because it got already set for the
+    # RCU_off() call.
+    recv.RCU_on()
+    # Per Paulus this should never take longer than 5 seconds.
+    # 2021-04-30, Thomas
+    # This should be refactored into a function.
+    timeout = 5.0
+    while recv.RCU_translator_busy_R is True:
+        logging.debug("After calling \"RCU_on()\"  Waiting on \"RCU_translator_busy_R\" to become \"True\"...")
+        timeout = timeout - 1.0
+        if timeout < 1.0:
+            # Switching the RCUs on should never take longer than
+            # a couple of seconds.  Here we ran into a timeout.
+            # Clean up and raise an exception.
+            recv.off()
+            raise Exception("After calling \"RCU_on\" a timeout occured while waiting for \"RCU_translator_busy_R\" to become \"True\".  Please investigate the reason why the RECV translator never set \"RCU_translator_busy_R\" to \"True\".  Aborting start-up procedure.")
+        sleep(1.0)
+    # Step 2.6
+    # Done.
+
+    # 3 ADCs
+    # If we get here, we only got to check if the ADCs are locked, too.
+    # 3.1 Check RCUs' I2C status
+    # 3.2 Check RCU_ADC_lock_R == [True, ] for RCUs that have a good I2C status
+    # 3.3 Done
+    #
+    #
+    # Steps 3.1 & 3.2
+    rcu_mask = recv.RCU_mask_RW
+    adc_locked = numpy.array(recv.RCU_ADC_lock_R)
+    for rcu, i2c_status in enumerate(recv.RCU_I2C_STATUS_R):
+        if i2c_status == 0:
+            rcu_mask[rcu] = True
+            logging.info("RCU #{} is available.".format(rcu))
+            for adc, adc_is_locked in enumerate(adc_locked[rcu]):
+                if adc_is_locked < 1:
+                    logging.warning("RCU#{}, ADC#{} is unlocked.  Please investigate!  Will continue with normal operation.".format(rcu, adc))
+        else:
+            # The RCU's I2C bus is not working.
+            rcu_mask[rcu] = False
+            logging.error("RCU #{}'s I2C is not working.  Please investigate!  Disabling RCU #{} to avoid damage.".format(rcu, rcu))
+    recv.RCU_mask_RW = rcu_mask
+    # Step 3.3
+    # Done
+
+    # Start-up APSCTL, i.e. Uniboard2s.
+    aps = startup("APSCTL/SDP/1")
+    logging.warning("Cannot start-up APSCTL because it requires manual actions.")
+
+    # Start up SDP, i.e. configure the firmware in the Unibards
+    sdp = startup("LTS/SDP/1")
+    logging.warning("Cannot start-up SDP because it requires manual actions.")
+
+    logging.info("LTS has been successfully started and configured.")
+
+def main(args=None, **kwargs):
+
+    return lts_cold_start()
diff --git a/tangostationcontrol/tangostationcontrol/toolkit/startup.py b/tangostationcontrol/tangostationcontrol/toolkit/startup.py
new file mode 100644
index 0000000000000000000000000000000000000000..66a8d2c496fc7e86d0d13086336e900fc1a1bfaf
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/toolkit/startup.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python3
+import tango
+import logging
+
+logger = logging.getLogger()
+
+def startup(device: str, force_restart: bool) -> tango.DeviceProxy:
+    '''
+    Start a LOFAR Tango device:
+    recv = startup(device = 'LTS/RECV/1', force_restart = False)
+    '''
+    proxy = tango.DeviceProxy(device)
+    state = proxy.state()
+
+    # go to OFF, but only if force_restart is True
+    if force_restart is True:
+        logger.warning(f"Forcing device {device} restart.")
+        proxy.off()
+        state = proxy.state()
+        if state is not tango._tango.DevState.OFF:
+            logger.error(f"Device {device} cannot perform off although restart has been enforced, state = {state}.  Please investigate.")
+            return proxy
+
+    if state is not tango._tango.DevState.OFF:
+        logger.error(f"Device {device} is not in OFF state, cannot start it.  state = {state}")
+        return proxy
+
+    # Initialise device
+    logger.info(f"Device {device} is in OFF, performing initialisation.")
+    proxy.initialise()
+    state = proxy.state()
+    if state is not tango._tango.DevState.STANDBY:
+        logger.error(f"Device {device} cannot perform initialise, state = {state}.  Please investigate.")
+        return proxy
+
+    # Set default values
+    logger.info(f"Device {device} is in STANDBY, setting default values.")
+    proxy.set_defaults()
+
+    # Turn on device
+    logger.info(f"Device {device} is in STANDBY, performing on.")
+    proxy.on()
+    state = proxy.state()
+    if state is not tango._tango.DevState.ON:
+        logger.error(f"Device {device} cannot perform on, state = {state}.  Please investigate.")
+    else:
+        logger.info(f"Device {device} has successfully reached ON state.")
+
+    return proxy
diff --git a/devices/toolkit/udp_simulator.py b/tangostationcontrol/tangostationcontrol/toolkit/udp_simulator.py
similarity index 100%
rename from devices/toolkit/udp_simulator.py
rename to tangostationcontrol/tangostationcontrol/toolkit/udp_simulator.py
diff --git a/devices/version.py b/tangostationcontrol/tangostationcontrol/version.py
similarity index 100%
rename from devices/version.py
rename to tangostationcontrol/tangostationcontrol/version.py
diff --git a/devices/test-requirements.txt b/tangostationcontrol/test-requirements.txt
similarity index 100%
rename from devices/test-requirements.txt
rename to tangostationcontrol/test-requirements.txt
diff --git a/devices/tox.ini b/tangostationcontrol/tox.ini
similarity index 95%
rename from devices/tox.ini
rename to tangostationcontrol/tox.ini
index 59d2347f3ff42ccb084033aea67d478fd63513cb..69782d5e33c1ab14ce9abcd50253e0db9eabdf9a 100644
--- a/devices/tox.ini
+++ b/tangostationcontrol/tox.ini
@@ -22,7 +22,7 @@ commands = stestr run {posargs}
 ; Warning running integration tests will make changes to your docker system!
 ; These tests should only be run by the integration-test docker container.
 passenv = TANGO_HOST
-setenv = TESTS_DIR=./integration_test
+setenv = TESTS_DIR=./tangostationcontrol/integration_test
 commands =
     stestr run --serial {posargs}