diff --git a/APSCT_CLK_I2C.py b/APSCT_CLK_I2C.py
index 1465aa7f3f0f03d773174fb7496fdf19b0292ef1..99309c6c99b0140bf4499697e2c9cc513bf45d57 100644
--- a/APSCT_CLK_I2C.py
+++ b/APSCT_CLK_I2C.py
@@ -26,7 +26,7 @@ else:
 DEBUG = False
 I2CBUSNR=5
 sleep_time = 0.15
-SET_PLL = False #True
+SET_PLL = True
 READ_LOCK = True
 READ_ALL = False
 CHECK_EEPROM = False
diff --git a/APSCT_I2C.py b/APSCT_I2C.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e10741eadd811dbd97ea5c2d3668a9104c9c918
--- /dev/null
+++ b/APSCT_I2C.py
@@ -0,0 +1,74 @@
+"""
+Copyright 2022 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
+ASTRON Netherlands Institute for Radio Astronomy
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+ Created: 2022-12-07
+This file contains the definitions of the APSPU registers etc.
+
+
+"""
+#
+# I2C address, registers and ports for APSCT
+# Created: 2023-01-06
+#
+
+#
+# Power supplies
+#
+
+PWR_LOCATIONS = {"INPUT":  0,
+                 "PLL_160M": 1,
+                 "PLL_200M": 2,
+                 "DIST_A": 3,
+                 "DIST_B": 4,
+                 "DIST_C": 5,
+                 "DIST_D": 6,
+                 "CTRL": 7}
+
+PWR_VOUT = {"INPUT":  3.3,
+            "PLL_160M": 3.3,
+            "PLL_200M": 3.3,
+            "DIST_A": 3.3,
+            "DIST_B": 3.3,
+            "DIST_C": 3.3,
+            "DIST_D": 3.3,
+            "CTRL": 3.3}
+
+#
+# PLL constants / pin locations
+#
+CS = 6
+SCLK = 4
+SDO = 5
+SDI = 7
+
+PLL_200M = 0x20
+PLL_160M = 0x21
+
+#
+# Central I2C Devices
+#
+EEPROM = 0x50
+
+#
+# I2C switch addresses
+#
+i2c_switch_addr_rcu = [0x70, 0x71, 0x72, 0x73]
+i2c_bus_rcu = 1
+i2c_switch_addr_unb = [0x70]
+i2c_bus_unb = 3
+
+#
+# ID Pins 
+#
+ID_PINS = [8, 7, 12, 16, 20, 21]
diff --git a/APSPU_I2C.py b/APSPU_I2C.py
index 60a47c53b4cfdd1e5b7e8401e0052ebe5b120ef2..ca6f97e258fae7fc30adf186a1893b04c53ac4b5 100644
--- a/APSPU_I2C.py
+++ b/APSPU_I2C.py
@@ -1,90 +1,117 @@
-#******************************************#
-# I2C address, registers and ports for UNB2c
-# Created: 2021-05-11
-# Last update 2023-01-27 for L2TS production series
-#******************************************#
-
-###################################
+"""
+Copyright 2022 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
+ASTRON Netherlands Institute for Radio Astronomy
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+ Created: 2022-12-07
+This file contains the definitions of the APSPU registers etc.
+
+
+"""
+#
+# I2C address, registers and ports for APSPU
+# Created: 2022-12-07
+#
+
+#
 # General, Point of load converters
-###################################
+#
 
-CTR_LBA    = 0x3C
+CTR_LBA = 0x3C
 CTR_RCU2_A = 0x3D
 CTR_RCU2_D = 0x0E
-CTR_POLS = {"CTR_LBA" :  0x3C,
-            "CTR_RCU2_A" : 0x3D,
-            "CTR_RCU2_D" : 0x3E}
-
-VOUT_POLS = {"CTR_LBA" :  8.0,
-            "CTR_RCU2_A" : 5.60009765625,
-            "CTR_RCU2_D" : 3.2998046875}
-
-IOUT_POLS = {"CTR_LBA" :  0.45,
-            "CTR_RCU2_A" : 0.7,
-            "CTR_RCU2_D" : 0.3}
-
-LP_VIN             = 0x88 #
-LP_VOUT_MODE       = 0x20
-LP_VOUT            = 0x8B #
-LP_temp            = 0x8D #
-LP_IOUT            = 0x8C
-LP_VOUT_COMMAND    = 0x21
-LP_VOUT_TRIM       = 0x22
-LP_VOUT_MAX        = 0x24
+CTR_POLS = {"CTR_LBA":  0x3C,
+            "CTR_RCU2_A": 0x3D,
+            "CTR_RCU2_D": 0x3E}
+
+VOUT_POLS = {"CTR_LBA":  8.0,
+             "CTR_RCU2_A": 5.60009765625,
+             "CTR_RCU2_D": 3.2998046875}
+
+IOUT_POLS = {"CTR_LBA":  0.2,
+             "CTR_RCU2_A": 0.6,
+             "CTR_RCU2_D": 0.2}
+
+LP_VIN = 0x88
+LP_VOUT_MODE = 0x20
+LP_VOUT = 0x8B
+LP_temp = 0x8D
+LP_IOUT = 0x8C
+LP_VOUT_COMMAND = 0x21
+LP_VOUT_TRIM = 0x22
+LP_VOUT_MAX = 0x24
 LP_VOUT_SCALE_LOOP = 0x29
-LP_VOUT_SCALE_MON  = 0x2A
-LP_VOUT_OV_LIMIT   = 0x40
-LP_VOUT_UV_LIMIT   = 0x44
-LP_STORE_USER_ALL  = 0x15
-LP_ON_OFF_CONFIG   = 0x02
-LP_OPERATION       = 0x01
-
-###################################
+LP_VOUT_SCALE_MON = 0x2A
+LP_VOUT_OV_LIMIT = 0x40
+LP_VOUT_UV_LIMIT = 0x44
+LP_STORE_USER_ALL = 0x15
+LP_ON_OFF_CONFIG = 0x02
+LP_OPERATION = 0x01
+LP_WRITE_PROTECT = 0x10
+LP_STORE_DEFAULT_ALL = 0x12
+#
 # Central I2C Devices
-###################################
-EEPROM            = 0x50
+#
+EEPROM = 0x50
 
-###################################
+#
 # FAN speed
-###################################
-MAX6620          = 0x29
+#
+MAX6620 = 0x29
 
 REG_GLOBAL = 0x00
-REG_TACH_MSP_REGS = [ 0x10, 0x12, 0x14]
-REG_TACH_LSP_REGS = [ 0x11, 0x13, 0x15]
+REG_TACH_MSP_REGS = [0x10, 0x12, 0x14]
+REG_TACH_LSP_REGS = [0x11, 0x13, 0x15]
 TACH_PERIODS = 16
 TACH_COUNT_FREQ = 8192
 FAN_TACHS = 1
 
 RUN_MONITOR = 0x02
-
 NOF_APS_FANS = 3
 
-######################
+#
 # Functions
-######################
+#
+
 
-# Calculate floating point value according PMBus lineair
 def calc_lin_2bytes(data):
-    expo = ((data[1] & 0xf8)>>3) 
+    #
+    # Calculate floating point value according PMBus lineair
+    #
+    # input data (Byte array)
+    # return value (float)
+    #
+    expo = ((data[1] & 0xf8) >> 3)
     if expo == 1:
         expo = -7
     if expo > (2**4):
         expo = expo-2**5
     mantisse = (data[1] & 0x7)*0x100 + data[0]
     if mantisse > (2**(8+2)):
-        mantisse = mantisse -2**(8+3)
+        mantisse = mantisse - 2**(8 + 3)
     output = mantisse * 2**expo
     return output
 
-# Calculate floating point value according PMBus lineair
-def calc_lin_3bytes(data,mode):
-    expo = (mode[0] & 0x1F) 
+
+def calc_lin_3bytes(data, mode):
+    #
+    # Calculate floating point value according PMBus lineair
+    #
+    # input data (Byte array)
+    # return value (float)
+    #
+    expo = (mode[0] & 0x1F)
     if expo > 2**4:
         expo = expo - 2**5
     mant = (data[1]*256 + data[0]) 
     output = mant * 2**expo
     return output
-
-
-
diff --git a/I2C_serial_pi.py b/I2C_serial_pi.py
index 6aaa3f5e84e50025927e29e38035072ea98e9328..d4cc4cccb0550528e753cc89ef9c46d8d9fa1f1b 100644
--- a/I2C_serial_pi.py
+++ b/I2C_serial_pi.py
@@ -42,15 +42,20 @@ class I2C:
             ret_ack = 1
             if SLOW:
                 sleep(0.2)
-        except IOError, err:
+        except IOError:
             ret_ack = 0
             ret_value = 'ffff'
             if DEBUG:
                 print("Reading error")
+        except err:
+            ret_ack = 0
+            ret_value = 'ffff'
+            if DEBUG:
+                print("Reading IO-error")
         return ret_ack, ret_value
 
     
-    def read_last_reg(self, bytes_to_read):
+    def read_last_reg(self, bytes_to_read, print_on = DEBUG):
         bus = smbus.SMBus(self.bus_nr)
         rd_value = []
         ret_value = ''
@@ -60,10 +65,15 @@ class I2C:
                 ret_ack = 1
                 if SLOW:
                     sleep(0.2)
-            except IOError, err:
+            except IOError:
+                ret_ack = 0
+                rd_value.append(0)
+                if print_on:
+                    print(f"Reading IOerror {rd_value}")
+            except err:
                 ret_ack = 0
                 rd_value.append(0)
-                if DEBUG:
+                if print_on:
                     print("Reading error")
         for cnt in range(bytes_to_read):
             ret_value += (hex(rd_value[cnt])[2:])
@@ -71,16 +81,23 @@ class I2C:
   
     def write_bytes(self, register, data):
         bus = smbus.SMBus(self.bus_nr)
+        if type(data) is not list:
+            data = [data]
         try:
-            bus.write_i2c_block_data(self.I2C_Address, register, [data])
+            bus.write_i2c_block_data(self.I2C_Address, register, data)
             ret_ack = 1
             if SLOW:
                 sleep(0.3)
-        except IOError, err:
+        except IOError:
             ret_ack = 0
             ret_value = 0
             if DEBUG:
                 print("Write error")
+        except err:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("Write IO-error")
         return ret_ack
 
     def write_register(self, register):
@@ -90,11 +107,16 @@ class I2C:
             ret_ack = 1
             if SLOW:
                 sleep(0.3)
-        except IOError, err:
+        except IOError:
             ret_ack = 0
             ret_value = 0
             if DEBUG:
                 print("Write error")
+        except err:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("Write IO-error")
         return ret_ack
 
     def write_pointer(self, register):
@@ -104,11 +126,16 @@ class I2C:
             ret_ack = 1
             if SLOW:
                 sleep(0.3)
-        except IOError, err:
+        except IOError:
             ret_ack = 0
             ret_value = 0
             if DEBUG:
                 print("Write error")
+        except err:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("Write IO-error")
         return ret_ack
 
     def ack_check(self):
@@ -119,11 +146,16 @@ class I2C:
             ret_ack = 1
             if SLOW:
                 sleep(0.3)
-        except IOError, err:
+        except IOError:
             ret_ack = 0
             ret_value = 0
             if DEBUG:
                 print("No ACK")
+        except err:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("No ACK IO-Error")
         return ret_ack
 
 if __name__ == "__main__":
diff --git a/I2C_serial_pi2.py b/I2C_serial_pi2.py
index 5ae5af3976e9d28807f0eaa4b0f894a72ff1b8ec..4da3bf406cf0a3e554eeb2dc3232ff5b96b915d3 100644
--- a/I2C_serial_pi2.py
+++ b/I2C_serial_pi2.py
@@ -97,7 +97,8 @@ class I2C:
     def write_pointer(self, register):
         bus = pi.i2c_open(self.bus_nr, self.I2C_Address)
         try:
-            pi.i2c_read_device(bus, 1)
+#            pi.i2c_read_device(bus, 1)
+            pi.i2c_read_device(bus, register)
             ret_ack = 1
             if SLOW:
                 sleep(0.3)
@@ -109,6 +110,20 @@ class I2C:
         pi.i2c_close(bus)
         return ret_ack
 
+    def write_register(self, register):
+        bus = pi.i2c_open(self.bus_nr, self.I2C_Address)
+        try:
+            ret_value = pi.i2c_write_device(bus, [register])
+            ret_ack = 1
+            if SLOW:
+                sleep(0.3)
+        except IOError:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("No ACK")
+        return ret_ack
+
     def ack_check(self):
         bus = smbus.SMBus(self.bus_nr)
         try:
diff --git a/I2C_serial_pi3.py b/I2C_serial_pi3.py
new file mode 100644
index 0000000000000000000000000000000000000000..adfa428918bf52094dd9ea55c851782bc7ad8668
--- /dev/null
+++ b/I2C_serial_pi3.py
@@ -0,0 +1,146 @@
+'''
+Copyright 2021 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
+ASTRON Netherlands Institute for Radio Astronomy
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+I2C_serial_Pi
+Started by Gijs
+
+Class for using the I2C bus of the I2C. This class is used for the
+basic I2C scripts to read and write the RCU2, PCC etc.
+
+'''
+import smbus2
+import sys
+from time import *
+
+DEBUG = False #True
+SLOW = False
+
+
+class I2C:
+
+    def __init__(self, ADDRESS='040',BUSNR=3):
+        self.I2C_Address = ADDRESS
+        self.bus_nr = BUSNR
+
+    def close(self):
+        bus = smbus2.SMBus(self.bus_nr)
+        bus.close()
+        return True
+
+    def open(self):
+        bus = smbus2.SMBus(self.bus_nr)
+        bus.open(self.bus_nr)
+        return True
+
+    def read_bytes(self, register, bytes_to_read=2):
+        bus = smbus2.SMBus(self.bus_nr)
+        try:
+            rd_value = bus.read_i2c_block_data(self.I2C_Address, register, bytes_to_read)
+            ret_value = ''
+            for cnt in range(bytes_to_read):
+                ret_value += (hex(rd_value[cnt])[2:])
+            ret_ack = 1
+            if SLOW:
+                sleep(0.2)
+        except IOError:
+            ret_ack = 0
+            ret_value = 'ffff'
+            if DEBUG:
+                print("Reading IO-error")
+        return ret_ack, ret_value
+
+    
+    def read_last_reg(self, bytes_to_read):
+        bus = smbus2.SMBus(self.bus_nr)
+        rd_value = []
+        ret_value = ''
+        for cnt in range(bytes_to_read):
+            try:
+                rd_value.append(bus.read_byte(self.I2C_Address))
+                ret_ack = 1
+                if SLOW:
+                    sleep(0.2)
+            except IOError:
+                ret_ack = 0
+                rd_value.append(0)
+                if DEBUG:
+                    print("IO-Reading error")
+        for cnt in range(bytes_to_read):
+            ret_value += (hex(rd_value[cnt])[2:])
+        return ret_ack,ret_value
+  
+    def write_bytes(self, register, data):
+        bus = smbus2.SMBus(self.bus_nr)
+        if type(data) is not list:
+            data = [data]
+        try:
+            bus.write_i2c_block_data(self.I2C_Address, register, data)
+            ret_ack = 1
+            if SLOW:
+                sleep(0.3)
+        except IOError:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("Write IO-error")
+        return ret_ack
+
+    def write_register(self, register):
+        bus = smbus2.SMBus(self.bus_nr)
+        try:
+            bus.write_byte(self.I2C_Address, register)
+#            print(f"Wrote {register:x}")
+            ret_ack = 1
+            if SLOW:
+                sleep(0.3)
+        except IOError:
+            ret_ack = 0
+            ret_value = 0
+#            if DEBUG:
+            print("Write IO-error")
+        return ret_ack
+
+    def write_pointer(self, register):
+        bus = smbus2.SMBus(self.bus_nr)
+        try:
+            ret_value = bus.read_i2c_block_data(self.I2C_Address, register, 1)
+            ret_ack = 1
+            if SLOW:
+                sleep(0.3)
+        except IOError:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("Write IO-error")
+        return ret_ack
+
+    def ack_check(self):
+        bus = smbus2.SMBus(self.bus_nr)
+        try:
+            print("check ACK")
+            ret_value = bus.write_quick(self.I2C_Address)
+            ret_ack = 1
+            if SLOW:
+                sleep(0.3)
+        except IOError:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("No ACK IO-Error")
+        return ret_ack
+
+if __name__ == "__main__":
+    I2C_Device = I2C(0x40)
+    I2C_Device.write_bytes(0x00, 0x00)
+    ret_ack, ret_value = I2C_Device.read_bytes(0x8C, 2)
+    print(ret_value)
diff --git a/apsct_lib.py b/apsct_lib.py
new file mode 100644
index 0000000000000000000000000000000000000000..905bd98d3d0f75ac0d57d79da8f1fa5e2c288e6d
--- /dev/null
+++ b/apsct_lib.py
@@ -0,0 +1,605 @@
+"""
+Copyright 2021 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
+ASTRON Netherlands Institute for Radio Astronomy
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Set APSCT_CLK
+
+"""
+import sys
+import APSCT_I2C
+import time
+import RPi.GPIO as gpio
+import random
+sys.path.insert(0, '.')
+import os
+if os.name == "posix":
+    from I2C_serial_pi2 import *
+else:
+    from I2C_serial import *
+
+I2CBUSNR = 5
+sleep_time = 0.15
+DEBUG = False
+
+
+class ApsctClass:
+    #
+    # Toplevel class that contains all controllable parts of the APSCT
+    #
+    def __init__(self, frequency="200MHz"):
+        self.status = False
+        self.frequency = frequency
+        self.eeprom = EepromClass()
+        self.pll_200 = PllClass("200MHz")
+        self.pll_160 = PllClass("160MHz")
+        self.sensors = ApsctSensors()
+        self.pps = PpsClass()
+        self.i2cswitch = []
+        for addr in APSCT_I2C.i2c_switch_addr_rcu:
+            self.i2cswitch.append(I2cSwitch(address=addr, bus=APSCT_I2C.i2c_bus_rcu))
+        for addr in APSCT_I2C.i2c_switch_addr_unb:
+            self.i2cswitch.append(I2cSwitch(address=addr, bus=APSCT_I2C.i2c_bus_unb))
+        self.apsct_id = ApsctId()
+
+    def read_io_expanderis(self):
+        #
+        # Read both IO-Expander lines and prints status on the screen
+        #
+        i2c_addr = [0x20, 0x21]
+        for addr in i2c_addr:
+            i2c_device = I2C(addr, BUSNR=I2CBUSNR)  # clock selection
+            for reg_cnt in range(8):
+                ack, ret_value = i2c_device.read_bytes(reg_cnt, 2)
+                stri = "Expander : 0x{:0>2x}, Reg 0x{:0>2x}, value 0x{}{}".format(addr, reg_cnt, ret_value[0],
+                                                                                  ret_value[1])  # [start+2:start])
+                print(stri)
+
+    def power(self, state):
+        #
+        # Set power supply APSCT in the give state
+        #
+        # state is True: Power on
+        # state is False: Power off
+        #
+        stri = "Power to {}".format(state)
+        print(stri)
+        i2c_io_device_a = I2C(0x20, BUSNR=I2CBUSNR)
+        i2c_io_device_a.write_bytes(0x06, 0x2C)  # '0' is output, '1' is input
+        i2c_io_device_a.write_bytes(0x07, 0x00)  # '0' is output, '1' is input
+        i2c_io_device_b = I2C(0x21, BUSNR=I2CBUSNR)
+        i2c_io_device_b.write_bytes(0x06, 0x2C)  # '0' is output, '1' is input
+        if state:
+            bits_to_set_a1 = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI)
+            bits_to_set_a2 = 0x04
+            bits_to_set_b1 = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI)
+        else:
+            bits_to_set_a1 = 0x00 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI)
+            bits_to_set_a2 = 0x00
+            bits_to_set_b1 = 0x00 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI)
+        if DEBUG:
+            stri = "Bits to reg 0 0x{0:x}".format(bits_to_set_a1)
+            print(stri)
+        i2c_io_device_a.write_bytes(0x02, bits_to_set_a1)
+        i2c_io_device_a.write_bytes(0x03, bits_to_set_a2)
+        i2c_io_device_b.write_bytes(0x02, bits_to_set_b1)
+
+    def set_apsct(self):
+        #
+        # set APSCT to 200MHz, 160MHz or off
+        #
+        if self.frequency == "200MHz":
+            self.power(True)
+            self.pll_200.setup_pll()
+        elif self.frequency == "160MHz":
+            self.power(True)
+            self.pll_160.setup_pll()
+        else:
+            self.power(False)
+
+    def check_apsct(self):
+        #
+        # Check voltages, temperature and PLL-lock on APSCT
+        #
+        # Return Result, True when OK, False in case of an error
+        result = self.sensors.check_values()
+        result = result & self.pps.check_timing()
+        result = result & self.apsct_id.check_id()
+        for i2c_switch in self.i2cswitch:
+            result = result & i2c_switch.check_switch(data=random.randint(0, 2**8))
+        if self.frequency == "200MHz":
+            self.pll_200.read_lock()
+            lock = self.pll_200.lock
+            result = result & lock
+        elif self.frequency == "160MHz":
+            self.pll_160.read_lock()
+            lock = self.pll_160.lock
+            result = result & lock
+        else:
+            result = result
+        return result
+
+
+class PllClass:
+    #
+    # Toplevel class that contains all function to set the PLL
+    #
+    def __init__(self, frequency="200MHz"):
+        self.status = False
+        self.lock = False
+        self.frequency = frequency
+        if self.frequency == "160MHz":
+            self.i2c_address = APSCT_I2C.PLL_160M
+        else:
+            self.i2c_address = APSCT_I2C.PLL_200M
+        self.dev_i2c_pll = I2C(self.i2c_address, BUSNR=I2CBUSNR)
+
+    def write_byte_pll(self, reg_address, wr_data):
+        #
+        # Write Byte to the PLL
+        #
+        pll_rw = 0x00  # 0 for write, 1 for read
+        if DEBUG:
+            stri = "Write to address : 0x{1:{fill}2x} value  0x{0:{fill}2x}".format(wr_data, reg_address, fill='0')
+            print(stri)
+        self.dev_i2c_pll.write_bytes(0x06, 0x2C)
+        rd_bytes = self.dev_i2c_pll.read_bytes(0x06, 1)
+        if DEBUG:
+            stri = "IO expander wrote 0x{0:x}, read 0x{1}".format(0x2C, rd_bytes[1])
+            print(stri)
+        data = (reg_address << 9) + (pll_rw << 8) + wr_data
+        bit_array = "{0:{fill}16b}".format(data, fill='0')
+        self.dev_i2c_pll.write_bytes(0x02, 0x02 | (0x1 << APSCT_I2C.CS))
+        for bit in bit_array:
+            for clk in range(2):
+                write_data = 0x02 | (0 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK) | (int(bit) << APSCT_I2C.SDI)
+                self.dev_i2c_pll.write_bytes(0x02, write_data)
+        for clk in range(2):
+            write_data = 0x02 | (0 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK)
+            self.dev_i2c_pll.write_bytes(0x02, write_data)
+        for clk in range(2):
+            write_data = 0x02 | (1 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK)
+            self.dev_i2c_pll.write_bytes(0x02, write_data)
+
+        write_data = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI)
+        self.dev_i2c_pll.write_bytes(0x02, write_data)
+
+    def read_byte_pll(self, reg_address, nof_bytes=1):
+        #
+        # Read byte from the PLL
+        #
+        pll_rw = 0x01  # 0 for write, 1 for read
+        self.dev_i2c_pll.write_bytes(0x06, 0x2C)
+        data = (reg_address << 7) + pll_rw
+        bit_array = "{0:{fill}8b}".format(data, fill='0')
+        for bit in bit_array:
+            for clk in range(2):
+                write_data = 0x02 | (0 << APSCT_I2C.CS) | (clk << APSCT_I2C.SCLK) | (int(bit) << APSCT_I2C.SDI)
+                self.dev_i2c_pll.write_bytes(0x02, write_data)
+                sleep(sleep_time)
+        read_bit = ''
+        for cnt in range(8*nof_bytes):
+            for clk in [0, 1]:  # Read after rizing edge
+                write_data = 0x02 | (clk << APSCT_I2C.SCLK) | (int(bit_array[-1]) << APSCT_I2C.SDI)
+                self.dev_i2c_pll.write_bytes(0x02, write_data)
+            ret_ack, ret_value = self.dev_i2c_pll.read_bytes(0x00, 1)
+            if ret_ack:
+                read_bit += str((int(ret_value, 16) >> APSCT_I2C.SDO) & 0x01)
+            else:
+                print("ACK nok")
+        write_data = 0x02 | (1 << APSCT_I2C.CS) | (0 << APSCT_I2C.SCLK) | (0 << APSCT_I2C.SDI)
+        self.dev_i2c_pll.write_bytes(0x02, write_data)
+        if DEBUG:
+            stri = "Read back at address 0x{0:{fill}2x} result : 0x{1:{fill}2x} ".format(reg_address,
+                                                                                         int(read_bit, 2), fill='0')
+            print(stri)
+        return read_bit
+
+    def setup_pll(self):
+        #
+        # Set registers on the PLL
+        #
+        print(f"Setup PPL {self.frequency}")
+        self.dev_i2c_pll.write_bytes(0x07, 0x00)
+        if self.frequency == '160MHz':
+            i2c_address = APSCT_I2C.PLL_200M
+            dev_i2c_pll_sel = I2C(i2c_address, BUSNR=I2CBUSNR)
+            dev_i2c_pll_sel.write_bytes(0x03, 0x08)
+        else:
+            self.dev_i2c_pll.write_bytes(0x03, 0x28)
+        self.write_byte_pll(0x03, 0x0C)
+        sleep(0.5)
+        self.write_byte_pll(0x03, 0x08)
+        self.write_byte_pll(0x03, 0x08)
+        self.write_byte_pll(0x04, 0xCF)  # CF disable not used outputs, 00 enable all
+        self.write_byte_pll(0x05, 0x97)
+        self.write_byte_pll(0x06, 0x10)  # cp inv = 0xF4 other 0xE4
+        self.write_byte_pll(0x07, 0x04)  # Divider R = 1 dec
+        self.write_byte_pll(0x08, 0x01)
+        self.write_byte_pll(0x07, 0x00)
+        self.write_byte_pll(0x09, 0x10)  # reset
+        if self.frequency == '160MHz':
+            self.write_byte_pll(0x0A, 0x10)
+        else:
+            self.write_byte_pll(0x0A, 0x14)
+        self.write_byte_pll(0x09, 0x00)
+        self.write_byte_pll(0x0C, 0x8F)
+        self.write_byte_pll(0x0D, 0x88)  # Dig CLK     = 200/1 = 200 MHz
+        self.write_byte_pll(0x0F, 0x08)  # RCU CLK     = 200/1 = 200 MHz
+        self.write_byte_pll(0x11, 0x08)  # PPS ref CLK = 200/1 = 200 MHz
+        self.write_byte_pll(0x13, 0x88)  # T.P.    CLK = 200/1 = 200 MHz
+
+    def read_all_regs_pll(self):
+        #
+        # Read all registers on the PLL and print on screen
+        #
+        self.dev_i2c_pll.write_bytes(0x07, 0x00)
+        bytes_to_read = 24
+        ret_value = self.read_byte_pll(0, nof_bytes=bytes_to_read)
+        for cnt in range(bytes_to_read):
+            start = cnt*8
+            stri = "Reg nr 0x{:0>2x} value: 0x{:0>2x}".format(cnt, int(ret_value[start:start+8], 2))
+            print(stri)
+
+    def read_lock(self, print_on=True):
+        #
+        # Read lock status
+        #
+        ret_value = self.read_byte_pll(0x00, nof_bytes=1)
+        status_pll = int(ret_value, 2)
+        if status_pll == 0x04:
+            self.lock = True
+            stri = f"PLL {self.frequency} is in lock"
+        elif (status_pll & 0x10) > 0:
+            self.lock = False
+            stri = f"PLL {self.frequency} Not Locked --> No 10 MHz ref"
+        else:
+            self.lock = False
+            stri = f"PLL {self.frequency} Not locked --> PLL Error"
+        if print_on:
+            print(stri)
+        return self.lock
+
+    def read_lol(self):
+        #
+        # Read loss of lock status
+        #
+        i2_c_io_device_a = I2C(0x20, BUSNR=I2CBUSNR)
+        i2_c_io_device_a.write_bytes(0x06, 0x2C)  # '0' is output
+        i2_c_io_device_a.write_bytes(0x07, 0x00)  # '0' is output
+        i2_c_io_device_b = I2C(0x21, BUSNR=I2CBUSNR)
+        i2_c_io_device_b.write_bytes(0x06, 0x2C)  # '0' is output
+        i2_c_io_device_b.write_bytes(0x07, 0xFF)  # '0' is output
+
+        ack, ret_value = i2_c_io_device_b.read_bytes(0x01, 1)
+        status_reg = int(ret_value, 16)
+        if (self.frequency == '200MHz') & ((status_reg & 0x10) > 0):
+            print("200MHz has lost lock")
+        if ((status_reg & 0x20) > 0) & (self.frequency == '160MHz'):
+            print("160MHz has last lock")
+        ack, ret_value = i2_c_io_device_a.read_bytes(0x01, 1)
+        old_reg = int(ret_value, 16)
+        i2_c_io_device_a.write_bytes(0x03, (old_reg | 0x10))  # '0' is output
+        sleep(1)
+        i2_c_io_device_a.write_bytes(0x03, (old_reg & 0xEF))  # '0' is output
+
+
+class EepromClass:
+    #
+    # Class to handle EEPROM communication
+    #
+    def __init__(self):
+        self.dev_i2c_eeprom = I2C(APSCT_I2C.EEPROM)
+        self.dev_i2c_eeprom.bus_nr = I2CBUSNR
+
+    def write_eeprom(self, data="APSCT", address=0):
+        #
+        # Write the EEPROM with the serial number etc.
+        #
+        # Data = data to write in string formal
+        # Address = address to write the data to
+        # Return True if successfully
+        #
+        ret_ack, ret_value = self.dev_i2c_eeprom.read_bytes(0)
+        if ret_ack < 1:
+            print("EEPROM not found during write")
+            return False
+        else:
+            wr_data = bytearray(data.encode("utf-8", errors="ignore"))
+            for loc, data_byte in enumerate(wr_data):
+                self.dev_i2c_eeprom.write_bytes(address + loc, data_byte)
+                sleep(0.1)
+            return True
+
+    def read_eeprom(self, address=0, nof_bytes=5):
+        #
+        # Read the EEPROM with the serial number etc.
+        #
+        # Address = address to read from
+        # nof_bytes = number of bytes to read
+        # return string with the data from the flash
+        #
+        ret_ack, ret_value = self.dev_i2c_eeprom.read_last_reg(1)
+        if ret_ack < 1:
+            print("no EEPROM found during read")
+            return False
+        else:
+            ret_ack, ret_value = self.dev_i2c_eeprom.read_bytes(address, nof_bytes)
+            ret_value = bytes.fromhex(ret_value[:nof_bytes * 2])
+            str_return = ret_value.decode('UTF-8')
+            return str_return
+
+    def wr_rd_eeprom(self, value="APSCT-1", address=0):
+        #
+        # Write and Read the EEPROM to check functionality
+        #
+        # value = string with data to write
+        # address = address to write the data to
+        # return True if read back is same as write value
+        #
+        if self.write_eeprom(value, address=0):
+            ret_value = self.read_eeprom(address=0, nof_bytes=len(value))
+            print(ret_value)
+            stri = "Wrote to EEPROM register 0x{2:x} : {0}, Read from EEPROM: {1}".format(value, ret_value, address)
+            print(stri)
+            if ret_value == value:
+                return True
+            else:
+                return False
+
+
+class ApsctSensors:
+    #
+    # All monitor. read and write functions for the voltage sensors on APSCT
+    #
+    def __init__(self):
+        #
+        # All monitor. read and write functions for the EEPROM
+        #
+        self.dev_i2c_sensor = I2C(0x74)
+        self.dev_i2c_sensor.bus_nr = I2CBUSNR
+        self.power_supplies = list(APSCT_I2C.PWR_LOCATIONS.keys())
+        self.voltages = {}
+        self.temperature = 9999
+        self.dev_i2c_sensor.write_bytes(0xB0, 0xB8)
+
+    def apsct_sensors(self):
+        #
+        # read all 7 power sens lines
+        #
+        # Return True when done
+        #
+        for sens_line in range(7):
+            self.read_voltage(sens_line)
+        self.read_temp()
+        return True
+
+    def read_all_voltages(self):
+        #
+        # Function to read and process one sensline
+        #
+        # Return True when done
+        #
+        # To remove errors, repeat measurement when returned voltage is < 3 V
+        #
+        for pwr in self.power_supplies:
+            self.voltages[pwr] = self.read_voltage(APSCT_I2C.PWR_LOCATIONS[pwr])
+            if self.voltages[pwr] < 3:
+                self.voltages[pwr] = self.read_voltage(APSCT_I2C.PWR_LOCATIONS[pwr])
+        return True
+
+    def check_values(self):
+        #
+        # Function to check sensor values (voltages and temperature)
+        #
+        # return result, True when OK, False in case of error
+        #
+        print("Check power sensor values")
+        result = True
+        self.read_all_voltages()
+        self.read_temp()
+        for pwr in self.power_supplies:
+            expected = APSCT_I2C.PWR_VOUT[pwr]
+            if not (0.9*expected < self.voltages[pwr] < 1.1*expected):
+                result = False
+                print(f"Error: {pwr: <9} expected: {expected} V read: {self.voltages[pwr]:4.2f} V")
+            else:
+                print(f"OK   : {pwr: <9} expected: {expected} V read: {self.voltages[pwr]:4.2f} V")
+        if not (15 < self.temperature < 50):
+            result = False
+            print(f"Error temperature read {self.temperature:4.2f} °C")
+        else:
+            print(f"OK    temperature read {self.temperature:4.2f} °C")
+        return result
+
+    def read_voltage(self, input_channel=0):
+        #
+        # Function to read a voltages of APSCT
+        #
+        # input_channel = sens port
+        # return value
+        #
+        voltage = 9999
+        vref = 3.0
+        one_step = vref/(2**16)
+        channel_select_word = 0xB0 | ((input_channel % 2) << 3) | ((input_channel >> 1) & 0x7)
+        if DEBUG:
+            stri = "Word to select sens input is 0x{0:x}".format(channel_select_word)
+            print(stri)
+        self.dev_i2c_sensor.write_bytes(channel_select_word, 0xB8)
+        sleep(0.3)  # Wait for device to take snapshot
+        ret_ack, ret_value = self.dev_i2c_sensor.read_last_reg(3)
+        if DEBUG:
+            stri = "Return value input 0 : 0x{0} ".format(ret_value)
+            print(stri)
+        if int(ret_value, 16) >= 0xC00000:
+            print("over range")
+        else:
+            steps = (int(ret_value, 16) & 0x1FFFFF) >> 6
+            voltage = one_step * steps
+            voltage = ((4.7+2.2)/2.2)*2*voltage  # Resistor network + half swing
+            if DEBUG:
+                string = "Voltage sens line {1} is {0:.4f} V".format(voltage, input_channel)
+                print(string)
+        sleep(0.2)  # wait for device to go to sleep
+        return voltage
+
+    def read_temp(self):
+        #
+        # Function to read temperature of APSCT
+        #
+        # return value
+        #
+        vref = 3.0
+        temp_slope = 93.5E-6 * 2**(16+1) / vref
+        ret_ack = self.dev_i2c_sensor.write_bytes(0xA0, 0xC0)
+        if not ret_ack:
+            self.temperature = 9999
+            return self.temperature
+        sleep(0.5)
+        self.temperature = 9999
+        loops = 0
+        while (self.temperature > 100) & (loops < 2):
+            loops = loops + 1
+            ret_ack, ret_value = self.dev_i2c_sensor.read_last_reg(3)
+            if ret_ack:
+                raw_value = (int(ret_value, 16) & 0x1FFFFF) >> 6
+                temperature_k = (raw_value/temp_slope)
+                self.temperature = temperature_k-273
+            else:
+                self.temperature = 9999
+        sleep(0.2)
+        return self.temperature
+
+
+class PpsClass:
+    #
+    # Class to check the PPS signal on GPIO24
+    #
+    def __init__(self):
+        gpio.setmode(gpio.BCM)
+        gpio.setup(24, gpio.IN)
+        self.pps_time = 999
+        self.timing = False
+
+    def time_pps(self):
+        #
+        # measure time between rising edges on (0.5Hz) PPS input
+        #
+        gpio.wait_for_edge(24, gpio.RISING)
+        a = time()
+        gpio.wait_for_edge(24, gpio.RISING)
+        b = time()
+        self.pps_time = (b-a)/2
+        return self.pps_time
+
+    def check_pps(self):
+        #
+        # Check is signal toggles PPS input within 5 second period
+        #
+        gpio.add_event_detect(24, gpio.RISING)
+        sleep(5)
+        pps_ok = gpio.event_detected(24)
+        gpio.remove_event_detect(24)
+        return pps_ok
+
+    def print_pps(self):
+        #
+        # Print PPS period time on screen
+        #
+        if self.check_pps():
+            print(f"Time between pps is {self.time_pps():4.2f} s")
+        else:
+            print("No PPS found")
+
+    def check_timing(self):
+        #
+        # Check is time is 1 second  +/- 10%
+        #
+        print("Check pps timing", end="")
+        self.timing = False
+        if not self.check_pps():
+            print(" no pps")
+            return False
+        timepps = self.time_pps()
+        if 0.9 < timepps < 1.1:
+            print(f", timing is OK: {timepps:4.2f} s")
+            self.timing = True    
+        return self.timing
+
+
+class I2cSwitch:
+    #
+    # Class to check a I2C-switch to RCU's and UNB2c's
+    #
+    def __init__(self, address=0x70, bus=1):
+        self.address = address  # Address of the switch
+        self.bus = bus  # I2C bus number on the Po
+        self.dev_i2c_switch = I2C(address)  # I2C software device
+        self.dev_i2c_switch.bus_nr = bus
+
+    def check_switch(self, data=0xa5):
+        #
+        # Check if you can write and read from the buffer
+        #
+        print(f"Check I2C switch at 0x{self.address:x} bus {self.bus}", end=' ')
+        ret_ack, ret_value = self.dev_i2c_switch.read_bytes(0)
+        if ret_ack < 1:
+            print("I2C-Switch not found")
+            return False
+        else:
+            self.dev_i2c_switch.write_bytes(0, data)
+            ret_ack, ret_value = self.dev_i2c_switch.read_last_reg(1)
+            read_value_int = int(ret_value, 16) 
+            if read_value_int == data:
+                print(f"OK   wrote 0x{data:02X} read back 0x{read_value_int:02X}")
+                return True
+            else:
+                print(f"ERROR wrote 0x{data:02X} read back 0x{read_value_int:02X}")
+                return False
+
+
+class ApsctId:
+    #
+    # Class to check ID pins
+    #
+    def __init__(self):
+        #
+        # APSCT Backplane ID 
+        #
+        self.id = 9999  # placeholder for APSCT-ID
+        gpio.setmode(gpio.BCM)  # Set IO pins used for the APSCT-ID
+        for pin in APSCT_I2C.ID_PINS:
+            gpio.setup(pin, gpio.IN)
+
+    def read_id(self):
+        #
+        # Function to read the APSCT-ID from the backplane
+        #
+        loc_id = 0
+        for pin in APSCT_I2C.ID_PINS:
+            loc_id = loc_id * 2
+            bit = gpio.input(pin)
+            loc_id = loc_id + bit
+        self.id = loc_id
+        return self.id
+
+    def check_id(self):
+        #
+        # Function Check the ID.
+        #
+        self.read_id()
+        if self.id == 63:
+            print(f"OK   : Back ID is 0x{self.id:02X}")
+            return True
+        else:
+            print(f"ERROR : Back ID is 0x{self.id:02X} expected 0x{63:2X}")
+            return False
diff --git a/apspu_lib.py b/apspu_lib.py
index 809a31b9820c37ea1367a72f6633eed1d113017e..be387ca6acb956d38e17198ba0911ad2e271b97a 100644
--- a/apspu_lib.py
+++ b/apspu_lib.py
@@ -12,8 +12,8 @@ See the License for the specific language governing permissions and
 limitations under the License.
 
 
-  Created: 2021-05-10
-This file contains the UniBoard2 class with all monitoring function. It can be used stand-alone to test it.
+  Created: 2022-17-12
+This file contains the APSPU class with all monitoring function. It can be used stand-alone to test it.
 
 """
 
@@ -21,20 +21,21 @@ import sys
 sys.path.insert(0, '.')
 import os
 import math
- 
 from APSPU_I2C import *
+
+
 if os.name == "posix":
-    from I2C_serial_pi2 import *
+    from I2C_serial_pi3 import *
 else:
     from I2C_serial import *
 
-
-I2CBUSNR = 1
-DEBUG = False
+I2CBUSNR = 1    # Bus used on the Pi
+DEBUG = False   # Set True to print debug information on the screen
+I_OK_MARGIN = 0.5 # I_error in Amps
 
 class ApspuClass:
     #
-    # Class that contains all parts on a UniBoard2
+    # Toplevel Class that contains all parts on a APSU
     #
     def __init__(self):
         self.status = False
@@ -46,7 +47,7 @@ class ApspuClass:
 
     def read_all(self):
         #
-        # Function to read all monitoring points of the UniBoard
+        # Function to read all monitoring points of the APSPU
         #
         print("--------- \nRead APSPU Status\n---------")
         for pol in self.pols:
@@ -64,24 +65,41 @@ class ApspuClass:
         return True
 
     def set_pols(self):
+        #
+        # Function to set output voltage level on the APSPU
+        # Values are read from the APSPU_I2C
+        #
         print("--------- \nProgram Pols\n---------")
         for pol in self.pols:
             pol.set_vout_pol(VOUT_POLS[pol.name])
+            vout = pol.read_vout_set()
+            if not (0.9*VOUT_POLS[pol.name] < vout < 1.1*VOUT_POLS[pol.name]):
+                print(f"POL {pol.name:10} Error setting Vout, "
+                      f"set to {VOUT_POLS[pol.name]} read back {vout}")
+                exit()
             pol.set_vout_ov_limit_pol(1.2*VOUT_POLS[pol.name])
+            ov_out = pol.read_ov_limit()
+            if not (1.1*VOUT_POLS[pol.name] < ov_out < 1.3*VOUT_POLS[pol.name]):
+                print(f"POL {pol.name:10} Error setting output overvoltage"
+                      f"set {1.2*VOUT_POLS[pol.name]} read back {ov_out}")
+                exit()
             pol.set_on_off_config()
-            if DEBUG:
-                pol.read_vout_set()
-                pol.read_ov_limit()
-                pol.read_uv_limit()
+            pol.on_off(True)
             pol.write_to_nvm()
         print("Done")
     
     def check_apspu(self):
+        #
+        # Function to check values of read_all() Used during production
+        #
+        # Return True is OK, False if not OK
+        #
         print("--------- \nCheck APSPU \n---------")
         check_ok = True
         for pol in self.pols:
             check_ok = check_ok & pol.check_pol()
         check_ok = check_ok & self.fans.check_fans()
+        check_ok = check_ok & self.eeprom.wr_rd_eeprom(value="PROD_CHECK", address=0x30)
         if check_ok:
             print("APSPU OK")
         else:
@@ -89,6 +107,12 @@ class ApspuClass:
         return check_ok
  
     def apspu_on_off(self, on):
+        #
+        # Function to switch off the POLs on APSPU
+        # on = True to switch APSU on
+        # on = False to switch APSU off
+        # Return: always True
+        #
         if on:
             print(f">> Switch APSPU ON")
         else:
@@ -96,14 +120,15 @@ class ApspuClass:
         for pol in self.pols:
             pol.on_off(on)
         return True
-        
+
+
 class EepromClass:
     #
     # Class to handle EEPROM communication 
     #
     def __init__(self):
         #
-        # All monitoring points Point of Load DC/DC converter
+        # All monitor. read and write functions for the EEPROM
         #
         self.dev_i2c_eeprom = I2C(EEPROM)
         self.dev_i2c_eeprom.bus_nr = I2CBUSNR
@@ -112,6 +137,10 @@ class EepromClass:
         #
         # Write the EEPROM with the serial number etc.
         #
+        # Data = data to write in string formal
+        # Address = address to write the data to
+        # Return True if successfully
+        #
         ret_ack, ret_value = self.dev_i2c_eeprom.read_bytes(0)
         if ret_ack < 1:
             print("EEPROM not found during write")
@@ -127,6 +156,10 @@ class EepromClass:
         #
         # Read the EEPROM with the serial number etc.
         #
+        # Address = address to read from
+        # nof_bytes = number of bytes to read
+        # return string with the data from the flash
+        #
         ret_ack, ret_value = self.dev_i2c_eeprom.read_last_reg(1)
         if ret_ack < 1:
             print("no EEPROM found during read")
@@ -136,15 +169,22 @@ class EepromClass:
             str_return = bytes.fromhex(ret_value[:nof_bytes*2]).decode('UTF-8')
             return str_return
 
-    def wr_rd_eeprom(self, value="APSPU-1", address = 0):
+    def wr_rd_eeprom(self, value="APSPU-1", address=0):
         #
         # Write and Read the EEPROM to check functionality
         #
+        # value = string with data to write
+        # address = address to write the data to
+        # return True if read back is same as write value
+        #
         if self.write_eeprom(value, address=0):
             ret_value = self.read_eeprom(address=0, nof_bytes=len(value))
             stri = "Wrote to EEPROM register 0x{2:x} : {0}, Read from EEPROM: {1}".format(value, ret_value, address)
             print(stri)
-        return True
+            if ret_value == value:
+                return True
+            else:
+                return False
 
 
 class PolClass:
@@ -170,17 +210,17 @@ class PolClass:
         else:
             self.status = True
 
-
     def read_vout_set(self):
         #
         # Function to read the output voltage of the Point of Load DC/DC converter
         #
+        # Return: output value is of else 999
+        #
         if self.status:
             ret_ack, raw_value = self.pol_dev.read_bytes(LP_VOUT_COMMAND, 2)
-            if len(raw_value)<4:
+            if len(raw_value) < 4:
                 raw_value = '0' + raw_value
-            ret_value = []
-            ret_value= int(raw_value[2:], 16) * 2**8  
+            ret_value = int(raw_value[2:], 16) * 2**8
             ret_value += int(raw_value[:2], 16)
             output_value = ret_value * 2**-11
             if DEBUG:
@@ -193,57 +233,91 @@ class PolClass:
         #
         # Function to set the output of the DC/DC converter
         #
+        # Return always I2C ack
+        #
+        ret_ack = False
         if self.status:
-           if DEBUG:
-               ret_ack, raw_value = self.pol_dev.read_bytes(LP_ON_OFF_CONFIG, 1)
-               print(f"Current setting ON/OFF register 0x{int(raw_value, 16):02x}")
-           on_off_bit   = not 1 << 0
-           polarity_pin = 1 << 1
-           use_external = 0 << 2
-           use_soft     = 1 << 3
-           default_off  = 1 << 4
-           wr_data = on_off_bit + polarity_pin + use_external + use_soft + default_off
-           ret_ack = self.pol_dev.write_bytes(LP_ON_OFF_CONFIG, wr_data)
-        return True
+            if DEBUG:
+                ret_ack, raw_value = self.pol_dev.read_bytes(LP_ON_OFF_CONFIG, 1)
+                print(f"Current setting ON/OFF register 0x{int(raw_value, 16):02x}")
+            on_off_bit = not 1 << 0
+            polarity_pin = 1 << 1
+            use_external = 0 << 2
+            use_soft = 1 << 3
+            default_off = 1 << 4
+            wr_data = on_off_bit + polarity_pin + use_external + use_soft + default_off
+            ret_ack = self.pol_dev.write_bytes(LP_ON_OFF_CONFIG, wr_data)
+        return ret_ack
 
     def on_off(self, on=True):
-           wr_data = (on << 7) + 0
-           ret_ack = self.pol_dev.write_bytes(LP_OPERATION, wr_data)
+        #
+        # Function to switch on or off a POL
+        #
+        # if on = True switch on the POL
+        # if on = Flase swtich off the POL
+        # Return I2C ack
+        #
+        wr_data = (on << 7) + 0
+        ret_ack = self.pol_dev.write_bytes(LP_OPERATION, wr_data)
+        return ret_ack
 
     def set_vout_pol(self, value):
         #
         # Function to read the output voltage of the Point of Load DC/DC converter
         #
+        # value is the output voltage level in V
+        # return I2C ack
+        #
+        ret_ack = False
         if self.status:
+            ret_ack = self.pol_dev.write_bytes(LP_WRITE_PROTECT, [0])
             set_value = int(value * (2**11))
             hex_set_value = hex(set_value)
             wr_value = (hex_set_value[4:6] + hex_set_value[2:4])
             if DEBUG:
                 print(f"Calculated wr_value is {wr_value}")
-            wr_data =[]
+            wr_data = []
             wr_data.append(int(hex_set_value[4:6], 16))
             wr_data.append(int(hex_set_value[2:4], 16))
             ret_ack = self.pol_dev.write_bytes(LP_VOUT_COMMAND, wr_data)
-        return True
+        return ret_ack
 
     def set_vout_ov_limit_pol(self, value):
         #
         # Function to read the output voltage of the Point of Load DC/DC converter
         #
+        # value is the overvoltage level of the output
+        # return I2C ack
+        #
+        ret_ack = False
         if self.status:
             set_value = int(value * (2**11))
             hex_set_value = hex(set_value)
             wr_value = (hex_set_value[4:6] + hex_set_value[2:4])
             if DEBUG:
                 print(f"Calculated wr_value is {wr_value}")
-            wr_data =[]
+            wr_data = []
             wr_data.append(int(hex_set_value[4:6], 16))
             wr_data.append(int(hex_set_value[2:4], 16))
             ret_ack = self.pol_dev.write_bytes(LP_VOUT_OV_LIMIT, wr_data)
-        return True
+        return ret_ack
     
     def write_to_nvm(self):
-        ret_ack = self.pol_dev.write_bytes(LP_STORE_USER_ALL, 0)
+        #
+        # Function to write the POL's registers to NVM memory
+        #
+        # return is always True
+        #
+        print(f"Store to NVM for POL {self.name}")
+        if False:
+            ret_ack = self.pol_dev.write_register(0x15)
+            sleep(1)
+        else:
+            self.pol_dev.close()
+            command = f"i2cset -y 1 0x{CTR_POLS[self.name]:02X} 0x15 cp" 
+            os.system(command)
+            os.system(command)
+            self.pol_dev.open()
         return True
 
     def read_vin(self):
@@ -252,13 +326,17 @@ class PolClass:
         #
         if self.status:
             ret_ack, raw_value = self.pol_dev.read_bytes(LP_VIN, 2)
-            if len(raw_value)<4:
+            if not ret_ack:
+                self.iout=999
+                return False
+            if len(raw_value) < 4:
                 raw_value = '0' + raw_value
             ret_value = []
             ret_value.append(int(raw_value[:2], 16))
-            ret_value.append(int(raw_value[2:], 16)) # * 2**8  
-            output_value = calc_lin_2bytes(ret_value)    #ret_value * 2**-11
+            ret_value.append(int(raw_value[2:], 16))
+            output_value = calc_lin_2bytes(ret_value)
             self.vin = output_value
+            return True
 
     def read_vout(self):
         #
@@ -266,55 +344,71 @@ class PolClass:
         #
         if self.status:
             ret_ack, vout_mod = self.pol_dev.read_bytes(LP_VOUT_MODE, 1)
+            if not ret_ack:
+                self.vout=999
+                return False
             ret_ack, raw_value = self.pol_dev.read_bytes(LP_VOUT, 2) 
+            if not ret_ack:
+                self.vout=999
+                return False
             vout_mod = int(vout_mod, 16)
             ret_value = []
             ret_value.append(int(raw_value[:2], 16))
             try:
                 ret_value.append(int(raw_value[2:], 16))
             except:
-               ret_value.append(0) 
+                ret_value.append(0)
             self.vout = calc_lin_3bytes(ret_value, [vout_mod])
+            return True
         else:
             self.vout = 999
+            return False
 
     def read_ov_limit(self):
         #
         # Function to read the output voltage of the Point of Load DC/DC converter
+        # and print on the screen
         #
+        # Return OV limit
+        #
+        output_value = 0
         if self.status:
             ret_ack, raw_value = self.pol_dev.read_bytes(LP_VOUT_OV_LIMIT, 2)
-            if len(raw_value)<4:
+            if len(raw_value) < 4:
                 raw_value = '0' + raw_value
-            ret_value = []
-            ret_value= int(raw_value[2:], 16) * 2**8  
+            ret_value = int(raw_value[2:], 16) * 2**8
             ret_value += int(raw_value[:2], 16)
             output_value = ret_value * 2**-11
-            print(f"Output OV limit is set to: = {output_value:5.2f} V using hex value {raw_value}")
+            if DEBUG:
+                print(f"Output OV limit is set to: = {output_value:5.2f} V using hex value {raw_value}")
+        return output_value
 
     def read_uv_limit(self):
         #
         # Function to read the output voltage of the Point of Load DC/DC converter
         #
+        # Return UV limit if OK else False
+        #
         if self.status:
             ret_ack, raw_value = self.pol_dev.read_bytes(LP_VOUT_UV_LIMIT, 2)
-            if len(raw_value)<4:
+            if len(raw_value) < 4:
                 raw_value = '0' + raw_value
             ret_value = []
-            ret_value= int(raw_value[2:], 16) * 2**8  
+            ret_value = int(raw_value[2:], 16) * 2**8
             ret_value += int(raw_value[:2], 16)
             output_value = ret_value * 2**-11
-            print(f"Output UV limit is set to: = {output_value:5.2f} V using hex value {raw_value}")
+            if DEBUG:
+                print(f"Output UV limit is set to: = {output_value:5.2f} V using hex value {raw_value}")
             return output_value
         else:
-            return 9999
+            return False
 
     def read_iout(self):
         #
         # Function to read the output current of the Point of Load DC/DC converter
         #
         if self.status:
-            ret_ack, raw_value = self.pol_dev.read_bytes(0x39,2)
+            ret_ack, raw_value = self.pol_dev.read_bytes(0x39, 2)
             ret_ack, raw_value = self.pol_dev.read_bytes(LP_IOUT, 2)
             ret_value = []
             ret_value.append(int(raw_value[:2], 16))
@@ -346,6 +440,11 @@ class PolClass:
         self.read_vin()
 
     def check_pol(self):
+        #
+        # Function to read all monitoring points of the Point of Load DC/DC converter
+        #
+        # Return True if OK, False if not OK
+        #
         self.read_all()
         self.on_off(on=True)
         check_ok = False
@@ -372,15 +471,15 @@ class PolClass:
             check_ok = False
             print(f"POL {self.name:10} TEMP not OK, expected {temp_low} C - {temp_high} C, measured {self.temp} C ")
             return check_ok
-        i_low = 0.75*IOUT_POLS[self.name]
-        i_high = 1.25*IOUT_POLS[self.name]
+        i_low = (1-I_OK_MARGIN)*IOUT_POLS[self.name]
+        i_high = (1+I_OK_MARGIN)*IOUT_POLS[self.name]
         if i_low < self.iout < i_high:
             check_ok = True
         else:
             check_ok = False
-            print(f"POL {self.name:10} Iout not OK, expected {i_low:4.2f} A - {i_high:4.2f} A, measured {self.iout:4.2f} A ")
+            print(f"POL {self.name:10} Iout not OK,"
+                  f" expected {i_low:4.2f} A - {i_high:4.2f} A, measured {self.iout:4.2f} A ")
         return check_ok
-            
 
     def print_status(self):
         #
@@ -395,13 +494,14 @@ class PolClass:
             print(stri)
             self.read_vout_set()
 
+
 class FanmonitorClass:
     #
-    # Class to read all monitoring points Point of Load DC/DC converter
+    # Class to read all monitoring points fan units in the APS
     #
     def __init__(self):
         #
-        # All monitoring points Point of Load DC/DC converter
+        # All monitoring points for the fans
         #
         self.rpm = []
         self.fanmonitor_dev = I2C(MAX6620)
@@ -426,7 +526,8 @@ class FanmonitorClass:
         self.fanmonitor_dev.write_bytes(REG_GLOBAL, RUN_MONITOR)
         ret_ack, reg_after = self.fanmonitor_dev.read_bytes(REG_GLOBAL, 1)
         if DEBUG:
-            stri = "Reg at address 0x{0} before : {1} and after write action {2}".format(REG_GLOBAL, reg_before, reg_after)
+            stri = "Reg at address 0x{0} before : {1} and after write action {2}"\
+                .format(REG_GLOBAL, reg_before, reg_after)
             print(stri)
         fan_config_reg = int((math.log(TACH_PERIODS) / math.log(2))) << 5
         for fan_cnt in range(NOF_APS_FANS):
@@ -436,13 +537,16 @@ class FanmonitorClass:
     def read_fan(self, fan_nr):
         #
         # Function to a single fan
+        # fan_nr is the fan to read ranging from 0 till 2
+        # return the speed of the fan
         #
+        if fan_nr > NOF_APS_FANS:
+            return 0
         ret_ack, tach_msb = self.fanmonitor_dev.read_bytes(REG_TACH_MSP_REGS[fan_nr], 1)
         tach_msb = int(tach_msb, 16) & 0xFF
+        tach_lsb = 255
+        tach = 99999
         if tach_msb > 254:
-            if DEBUG :
-                tach_lsb = 255
-                tach = 99999
             rpm = 0
         else:
             ret_ack, tach_lsb = self.fanmonitor_dev.read_bytes(REG_TACH_LSP_REGS[fan_nr], 1)
@@ -458,11 +562,14 @@ class FanmonitorClass:
         #
         # Function to read all fan's
         #
-        self.rpm=[]
+        self.rpm = []
         for fan_counter in range(NOF_APS_FANS):
             self.rpm.append(self.read_fan(fan_counter))
             
     def check_fans(self):
+        #
+        # Function to check fan speeds
+        #
         self.read_all()
         check_ok = True
         for cnt, speed in enumerate(self.rpm):
diff --git a/production_apsct.py b/production_apsct.py
new file mode 100644
index 0000000000000000000000000000000000000000..a55d7dee8a724aac5c2e082e8064f6bf59873a7f
--- /dev/null
+++ b/production_apsct.py
@@ -0,0 +1,61 @@
+"""
+Copyright 2021 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
+ASTRON Netherlands Institute for Radio Astronomy
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Check APSCT_CLK
+
+"""
+import apsct_lib
+import sys
+
+READ_ALL = False  # True
+
+CLK_FREQ = '200MHz'
+
+if len(sys.argv) < 2:
+    print("Production_apsct.py <ASTRON NR> <Serial number>")
+    print("e.g. python3 production_apsct.py 2023-01 1234")
+    exit()
+
+apsct = apsct_lib.ApsctClass(CLK_FREQ)
+state = True
+modi = ["200MHz", "160MHz", "OFF"]
+for mode in modi:
+    print(f"Check APSCT in {mode} mode")
+    apsct.frequency = mode
+    apsct.set_apsct()
+    if mode == "200MHz":
+        state = state & apsct.pll_200.read_lock()
+    if mode == "160MHz":
+        state = state & apsct.pll_160.read_lock()
+
+apsct.frequency = "200MHz"
+apsct.set_apsct()
+apsct.pll_200.read_lock()
+apsct.sensors.apsct_sensors()
+state = state & apsct.check_apsct()
+
+if READ_ALL:
+    apsct.pll_200.read_all_regs_pll()
+    apsct.pll_160.read_all_regs_pll()
+    apsct.read_io_expanderis()
+
+if state:
+    apsct_id = "APSCT-" + sys.argv[1]
+    serial = sys.argv[2]
+    rw_ok = apsct.eeprom.wr_rd_eeprom(apsct_id, address=0)
+    if rw_ok:
+        rw_ok = apsct.eeprom.wr_rd_eeprom(serial, address=0x20)
+    if not rw_ok:
+        print("EEPROM Error")
+else:
+    print("\n >>> Errors during testing <<<\n")
diff --git a/production_apspu.py b/production_apspu.py
index 481bb2fcb4346057914cb190c63e9b4d948adb3f..6e4431caa69593e92d394075884a9ca7324746f0 100644
--- a/production_apspu.py
+++ b/production_apspu.py
@@ -1,5 +1,5 @@
 """
-Copyright 2021 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
+Copyright 2022 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
 ASTRON Netherlands Institute for Radio Astronomy
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -12,40 +12,36 @@ See the License for the specific language governing permissions and
 limitations under the License.
 
 
-  Created: 2021-05-10
-This file contains the UniBoard2 class with all monitoring function. It can be used stand-alone to test it.
+ Created: 2022-12-07
+This file contains the APSPU production script.
 
 """
 
 import sys
 sys.path.insert(0, '.')
-import os
-import math
+
 from apspu_lib import *
- 
 
 
-if len(sys.argv)<2:
+if len(sys.argv) < 2:
     print("Production_apspy.py <ASTRON NR> <Serial number>")
     print("e.g. python production_apspy.py 2022-01 1234")
     exit()
 
 apspu = ApspuClass()
 apspu.apspu_on_off(False)
-sleep(5)
+sleep(5)  # Wait for outputs to be stable off
 apspu.set_pols()
-x = input("Change dipswitches and press key to continue..")
+input("Change dipswitches and press key to continue..")
 apspu.apspu_on_off(True)
-sleep(10)
+sleep(5)  # Wait for outputs to be stable on
 apspu.read_all()
 apspu.print_status()
 if apspu.check_apspu():
-    apspu.apspu_on_off(False)
-    sleep(10)
-    apspu.read_all()
-    apspu.print_status()
-    apspu.apspu_on_off(True)
-    id = "APSPU-" + sys.argv[1]
+    apspu_id = "APSPU-" + sys.argv[1]
     serial = sys.argv[2]
-    apspu.eeprom.wr_rd_eeprom(id, address=0)
-    apspu.eeprom.wr_rd_eeprom(serial, address=0x20)
+    rw_ok = apspu.eeprom.wr_rd_eeprom(apspu_id, address=0)
+    if rw_ok:
+        rw_ok = apspu.eeprom.wr_rd_eeprom(serial, address=0x20)
+    if not rw_ok:
+        print("EEPROM Error")