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/I2C_serial_pi.py b/I2C_serial_pi.py
index 010041ab1d7d95894213dc9cf797329b2bf97eeb..d4cc4cccb0550528e753cc89ef9c46d8d9fa1f1b 100644
--- a/I2C_serial_pi.py
+++ b/I2C_serial_pi.py
@@ -43,6 +43,11 @@ class I2C:
             if SLOW:
                 sleep(0.2)
         except IOError:
+            ret_ack = 0
+            ret_value = 'ffff'
+            if DEBUG:
+                print("Reading error")
+        except err:
             ret_ack = 0
             ret_value = 'ffff'
             if DEBUG:
@@ -50,7 +55,7 @@ class I2C:
         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 = ''
@@ -63,8 +68,13 @@ class I2C:
             except IOError:
                 ret_ack = 0
                 rd_value.append(0)
-                if DEBUG:
-                    print("IO-Reading error")
+                if print_on:
+                    print(f"Reading IOerror {rd_value}")
+            except err:
+                ret_ack = 0
+                rd_value.append(0)
+                if print_on:
+                    print("Reading error")
         for cnt in range(bytes_to_read):
             ret_value += (hex(rd_value[cnt])[2:])
         return ret_ack,ret_value
@@ -79,6 +89,11 @@ class I2C:
             if SLOW:
                 sleep(0.3)
         except IOError:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("Write error")
+        except err:
             ret_ack = 0
             ret_value = 0
             if DEBUG:
@@ -93,6 +108,11 @@ class I2C:
             if SLOW:
                 sleep(0.3)
         except IOError:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("Write error")
+        except err:
             ret_ack = 0
             ret_value = 0
             if DEBUG:
@@ -107,6 +127,11 @@ class I2C:
             if SLOW:
                 sleep(0.3)
         except IOError:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("Write error")
+        except err:
             ret_ack = 0
             ret_value = 0
             if DEBUG:
@@ -122,6 +147,11 @@ class I2C:
             if SLOW:
                 sleep(0.3)
         except IOError:
+            ret_ack = 0
+            ret_value = 0
+            if DEBUG:
+                print("No ACK")
+        except err:
             ret_ack = 0
             ret_value = 0
             if DEBUG:
diff --git a/apsct_lib.py b/apsct_lib.py
index 99309c6c99b0140bf4499697e2c9cc513bf45d57..905bd98d3d0f75ac0d57d79da8f1fa5e2c288e6d 100644
--- a/apsct_lib.py
+++ b/apsct_lib.py
@@ -1,4 +1,4 @@
-'''
+"""
 Copyright 2021 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
 ASTRON Netherlands Institute for Radio Astronomy
 Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,249 +13,430 @@ limitations under the License.
 
 Set APSCT_CLK
 
-'''
+"""
 import sys
+import APSCT_I2C
 import time
-sys.path.insert(0,'.')
+import RPi.GPIO as gpio
+import random
+sys.path.insert(0, '.')
 import os
-if os.name =="posix":
+if os.name == "posix":
     from I2C_serial_pi2 import *
 else:
     from I2C_serial import *
 
-DEBUG = False
-I2CBUSNR=5
+I2CBUSNR = 5
 sleep_time = 0.15
-SET_PLL = True
-READ_LOCK = True
-READ_ALL = False
-CHECK_EEPROM = False
-PWR_RST = False #True #False
-READ_SENSOR = False #True
-READ_REGS = False #True
-
-CLK_FREQ = '200MHz'
-dev_i2c_eeprom = I2C(0x50)
-dev_i2c_eeprom.bus_nr = I2CBUSNR
-
-CS = 6
-SCLK = 4
-SDO = 5
-SDI = 7
-
-PLL_200M = 0x20
-PLL_160M = 0x21
-
-def Write_byte_PLL(reg_address, wr_data, ADDRESS=0x20):
+DEBUG = False
+
+
+class ApsctClass:
     #
-    # Write Byte to the ADC
+    # Toplevel class that contains all controllable parts of the APSCT
     #
-    I2C_device = I2C(ADDRESS, BUSNR=I2CBUSNR)
-    PLL_rw = 0x00 # 0 for write, 1 for read
-    stri = "Write to address : 0x{1:{fill}2x} value  0x{0:{fill}2x}".format(wr_data, reg_address, fill='0')
-    print(stri)
-    I2C_device.write_bytes(0x06, 0x2C)
-    if DEBUG:
-        rd_bytes = I2C_device.read_bytes(0x06, 1)
-        stri = "IO expander wrote 0x{0:x}, read 0x{1}".format(0x2C, rd_bytes[1])
+    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)
-    data = (reg_address << 9) + (PLL_rw << 8) + wr_data
-    bit_array = "{0:{fill}16b}".format(data, fill='0')
-    I2C_device.write_bytes(0x02, 0x02 | (0x1 << CS))
-    for bit in bit_array:
-        for clk in range(2):
-            Write_data = 0x02 | (0 << CS) | (clk << SCLK) | (int(bit) << SDI)
-            I2C_device.write_bytes(0x02, Write_data)
-    for clk in range(2):
-            Write_data = 0x02 | (0 << CS) | (clk << SCLK)
-            I2C_device.write_bytes(0x02, Write_data)
-    for clk in range(2):
-            Write_data = 0x02 | (1 << CS) | (clk << SCLK)
-            I2C_device.write_bytes(0x02, Write_data)
-
-    Write_data = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI)
-    I2C_device.write_bytes(0x02, Write_data)
-    if DEBUG:
-        read_bits = Read_byte_PLL(reg_address, nof_bytes=1, ADDRESS=ADDRESS)
-#        stri = "Bits written 0x{0:x} to register 0x{1:x} read from PLL are {2}".format(wr_data, reg_address, read_bits)
-#        print(stri)
-
-
-def Read_byte_PLL(reg_address, nof_bytes=1, ADDRESS=0x20 ):
-    #
-    # Read Byte from the ADC
-    #
-    I2C_device = I2C(ADDRESS, BUSNR=I2CBUSNR)
-    PLL_rw    = 0x01 # 0 for write, 1 for read
+        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
 
-    I2C_device.write_bytes(0x06, 0x2C)
-    data =  ( reg_address << 7 ) + PLL_rw
 
-    bit_array = "{0:{fill}8b}".format(data, fill='0')
-    for bit in bit_array:
+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 | (0 << CS) | (clk << SCLK) | ( int(bit) << SDI)
-            I2C_device.write_bytes(0x02, Write_data)
-            sleep(sleep_time)
-
-#    print("read byte")
-    read_bit = ''
-    for cnt in range(8*nof_bytes):
-        for clk in [0, 1]: # Read after rizing edge
-            Write_data = 0x02 | (clk << SCLK) | ( int(bit) << SDI )
-            I2C_device.write_bytes(0x02, Write_data)
-        ret_ack, ret_value = I2C_device.read_bytes(0x00, 1)
-#        stri= "ret_value = {}".format(int(ret_value,16))
-#        print(stri)
-        if ret_ack:
-            read_bit += str((int(ret_value, 16) >> SDO) & 0x01)
+            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:
-            print("ACK nok")
-    Write_data = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI)
-    I2C_device.write_bytes(0x02, Write_data)
-    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 read_all_regs_pll(pll_frequency='200MHz') :
-    I2C_device = I2C(0x20, BUSNR=I2CBUSNR) #clock selection
-    I2C_device.write_bytes(0x07, 0x00)
-    if pll_frequency == '160MHz':
-        print("Read PLL 160 MHz")
-        pll_address = PLL_160M
-#        I2C_device.write_bytes(0x03, 0x0F)
-    else:
-        print("Read PLL 200 MHz")
-        pll_address=PLL_200M
-#        I2C_device.write_bytes(0x03, 0xF8)
-#    for reg_cnt in range(0x15):
-    bytes_to_read = 24
-    ret_value = Read_byte_PLL(0, nof_bytes = bytes_to_read, ADDRESS=pll_address)
-    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)
+            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_IO_expanderis():
-    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])
+    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
 
-def setup_pll(pll_frequency='200MHz') :
-    I2C_device = I2C(0x20, BUSNR=I2CBUSNR) #clock selection
-    I2C_device.write_bytes(0x07, 0x00)
-    if pll_frequency == '160MHz':
-        print("Set PLL to 160 MHz mode")
-        pll_address = PLL_160M
-        I2C_device.write_bytes(0x03, 0x08)
-    else:
-        print("Set PLL to 200 MHz mode")
-        pll_address=PLL_200M
-        I2C_device.write_bytes(0x03, 0x28)
-    Write_byte_PLL(0x03, 0x0C, pll_address)
-    sleep(0.5)
-    Write_byte_PLL(0x03, 0x08, pll_address)
-    Write_byte_PLL(0x03, 0x08, pll_address)
-    Write_byte_PLL(0x04, 0xCF, pll_address) # CF disable not used outputs, 00 enable all
-    Write_byte_PLL(0x05, 0x97, pll_address)
-    Write_byte_PLL(0x06, 0x10, pll_address) # cp inv = 0xF4 other 0xE4
-    Write_byte_PLL(0x07, 0x04, pll_address) # Divider R = 1 dec
-    Write_byte_PLL(0x08, 0x01, pll_address)
-    Write_byte_PLL(0x07, 0x00, pll_address)
-    Write_byte_PLL(0x09, 0x10, pll_address) # reset
-    if pll_frequency == '160MHz' :
-        Write_byte_PLL(0x0A, 0x10, pll_address)
-    else:
-        Write_byte_PLL(0x0A, 0x14, pll_address)
-    Write_byte_PLL(0x09, 0x00, pll_address)
-    Write_byte_PLL(0x0C, 0x8F, pll_address)
-    Write_byte_PLL(0x0D, 0x88, pll_address) # Dig CLK     = 200/1 = 200 MHz
-    Write_byte_PLL(0x0F, 0x08, pll_address) # RCU CLK     = 200/1 = 200 MHz
-    Write_byte_PLL(0x11, 0x08, pll_address) # PPS ref CLK = 200/1 = 200 MHz
-    Write_byte_PLL(0x13, 0x88, pll_address) # T.P.    CLK = 200/1 = 200 MHz
-
-
-def power(state):
-    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
-    I2C_IO_device_A.write_bytes(0x07, 0x00) # '0' is output
-    I2C_IO_device_B = I2C(0x21, BUSNR=I2CBUSNR)
-    I2C_IO_device_B.write_bytes(0x06, 0x2C) # '0' is output
-    if state:
-        bits_to_set_A1 = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI)
-        bits_to_set_A2 = 0x04
-        bits_to_set_B1 = 0x02 | (1 << CS) | (0 << SCLK) | (0 << SDI)
-    else:
-        bits_to_set_A1 = 0x00 | (1 << CS) | (0 << SCLK) | (0 << SDI)
-        bits_to_set_A2 = 0x00
-        bits_to_set_B1 = 0x00 | (1 << CS) | (0 << SCLK) | (0 << 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 write_eeprom( data=0x01):
+class ApsctSensors:
     #
-    # Write the EEPROM with the serial number etc.
+    # All monitor. read and write functions for the voltage sensors on APSCT
     #
-    ret_ack, ret_value = dev_i2c_eeprom.read_bytes(0)
-    if ret_ack < 1:
-        print("EEPROM not found during write")
-        return False
-    else:
-        dev_i2c_eeprom.write_bytes(0x00, data)
-        sleep(0.1)
+    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_eeprom():
-    #
-    # Read the EEPROM with the serial number etc.
-    #
-    ret_ack, ret_value = dev_i2c_eeprom.read_last_reg(1)
-    if ret_ack < 1:
-        print("no EEPROM found during read")
-        return False
-    else:
-        ret_ack, ret_value = dev_i2c_eeprom.read_bytes(0x00, 1)
-        return ret_value
-
-def wr_rd_eeprom(value=0x34):
-    #
-    # Write and Read the EEPROM to check functionality
-    #
-    if write_eeprom(value):
-        ret_value = read_eeprom()
-        stri = "Wrote to EEPROM: 0x{0:X}, Read from EEPROM: 0x{1} ".format(value, ret_value)
-        print(stri)
-    return True
-
-def apsct_sensors():
-    for sens_line in range(7):
-        read_voltage(sens_line)
-    read_temp()
-
-def read_voltage(input_channel=0):
-    addr = 0x74
-    Vref = 3.0
-    one_step = Vref/(2**(16))
-    I2C_device = I2C(addr, BUSNR=I2CBUSNR)
-    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)
-    sleep(0.1)
-    I2C_device.write_bytes(channel_select_word, 0xB8)
-    sleep(0.5)
-    ret_ack, ret_value = I2C_device.read_last_reg(3)
-    if 1: #ret_ack:
+    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)
@@ -264,94 +445,161 @@ def read_voltage(input_channel=0):
         else:
             steps = (int(ret_value, 16) & 0x1FFFFF) >> 6
             voltage = one_step * steps
-            voltage = ((4.7+2.2)/2.2)*2*voltage
-            string = "Voltage sens line {1} is {0:.4f} V".format(voltage, input_channel)
-            print(string)
-    else:
-        stri = " No ACK on device 0x{0:x} ".format(addr)
-        print(stri)
-
-def read_temp():
-    Vref = 3.0
-    addr = 0x74
-    one_step = Vref/(2**(16))
-    I2C_device = I2C(addr, BUSNR=I2CBUSNR)
-    temp_slope = 93.5E-6 * 2**(16+1) / Vref
-    sleep(1.0)
-    I2C_device.write_bytes(0xA0, 0xC0)
-    sleep(1.0)
-    ret_ack, ret_value = I2C_device.read_last_reg(3)
-    if ret_ack:
-        raw_value  = (int(ret_value, 16) & 0x1FFFFF) >> 6
-        temperature_K = (raw_value/temp_slope)
-        temperature = temperature_K-273
-        stri = "Temperature : {0:.2f} gr. C".format(temperature)
-        print(stri)
-    else:
-        print("Error reading tempeature")
-
-
-def read_lol(pll_frequency='200MHz'):
-    I2C_IO_device_A = I2C(0x20, BUSNR=I2CBUSNR)
-    I2C_IO_device_A.write_bytes(0x06, 0x2C)  # '0' is output
-    I2C_IO_device_A.write_bytes(0x07, 0x00)  # '0' is output
-    I2C_IO_device_B = I2C(0x21, BUSNR=I2CBUSNR)
-    I2C_IO_device_B.write_bytes(0x06, 0x2C)  # '0' is output
-    I2C_IO_device_B.write_bytes(0x07, 0xFF)  # '0' is output
-
-    ack, ret_value = I2C_IO_device_B.read_bytes(0x01, 1)
-    status_reg = int(ret_value,16)
-    if (pll_frequency=='200MHz') & ((status_reg & 0x10) > 0):
-        print("lost lock 200MHz")
-    if ((status_reg & 0x20) > 0) & (pll_frequency=='160MHz'):
-        print("lost lock 160MHz")
-    ack, ret_value = I2C_IO_device_A.read_bytes(0x01, 1)
-    old_reg = int(ret_value,16)
-    I2C_IO_device_A.write_bytes(0x03, (old_reg | 0x10))  # '0' is output
-    sleep(1)
-    I2C_IO_device_A.write_bytes(0x03, (old_reg & 0xEF))  # '0' is output
-
-
-
-
-
-#if READ_REGS:
-#    read_all_regs_pll(CLK_FREQ)
-
-read_temp()
-
-if CHECK_EEPROM :
-    wr_rd_eeprom()
-
-if PWR_RST :
-    power(False)
-    sleep(10)
-    power(True)
-
-if SET_PLL :
-    setup_pll(CLK_FREQ)
-
-if READ_LOCK:
-    if CLK_FREQ == '160MHz' :
-        pll_addr = PLL_160M
-    else:
-        pll_addr = PLL_200M
-    ret_value = Read_byte_PLL(0x00, nof_bytes = 1, ADDRESS=pll_addr)
-    status_pll = int(ret_value,2)
-    if status_pll == 0x04:
-        print("PLL in lock")
-    elif (status_pll & 0x10) > 0:
-        print("Not Locked --> No 10 MHz ref")
-    else:
-        print("Not locked --> PLL Error")
-
-if READ_REGS:
-    read_all_regs_pll(CLK_FREQ)
-
-if READ_ALL:
-    read_all_regs_pll(CLK_FREQ)
-    read_IO_expanderis()
-
-if READ_SENSOR:
-    apsct_sensors()
-read_lol(CLK_FREQ) 
+            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/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")