Repository: linien-org/linien (language: python)

Issue:
Fix PSD algorithm selection
Fixes #414.


Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d8c3b52f..30d88e4f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
 repos:
   - repo: https://github.com/psf/black
-    rev: 24.4.2
+    rev: 24.8.0
     hooks:
       - id: black
         exclude: ^(gateware/logic/|gateware/lowlevel/|gateware/linien_module.py|linien-server/linien_server/csrmap.py)
@@ -12,7 +12,7 @@ repos:
         name: isort (python)
 
   - repo: https://github.com/pycqa/flake8
-    rev: 7.0.0
+    rev: 7.1.1
     hooks:
       - id: flake8
         exclude: linien-server/linien_server/csrmap.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76c7496b..7d80aa98 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [2.1.0]
+
+### Added
+* Show differences when local and remote parameters do not match by @bleykauf in https://github.com/linien-org/linien/pull/400
+* Show voltage on the x-axis when sweeping by @bleykauf in https://github.com/linien-org/linien/pull/404
+
+### Changed
+* Switched to Tableau color scheme and make colors consistent, i.e. signals have the same color while sweeping and when locked. By @bleykauf in https://github.com/linien-org/linien/pull/419.
+* Increase upper version constraint for `importlib-metadata` by @doronbehar in https://github.com/linien-org/linien/pull/416
+
+### Fixed
+
+* Fixed example code in the readme by @bleykauf in https://github.com/linien-org/linien/pull/420, thanks to @Andrew-wi for reporting this issue
+* Fixed a bug preventing the selection of a PSD algorithm via the GUI by @bleykauf in https://github.com/linien-org/linien/pull/421, thanks to @martinssonh for reporting this issue
+
 ## [2.0.4] - 2024-05-30
 
 ### Fixed
@@ -18,7 +33,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 * Handle corrupted json files by @bleykauf in https://github.com/linien-org/linien/pull/399
 
-
 ## [2.0.2] - 2024-05-14
 
 ### Fixed
@@ -230,6 +244,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 * **Bug fixes and performance improvements**
 
+[2.1.0]: https://github.com/linien-org/linien/compare/v2.0.4...v2.1.0
 [2.0.4]: https://github.com/linien-org/linien/compare/v2.0.3...v2.0.4
 [2.0.3]: https://github.com/linien-org/linien/compare/v2.0.2...v2.0.3
 [2.0.2]: https://github.com/linien-org/linien/compare/v2.0.1...v2.0.2
diff --git a/README.md b/README.md
index b7fc00ba..33dd4ba3 100644
--- a/README.md
+++ b/README.md
@@ -89,10 +89,10 @@ from linien_client.deploy import install_remote_server
 
 device = Device(
     host="rp-xxxxxx.local",
-    user="root",
+    username="root",
     password="root"    
 )
-instalL_remote_server(device)
+install_remote_server(device)
 ```
 
 Finally, you can install the server manually, by connecting to the RedPitaya via SSH and
@@ -240,7 +240,7 @@ from linien_common.common import  MHz, Vpp, ANALOG_OUT_V
 
 dev = Device(
     host="rp-xxxxxx.local",
-    user="root",
+    username="root",
     password="root"    
 )
 c = LinienClient(dev)
@@ -259,7 +259,7 @@ c.parameters.modulation_amplitude.value = 1 * Vpp
 c.connection.root.write_registers()
 
 # additionally set ANALOG_OUT_1 to 1.2 volts DC (you can use this to control other devices of your experiment)
-c.parameters.analog_out_1.value = 1.2 * ANALOG_OUT_V
+c.parameters.analog_out_1.value = 1.2 / ANALOG_OUT_V
 
 # GPIO outputs can also be set
 # each bit corresponds to a pin
@@ -335,7 +335,7 @@ from time import sleep
 
 c = LinienClient(
     host="rp-xxxxxx.local",
-    user="root",
+    username="root",
     password="root"
 )
 c.connect(autostart_server=True, use_parameter_cache=True)
@@ -496,7 +496,9 @@ Linien ‒ User-friendly locking of lasers using RedPitaya (STEMlab 125-14) that
 Copyright © 2014-2015 Robert Jördens\
 Copyright © 2018-2022 Benjamin Wiegand\
 Copyright © 2021-2024 Bastian Leykauf\
-Copyright © 2022 Christian Freier
+Copyright © 2022 Christian Freier\
+Copyright © 2023-2024 Doron Behar\
+
 
 Linien is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 
diff --git a/gateware/bit2bin.py b/gateware/bit2bin.py
index 5c81cdc7..820ecf51 100644
--- a/gateware/bit2bin.py
+++ b/gateware/bit2bin.py
@@ -1,7 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/fpga_image_helper.py b/gateware/fpga_image_helper.py
index e08aefc0..c3e20e70 100644
--- a/gateware/fpga_image_helper.py
+++ b/gateware/fpga_image_helper.py
@@ -1,8 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/hw_platform.py b/gateware/hw_platform.py
index ca8f2579..0fcd12da 100644
--- a/gateware/hw_platform.py
+++ b/gateware/hw_platform.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/linien_module.py b/gateware/linien_module.py
index 16ca186c..5ea11bf1 100644
--- a/gateware/linien_module.py
+++ b/gateware/linien_module.py
@@ -1,10 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-# Copyright 2022 Christian Freier <christian.freier@nomadatomics.com>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/autolock.py b/gateware/logic/autolock.py
index cc537316..907fb203 100644
--- a/gateware/logic/autolock.py
+++ b/gateware/logic/autolock.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/autolock_utils.py b/gateware/logic/autolock_utils.py
index a52e58aa..a9a8d055 100644
--- a/gateware/logic/autolock_utils.py
+++ b/gateware/logic/autolock_utils.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/chains.py b/gateware/logic/chains.py
index a8908498..be60af23 100644
--- a/gateware/logic/chains.py
+++ b/gateware/logic/chains.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/cordic.py b/gateware/logic/cordic.py
index 659b59aa..c85ca9f8 100644
--- a/gateware/logic/cordic.py
+++ b/gateware/logic/cordic.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/decimation.py b/gateware/logic/decimation.py
index c2ecc888..296f6488 100644
--- a/gateware/logic/decimation.py
+++ b/gateware/logic/decimation.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/delta_sigma.py b/gateware/logic/delta_sigma.py
index 23aec60d..ca3ef00a 100644
--- a/gateware/logic/delta_sigma.py
+++ b/gateware/logic/delta_sigma.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/filter.py b/gateware/logic/filter.py
index 62a0dcdb..90b06c4c 100644
--- a/gateware/logic/filter.py
+++ b/gateware/logic/filter.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/iir.py b/gateware/logic/iir.py
index 1aa5de7a..02679f0d 100644
--- a/gateware/logic/iir.py
+++ b/gateware/logic/iir.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/limit.py b/gateware/logic/limit.py
index e3c0315d..eac9bd8b 100644
--- a/gateware/logic/limit.py
+++ b/gateware/logic/limit.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/modulate.py b/gateware/logic/modulate.py
index c7503060..e0d59f88 100644
--- a/gateware/logic/modulate.py
+++ b/gateware/logic/modulate.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/pid.py b/gateware/logic/pid.py
index 4320f94e..52934a84 100644
--- a/gateware/logic/pid.py
+++ b/gateware/logic/pid.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/logic/sweep.py b/gateware/logic/sweep.py
index fd0b75d1..ad7d4309 100644
--- a/gateware/logic/sweep.py
+++ b/gateware/logic/sweep.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/lowlevel/analog.py b/gateware/lowlevel/analog.py
index b4e36171..cc607563 100644
--- a/gateware/lowlevel/analog.py
+++ b/gateware/lowlevel/analog.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/lowlevel/crg.py b/gateware/lowlevel/crg.py
index 905b7520..ae9bafb9 100644
--- a/gateware/lowlevel/crg.py
+++ b/gateware/lowlevel/crg.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/lowlevel/dna.py b/gateware/lowlevel/dna.py
index a74aa423..aff4c68f 100644
--- a/gateware/lowlevel/dna.py
+++ b/gateware/lowlevel/dna.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/lowlevel/gpio.py b/gateware/lowlevel/gpio.py
index 3d38859d..de8ca793 100644
--- a/gateware/lowlevel/gpio.py
+++ b/gateware/lowlevel/gpio.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/lowlevel/pitaya_ps.py b/gateware/lowlevel/pitaya_ps.py
index a6508f5b..984fe242 100644
--- a/gateware/lowlevel/pitaya_ps.py
+++ b/gateware/lowlevel/pitaya_ps.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/lowlevel/scopegen.py b/gateware/lowlevel/scopegen.py
index d4a0c44a..b62fda6f 100644
--- a/gateware/lowlevel/scopegen.py
+++ b/gateware/lowlevel/scopegen.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/gateware/lowlevel/xadc.py b/gateware/lowlevel/xadc.py
index 352aa86b..de06d319 100644
--- a/gateware/lowlevel/xadc.py
+++ b/gateware/lowlevel/xadc.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-client/linien_client/__init__.py b/linien-client/linien_client/__init__.py
index 3721fe42..022cad8b 100644
--- a/linien-client/linien_client/__init__.py
+++ b/linien-client/linien_client/__init__.py
@@ -1,3 +1,20 @@
+# This file is part of Linien and based on redpid.
+#
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
+# Linien is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Linien is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Linien.  If not, see <http://www.gnu.org/licenses/>.
+
 import importlib.metadata
 import logging
 from logging.handlers import RotatingFileHandler
diff --git a/linien-client/linien_client/connection.py b/linien-client/linien_client/connection.py
index 3fa07b17..c6de5780 100644
--- a/linien-client/linien_client/connection.py
+++ b/linien-client/linien_client/connection.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-client/linien_client/deploy.py b/linien-client/linien_client/deploy.py
index d028b52b..df6683fe 100644
--- a/linien-client/linien_client/deploy.py
+++ b/linien-client/linien_client/deploy.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-client/linien_client/device.py b/linien-client/linien_client/device.py
index 559933a1..ae9ee374 100644
--- a/linien-client/linien_client/device.py
+++ b/linien-client/linien_client/device.py
@@ -1,7 +1,7 @@
-# Copyright 2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-client/linien_client/exceptions.py b/linien-client/linien_client/exceptions.py
index 7ecb3c00..dd96628e 100644
--- a/linien-client/linien_client/exceptions.py
+++ b/linien-client/linien_client/exceptions.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-client/linien_client/remote_parameters.py b/linien-client/linien_client/remote_parameters.py
index e8756383..a81f9bf6 100644
--- a/linien-client/linien_client/remote_parameters.py
+++ b/linien-client/linien_client/remote_parameters.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-client/pyproject.toml b/linien-client/pyproject.toml
index 5abfa8a8..35a89a36 100644
--- a/linien-client/pyproject.toml
+++ b/linien-client/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "linien-client"
-version = "2.0.4"
+version = "2.1.0"
 authors = [
     { name = "Benjamin Wiegand", email = "benjamin.wiegand@physik.hu-berlin.de" },
     { name = "Bastian Leykauf", email = "leykauf@physik.hu-berlin.de" },
@@ -25,7 +25,7 @@ requires-python = ">=3.8"
 dependencies = [
     "fabric>=3.2.2,<4.0",
     "typing_extensions>=4.5.0,<5.0",
-    "linien-common==2.0.4",
+    "linien-common==2.1.0",
 ]
 
 [project.readme]
diff --git a/linien-common/linien_common/common.py b/linien-common/linien_common/common.py
index c9157f82..854d776f 100644
--- a/linien-common/linien_common/common.py
+++ b/linien-common/linien_common/common.py
@@ -1,7 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -20,7 +20,7 @@
 
 from enum import IntEnum
 from time import time
-from typing import Dict, List, Tuple, Union
+from typing import Dict, Iterable, List, Tuple, Union
 
 import numpy as np
 from scipy.signal import correlate, resample
@@ -278,8 +278,12 @@ def convert_channel_mixing_value(value: int) -> Tuple[int, int]:
 
 
 def combine_error_signal(
-    error_signals, dual_channel, channel_mixing, combined_offset, chain_factor_width=8
-):
+    error_signals: tuple[Iterable[int], Iterable[int]],
+    dual_channel: bool,
+    channel_mixing: int,
+    combined_offset: int,
+    chain_factor_width: int = 8,
+) -> np.ndarray:
     if not dual_channel:
         signal = error_signals[0]
     else:
diff --git a/linien-common/linien_common/communication.py b/linien-common/linien_common/communication.py
index e35d4d6e..81b43dd3 100644
--- a/linien-common/linien_common/communication.py
+++ b/linien-common/linien_common/communication.py
@@ -1,7 +1,7 @@
-# Copyright 2023-2024 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-common/linien_common/config.py b/linien-common/linien_common/config.py
index 0728aff5..6ba92e67 100644
--- a/linien-common/linien_common/config.py
+++ b/linien-common/linien_common/config.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-common/linien_common/influxdb.py b/linien-common/linien_common/influxdb.py
index 24557af6..e17b5109 100644
--- a/linien-common/linien_common/influxdb.py
+++ b/linien-common/linien_common/influxdb.py
@@ -1,7 +1,7 @@
-# Copyright 2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-common/pyproject.toml b/linien-common/pyproject.toml
index 6251f6f6..5ab51d34 100644
--- a/linien-common/pyproject.toml
+++ b/linien-common/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "linien-common"
-version = "2.0.4"
+version = "2.1.0"
 authors = [
     { name = "Benjamin Wiegand", email = "benjamin.wiegand@physik.hu-berlin.de" },
     { name = "Bastian Leykauf", email = "leykauf@physik.hu-berlin.de" },
@@ -24,7 +24,7 @@ classifiers = [
 requires-python = ">=3.8"
 dependencies = [
     "appdirs>=1.4.4,<2.0",
-    "importlib_metadata>=2.1.3,<5.0",
+    "importlib_metadata>=2.1.3,<9.0",
     "numpy>=1.21.5,<2.0",
     "rpyc>=6.0,<7.0",
     "scipy>=1.8.0,<2.0",
diff --git a/linien-gui/linien_gui/__init__.py b/linien-gui/linien_gui/__init__.py
index 7f5d7649..da24c9a6 100644
--- a/linien-gui/linien_gui/__init__.py
+++ b/linien-gui/linien_gui/__init__.py
@@ -1,3 +1,20 @@
+# This file is part of Linien and based on redpid.
+#
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
+# Linien is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Linien is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Linien.  If not, see <http://www.gnu.org/licenses/>.
+
 import importlib.metadata
 import logging
 from logging.handlers import RotatingFileHandler
diff --git a/linien-gui/linien_gui/app.py b/linien-gui/linien_gui/app.py
index abda7037..d7561feb 100644
--- a/linien-gui/linien_gui/app.py
+++ b/linien-gui/linien_gui/app.py
@@ -1,7 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/config.py b/linien-gui/linien_gui/config.py
index d41dd7f8..69870d36 100644
--- a/linien-gui/linien_gui/config.py
+++ b/linien-gui/linien_gui/config.py
@@ -1,9 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -34,23 +32,27 @@
 DEFAULT_PLOT_RATE_LIMIT = 0.1
 
 DEFAULT_COLORS = [
-    (200, 0, 0, 200),
-    (0, 200, 0, 200),
-    (0, 0, 200, 200),
-    (200, 200, 0, 200),
-    (200, 0, 200, 200),
+    (214, 39, 40, 200),  # 0: red
+    (44, 160, 44, 200),  # 1: green
+    (31, 119, 180, 200),  # 2: blue
+    (188, 189, 34, 200),  # 3: yellow
+    (227, 119, 194, 200),  # 4: pink
+    (255, 127, 14, 200),  # 5: orange
+    (148, 103, 189, 200),  # 6: purple
+    (23, 190, 207, 200),  # 7: turquoise
 ]
 N_COLORS = len(DEFAULT_COLORS)
 
 
 class Color(Enum):
-    SPECTRUM1 = 0
-    SPECTRUM2 = 1
-    SPECTRUM_COMBINED = 2
-    CONTROL_SIGNAL = 0
-    CONTROL_SIGNAL_HISTORY = 1
-    SLOW_HISTORY = 3
-    MONITOR_SIGNAL_HISTORY = 4
+    ERROR_COMBINED = 0
+    SLOW_HISTORY = 1
+    MONITOR = 2
+    CONTROL_SIGNAL = 3
+    ERROR1 = 4
+    CONTROL_SIGNAL_HISTORY = 5
+    ERROR2 = 6
+    MONITOR_SIGNAL_HISTORY = 7
 
 
 class Setting:
@@ -105,6 +107,9 @@ def __init__(self):
         self.plot_color_2 = Setting(start=DEFAULT_COLORS[2])
         self.plot_color_3 = Setting(start=DEFAULT_COLORS[3])
         self.plot_color_4 = Setting(start=DEFAULT_COLORS[4])
+        self.plot_color_5 = Setting(start=DEFAULT_COLORS[5])
+        self.plot_color_6 = Setting(start=DEFAULT_COLORS[6])
+        self.plot_color_7 = Setting(start=DEFAULT_COLORS[7])
 
         # save changed settings to disk
         for _, setting in self:
diff --git a/linien-gui/linien_gui/dialogs.py b/linien-gui/linien_gui/dialogs.py
index 30ee8c2b..1283e611 100644
--- a/linien-gui/linien_gui/dialogs.py
+++ b/linien-gui/linien_gui/dialogs.py
@@ -1,7 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -97,10 +97,10 @@ def __init__(self, parent: QWidget, host: str):
         self.setStandardButtons(QMessageBox.NoButton)
         self.show()
 
-    def closeEvent(self, *args):
+    def closeEvent(self, *args) -> None:
         self.aborted.emit()
 
-    def keyPressEvent(self, event):
+    def keyPressEvent(self, event) -> None:
         key = event.key()
         if key == QtCore.Qt.Key_Escape:
             self.close()
@@ -110,7 +110,7 @@ def error_dialog(parent: QWidget, error):
     return QMessageBox.question(parent, "Error", error, QMessageBox.Ok, QMessageBox.Ok)
 
 
-def question_dialog(parent, question, title):
+def question_dialog(parent, question: str, title: str) -> bool:
     box = QMessageBox(parent)
     box.setText(question)
     box.setWindowTitle(title)
@@ -121,12 +121,11 @@ def question_dialog(parent, question, title):
     return reply == QMessageBox.Yes
 
 
-def ask_for_parameter_restore_dialog(parent, question, title):
+def ask_for_parameter_restore_dialog(parent, question: str, title: str) -> bool:
     box = QMessageBox(parent)
     box.setText(question)
     box.setWindowTitle(title)
-    # do_nothing_button
-    _ = box.addButton("Keep remote parameters", QMessageBox.NoRole)
+    _ = box.addButton("Keep remote parameters", QMessageBox.NoRole)  # do nothing
     upload_button = box.addButton("Upload local parameters", QMessageBox.YesRole)
 
     box.exec_()
diff --git a/linien-gui/linien_gui/threads.py b/linien-gui/linien_gui/threads.py
index bab3963d..dd43d77b 100644
--- a/linien-gui/linien_gui/threads.py
+++ b/linien-gui/linien_gui/threads.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/device_manager.py b/linien-gui/linien_gui/ui/device_manager.py
index 6adc5124..e5848259 100644
--- a/linien-gui/linien_gui/ui/device_manager.py
+++ b/linien-gui/linien_gui/ui/device_manager.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -164,9 +163,13 @@ def ask_for_parameter_restore(
                 "only saved if Linien server was shut down properly, not when "
                 "unplugging the power plug. In this case, you should update your local "
                 "parameters."
+                "\n"
+                "The following parameters changed (local ↔ remote):"
             )
+            for param_name, (local_value, remote_value) in parameter_difference.items():
+                question += f"\n{param_name}:\n          {local_value} ↔ {remote_value}"
             should_restore = ask_for_parameter_restore_dialog(
-                self, question, "Restore parameters?"
+                self, question, title="Restore parameters?"
             )
             if should_restore:
                 self.connection_thread.restore_parameters(parameter_difference)
diff --git a/linien-gui/linien_gui/ui/general_panel.py b/linien-gui/linien_gui/ui/general_panel.py
index 53ff7ed1..cad2d918 100644
--- a/linien-gui/linien_gui/ui/general_panel.py
+++ b/linien-gui/linien_gui/ui/general_panel.py
@@ -1,9 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-# Copyright 2022 Christian Freier <christian.freier@nomadatomics.com>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/lock_status_panel.py b/linien-gui/linien_gui/ui/lock_status_panel.py
index bb11b259..07e3cb4c 100644
--- a/linien-gui/linien_gui/ui/lock_status_panel.py
+++ b/linien-gui/linien_gui/ui/lock_status_panel.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/locking_panel.py b/linien-gui/linien_gui/ui/locking_panel.py
index 86d8c17a..9fc23623 100644
--- a/linien-gui/linien_gui/ui/locking_panel.py
+++ b/linien-gui/linien_gui/ui/locking_panel.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/logging_panel.py b/linien-gui/linien_gui/ui/logging_panel.py
index 6256abcd..2fa1f732 100644
--- a/linien-gui/linien_gui/ui/logging_panel.py
+++ b/linien-gui/linien_gui/ui/logging_panel.py
@@ -1,7 +1,7 @@
-# Copyright 2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/main_window.py b/linien-gui/linien_gui/ui/main_window.py
index 5ca799a2..dad465e2 100644
--- a/linien-gui/linien_gui/ui/main_window.py
+++ b/linien-gui/linien_gui/ui/main_window.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -50,28 +49,29 @@ class MainWindow(QtWidgets.QMainWindow):
     power_channel_1: QtWidgets.QLabel
     power_channel_2: QtWidgets.QLabel
     legend_unlocked: QtWidgets.QHBoxLayout
-    legend_spectrum_1: QtWidgets.QLabel
-    legend_spectrum_2: QtWidgets.QLabel
-    legend_spectrum_combined: QtWidgets.QLabel
+    error1LegendLabel: QtWidgets.QLabel
+    error2LegendLabel: QtWidgets.QLabel
+    monitorLegendLabel: QtWidgets.QLabel
+    combinedErrorLegendLabel: QtWidgets.QLabel
     sweepControlWidget: SweepControlWidget
     sweepAmplitudeSpinBox: CustomDoubleSpinBox
     sweepCenterSpinBox: CustomDoubleSpinBox
     sweepSlider: SweepSlider
     sweepStartStopPushButton: QtWidgets.QPushButton
-    top_lock_panel: QtWidgets.QWidget
-    control_std: QtWidgets.QLabel
-    error_std: QtWidgets.QLabel
-    legend_control_signal: QtWidgets.QLabel
-    legend_control_signal_history: QtWidgets.QLabel
-    legend_error_signal: QtWidgets.QLabel
-    legend_monitor_signal_history: QtWidgets.QLabel
-    legend_slow_signal_history: QtWidgets.QLabel
+    topLockPanelWidget: QtWidgets.QWidget
+    controlStdLabel: QtWidgets.QLabel
+    errorStdLabel: QtWidgets.QLabel
+    controlSignalLegendLabel: QtWidgets.QLabel
+    controlSignalHistoryLegendLabel: QtWidgets.QLabel
+    errorSignalLegendLabel: QtWidgets.QLabel
+    monitorSignalHistoryLegendLabel: QtWidgets.QLabel
+    slowSignalHistoryLegendLabel: QtWidgets.QLabel
     rightPanel: RightPanel
     exportParametersButton: QtWidgets.QPushButton
     importParametersButton: QtWidgets.QPushButton
     newVersionAvailableLabel: QtWidgets.QLabel
-    pid_parameter_optimization_button: QtWidgets.QPushButton
-    settings_toolbox: QtWidgets.QToolBox
+    PIDParameterOptimizationButton: QtWidgets.QPushButton
+    settingsToolbox: QtWidgets.QToolBox
     generalPanel: QtWidgets.QWidget
     modSpectroscopyPanel: QtWidgets.QWidget
     optimizationPanel: QtWidgets.QWidget
@@ -134,19 +134,19 @@ def on_connection_established(self):
         self.parameters.to_plot.add_callback(self.update_std)
 
         self.parameters.pid_on_slow_enabled.add_callback(
-            lambda v: self.legend_slow_signal_history.setVisible(v)
+            lambda v: self.slowSignalHistoryLegendLabel.setVisible(v)
         )
         self.parameters.dual_channel.add_callback(
-            lambda v: self.legend_monitor_signal_history.setVisible(not v)
+            lambda v: self.monitorSignalHistoryLegendLabel.setVisible(not v)
         )
 
-        self.settings_toolbox.setCurrentIndex(0)
+        self.settingsToolbox.setCurrentIndex(0)
 
         self.parameters.lock.add_callback(lambda *args: self.reset_std_history())
 
         for color_idx in range(N_COLORS):
             getattr(self.app.settings, f"plot_color_{color_idx}").add_callback(
-                self.update_legend_color
+                self.on_plot_color_changed
             )
 
         self.parameters.dual_channel.add_callback(self.update_legend_text)
@@ -159,12 +159,12 @@ def change_sweep_control_visibility(self, *args):
         self.sweepControlWidget.setVisible(
             not al_running and not locked and not optimization
         )
-        self.top_lock_panel.setVisible(locked)
+        self.topLockPanelWidget.setVisible(locked)
         self.statusbar_unlocked.setVisible(
             not al_running and not locked and not optimization
         )
 
-    def update_legend_color(self, *args):
+    def on_plot_color_changed(self, *args):
         def set_color(el, color: Color):
             return el.setStyleSheet(
                 "color: "
@@ -173,23 +173,23 @@ def set_color(el, color: Color):
                 )
             )
 
-        set_color(self.legend_spectrum_1, Color.SPECTRUM1)
-        set_color(self.legend_spectrum_2, Color.SPECTRUM2)
-        set_color(self.legend_spectrum_combined, Color.SPECTRUM_COMBINED)
-        set_color(self.legend_error_signal, Color.SPECTRUM_COMBINED)
-        set_color(self.legend_control_signal, Color.CONTROL_SIGNAL)
-        set_color(self.legend_control_signal_history, Color.CONTROL_SIGNAL_HISTORY)
-        set_color(self.legend_slow_signal_history, Color.SLOW_HISTORY)
-        set_color(self.legend_monitor_signal_history, Color.MONITOR_SIGNAL_HISTORY)
-
-    def update_legend_text(self, dual_channel):
-        self.legend_spectrum_1.setText(
-            "error signal" if not dual_channel else "error signal 1"
-        )
-        self.legend_spectrum_2.setText(
-            "monitor" if not dual_channel else "error signal 2"
+        set_color(self.error1LegendLabel, Color.ERROR1)
+        set_color(self.error2LegendLabel, Color.ERROR2)
+        set_color(self.monitorLegendLabel, Color.MONITOR)
+        set_color(self.combinedErrorLegendLabel, Color.ERROR_COMBINED)
+        set_color(self.errorSignalLegendLabel, Color.ERROR_COMBINED)
+        set_color(self.controlSignalLegendLabel, Color.CONTROL_SIGNAL)
+        set_color(self.controlSignalHistoryLegendLabel, Color.CONTROL_SIGNAL_HISTORY)
+        set_color(self.slowSignalHistoryLegendLabel, Color.SLOW_HISTORY)
+        set_color(self.monitorSignalHistoryLegendLabel, Color.MONITOR_SIGNAL_HISTORY)
+
+    def update_legend_text(self, dual_channel: bool) -> None:
+        self.error1LegendLabel.setVisible(dual_channel)
+        self.error2LegendLabel.setVisible(dual_channel)
+        self.monitorLegendLabel.setVisible(not dual_channel)
+        self.combinedErrorLegendLabel.setText(
+            "error" if not dual_channel else "combined error"
         )
-        self.legend_spectrum_combined.setVisible(dual_channel)
 
     def show(self, host: str, name: str) -> None:  # type: ignore[override]
         self.setWindowTitle(
@@ -270,8 +270,10 @@ def update_std(self, to_plot, max_std_history_length=10):
                 ]
 
                 if error_signal is not None and control_signal is not None:
-                    self.error_std.setText(f"{np.mean(self.error_std_history):.2f}")
-                    self.control_std.setText(f"{np.mean(self.control_std_history):.2f}")
+                    self.errorStdLabel.setText(f"{np.mean(self.error_std_history):.2f}")
+                    self.controlStdLabel.setText(
+                        f"{np.mean(self.control_std_history):.2f}"
+                    )
 
     def reset_std_history(self):
         self.error_std_history = []
diff --git a/linien-gui/linien_gui/ui/main_window.ui b/linien-gui/linien_gui/ui/main_window.ui
index 24cb6eda..3d8b8bf8 100644
--- a/linien-gui/linien_gui/ui/main_window.ui
+++ b/linien-gui/linien_gui/ui/main_window.ui
@@ -236,7 +236,7 @@ p, li { white-space: pre-wrap; }
           </widget>
          </item>
          <item>
-          <widget class="QWidget" name="top_lock_panel" native="true">
+          <widget class="QWidget" name="topLockPanelWidget" native="true">
            <layout class="QHBoxLayout" name="horizontalLayout_4">
             <item>
              <widget class="QLabel" name="label_4">
@@ -252,7 +252,7 @@ p, li { white-space: pre-wrap; }
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="error_std">
+             <widget class="QLabel" name="errorStdLabel">
               <property name="text">
                <string>TextLabel</string>
               </property>
@@ -288,7 +288,7 @@ p, li { white-space: pre-wrap; }
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="control_std">
+             <widget class="QLabel" name="controlStdLabel">
               <property name="text">
                <string>TextLabel</string>
               </property>
@@ -311,7 +311,7 @@ p, li { white-space: pre-wrap; }
              </spacer>
             </item>
             <item>
-             <widget class="QLabel" name="legend_error_signal">
+             <widget class="QLabel" name="errorSignalLegendLabel">
               <property name="text">
                <string>error</string>
               </property>
@@ -321,7 +321,7 @@ p, li { white-space: pre-wrap; }
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="legend_control_signal">
+             <widget class="QLabel" name="controlSignalLegendLabel">
               <property name="text">
                <string>control</string>
               </property>
@@ -331,7 +331,7 @@ p, li { white-space: pre-wrap; }
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="legend_control_signal_history">
+             <widget class="QLabel" name="controlSignalHistoryLegendLabel">
               <property name="text">
                <string>control history</string>
               </property>
@@ -341,14 +341,14 @@ p, li { white-space: pre-wrap; }
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="legend_slow_signal_history">
+             <widget class="QLabel" name="slowSignalHistoryLegendLabel">
               <property name="text">
                <string>slow out history</string>
               </property>
              </widget>
             </item>
             <item>
-             <widget class="QLabel" name="legend_monitor_signal_history">
+             <widget class="QLabel" name="monitorSignalHistoryLegendLabel">
               <property name="text">
                <string>monitor signal</string>
               </property>
@@ -469,11 +469,11 @@ p, li { white-space: pre-wrap; }
              </spacer>
             </item>
             <item>
-             <layout class="QHBoxLayout" name="legend_unlocked">
+             <layout class="QHBoxLayout" name="unlockedLegendLayout">
               <item>
-               <widget class="QLabel" name="legend_spectrum_1">
+               <widget class="QLabel" name="error1LegendLabel">
                 <property name="text">
-                 <string>sig1</string>
+                 <string>error 1</string>
                 </property>
                 <property name="wordWrap">
                  <bool>false</bool>
@@ -481,9 +481,9 @@ p, li { white-space: pre-wrap; }
                </widget>
               </item>
               <item>
-               <widget class="QLabel" name="legend_spectrum_2">
+               <widget class="QLabel" name="error2LegendLabel">
                 <property name="text">
-                 <string>sig2</string>
+                 <string>error 2</string>
                 </property>
                 <property name="wordWrap">
                  <bool>false</bool>
@@ -491,7 +491,14 @@ p, li { white-space: pre-wrap; }
                </widget>
               </item>
               <item>
-               <widget class="QLabel" name="legend_spectrum_combined">
+               <widget class="QLabel" name="monitorLegendLabel">
+                <property name="text">
+                 <string>monitor</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QLabel" name="combinedErrorLegendLabel">
                 <property name="text">
                  <string>combined</string>
                 </property>
@@ -592,7 +599,7 @@ p, li { white-space: pre-wrap; }
         </layout>
        </item>
        <item>
-        <widget class="QPushButton" name="pid_parameter_optimization_button">
+        <widget class="QPushButton" name="PIDParameterOptimizationButton">
          <property name="text">
           <string>Noise analysis</string>
          </property>
@@ -1027,7 +1034,7 @@ p, li { white-space: pre-wrap; }
         </widget>
        </item>
        <item>
-        <widget class="QToolBox" name="settings_toolbox">
+        <widget class="QToolBox" name="settingsToolbox">
          <property name="enabled">
           <bool>true</bool>
          </property>
@@ -1054,8 +1061,8 @@ p, li { white-space: pre-wrap; }
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>332</width>
-            <height>400</height>
+            <width>338</width>
+            <height>488</height>
            </rect>
           </property>
           <attribute name="label">
@@ -1072,8 +1079,8 @@ p, li { white-space: pre-wrap; }
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>332</width>
-            <height>400</height>
+            <width>338</width>
+            <height>488</height>
            </rect>
           </property>
           <attribute name="label">
@@ -1090,8 +1097,8 @@ p, li { white-space: pre-wrap; }
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>332</width>
-            <height>400</height>
+            <width>338</width>
+            <height>488</height>
            </rect>
           </property>
           <attribute name="label">
@@ -1108,8 +1115,8 @@ p, li { white-space: pre-wrap; }
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>332</width>
-            <height>400</height>
+            <width>338</width>
+            <height>488</height>
            </rect>
           </property>
           <attribute name="label">
@@ -1131,8 +1138,8 @@ p, li { white-space: pre-wrap; }
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>332</width>
-            <height>400</height>
+            <width>338</width>
+            <height>488</height>
            </rect>
           </property>
           <property name="sizePolicy">
@@ -1155,8 +1162,8 @@ p, li { white-space: pre-wrap; }
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>332</width>
-            <height>400</height>
+            <width>338</width>
+            <height>488</height>
            </rect>
           </property>
           <property name="sizePolicy">
diff --git a/linien-gui/linien_gui/ui/modulation_sweep_panel.py b/linien-gui/linien_gui/ui/modulation_sweep_panel.py
index 4a48b820..4013cc6f 100644
--- a/linien-gui/linien_gui/ui/modulation_sweep_panel.py
+++ b/linien-gui/linien_gui/ui/modulation_sweep_panel.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/new_device_dialog.py b/linien-gui/linien_gui/ui/new_device_dialog.py
index 6a8cc977..6c5e57e3 100644
--- a/linien-gui/linien_gui/ui/new_device_dialog.py
+++ b/linien-gui/linien_gui/ui/new_device_dialog.py
@@ -1,7 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/optimization_panel.py b/linien-gui/linien_gui/ui/optimization_panel.py
index 740927ed..6bf9e4cb 100644
--- a/linien-gui/linien_gui/ui/optimization_panel.py
+++ b/linien-gui/linien_gui/ui/optimization_panel.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/plot_widget.py b/linien-gui/linien_gui/ui/plot_widget.py
index ee7d7b16..dbe6c659 100644
--- a/linien-gui/linien_gui/ui/plot_widget.py
+++ b/linien-gui/linien_gui/ui/plot_widget.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -73,32 +72,32 @@ def on_connection_established(self):
         QtCore.QTimer.singleShot(100, self.listen_to_parameter_changes)
 
     def listen_to_parameter_changes(self):
-        self.parent.parameters.sweep_speed.add_callback(self.force_repaint_tick_strings)
-        self.parent.parameters.lock.add_callback(self.force_repaint_tick_strings)
-        self.force_repaint_tick_strings()
-
-    def force_repaint_tick_strings(self, *args):
+        self.parent.parameters.sweep_center.add_callback(self.on_lock_changed)
+        self.parent.parameters.sweep_amplitude.add_callback(self.on_lock_changed)
+        self.parent.parameters.lock.add_callback(self.on_lock_changed)
+        self.on_lock_changed()
+
+    def tickStrings(self, values, scale, spacing) -> list[str]:
+        if self.parent.parameters.lock.value:
+            # use µs for the x axis
+            spacing = DECIMATION / 125e6
+            values = [1e6 * scale * v * spacing for v in values]
+            precision_specifier = 1
+        else:
+            # use voltage for the x axis
+            center = self.parent.parameters.sweep_center.value
+            amplitude = self.parent.parameters.sweep_amplitude.value
+            min_ = center - amplitude
+            max_ = center + amplitude
+            spacing = abs(max_ - min_) / (N_POINTS - 1)
+            values = [scale * (v * spacing + min_) for v in values]
+            precision_specifier = 2
+        return [f"{v:.{precision_specifier}f}" for v in values]
+
+    def on_lock_changed(self, *args) -> None:
         self.picture = None
         self.update()
 
-    def tickStrings(self, values, scale, spacing):
-        locked = self.parent.parameters.lock.value
-        sweep_speed = self.parent.parameters.sweep_speed.value if not locked else 0
-        time_between_points = (1 / 125e6) * 2 ** (sweep_speed) * DECIMATION
-        values = [v * time_between_points for v in values]
-        spacing *= time_between_points
-
-        places = max(0, np.ceil(-np.log10(spacing * scale)))
-        strings = []
-        for v in values:
-            vs = v * scale
-            if abs(vs) < 0.001 or abs(vs) >= 10000:
-                vstr = "%g" % vs
-            else:
-                vstr = ("%%0.%df" % places) % vs
-            strings.append(vstr)
-        return strings
-
 
 class PlotWidget(pg.PlotWidget):
     signal_power1 = pyqtSignal(float)
@@ -116,13 +115,18 @@ def __init__(self, *args, **kwargs):
 
         self.getAxis("bottom").enableAutoSIPrefix(False)
         self.showGrid(x=True, y=True)
-        self.setLabel("bottom", "time", units="s")
 
         # Causes auto-scale button (‘A’ in lower-left corner) to be hidden for this
         # PlotItem
         self.hideButtons()
         # we have our own "reset view" button instead
-        self.init_reset_view_button()
+        self.reset_view_button = QtWidgets.QPushButton(self)
+        self.reset_view_button.setText("Reset view")
+        self.reset_view_button.setStyleSheet("padding: 10px; font-weight: bold")
+        icon = QtGui.QIcon.fromTheme("view-restore")
+        self.reset_view_button.setIcon(icon)
+        self.reset_view_button.clicked.connect(self.reset_view)
+        self.position_reset_view_button()
 
         # copied from https://github.com/pyqtgraph/pyqtgraph/blob/master/pyqtgraph/graphicsItems/PlotItem/PlotItem.py#L133 # noqa: E501
         # whenever something changes, we check whether to show "auto scale" button
@@ -140,48 +144,49 @@ def __init__(self, *args, **kwargs):
         # OpenGL is enabled in the beginning of this file.
         # NOTE: OpenGL has a bug that causes the plot to be way too small. Therefore,
         # self.resize() is called below.
-
         self.crosshair = pg.InfiniteLine(pos=N_POINTS / 2, pen=pg.mkPen("w", width=1))
         self.addItem(self.crosshair)
 
-        self.zero_line = pg.PlotCurveItem(pen=pg.mkPen("w", width=1))
-        self.addItem(self.zero_line)
-        self.signal_strength_a = pg.PlotCurveItem()
-        self.addItem(self.signal_strength_a)
-        self.signal_strength_a2 = pg.PlotCurveItem()
-        self.addItem(self.signal_strength_a2)
-        self.signal_strength_a_fill = pg.FillBetweenItem(
-            self.signal_strength_a, self.signal_strength_a2
+        self.zeroLine = pg.PlotCurveItem(pen=pg.mkPen("w", width=1))
+        self.addItem(self.zeroLine)
+        self.signalStrengthA = pg.PlotCurveItem()
+        self.addItem(self.signalStrengthA)
+        self.signalStrengthA2 = pg.PlotCurveItem()
+        self.addItem(self.signalStrengthA2)
+        self.signalStrengthAFill = pg.FillBetweenItem(
+            self.signalStrengthA, self.signalStrengthA2
         )
-        self.addItem(self.signal_strength_a_fill)
-        self.signal_strength_b = pg.PlotCurveItem()
-        self.addItem(self.signal_strength_b)
-        self.signal_strength_b2 = pg.PlotCurveItem()
-        self.addItem(self.signal_strength_b2)
-        self.signal_strength_b_fill = pg.FillBetweenItem(
-            self.signal_strength_b, self.signal_strength_b2
+        self.addItem(self.signalStrengthAFill)
+        self.signalStrengthB = pg.PlotCurveItem()
+        self.addItem(self.signalStrengthB)
+        self.signalStrengthB2 = pg.PlotCurveItem()
+        self.addItem(self.signalStrengthB2)
+        self.signalStrengthBFill = pg.FillBetweenItem(
+            self.signalStrengthB, self.signalStrengthB2
         )
-        self.addItem(self.signal_strength_b_fill)
-        self.signal1 = pg.PlotCurveItem()
-        self.addItem(self.signal1)
-        self.signal2 = pg.PlotCurveItem()
-        self.addItem(self.signal2)
-        self.combined_signal = pg.PlotCurveItem()
-        self.addItem(self.combined_signal)
-
-        self.control_signal = pg.PlotCurveItem()
-        self.addItem(self.control_signal)
-        self.control_signal_history = pg.PlotCurveItem()
-        self.addItem(self.control_signal_history)
-        self.slow_history = pg.PlotCurveItem()
-        self.addItem(self.slow_history)
-        self.monitor_signal_history = pg.PlotCurveItem()
-        self.addItem(self.monitor_signal_history)
-
-        self.zero_line.setData([0, N_POINTS - 1], [0, 0])
-        self.signal1.setData([0, N_POINTS - 1], [1, 1])
-        self.signal1.setData([0, N_POINTS - 1], [1, 1])
-        self.combined_signal.setData([0, N_POINTS - 1], [1, 1])
+        self.addItem(self.signalStrengthBFill)
+        self.errorSignal1 = pg.PlotCurveItem()
+        self.addItem(self.errorSignal1)
+        self.errorSignal2 = pg.PlotCurveItem()
+        self.addItem(self.errorSignal2)
+        self.combinedErrorSignal = pg.PlotCurveItem()
+        self.addItem(self.combinedErrorSignal)
+        self.monitorSignal = pg.PlotCurveItem()
+        self.addItem(self.monitorSignal)
+
+        self.controlSignal = pg.PlotCurveItem()
+        self.addItem(self.controlSignal)
+        self.controlSignalHistory = pg.PlotCurveItem()
+        self.addItem(self.controlSignalHistory)
+        self.slowHistory = pg.PlotCurveItem()
+        self.addItem(self.slowHistory)
+        self.monitorSignalHistory = pg.PlotCurveItem()
+        self.addItem(self.monitorSignalHistory)
+
+        self.zeroLine.setData([0, N_POINTS - 1], [0, 0])
+        self.errorSignal1.setData([0, N_POINTS - 1], [1, 1])
+        self.errorSignal1.setData([0, N_POINTS - 1], [1, 1])
+        self.combinedErrorSignal.setData([0, N_POINTS - 1], [1, 1])
 
         self.connection = None
         self.parameters = None
@@ -194,84 +199,78 @@ def __init__(self, *args, **kwargs):
         self.selection_running = False
         self.selection_boundaries = None
 
-        self.init_overlays()
-        self.init_lock_target_line()
+        self.overlay = pg.LinearRegionItem(values=(0, 0), movable=False)
+        self.overlay.setVisible(False)
+        self.addItem(self.overlay)
 
+        self.boundary_overlays = [
+            pg.LinearRegionItem(values=(0, 0), movable=False, brush=(0, 0, 0, 200))
+            for _ in range(2)
+        ]
+        for i, overlay in enumerate(self.boundary_overlays):
+            overlay.setVisible(False)
+            # make outer borders invisible, see
+            # https://github.com/pyqtgraph/pyqtgraph/issues/462
+            overlay.lines[i].setPen((0, 0, 0, 0))
+            self.addItem(overlay)
+
+        self.lock_target_line = pg.InfiniteLine(movable=False)
+        self.lock_target_line.setValue(1000)
+        self.addItem(self.lock_target_line)
         self._fixed_opengl_bug = False
 
         self.last_plot_time = 0
         self.plot_rate_limit = DEFAULT_PLOT_RATE_LIMIT
 
-        self._plot_paused = False
-        self._cached_plot_data = []
+        self.plot_paused = False
+        self.cached_plot_data = []
         self._should_reposition_reset_view_button = False
 
     def on_connection_established(self):
         self.parameters = self.app.parameters
         self.control = self.app.control
 
-        def set_pens(*args):
-            pen_width = self.app.settings.plot_line_width.value
-
-            for curve, color in {
-                self.signal1: Color.SPECTRUM1,
-                self.signal2: Color.SPECTRUM2,
-                self.combined_signal: Color.SPECTRUM_COMBINED,
-                self.control_signal: Color.CONTROL_SIGNAL,
-                self.control_signal_history: Color.CONTROL_SIGNAL_HISTORY,
-                self.slow_history: Color.SLOW_HISTORY,
-                self.monitor_signal_history: Color.MONITOR_SIGNAL_HISTORY,
-            }.items():
-                r, g, b, _ = getattr(
-                    self.app.settings, f"plot_color_{color.value}"
-                ).value
-                a = self.app.settings.plot_line_opacity.value
-                curve.setPen(pg.mkPen((r, g, b, a), width=pen_width))
-
         for color_idx in range(N_COLORS):
-            getattr(self.app.settings, f"plot_color_{color_idx}").add_callback(set_pens)
-        self.app.settings.plot_line_width.add_callback(set_pens)
-        self.app.settings.plot_line_opacity.add_callback(set_pens)
+            getattr(self.app.settings, f"plot_color_{color_idx}").add_callback(
+                self.on_plot_settings_changed
+            )
+        self.app.settings.plot_line_width.add_callback(self.on_plot_settings_changed)
+        self.app.settings.plot_line_opacity.add_callback(self.on_plot_settings_changed)
 
         self.control_signal_history_data = self.parameters.control_signal_history.value
         self.monitor_signal_history_data = self.parameters.monitor_signal_history.value
 
-        self.parameters.to_plot.add_callback(self.replot)
-
-        def autolock_selection_changed(value):
-            if value:
-                self.parameters.optimization_selection.value = False
-                self.enable_area_selection(selectable_width=0.99)
-                self.pause_plot_and_cache_data()
-            elif not self.parameters.optimization_selection.value:
-                self.disable_area_selection()
-                self.resume_plot_and_clear_cache()
-
-        self.parameters.autolock_selection.add_callback(autolock_selection_changed)
-
-        def optimization_selection_changed(value):
-            if value:
-                self.parameters.autolock_selection.value = False
-                self.enable_area_selection(selectable_width=0.75)
-                self.pause_plot_and_cache_data()
-            elif not self.parameters.autolock_selection.value:
-                self.disable_area_selection()
-                self.resume_plot_and_clear_cache()
-
+        self.parameters.to_plot.add_callback(self.on_new_plot_data_received)
+        self.parameters.autolock_selection.add_callback(
+            self.on_autolock_selection_changed
+        )
         self.parameters.optimization_selection.add_callback(
-            optimization_selection_changed
+            self.on_optimization_selection_changed
         )
-
-        def show_or_hide_crosshair(automatic_mode):
-            self.crosshair.setVisible(not automatic_mode)
-
-        self.parameters.automatic_mode.add_callback(show_or_hide_crosshair)
+        self.parameters.automatic_mode.add_callback(self.on_automatic_mode_changed)
+        self.parameters.lock.add_callback(self.on_lock_changed)
 
     def _to_data_coords(self, event):
         pos = self.plotItem.vb.mapSceneToView(event.pos())
         x, y = pos.x(), pos.y()
         return x, y
 
+    def _within_boundaries(self, x):
+        boundaries = (
+            self.selection_boundaries if self.selection_running else [0, N_POINTS]
+        )
+
+        if x < boundaries[0]:
+            return boundaries[0]
+        if x > boundaries[1]:
+            return boundaries[1]
+        return x
+
+    def keyPressEvent(self, event):
+        # we listen here in addition to the main window because some events are only
+        # caught here
+        self.keyPressed.emit(event.key())
+
     def mouseMoveEvent(self, event):
         if not self.selection_running:
             super().mouseMoveEvent(event)
@@ -285,62 +284,6 @@ def mouseMoveEvent(self, event):
             x = self._within_boundaries(x)
             self.set_selection_overlay(x0, x - x0)
 
-    def init_overlays(self):
-        self.overlay = pg.LinearRegionItem(values=(0, 0), movable=False)
-        self.overlay.setVisible(False)
-        self.addItem(self.overlay)
-
-        self.boundary_overlays = [
-            pg.LinearRegionItem(
-                values=(0, 0),
-                movable=False,
-                brush=(0, 0, 0, 200),
-            )
-            for i in range(2)
-        ]
-        for i, overlay in enumerate(self.boundary_overlays):
-            overlay.setVisible(False)
-            # make outer borders invisible, see
-            # https://github.com/pyqtgraph/pyqtgraph/issues/462
-            overlay.lines[i].setPen((0, 0, 0, 0))
-            self.addItem(overlay)
-
-    def init_lock_target_line(self):
-        self.lock_target_line = pg.InfiniteLine(movable=False)
-        self.lock_target_line.setValue(1000)
-        self.addItem(self.lock_target_line)
-
-    def set_selection_overlay(self, x_start, width):
-        self.overlay.setRegion((x_start, x_start + width))
-
-    def mousePressEvent(self, event):
-        super().mousePressEvent(event)
-
-        if self.selection_running:
-            if event.button() == QtCore.Qt.RightButton:
-                return
-
-            x, y = self._to_data_coords(event)
-
-            if self.selection_running:
-                if x < self.selection_boundaries[0] or x > self.selection_boundaries[1]:
-                    return
-
-            self.touch_start = x, y
-            self.set_selection_overlay(x, 0)
-            self.overlay.setVisible(True)
-
-    def _within_boundaries(self, x):
-        boundaries = (
-            self.selection_boundaries if self.selection_running else [0, N_POINTS]
-        )
-
-        if x < boundaries[0]:
-            return boundaries[0]
-        if x > boundaries[1]:
-            return boundaries[1]
-        return x
-
     def mouseReleaseEvent(self, event):
         super().mouseReleaseEvent(event)
 
@@ -368,7 +311,7 @@ def mouseReleaseEvent(self, event):
                             # transmitted which blocks the autolock
                             *sorted([x0, x]),
                             pickle.dumps(last_combined_error_signal),
-                            additional_spectra=pickle.dumps(self._cached_plot_data),
+                            additional_spectra=pickle.dumps(self.cached_plot_data),
                         )
 
                         (
@@ -383,10 +326,13 @@ def mouseReleaseEvent(self, event):
                         )
                         self.autolock_ref_spectrum = rolled_error_signal
                     elif self.parameters.optimization_selection.value:
-                        dual_channel = self.parameters.dual_channel.value
                         channel = self.parameters.optimization_channel.value
                         spectrum = self.last_plot_data[
-                            0 if not dual_channel else (0, 1)[channel]
+                            (
+                                0
+                                if not self.parameters.dual_channel.value
+                                else (0, 1)[channel]
+                            )
                         ]
                         self.parameters.optimization_selection.value = False
                         points = sorted([int(x0), int(x)])
@@ -395,7 +341,72 @@ def mouseReleaseEvent(self, event):
             self.overlay.setVisible(False)
             self.touch_start = None
 
-    def replot(self, to_plot):
+    def on_plot_settings_changed(self, *args):
+        pen_width = self.app.settings.plot_line_width.value
+
+        for curve, color in {
+            self.errorSignal1: Color.ERROR1,
+            self.errorSignal2: Color.ERROR2,
+            self.combinedErrorSignal: Color.ERROR_COMBINED,
+            self.monitorSignal: Color.MONITOR,
+            self.controlSignal: Color.CONTROL_SIGNAL,
+            self.controlSignalHistory: Color.CONTROL_SIGNAL_HISTORY,
+            self.slowHistory: Color.SLOW_HISTORY,
+            self.monitorSignalHistory: Color.MONITOR_SIGNAL_HISTORY,
+        }.items():
+            r, g, b, _ = getattr(self.app.settings, f"plot_color_{color.value}").value
+            a = self.app.settings.plot_line_opacity.value
+            curve.setPen(pg.mkPen((r, g, b, a), width=pen_width))
+
+    def on_autolock_selection_changed(self, value):
+        if value:
+            self.parameters.optimization_selection.value = False
+            self.enable_area_selection(selectable_width=0.99)
+            self.pause_plot()
+        elif not self.parameters.optimization_selection.value:
+            self.disable_area_selection()
+            self.resume_plot_and_clear_cache()
+
+    def on_optimization_selection_changed(self, value):
+        if value:
+            self.parameters.autolock_selection.value = False
+            self.enable_area_selection(selectable_width=0.75)
+            self.pause_plot()
+        elif not self.parameters.autolock_selection.value:
+            self.disable_area_selection()
+            self.resume_plot_and_clear_cache()
+
+    def on_automatic_mode_changed(self, automatic_mode: bool) -> None:
+        """Show or hide crosshair"""
+        self.crosshair.setVisible(not automatic_mode)
+
+    def on_lock_changed(self, lock: bool) -> None:
+        if not lock:
+            self.setLabel("bottom", "sweep voltage", units="V")
+        else:
+            self.setLabel("bottom", "time", units="µs")
+
+    def set_selection_overlay(self, x_start, width):
+        self.overlay.setRegion((x_start, x_start + width))
+
+    def mousePressEvent(self, event):
+        super().mousePressEvent(event)
+
+        if self.selection_running:
+            if event.button() == QtCore.Qt.RightButton:
+                return
+
+            x, y = self._to_data_coords(event)
+
+            if self.selection_running:
+                if x < self.selection_boundaries[0] or x > self.selection_boundaries[1]:
+                    return
+
+            self.touch_start = x, y
+            self.set_selection_overlay(x, 0)
+            self.overlay.setVisible(True)
+
+    def on_new_plot_data_received(self, to_plot):
         time_beginning = time()
 
         if self._should_reposition_reset_view_button:
@@ -404,7 +415,7 @@ def replot(self, to_plot):
 
         if (
             time_beginning - self.last_plot_time <= self.plot_rate_limit
-            and not self._plot_paused
+            and not self.plot_paused
         ):
             # don't plot too often as it only causes unnecessary load this does not
             # apply if plot is paused, because in this case we want to collect all the
@@ -437,103 +448,153 @@ def replot(self, to_plot):
 
             # we also call this if the laser is not locked because it resets the history
             # in this case
-            history, slow_history = self.update_signal_history(to_plot)
+            update_signal_history(
+                self.control_signal_history_data,
+                self.monitor_signal_history_data,
+                to_plot,
+                self.parameters.lock.value,
+                self.parameters.control_signal_history_length.value,
+            )
 
             if self.parameters.lock.value:
-                self.signal1.setVisible(False)
-                self.signal2.setVisible(False)
-                self.control_signal.setVisible(True)
-                self.control_signal_history.setVisible(True)
-                self.slow_history.setVisible(self.parameters.pid_on_slow_enabled.value)
-                self.monitor_signal_history.setVisible(
-                    not self.parameters.dual_channel.value
+                dual_channel = self.parameters.dual_channel.value
+                timescale = self.parameters.control_signal_history_length.value
+
+                self.errorSignal1.setVisible(False)
+                self.errorSignal2.setVisible(False)
+                self.monitorSignal.setVisible(False)
+                self.signalStrengthA.setVisible(False)
+                self.signalStrengthB.setVisible(False)
+                self.signalStrengthA2.setVisible(False)
+                self.signalStrengthB2.setVisible(False)
+                self.signalStrengthAFill.setVisible(False)
+                self.signalStrengthBFill.setVisible(False)
+
+                self.combinedErrorSignal.setVisible(True)
+                self.combinedErrorSignal.setData(
+                    list(range(len(to_plot["error_signal"]))),
+                    to_plot["error_signal"] / V,
+                )
+
+                self.controlSignal.setVisible(True)
+                self.controlSignal.setData(
+                    list(range(len(to_plot["control_signal"]))),
+                    to_plot["control_signal"] / V,
+                )
+
+                self.controlSignalHistory.setVisible(True)
+                self.controlSignalHistory.setData(
+                    scale_history_times(
+                        self.control_signal_history_data["times"], timescale
+                    ),
+                    np.array(self.control_signal_history_data["values"]) / V,
                 )
-                self.combined_signal.setVisible(True)
-                self.signal_strength_a.setVisible(False)
-                self.signal_strength_b.setVisible(False)
-                self.signal_strength_a2.setVisible(False)
-                self.signal_strength_b2.setVisible(False)
-                self.signal_strength_a_fill.setVisible(False)
-                self.signal_strength_b_fill.setVisible(False)
-
-                error_signal, control_signal = (
-                    to_plot["error_signal"],
-                    to_plot["control_signal"],
+
+                self.slowHistory.setVisible(self.parameters.pid_on_slow_enabled.value)
+                self.slowHistory.setData(
+                    scale_history_times(
+                        self.control_signal_history_data["slow_times"], timescale
+                    ),
+                    np.array(self.control_signal_history_data["slow_values"]) / V,
                 )
-                all_signals = (error_signal, control_signal, history, slow_history)
 
-                self.plot_data_locked(to_plot)
+                self.monitorSignalHistory.setVisible(not dual_channel)
+                if not dual_channel:
+                    self.monitorSignalHistory.setData(
+                        scale_history_times(
+                            self.monitor_signal_history_data["times"], timescale
+                        ),
+                        np.array(self.monitor_signal_history_data["values"]) / V,
+                    )
                 self.plot_autolock_target_line(None)
             else:
                 dual_channel = self.parameters.dual_channel.value
-                self.signal1.setVisible(True)
                 monitor_signal = to_plot.get("monitor_signal")
                 error_signal_2 = to_plot.get("error_signal_2")
-                self.signal2.setVisible(
-                    error_signal_2 is not None or monitor_signal is not None
+                error_signal_1 = to_plot["error_signal_1"]
+                monitor_or_error_signal_2 = (
+                    error_signal_2 if error_signal_2 is not None else monitor_signal
                 )
-                self.combined_signal.setVisible(dual_channel)
-                self.control_signal.setVisible(False)
-                self.control_signal_history.setVisible(False)
-                self.slow_history.setVisible(False)
-                self.monitor_signal_history.setVisible(False)
-
-                s1 = to_plot["error_signal_1"]
-                s2 = error_signal_2 if error_signal_2 is not None else monitor_signal
 
                 combined_error_signal = combine_error_signal(
-                    (s1, s2),
+                    (error_signal_1, monitor_or_error_signal_2),
                     dual_channel,
                     self.parameters.channel_mixing.value,
-                    self.parameters.combined_offset.value,
+                    self.parameters.combined_offset.value if dual_channel else 0,
                 )
 
-                if self._plot_paused:
-                    self._cached_plot_data.append(combined_error_signal)
+                if self.plot_paused:
+                    self.cached_plot_data.append(combined_error_signal)
                     # don't save too much
-                    self._cached_plot_data = self._cached_plot_data[-20:]
+                    self.cached_plot_data = self.cached_plot_data[-20:]
                     return
 
-                all_signals = [s1, s2] + [combined_error_signal]
-                self.last_plot_data = all_signals
+                self.last_plot_data = [error_signal_1, monitor_or_error_signal_2] + [
+                    combined_error_signal
+                ]
+
+                self.controlSignal.setVisible(False)
+                self.controlSignalHistory.setVisible(False)
+                self.slowHistory.setVisible(False)
+                self.monitorSignalHistory.setVisible(False)
+
+                self.combinedErrorSignal.setVisible(True)
+                self.combinedErrorSignal.setData(
+                    list(range(len(error_signal_1))), error_signal_1 / V
+                )
+
+                self.errorSignal1.setVisible(dual_channel)
+                if error_signal_1 is not None:
+                    self.errorSignal1.setData(
+                        list(range(len(error_signal_1))), error_signal_1 / V
+                    )
+
+                self.errorSignal2.setVisible(dual_channel)
+                if error_signal_2 is not None:
+                    self.errorSignal2.setData(
+                        list(range(len(error_signal_2))), error_signal_2 / V
+                    )
+
+                self.monitorSignal.setVisible(not dual_channel)
+                if monitor_signal is not None:
+                    self.monitorSignal.setData(
+                        list(range(len(monitor_signal))), monitor_signal / V
+                    )
 
-                self.plot_data_unlocked((s1, s2), combined_error_signal)
                 self.plot_autolock_target_line(combined_error_signal)
 
                 if (self.parameters.modulation_frequency.value != 0) and (
                     not self.parameters.pid_only_mode.value
                 ):
                     # check whether to plot signal strengths using quadratures
-                    s1q = to_plot.get("error_signal_1_quadrature")
-                    s2q = to_plot.get("error_signal_2_quadrature")
-
-                    self.signal_strength_a.setVisible(s1q is not None)
-                    self.signal_strength_a2.setVisible(s1q is not None)
-                    self.signal_strength_a_fill.setVisible(s1q is not None)
-
-                    self.signal_strength_b.setVisible(s2q is not None)
-                    self.signal_strength_b2.setVisible(s2q is not None)
-                    self.signal_strength_b_fill.setVisible(s2q is not None)
-
-                    if s1q is not None:
+                    error_1_quadrature = to_plot.get("error_signal_1_quadrature")
+                    error_2_quadrature = to_plot.get("error_signal_2_quadrature")
+
+                    self.signalStrengthA.setVisible(error_1_quadrature is not None)
+                    self.signalStrengthA2.setVisible(error_1_quadrature is not None)
+                    self.signalStrengthAFill.setVisible(error_1_quadrature is not None)
+                    self.signalStrengthB.setVisible(error_2_quadrature is not None)
+                    self.signalStrengthB2.setVisible(error_2_quadrature is not None)
+                    self.signalStrengthBFill.setVisible(error_2_quadrature is not None)
+
+                    if error_1_quadrature is not None:
+
+                        if self.parameters.dual_channel.value:
+                            color = Color.ERROR1.value
+                        else:
+                            color = Color.ERROR_COMBINED.value
                         max_signal_strength_V = (
                             self.plot_signal_strength(
-                                s1,
-                                s1q,
-                                self.signal_strength_a,
-                                self.signal_strength_a2,
-                                self.signal_strength_a_fill,
+                                error_signal_1,
+                                error_1_quadrature,
+                                self.signalStrengthA,
+                                self.signalStrengthA2,
+                                self.signalStrengthAFill,
                                 self.parameters.offset_a.value,
-                                self.app.settings.plot_color_0.value,
+                                getattr(self.app.settings, f"plot_color_{color}").value,
                             )
                             / V
                         )
-                        all_signals.append(
-                            [
-                                max_signal_strength_V * V,
-                                -1 * max_signal_strength_V * V,
-                            ]
-                        )
 
                         self.signal_power1.emit(
                             peak_voltage_to_dBm(max_signal_strength_V)
@@ -541,39 +602,35 @@ def replot(self, to_plot):
                     else:
                         self.signal_power1.emit(INVALID_POWER)
 
-                    if s2q is not None:
+                    if error_2_quadrature is not None:
                         max_signal_strength2_V = (
                             self.plot_signal_strength(
-                                s2,
-                                s2q,
-                                self.signal_strength_b,
-                                self.signal_strength_b2,
-                                self.signal_strength_b_fill,
+                                monitor_or_error_signal_2,
+                                error_2_quadrature,
+                                self.signalStrengthB,
+                                self.signalStrengthB2,
+                                self.signalStrengthBFill,
                                 self.parameters.offset_b.value,
-                                self.app.settings.plot_color_1.value,
+                                getattr(
+                                    self.app.settings,
+                                    f"plot_color_{Color.ERROR2.value}",
+                                ).value,
                             )
                             / V
                         )
 
-                        all_signals.append(
-                            [
-                                max_signal_strength2_V * V,
-                                -1 * max_signal_strength2_V * V,
-                            ]
-                        )
-
                         self.signal_power2.emit(
                             peak_voltage_to_dBm(max_signal_strength2_V)
                         )
                     else:
                         self.signal_power2.emit(INVALID_POWER)
                 else:
-                    self.signal_strength_a.setVisible(False)
-                    self.signal_strength_b.setVisible(False)
-                    self.signal_strength_a2.setVisible(False)
-                    self.signal_strength_b2.setVisible(False)
-                    self.signal_strength_a_fill.setVisible(False)
-                    self.signal_strength_b_fill.setVisible(False)
+                    self.signalStrengthA.setVisible(False)
+                    self.signalStrengthB.setVisible(False)
+                    self.signalStrengthA2.setVisible(False)
+                    self.signalStrengthB2.setVisible(False)
+                    self.signalStrengthAFill.setVisible(False)
+                    self.signalStrengthBFill.setVisible(False)
 
                     self.signal_power1.emit(INVALID_POWER)
                     self.signal_power2.emit(INVALID_POWER)
@@ -595,7 +652,7 @@ def plot_signal_strength(
         q -= int(round(channel_offset))
         signal_strength = get_signal_strength_from_i_q(i, q)
 
-        r, g, b, *stuff = color
+        r, g, b, *_ = color
 
         x = list(range(len(signal_strength)))
         signal_strength_scaled = signal_strength / V
@@ -610,20 +667,6 @@ def plot_signal_strength(
         neg_signal.setData(x, lower, pen=invisible_pen)
         return np.max([np.max(upper), -1 * np.min(lower)]) * V
 
-    def plot_data_unlocked(self, error_signals, combined_signal):
-        error_signal1, error_signal2 = error_signals
-        self.signal1.setData(list(range(len(error_signal1))), error_signal1 / V)
-        self.signal2.setData(list(range(len(error_signal2))), error_signal2 / V)
-        self.combined_signal.setData(
-            list(range(len(combined_signal))), combined_signal / V
-        )
-
-    def plot_data_locked(self, signals):
-        error_signal = signals["error_signal"]
-        control_signal = signals["control_signal"]
-        self.combined_signal.setData(list(range(len(error_signal))), error_signal / V)
-        self.control_signal.setData(list(range(len(error_signal))), control_signal / V)
-
     def plot_autolock_target_line(self, combined_error_signal):
         if (
             self.autolock_ref_spectrum is not None
@@ -653,65 +696,19 @@ def plot_autolock_target_line(self, combined_error_signal):
         else:
             self.lock_target_line.setVisible(False)
 
-    def keyPressEvent(self, event):
-        # we listen here in addition to the main window because some events are only
-        # caught here
-        self.keyPressed.emit(event.key())
-
-    def update_signal_history(self, to_plot):
-        update_signal_history(
-            self.control_signal_history_data,
-            self.monitor_signal_history_data,
-            to_plot,
-            self.parameters.lock.value,
-            self.parameters.control_signal_history_length.value,
-        )
-
-        if self.parameters.lock.value:
-            x_axis_length = N_POINTS
-
-            def scale(arr):
-                timescale = self.parameters.control_signal_history_length.value
-                if arr:
-                    arr = np.array(arr)
-                    arr -= arr[0]
-                    arr *= 1 / timescale * x_axis_length
-                return arr
-
-            history = self.control_signal_history_data["values"]
-            self.control_signal_history.setData(
-                scale(self.control_signal_history_data["times"]), np.array(history) / V
-            )
-
-            slow_values = self.control_signal_history_data["slow_values"]
-            self.slow_history.setData(
-                scale(self.control_signal_history_data["slow_times"]),
-                np.array(slow_values) / V,
-            )
-
-            if not self.parameters.dual_channel.value:
-                self.monitor_signal_history.setData(
-                    scale(self.monitor_signal_history_data["times"]),
-                    np.array(self.monitor_signal_history_data["values"]) / V,
-                )
-
-            return history, slow_values
-        return [], []
-
     def enable_area_selection(self, selectable_width=0.5):
         self.selection_running = True
 
         # there are some glitches if the width of the overlay is exactly right.
         # Therefore we make it a little wider.
         extra_width = N_POINTS / 100
-        x_axis_length = N_POINTS
-        boundary_width = (x_axis_length * (1 - selectable_width)) / 2.0
+        boundary_width = (N_POINTS * (1 - selectable_width)) / 2.0
 
-        self.selection_boundaries = (boundary_width, x_axis_length - boundary_width)
+        self.selection_boundaries = (boundary_width, N_POINTS - boundary_width)
 
         self.boundary_overlays[0].setRegion((-extra_width, boundary_width))
         self.boundary_overlays[1].setRegion(
-            (x_axis_length - boundary_width, x_axis_length + extra_width)
+            (N_POINTS - boundary_width, N_POINTS + extra_width)
         )
 
         for overlay in self.boundary_overlays:
@@ -723,25 +720,17 @@ def disable_area_selection(self):
         for overlay in self.boundary_overlays:
             overlay.setVisible(False)
 
-    def pause_plot_and_cache_data(self):
-        """This function pauses plot updates. All incoming data is cached though.
-        This is useful for letting the user select a line that is then used in
-        the autolock."""
-        self._plot_paused = True
+    def pause_plot(self):
+        """
+        Pauses plot updates. All incoming data is cached though. This is useful for
+        letting the user select a line that is then used in the autolock.
+        """
+        self.plot_paused = True
 
     def resume_plot_and_clear_cache(self):
-        """Resumes plotting again."""
-        self._plot_paused = False
-        self._cached_plot_data = []
-
-    def init_reset_view_button(self):
-        self.reset_view_button = QtWidgets.QPushButton(self)
-        self.reset_view_button.setText("Reset view")
-        self.reset_view_button.setStyleSheet("padding: 10px; font-weight: bold")
-        icon = QtGui.QIcon.fromTheme("view-restore")
-        self.reset_view_button.setIcon(icon)
-        self.reset_view_button.clicked.connect(self.reset_view)
-        self.position_reset_view_button()
+        """Resume plotting again."""
+        self.plot_paused = False
+        self.cached_plot_data = []
 
     # called when widget is resized
     def position_reset_view_button(self):
@@ -767,3 +756,11 @@ def resizeEvent(self, event, *args, **kwargs):
 
         # we don't do it directly here because this causes problems for some reason
         self._should_reposition_reset_view_button = True
+
+
+def scale_history_times(arr: np.ndarray, timescale: int) -> np.ndarray:
+    if arr:
+        arr = np.array(arr)
+        arr -= arr[0]
+        arr *= 1 / timescale * N_POINTS
+    return arr
diff --git a/linien-gui/linien_gui/ui/psd_plot_widget.py b/linien-gui/linien_gui/ui/psd_plot_widget.py
index 038aaa20..09c3ba1e 100644
--- a/linien-gui/linien_gui/ui/psd_plot_widget.py
+++ b/linien-gui/linien_gui/ui/psd_plot_widget.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -212,7 +211,9 @@ def show_cursor_position(self, show):
         self.cursor_label.setVisible(show)
 
     def leaveEvent(self, QEvent):
-        """This method is called when the mouse used to hover the plot and
-        not left. In this case, we hide crosshair and cursor label."""
+        """
+        This method is called when the mouse used to hover the plot and not left. In
+        this case, we hide crosshair and cursor label.
+        """
         super().leaveEvent(QEvent)
         self.show_cursor_position(False)
diff --git a/linien-gui/linien_gui/ui/psd_table_widget.py b/linien-gui/linien_gui/ui/psd_table_widget.py
index 48d99eeb..6a3d2f80 100644
--- a/linien-gui/linien_gui/ui/psd_table_widget.py
+++ b/linien-gui/linien_gui/ui/psd_table_widget.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/psd_window.py b/linien-gui/linien_gui/ui/psd_window.py
index dea1dc7e..7e6bd047 100644
--- a/linien-gui/linien_gui/ui/psd_window.py
+++ b/linien-gui/linien_gui/ui/psd_window.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -23,6 +22,8 @@
 from linien_common.common import PSDAlgorithm
 from linien_gui.config import UI_PATH
 from linien_gui.dialogs import error_dialog
+from linien_gui.ui.psd_plot_widget import PSDPlotWidget
+from linien_gui.ui.psd_table_widget import PSDTableWidget
 from linien_gui.utils import (
     RandomColorChoser,
     get_linien_app_instance,
@@ -33,6 +34,18 @@
 
 
 class PSDWindow(QtWidgets.QMainWindow):
+    PSDPlot: PSDPlotWidget
+    curveTable: PSDTableWidget
+    maxMeasurementTimeComboBox: QtWidgets.QComboBox
+    PSDAlgorithmComboBox: QtWidgets.QComboBox
+    startPSDPushButton: QtWidgets.QPushButton
+    stopPSDPushButton: QtWidgets.QPushButton
+    exportPSDPushButton: QtWidgets.QPushButton
+    importPSDPushButton: QtWidgets.QPushButton
+    deleteCurveButton: QtWidgets.QPushButton
+    PSDRunningContainer: QtWidgets.QWidget
+    PSDNotRunningContainer: QtWidgets.QGroupBox
+
     def __init__(self, *args, **kwargs):
         super(PSDWindow, self).__init__(*args, **kwargs)
         uic.loadUi(UI_PATH / "psd_window.ui", self)
@@ -46,21 +59,19 @@ def __init__(self, *args, **kwargs):
         self.data = {}
         self.complete_uids = []
 
-        self.start_psd_button.clicked.connect(self.start_psd)
-        self.stop_psd_button.clicked.connect(self.stop_psd)
+        self.startPSDPushButton.clicked.connect(self.start_psd)
+        self.stopPSDPushButton.clicked.connect(self.stop_psd)
 
-        self.curve_table.show_or_hide_curve.connect(
-            self.psd_plot_widget.show_or_hide_curve
-        )
-        self.delete_curve_button.clicked.connect(self.delete_curve)
-        self.export_psd_button.clicked.connect(self.export_psd)
-        self.import_psd_button.clicked.connect(self.import_psd)
+        self.curveTable.show_or_hide_curve.connect(self.PSDPlot.show_or_hide_curve)
+        self.deleteCurveButton.clicked.connect(self.delete_curve)
+        self.exportPSDPushButton.clicked.connect(self.export_psd)
+        self.importPSDPushButton.clicked.connect(self.import_psd)
 
-        self.maximum_measurement_time.currentIndexChanged.connect(
-            self.change_maximum_measurement_time
+        self.maxMeasurementTimeComboBox.currentIndexChanged.connect(
+            self.on_maximum_measurement_time_changed
         )
 
-        self.psd_algorithm.currentIndexChanged.connect(self.change_psd_algorithm)
+        self.PSDAlgorithmComboBox.currentIndexChanged.connect(self.change_psd_algorithm)
 
     def on_connection_established(self):
         self.parameters = self.app.parameters
@@ -75,19 +86,19 @@ def on_connection_established(self):
 
         param2ui(
             self.parameters.psd_acquisition_max_decimation,
-            self.maximum_measurement_time,
+            self.maxMeasurementTimeComboBox,
             lambda max_decimation: max_decimation - 12,
         )
-        param2ui(self.parameters.psd_algorithm, self.psd_algorithm)
+        param2ui(self.parameters.psd_algorithm, self.PSDAlgorithmComboBox)
 
         def update_status(_):
             psd_running = self.parameters.psd_acquisition_running.value
             if psd_running:
-                self.container_psd_running.show()
-                self.container_psd_not_running.hide()
+                self.PSDRunningContainer.show()
+                self.PSDNotRunningContainer.hide()
             else:
-                self.container_psd_running.hide()
-                self.container_psd_not_running.show()
+                self.PSDRunningContainer.hide()
+                self.PSDNotRunningContainer.show()
 
         self.parameters.psd_acquisition_running.add_callback(update_status)
 
@@ -97,13 +108,11 @@ def closeEvent(self, event, *args, **kwargs):
         event.ignore()
         self.hide()
 
-    def change_maximum_measurement_time(self, index):
+    def on_maximum_measurement_time_changed(self, index):
         self.parameters.psd_acquisition_max_decimation.value = 12 + index
 
-    def change_psd_algorithm(self, index):
-        self.parameters.psd_algorithm.value = [PSDAlgorithm.LPSD, PSDAlgorithm.WELCH][
-            index
-        ]
+    def change_psd_algorithm(self, index: int) -> None:
+        self.parameters.psd_algorithm.value = list(PSDAlgorithm)[index]
 
     def psd_data_received(self, data_pickled):
         if data_pickled is None:
@@ -130,8 +139,8 @@ def psd_data_received(self, data_pickled):
         else:
             color = self.colors[curve_uuid]
 
-        self.psd_plot_widget.plot_curve(curve_uuid, data["psds"], color)
-        self.curve_table.add_curve(curve_uuid, data, color)
+        self.PSDPlot.plot_curve(curve_uuid, data["psds"], color)
+        self.curveTable.add_curve(curve_uuid, data, color)
 
         self.data[curve_uuid] = data
 
@@ -150,9 +159,9 @@ def start_pid_optimization(self):
         self.control.start_pid_optimization()
 
     def delete_curve(self):
-        uuid = self.curve_table.delete_selected_curve()
+        uuid = self.curveTable.delete_selected_curve()
         if uuid is not None:
-            self.psd_plot_widget.delete_curve(uuid)
+            self.PSDPlot.delete_curve(uuid)
             del self.data[uuid]
 
     def export_psd(self):
diff --git a/linien-gui/linien_gui/ui/psd_window.ui b/linien-gui/linien_gui/ui/psd_window.ui
index d35aec83..44b88fa2 100644
--- a/linien-gui/linien_gui/ui/psd_window.ui
+++ b/linien-gui/linien_gui/ui/psd_window.ui
@@ -16,7 +16,7 @@
   <widget class="QWidget" name="centralwidget">
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
-     <widget class="PSDPlotWidget" name="psd_plot_widget">
+     <widget class="PSDPlotWidget" name="PSDPlot">
       <property name="sizePolicy">
        <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
         <horstretch>0</horstretch>
@@ -46,7 +46,7 @@
         </property>
         <layout class="QVBoxLayout" name="verticalLayout_3">
          <item>
-          <widget class="QComboBox" name="maximum_measurement_time">
+          <widget class="QComboBox" name="maxMeasurementTimeComboBox">
            <property name="currentIndex">
             <number>5</number>
            </property>
@@ -161,13 +161,13 @@
        </widget>
       </item>
       <item>
-       <widget class="QGroupBox" name="groupBox_2">
+       <widget class="QGroupBox" name="algorithmGroupBox">
         <property name="title">
          <string>Algorithm</string>
         </property>
         <layout class="QVBoxLayout" name="verticalLayout_2">
          <item>
-          <widget class="QComboBox" name="psd_algorithm">
+          <widget class="QComboBox" name="PSDAlgorithmComboBox">
            <item>
             <property name="text">
              <string>LPSD</string>
@@ -184,13 +184,13 @@
        </widget>
       </item>
       <item>
-       <widget class="QGroupBox" name="container_psd_not_running">
+       <widget class="QGroupBox" name="PSDNotRunningContainer">
         <property name="title">
          <string>Single PSD measurement</string>
         </property>
         <layout class="QVBoxLayout" name="verticalLayout_4">
          <item>
-          <widget class="QPushButton" name="start_psd_button">
+          <widget class="QPushButton" name="startPSDPushButton">
            <property name="font">
             <font>
              <weight>75</weight>
@@ -209,7 +209,7 @@
        </widget>
       </item>
       <item>
-       <widget class="QWidget" name="container_psd_running" native="true">
+       <widget class="QWidget" name="PSDRunningContainer" native="true">
         <layout class="QVBoxLayout" name="verticalLayout_5">
          <item>
           <widget class="QLabel" name="label_2">
@@ -232,7 +232,7 @@
           </widget>
          </item>
          <item>
-          <widget class="QPushButton" name="stop_psd_button">
+          <widget class="QPushButton" name="stopPSDPushButton">
            <property name="styleSheet">
             <string notr="true">background-color: #d40000;</string>
            </property>
@@ -245,7 +245,7 @@
        </widget>
       </item>
       <item>
-       <widget class="PSDTableWidget" name="curve_table">
+       <widget class="PSDTableWidget" name="curveTable">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
           <horstretch>0</horstretch>
@@ -290,7 +290,7 @@
        </widget>
       </item>
       <item>
-       <widget class="QPushButton" name="delete_curve_button">
+       <widget class="QPushButton" name="deleteCurveButton">
         <property name="text">
          <string>Delete selected</string>
         </property>
@@ -302,14 +302,14 @@
          <number>6</number>
         </property>
         <item>
-         <widget class="QPushButton" name="import_psd_button">
+         <widget class="QPushButton" name="importPSDPushButton">
           <property name="text">
            <string>Import traces</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QPushButton" name="export_psd_button">
+         <widget class="QPushButton" name="exportPSDPushButton">
           <property name="text">
            <string>Export traces</string>
           </property>
diff --git a/linien-gui/linien_gui/ui/right_panel.py b/linien-gui/linien_gui/ui/right_panel.py
index 4dc4f707..86dc4e5f 100644
--- a/linien-gui/linien_gui/ui/right_panel.py
+++ b/linien-gui/linien_gui/ui/right_panel.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -34,7 +33,7 @@ def ready(self) -> None:
         self.main_window.openDeviceManagerButton.clicked.connect(
             self.open_device_manager
         )
-        self.main_window.pid_parameter_optimization_button.clicked.connect(
+        self.main_window.PIDParameterOptimizationButton.clicked.connect(
             self.open_psd_window
         )
 
@@ -49,7 +48,7 @@ def on_connection_established(self) -> None:
         self.parameters.lock.add_callback(self.enable_or_disable_panels)
 
         def highlight_psd_button(locked: bool) -> None:
-            self.main_window.pid_parameter_optimization_button.setStyleSheet(
+            self.main_window.PIDParameterOptimizationButton.setStyleSheet(
                 "background: #00aa00;" if locked else ""
             )
 
@@ -66,7 +65,7 @@ def open_device_manager(self) -> None:
 
     def autolock_status_changed(self, value: bool) -> None:
         if value:
-            self.main_window.settings_toolbox.setCurrentWidget(
+            self.main_window.settingsToolbox.setCurrentWidget(
                 self.main_window.lockingPanel
             )
 
@@ -74,7 +73,7 @@ def autolock_status_changed(self, value: bool) -> None:
 
     def optimization_status_changed(self, value: bool) -> None:
         if value:
-            self.main_window.settings_toolbox.setCurrentWidget(
+            self.main_window.settingsToolbox.setCurrentWidget(
                 self.main_window.optimizationPanel
             )
 
diff --git a/linien-gui/linien_gui/ui/spectroscopy_panel.py b/linien-gui/linien_gui/ui/spectroscopy_panel.py
index b4c76aa3..0fffee3c 100644
--- a/linien-gui/linien_gui/ui/spectroscopy_panel.py
+++ b/linien-gui/linien_gui/ui/spectroscopy_panel.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/spin_box.py b/linien-gui/linien_gui/ui/spin_box.py
index f2884d9a..91747ecd 100644
--- a/linien-gui/linien_gui/ui/spin_box.py
+++ b/linien-gui/linien_gui/ui/spin_box.py
@@ -1,6 +1,6 @@
-# Copyright 2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
+# This file is part of Linien and based on redpid.
 #
-# This file is part of Linien.
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
 #
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
diff --git a/linien-gui/linien_gui/ui/sweep_control.py b/linien-gui/linien_gui/ui/sweep_control.py
index 61f41122..7d2bb2d4 100644
--- a/linien-gui/linien_gui/ui/sweep_control.py
+++ b/linien-gui/linien_gui/ui/sweep_control.py
@@ -1,7 +1,7 @@
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/version_checker.py b/linien-gui/linien_gui/ui/version_checker.py
index fa4bfa7d..8433e115 100644
--- a/linien-gui/linien_gui/ui/version_checker.py
+++ b/linien-gui/linien_gui/ui/version_checker.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/linien_gui/ui/view_panel.py b/linien-gui/linien_gui/ui/view_panel.py
index d39d3857..beca4743 100644
--- a/linien-gui/linien_gui/ui/view_panel.py
+++ b/linien-gui/linien_gui/ui/view_panel.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -28,21 +27,21 @@
 
 
 class ViewPanel(QtWidgets.QWidget):
-    plot_line_width: CustomDoubleSpinBoxNoSign
-    plot_line_opacity: CustomSpinBox
-    plot_fill_opacity: CustomSpinBox
-    display_color_0: QtWidgets.QLabel
-    display_color_1: QtWidgets.QLabel
-    display_color_2: QtWidgets.QLabel
-    display_color_3: QtWidgets.QLabel
-    display_color_4: QtWidgets.QLabel
-    edit_color_0: QtWidgets.QToolButton
-    edit_color_1: QtWidgets.QToolButton
-    edit_color_2: QtWidgets.QToolButton
-    edit_color_3: QtWidgets.QToolButton
-    edit_color_4: QtWidgets.QToolButton
-    export_data: QtWidgets.QPushButton
-    export_select_file: QtWidgets.QPushButton
+    plotLineWidthSpinBox: CustomDoubleSpinBoxNoSign
+    plotLineOpacitySpinBox: CustomSpinBox
+    plotFillOpacitySpinBox: CustomSpinBox
+    displayColorLabel0: QtWidgets.QLabel
+    displayColorLabel1: QtWidgets.QLabel
+    displayColorLabel2: QtWidgets.QLabel
+    displayColorLabel3: QtWidgets.QLabel
+    displayColorLabel4: QtWidgets.QLabel
+    editColorButton0: QtWidgets.QToolButton
+    editColorButton1: QtWidgets.QToolButton
+    editColorButton2: QtWidgets.QToolButton
+    editColorButton3: QtWidgets.QToolButton
+    editColorButton4: QtWidgets.QToolButton
+    exportDataPushButton: QtWidgets.QPushButton
+    exportSelectFilePushButton: QtWidgets.QPushButton
 
     def __init__(self, *args, **kwargs):
         super(ViewPanel, self).__init__(*args, **kwargs)
@@ -50,20 +49,24 @@ def __init__(self, *args, **kwargs):
         self.app = get_linien_app_instance()
         self.app.connection_established.connect(self.on_connection_established)
 
-        self.export_select_file.clicked.connect(self.do_export_select_file)
-        self.export_data.clicked.connect(self.do_export_data)
+        self.exportSelectFilePushButton.clicked.connect(self.export_select_file)
+        self.exportDataPushButton.clicked.connect(self.export_data)
 
-        self.plot_line_width.setKeyboardTracking(False)
-        self.plot_line_width.valueChanged.connect(self.plot_line_width_changed)
+        self.plotLineWidthSpinBox.setKeyboardTracking(False)
+        self.plotLineWidthSpinBox.valueChanged.connect(self.on_plot_line_width_changed)
 
-        self.plot_line_opacity.setKeyboardTracking(False)
-        self.plot_line_opacity.valueChanged.connect(self.plot_line_opacity_changed)
+        self.plotLineOpacitySpinBox.setKeyboardTracking(False)
+        self.plotLineOpacitySpinBox.valueChanged.connect(
+            self.on_plot_line_opacity_changed
+        )
 
-        self.plot_fill_opacity.setKeyboardTracking(False)
-        self.plot_fill_opacity.valueChanged.connect(self.plot_fill_opacity_changed)
+        self.plotFillOpacitySpinBox.setKeyboardTracking(False)
+        self.plotFillOpacitySpinBox.valueChanged.connect(
+            self.on_plot_fill_opacity_changed
+        )
 
         for color_idx in range(N_COLORS):
-            getattr(self, f"edit_color_{color_idx}").clicked.connect(
+            getattr(self, f"editColorButton{color_idx}").clicked.connect(
                 lambda *args, color_idx=color_idx: self.edit_color(color_idx)
             )
 
@@ -77,13 +80,13 @@ def on_connection_established(self):
         self.parameters = self.app.parameters
         self.control = self.app.control
 
-        param2ui(self.app.settings.plot_line_width, self.plot_line_width)
-        param2ui(self.app.settings.plot_line_opacity, self.plot_line_opacity)
-        param2ui(self.app.settings.plot_fill_opacity, self.plot_fill_opacity)
+        param2ui(self.app.settings.plot_line_width, self.plotLineWidthSpinBox)
+        param2ui(self.app.settings.plot_line_opacity, self.plotLineOpacitySpinBox)
+        param2ui(self.app.settings.plot_fill_opacity, self.plotFillOpacitySpinBox)
 
         def preview_colors(*args):
             for color_idx in range(N_COLORS):
-                element = getattr(self, f"display_color_{color_idx}")
+                element = getattr(self, f"displayColorLabel{color_idx}")
                 setting = getattr(self.app.settings, f"plot_color_{color_idx}")
                 element.setStyleSheet(
                     f"background-color: {color_to_hex(setting.value)}"
@@ -94,16 +97,16 @@ def preview_colors(*args):
                 preview_colors
             )
 
-    def plot_line_width_changed(self):
-        self.app.settings.plot_line_width.value = self.plot_line_width.value()
+    def on_plot_line_width_changed(self):
+        self.app.settings.plot_line_width.value = self.plotLineWidthSpinBox.value()
 
-    def plot_line_opacity_changed(self):
-        self.app.settings.plot_line_opacity.value = self.plot_line_opacity.value()
+    def on_plot_line_opacity_changed(self):
+        self.app.settings.plot_line_opacity.value = self.plotLineOpacitySpinBox.value()
 
-    def plot_fill_opacity_changed(self):
-        self.app.settings.plot_fill_opacity.value = self.plot_fill_opacity.value()
+    def on_plot_fill_opacity_changed(self):
+        self.app.settings.plot_fill_opacity.value = self.plotFillOpacitySpinBox.value()
 
-    def do_export_select_file(self):
+    def export_select_file(self):
         options = QtWidgets.QFileDialog.Options()
         # options |= QtWidgets.QFileDialog.DontUseNativeDialog
         default_ext = ".json"
@@ -118,10 +121,12 @@ def do_export_select_file(self):
             if not fn.endswith(default_ext):
                 fn = fn + default_ext
             self.export_fn = fn
-            self.export_select_file.setText(f"File selected: {path.split(fn)[-1]}")
-            self.export_data.setEnabled(True)
+            self.exportSelectFilePushButton.setText(
+                f"File selected: {path.split(fn)[-1]}"
+            )
+            self.exportDataPushButton.setEnabled(True)
 
-    def do_export_data(self):
+    def export_data(self):
         fn = self.export_fn
         counter = 0
 
diff --git a/linien-gui/linien_gui/ui/view_panel.ui b/linien-gui/linien_gui/ui/view_panel.ui
index 29e70899..94ed2414 100644
--- a/linien-gui/linien_gui/ui/view_panel.ui
+++ b/linien-gui/linien_gui/ui/view_panel.ui
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>400</width>
+    <width>399</width>
     <height>672</height>
    </rect>
   </property>
@@ -15,7 +15,7 @@
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
-    <widget class="QGroupBox" name="groupBox_10">
+    <widget class="QGroupBox" name="scalingLayout">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
        <horstretch>0</horstretch>
@@ -27,7 +27,7 @@
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_14">
       <item>
-       <widget class="QLabel" name="label_7">
+       <widget class="QLabel" name="scalingLabel">
         <property name="text">
          <string>Just pan (zoom) the plot with your mouse (wheel). Click &quot;Reset View&quot; button for automatic scaling. Right click on plot to set up fixed scaling.</string>
         </property>
@@ -40,13 +40,13 @@
     </widget>
    </item>
    <item>
-    <widget class="QGroupBox" name="groupBox">
+    <widget class="QGroupBox" name="graphGroupBox">
      <property name="title">
       <string>Graph</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_2">
       <item>
-       <layout class="QHBoxLayout" name="horizontalLayout">
+       <layout class="QHBoxLayout" name="plotLineWidthLayout">
         <item>
          <widget class="QLabel" name="label">
           <property name="text">
@@ -55,7 +55,7 @@
          </widget>
         </item>
         <item>
-         <widget class="CustomDoubleSpinBoxNoSign" name="plot_line_width">
+         <widget class="CustomDoubleSpinBoxNoSign" name="plotLineWidthSpinBox">
           <property name="decimals">
            <number>1</number>
           </property>
@@ -76,7 +76,7 @@
        </layout>
       </item>
       <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_6">
+       <layout class="QHBoxLayout" name="plotLineOpacityLayout">
         <item>
          <widget class="QLabel" name="label_2">
           <property name="text">
@@ -85,7 +85,7 @@
          </widget>
         </item>
         <item>
-         <widget class="CustomSpinBox" name="plot_line_opacity">
+         <widget class="CustomSpinBox" name="plotLineOpacitySpinBox">
           <property name="maximum">
            <number>255</number>
           </property>
@@ -97,7 +97,7 @@
        </layout>
       </item>
       <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_7">
+       <layout class="QHBoxLayout" name="fillOpacityLayout">
         <item>
          <widget class="QLabel" name="label_5">
           <property name="text">
@@ -106,7 +106,7 @@
          </widget>
         </item>
         <item>
-         <widget class="CustomSpinBox" name="plot_fill_opacity">
+         <widget class="CustomSpinBox" name="plotFillOpacitySpinBox">
           <property name="maximum">
            <number>255</number>
           </property>
@@ -118,16 +118,16 @@
        </layout>
       </item>
       <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_4">
+       <layout class="QHBoxLayout" name="displayColorLayout0">
         <item>
-         <widget class="QLabel" name="label_6">
+         <widget class="QLabel" name="colorLabel0">
           <property name="text">
-           <string>Color 1</string>
+           <string>(Combined) Error Signal</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QLabel" name="display_color_0">
+         <widget class="QLabel" name="displayColorLabel0">
           <property name="maximumSize">
            <size>
             <width>50</width>
@@ -140,7 +140,7 @@
          </widget>
         </item>
         <item>
-         <widget class="QToolButton" name="edit_color_0">
+         <widget class="QToolButton" name="editColorButton0">
           <property name="text">
            <string>...</string>
           </property>
@@ -152,16 +152,16 @@
        </layout>
       </item>
       <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_3">
+       <layout class="QHBoxLayout" name="displayColorLayout1">
         <item>
-         <widget class="QLabel" name="label_4">
+         <widget class="QLabel" name="colorLabel1">
           <property name="text">
-           <string>Color 2</string>
+           <string>Slow Control History</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QLabel" name="display_color_1">
+         <widget class="QLabel" name="displayColorLabel1">
           <property name="maximumSize">
            <size>
             <width>50</width>
@@ -174,7 +174,7 @@
          </widget>
         </item>
         <item>
-         <widget class="QToolButton" name="edit_color_1">
+         <widget class="QToolButton" name="editColorButton1">
           <property name="text">
            <string>...</string>
           </property>
@@ -183,16 +183,16 @@
        </layout>
       </item>
       <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <layout class="QHBoxLayout" name="displayColorLayout2">
         <item>
-         <widget class="QLabel" name="label_3">
+         <widget class="QLabel" name="colorLabel2">
           <property name="text">
-           <string>Color 3</string>
+           <string>Monitor Signal</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QLabel" name="display_color_2">
+         <widget class="QLabel" name="displayColorLabel2">
           <property name="maximumSize">
            <size>
             <width>50</width>
@@ -205,7 +205,7 @@
          </widget>
         </item>
         <item>
-         <widget class="QToolButton" name="edit_color_2">
+         <widget class="QToolButton" name="editColorButton2">
           <property name="text">
            <string>...</string>
           </property>
@@ -214,16 +214,16 @@
        </layout>
       </item>
       <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_5">
+       <layout class="QHBoxLayout" name="displayColorLayout3">
         <item>
-         <widget class="QLabel" name="color_3">
+         <widget class="QLabel" name="colorLabel3">
           <property name="text">
-           <string>Color 4</string>
+           <string>Control Signal</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QLabel" name="display_color_3">
+         <widget class="QLabel" name="displayColorLabel3">
           <property name="maximumSize">
            <size>
             <width>50</width>
@@ -236,7 +236,7 @@
          </widget>
         </item>
         <item>
-         <widget class="QToolButton" name="edit_color_3">
+         <widget class="QToolButton" name="editColorButton3">
           <property name="text">
            <string>...</string>
           </property>
@@ -245,16 +245,16 @@
        </layout>
       </item>
       <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_8">
+       <layout class="QHBoxLayout" name="displayColorLayout4">
         <item>
-         <widget class="QLabel" name="color_4">
+         <widget class="QLabel" name="colorLabel4">
           <property name="text">
-           <string>Color 5</string>
+           <string>Error Signal 1</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QLabel" name="display_color_4">
+         <widget class="QLabel" name="displayColorLabel4">
           <property name="maximumSize">
            <size>
             <width>50</width>
@@ -267,7 +267,100 @@
          </widget>
         </item>
         <item>
-         <widget class="QToolButton" name="edit_color_4">
+         <widget class="QToolButton" name="editColorButton4">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="displayColorLayout5">
+        <item>
+         <widget class="QLabel" name="colorLabel5">
+          <property name="text">
+           <string>Control Signal History</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="displayColorLabel5">
+          <property name="maximumSize">
+           <size>
+            <width>50</width>
+            <height>16777215</height>
+           </size>
+          </property>
+          <property name="text">
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QToolButton" name="editColorButton5">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="displayColorLayout6">
+        <item>
+         <widget class="QLabel" name="colorLabel6">
+          <property name="text">
+           <string>Error Signal 2</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="displayColorLabel6">
+          <property name="maximumSize">
+           <size>
+            <width>50</width>
+            <height>16777215</height>
+           </size>
+          </property>
+          <property name="text">
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QToolButton" name="editColorButton6">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="displayColorLayout7">
+        <item>
+         <widget class="QLabel" name="colorLabel7">
+          <property name="text">
+           <string>Monitor Signal History</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="displayColorLabel7">
+          <property name="maximumSize">
+           <size>
+            <width>50</width>
+            <height>16777215</height>
+           </size>
+          </property>
+          <property name="text">
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QToolButton" name="editColorButton7">
           <property name="text">
            <string>...</string>
           </property>
@@ -291,7 +384,7 @@
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_18">
       <item>
-       <widget class="QPushButton" name="export_select_file">
+       <widget class="QPushButton" name="exportSelectFilePushButton">
         <property name="text">
          <string>Select file</string>
         </property>
@@ -311,7 +404,7 @@
        </widget>
       </item>
       <item>
-       <widget class="QPushButton" name="export_data">
+       <widget class="QPushButton" name="exportDataPushButton">
         <property name="enabled">
          <bool>false</bool>
         </property>
diff --git a/linien-gui/linien_gui/utils.py b/linien-gui/linien_gui/utils.py
index 1e4bd360..37224d29 100644
--- a/linien-gui/linien_gui/utils.py
+++ b/linien-gui/linien_gui/utils.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-gui/pyproject.toml b/linien-gui/pyproject.toml
index f93b0e8b..ee60c071 100644
--- a/linien-gui/pyproject.toml
+++ b/linien-gui/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "linien-gui"
-version = "2.0.4"
+version = "2.1.0"
 authors = [
     { name = "Benjamin Wiegand", email = "benjamin.wiegand@physik.hu-berlin.de" },
     { name = "Bastian Leykauf", email = "leykauf@physik.hu-berlin.de" },
@@ -28,7 +28,7 @@ dependencies = [
     "PyQt5>=5.12.0,<6.0",
     "requests>=2.31.0,<3.0",
     "superqt>=0.2.3",
-    "linien_client==2.0.4",
+    "linien_client==2.1.0",
 ]
 
 [project.readme]
diff --git a/linien-server/linien_server/__init__.py b/linien-server/linien_server/__init__.py
index a12da2ee..b578ef67 100644
--- a/linien-server/linien_server/__init__.py
+++ b/linien-server/linien_server/__init__.py
@@ -1,3 +1,20 @@
+# This file is part of Linien and based on redpid.
+#
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
+# Linien is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Linien is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Linien.  If not, see <http://www.gnu.org/licenses/>.
+
 import logging
 from logging.handlers import RotatingFileHandler
 
diff --git a/linien-server/linien_server/acquisition.py b/linien-server/linien_server/acquisition.py
index 39a6921a..8f1bb6ac 100644
--- a/linien-server/linien_server/acquisition.py
+++ b/linien-server/linien_server/acquisition.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2023 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/autolock/algorithm_selection.py b/linien-server/linien_server/autolock/algorithm_selection.py
index 54884f89..17f438f9 100644
--- a/linien-server/linien_server/autolock/algorithm_selection.py
+++ b/linien-server/linien_server/autolock/algorithm_selection.py
@@ -1,7 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/autolock/autolock.py b/linien-server/linien_server/autolock/autolock.py
index feecf4d7..07507cd8 100644
--- a/linien-server/linien_server/autolock/autolock.py
+++ b/linien-server/linien_server/autolock/autolock.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/autolock/robust.py b/linien-server/linien_server/autolock/robust.py
index faaf8f9a..67cd0bcf 100644
--- a/linien-server/linien_server/autolock/robust.py
+++ b/linien-server/linien_server/autolock/robust.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/autolock/simple.py b/linien-server/linien_server/autolock/simple.py
index 11d8eb96..efac90d6 100644
--- a/linien-server/linien_server/autolock/simple.py
+++ b/linien-server/linien_server/autolock/simple.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/autolock/utils.py b/linien-server/linien_server/autolock/utils.py
index 3d788256..eab8a410 100644
--- a/linien-server/linien_server/autolock/utils.py
+++ b/linien-server/linien_server/autolock/utils.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/cli.py b/linien-server/linien_server/cli.py
index b7a53bde..98539b25 100644
--- a/linien-server/linien_server/cli.py
+++ b/linien-server/linien_server/cli.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/csr.py b/linien-server/linien_server/csr.py
index 8fbab436..ce49f996 100644
--- a/linien-server/linien_server/csr.py
+++ b/linien-server/linien_server/csr.py
@@ -1,9 +1,7 @@
-# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/iir_coeffs.py b/linien-server/linien_server/iir_coeffs.py
index 8d48cdc8..f6bcc96c 100644
--- a/linien-server/linien_server/iir_coeffs.py
+++ b/linien-server/linien_server/iir_coeffs.py
@@ -1,8 +1,6 @@
-# Copyright 2014-2015 Robert Jordens <jordens@gmail.com>
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
+# This file is part of Linien and based on redpid.
 #
-# This file is part of Linien.
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
 #
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
diff --git a/linien-server/linien_server/influxdb.py b/linien-server/linien_server/influxdb.py
index e15be813..6b633d4b 100644
--- a/linien-server/linien_server/influxdb.py
+++ b/linien-server/linien_server/influxdb.py
@@ -1,7 +1,7 @@
-# Copyright 2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/mdio_tool.py b/linien-server/linien_server/mdio_tool.py
index 2b96e938..6f2cf0f9 100644
--- a/linien-server/linien_server/mdio_tool.py
+++ b/linien-server/linien_server/mdio_tool.py
@@ -1,7 +1,7 @@
-# Copyright 2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/noise_analysis.py b/linien-server/linien_server/noise_analysis.py
index 1dff054a..acf91ddc 100644
--- a/linien-server/linien_server/noise_analysis.py
+++ b/linien-server/linien_server/noise_analysis.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/optimization/approach_line.py b/linien-server/linien_server/optimization/approach_line.py
index e6d25735..5f9c3ca1 100644
--- a/linien-server/linien_server/optimization/approach_line.py
+++ b/linien-server/linien_server/optimization/approach_line.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/optimization/engine.py b/linien-server/linien_server/optimization/engine.py
index 22d587e8..a7006094 100644
--- a/linien-server/linien_server/optimization/engine.py
+++ b/linien-server/linien_server/optimization/engine.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/optimization/general.py b/linien-server/linien_server/optimization/general.py
index 1a49266c..c2de8859 100644
--- a/linien-server/linien_server/optimization/general.py
+++ b/linien-server/linien_server/optimization/general.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/optimization/optimization.py b/linien-server/linien_server/optimization/optimization.py
index 392de73f..a42e837d 100644
--- a/linien-server/linien_server/optimization/optimization.py
+++ b/linien-server/linien_server/optimization/optimization.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/optimization/utils.py b/linien-server/linien_server/optimization/utils.py
index 5f0d7c98..7bdc071f 100644
--- a/linien-server/linien_server/optimization/utils.py
+++ b/linien-server/linien_server/optimization/utils.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/parameters.py b/linien-server/linien_server/parameters.py
index 4364b54f..12f16ed6 100644
--- a/linien-server/linien_server/parameters.py
+++ b/linien-server/linien_server/parameters.py
@@ -1,9 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-# Copyright 2023 Christian Freier <christian.freier@nomadatomics.com>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -188,13 +186,13 @@ def __init__(self):
 
         self.gpio_p_out = Parameter(start=0, min_=0, max_=0b11111111)
         """
-        set the output of GPIO pins. Each bit corresponds to one pin, i.e.
+        Set the output of GPIO pins. Each bit corresponds to one pin, i.e.
         `parameters.gpio_p_out.value = 0b11110000` turns on the first 4 pins and turns
         off the other ones.
         """
         self.gpio_n_out = Parameter(start=0, min_=0, max_=0b11111111)
         """
-        set the output of GPIO pins. Each bit corresponds to one pin, i.e.
+        Set the output of GPIO pins. Each bit corresponds to one pin, i.e.
         `parameters.gpio_p_out.value = 0b11110000` turns on the first 4 pins and turns
         off the other ones.
         """
@@ -204,25 +202,27 @@ def __init__(self):
             start=0, min_=0, max_=(2**15) - 1, restorable=True
         )
         """
-        parameters for setting ANALOG_OUT 1 voltage.
-        Usage: `parameters.analog_out_1.value = 1.2 * ANALOG_OUT_V`
-        Minimum value is 0 and maximum 1.8 * ANALOG_OUT_V
+        Set output for Analog OUT 1.
+        Usage: `parameters.analog_out_1.value = 1.2 / ANALOG_OUT_V`. Minimum value is 0
+        and maximum 1.8 / ANALOG_OUT_V
         """
+
         self.analog_out_2 = Parameter(
             start=0, min_=0, max_=(2**15) - 1, restorable=True
         )
         """
-        parameters for setting ANALOG_OUT 1 voltage.
-        Usage: `parameters.analog_out_2.value = 1.2 * ANALOG_OUT_V`
-        Minimum value is 0 and maximum 1.8 * ANALOG_OUT_V
+        Set output for Analog OUT 2.
+        Usage: `parameters.analog_out_1.value = 1.2 / ANALOG_OUT_V`. Minimum value is 0
+        and maximum 1.8 / ANALOG_OUT_V
         """
+
         self.analog_out_3 = Parameter(
             start=0, min_=0, max_=(2**15) - 1, restorable=True
         )
         """
-        parameters for setting ANALOG_OUT 1 voltage.
-        Usage: `parameters.analog_out_3.value = 1.2 * ANALOG_OUT_V`
-        Minimum value is 0 and maximum 1.8 * ANALOG_OUT_V
+        Set output for Analog OUT 3.
+        Usage: `parameters.analog_out_1.value = 1.2 / ANALOG_OUT_V`. Minimum value is 0
+        and maximum 1.8 / ANALOG_OUT_V
         """
 
         self.lock = Parameter(start=False, loggable=True)
@@ -285,7 +285,7 @@ def __init__(self):
         self.sweep_amplitude = Parameter(min_=0.001, max_=1, start=1, loggable=True)
         """
         Amplitude of the sweep in units of 0.5 * Vpp of the output (2 V for fast outputs
-        (range +/- 1 V) and 0.9 V for slow outputs (range 0 V to 1.8 V). That means an
+        (range +/- 1 V) and 0.9 V for slow outputs (range 0 V to 1.8 V)). That means an
         amplitude of 1.0 corresponds to the full sweep range in both cases.
         """
 
@@ -297,12 +297,12 @@ def __init__(self):
         """
 
         self.sweep_speed = Parameter(
-            min_=0, max_=32, start=8, restorable=True, loggable=True
+            min_=0, max_=15, start=8, restorable=True, loggable=True
         )
         """
         The sweep speed in internal units. The actual speed is given by
         f_real = 3.8 kHz / (2 ** sweep_speed)
-        Allowed values are [0, ..., 16]
+        Allowed values are [0, ..., 15]
         """
 
         self.sweep_pause = Parameter(start=False, loggable=True)
@@ -585,7 +585,7 @@ def __init__(self):
 
         self.psd_data_partial = Parameter(start=None)
         self.psd_data_complete = Parameter(start=None)
-        self.psd_algorithm = Parameter(start=PSDAlgorithm.LPSD)
+        self.psd_algorithm = Parameter(start=PSDAlgorithm.LPSD, restorable=True)
         self.psd_acquisition_running = Parameter(start=False)
         self.psd_optimization_running = Parameter(start=False)
         self.psd_acquisition_max_decimation = Parameter(
diff --git a/linien-server/linien_server/registers.py b/linien-server/linien_server/registers.py
index d29f8cb3..365c2540 100644
--- a/linien-server/linien_server/registers.py
+++ b/linien-server/linien_server/registers.py
@@ -1,9 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-# Copyright 2022 Christian Freier <christian.freier@nomadatomics.com>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/linien_server/server.py b/linien-server/linien_server/server.py
index f2729c2b..99a2cc30 100644
--- a/linien-server/linien_server/server.py
+++ b/linien-server/linien_server/server.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/linien-server/pyproject.toml b/linien-server/pyproject.toml
index d6ce8d9b..fa1bedee 100644
--- a/linien-server/pyproject.toml
+++ b/linien-server/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "linien-server"
-version = "2.0.4"
+version = "2.1.0"
 authors = [
     { name = "Benjamin Wiegand", email = "benjamin.wiegand@physik.hu-berlin.de" },
     { name = "Bastian Leykauf", email = "leykauf@physik.hu-berlin.de" },
@@ -27,8 +27,8 @@ dependencies = [
     "fire>=0.6.0",
     "influxdb-client[ciso]>=1.9,<2.0",
     "pylpsd>=0.1.4",
-    "pyrp3>=2.0.1,<3.0;platform_machine=='armv7l'",
-    "linien-common==2.0.4",
+    "pyrp3>=2.1.0,<3.0;platform_machine=='armv7l'",
+    "linien-common==2.1.0",
 ]
 
 [project.readme]


Test patch:
diff --git a/tests/test_algorithm_selection.py b/tests/test_algorithm_selection.py
index 4dc595ab..5e2fdf62 100644
--- a/tests/test_algorithm_selection.py
+++ b/tests/test_algorithm_selection.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_approacher.py b/tests/test_approacher.py
index 1aef3eca..b5a6d68c 100644
--- a/tests/test_approacher.py
+++ b/tests/test_approacher.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_common.py b/tests/test_common.py
index a7e4e1ce..50acdee9 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_delta_sigma.py b/tests/test_delta_sigma.py
index 8f9623fd..f648ae01 100644
--- a/tests/test_delta_sigma.py
+++ b/tests/test_delta_sigma.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_limit.py b/tests/test_limit.py
index 9d38b372..7194ffa8 100644
--- a/tests/test_limit.py
+++ b/tests/test_limit.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_modulate.py b/tests/test_modulate.py
index ee8ff29f..9349811d 100644
--- a/tests/test_modulate.py
+++ b/tests/test_modulate.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_optimizer_engines.py b/tests/test_optimizer_engines.py
index fa7a890b..e1940017 100644
--- a/tests/test_optimizer_engines.py
+++ b/tests/test_optimizer_engines.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_optimizer_utils.py b/tests/test_optimizer_utils.py
index 591910c8..677e0286 100644
--- a/tests/test_optimizer_utils.py
+++ b/tests/test_optimizer_utils.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_pid.py b/tests/test_pid.py
index ed745aba..4ac2709d 100644
--- a/tests/test_pid.py
+++ b/tests/test_pid.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_pid_transfer.py b/tests/test_pid_transfer.py
index b5f8facd..cb3e0747 100644
--- a/tests/test_pid_transfer.py
+++ b/tests/test_pid_transfer.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_robust_autolock.py b/tests/test_robust_autolock.py
index 5d003242..389f8983 100644
--- a/tests/test_robust_autolock.py
+++ b/tests/test_robust_autolock.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_simple_autolock_cpu.py b/tests/test_simple_autolock_cpu.py
index 681f661b..6928f0fb 100644
--- a/tests/test_simple_autolock_cpu.py
+++ b/tests/test_simple_autolock_cpu.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_simple_autolock_fpga.py b/tests/test_simple_autolock_fpga.py
index c9acc965..b1c55d29 100644
--- a/tests/test_simple_autolock_fpga.py
+++ b/tests/test_simple_autolock_fpga.py
@@ -1,8 +1,7 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
-#
 # This file is part of Linien and based on redpid.
 #
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+#
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
diff --git a/tests/test_sweep.py b/tests/test_sweep.py
index bf5f3ced..b39d0667 100644
--- a/tests/test_sweep.py
+++ b/tests/test_sweep.py
@@ -1,7 +1,6 @@
-# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
-# Copyright 2021-2022 Bastian Leykauf <leykauf@physik.hu-berlin.de>
+# This file is part of Linien and based on redpid.#
 #
-# This file is part of Linien and based on redpid.
+# Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
 #
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by