diff --git a/applications/apertif/commissioning/tests/verify_correlator_db_output.py b/applications/apertif/commissioning/tests/verify_correlator_db_output.py
index 71e9f272c0ed2d7456aef4833e069355cab527c6..798830803cfda79fda65673daefe5a79b09fa0ab 100644
--- a/applications/apertif/commissioning/tests/verify_correlator_db_output.py
+++ b/applications/apertif/commissioning/tests/verify_correlator_db_output.py
@@ -32,16 +32,20 @@
    
    The test verifies the read data and runs independently per PN on the correlator. The test does not access
    the telescopes, but some --tel must be specified because --tel and --pol are used to derive the active 
-   telescope paths (TP) which will result in non-zero visibility data. The assumption is that the telescopes
-   are receiving (any noise) and outputting beamlets.
+   telescope paths (TP) which will result in non-zero visibility data. The assumption is that the selected
+   --tel telescopes are receiving (sky) noise and are outputting beamlets.
    
    Usage:
-   # -v 5 or 6 for debugging
-   # -v 0, 1, 2 or 3 for regression test
+   # use verbosity level -v 5 or 6 for debugging, or -v 0, 1, 2 or 3 for regression test
    
-   # At central correlator verify expected result via DB
+   # First start the dishes and central boards
    > python $RADIOHDL/applications/apertif/commissioning/main.py --app apertif-dev --tel a --pol 0,1 --unb 0,1
-   > python $RADIOHDL/applications/apertif/commissioning/tests/verify_correlator_db_output.py -v 3 --tel a --unb 0 --fn 0:3 --bn 0:3 --beamlets 0 --channels 0
+
+   # Set dish BF weights to have one SP per CB for all beamlets and rely on the measured sky noise to provide ADC input
+   > ssh lcu-rta "python $UPE/peripherals/pi_apertif_system.py -v 3 --unb 0:7 --fn 0:3 --cmd 62 --cbeams 0:36 --pol 0,1 --subbands 0:511 --globalsp 56 --weight 32767,0"
+
+   # At central correlator verify expected visibility packet result via DB output
+   > python $RADIOHDL/applications/apertif/commissioning/tests/verify_correlator_db_output.py -v 3 --tel a --unb 0,1 --fn 0:3 --bn 0:3 --beamlets 0 --channels 0
 """
 
 ###############################################################################
@@ -53,39 +57,11 @@ import test_case
 import node_io
 import pi_diag_data_buffer
 import unb_apertif as apr
+import pi_apertif_system as pi_apr
 
-N_dish         = 12
-N_pol          = 2
-N_tp           = N_pol * N_dish  # = 24
-N_vis          = N_tp * (N_tp + 1) / 2
-N_band         = 16
-N_slot         = 1024
-N_clk          = 256
-N_blk          = 176      # 8b mode
-Q_interleave   = 2
-M_blk          = N_blk / Q_interleave   # = 88
-N_int_x        = 800000
-N_chan_x       = 64
-N_chan_x_w     = cm.ceil_log2(N_chan_x)
-nof_bn         = 4        # Number of back node FPGAs (BN) per UniBoard
-nof_fn         = 4        # Number of front node FPGAs (FN) per UniBoard
-nof_pn         = 8        # Number of processing node FPGAs per UniBoard, = nof_fn + nof_bn
-nof_10g        = 3        # Number of external 10G links per PN on UniBoard
-c_dbSize       = 1024
-c_dbPattern    = [13]*c_dbSize
-
-c_baseSrcMac = 0x002286080000
-
-c_wcudata = 1      # is 1 if Data Write is 'wcudata1', else is 2 if DataWriter is 'wcudata2', as defined in central_commands.sh
-if c_wcudata==1:
-    c_baseDstMacEth01 = 0xe41d2de42690
-    c_baseDstMacEth23 = 0xe41d2dbc3cd0
-else:
-    c_baseDstMacEth01 = 0xe41d2dbc3dc0
-    c_baseDstMacEth23 = 0xe41d2de40d30
-
-c_expIdMarker = 65
-c_expIdVersion = 2
+N_vis          = pi_apr.N_vis        # = 300
+M_blk          = pi_apr.M_blk        # = 88 or 120
+N_chan_x       = pi_apr.N_chan_x     # = 64
 
 ###############################################################################
 # Instantiations
@@ -97,219 +73,81 @@ activeDishes        = apr.get_active_dishes(tc.telStrList)
 activePolarizations = tc.polNrs
 activeTp            = apr.get_active_telescope_paths(activeDishes, activePolarizations)
 activeVis           = apr.get_active_visibilities(activeTp)
+activeAutoVis       = apr.get_auto_visibilities(activeTp)
 
 # Declaring an object of this class is equivalent to func_apertif_unb1_correlator_packet_info() in VHDL.
 corrPacketInfo = apr.CorrelatorPacketInfo(N_vis)
 
-# One or more beamlet and channel indices to select the visibility packets
-beamlets = tc.beamlets
-if not cm.exist_all_elements_from_a_in_b(beamlets, range(M_blk)):
-    tc.append_log(tc.V_ERRORS, 'Specified --beamlets %s must fit in range(%d)' % (tc.beamlets, M_blk))
+# One or more beamlet indices to select the channel visibility packets
+beamletIndices = tc.beamlets
+if not cm.exist_all_elements_from_a_in_b(beamletIndices, range(M_blk)):
+    tc.append_log(tc.V_ERRORS, 'Specified --beamlets %s must fit in range(%d)' % (beamletIndices, M_blk))
     tc.set_result('FAILED')
     sys.exit()
 
-channels = tc.channels
-if not cm.exist_all_elements_from_a_in_b(channels, range(N_chan_x)):
-    tc.append_log(tc.V_ERRORS, 'Specified --channels %s must fit in range(%d)' % (tc.channels, N_chan_x))
+channelIndices = tc.channels
+if not cm.exist_all_elements_from_a_in_b(channelIndices, range(N_chan_x)):
+    tc.append_log(tc.V_ERRORS, 'Specified --channels %s must fit in range(%d)' % (channelIndices, N_chan_x))
     tc.set_result('FAILED')
     sys.exit()
 
 tc.append_log(3, '>>>')
 tc.append_log(1, '>>> Title : Test case to verify visibility packet header and zero/non-zero payload of correlator DB OUTPUT for %s' % tc.unb_nodes_string())
-tc.append_log(3, '>>>         . beamlet(s) : %s (from range(M_blk) = [0:%d])' % (beamlets, M_blk-1))
-tc.append_log(3, '>>>         . channel(s) : %s (from range(N_chan_x) = [0:%d])' % (channels, N_chan_x-1))
+tc.append_log(3, '>>> . beamlet(s) : %s (from index range(M_blk) = [0:%d])' % (beamletIndices, M_blk-1))
+tc.append_log(3, '>>> . channel(s) : %s (from index range(N_chan_x) = [0:%d])' % (channelIndices, N_chan_x-1))
+tc.append_log(3, '')
+tc.append_log(3, '>>> . activeDishes        : %s is %s from %s' % (activeDishes, tc.telStrList, apr.wsrtTelescopes))
+tc.append_log(3, '>>> . activePolarizations : %s is %s' % (activePolarizations, tc.to_pol_xy(tc.polNrs)))
+tc.append_log(3, '>>> . activeTp            : %s' % activeTp)
+tc.append_log(3, '>>> . activeVis           : %s' % activeVis)
+tc.append_log(3, '>>> . activeAutoVis       : %s' % activeAutoVis)
 tc.append_log(3, '>>>')
 tc.append_log(3, '')
 
 io = node_io.NodeIO(tc.nodeImages, tc.base_ip)
 
-# Create one DB object for all PN
-db_output = pi_diag_data_buffer.PiDiagDataBuffer(tc, io, instanceName='OUTPUT', nofStreams=1, ramSizePerStream=c_dbSize, version=0)
+# Create correlator output object
+rsub  = pi_apr.declare_reorder_subbands(tc, io)
+rbeam = pi_apr.declare_reorder_beamlets(tc, io)
+corDb = pi_apr.CorrelatorOutputDataBuffer(tc, io, rsub, rbeam, dbSize=1024)   # assume minimum DB size that can fit 1 visibility packet
 
+###############################################################################
+# Stimuli 
 
-for bui in beamlets:
-    for ch in channels:
-        ###############################################################################
-        # Stimuli 
+# Setup and read DB output to capture the visibility packets
+for bui in beamletIndices:
+    for chi in channelIndices:
+        corDb.read_correlator_db_output(bui, chi)
         
-        # Overwrite DB data to be able to recognize new data
-        db_output.overwrite_all_data_buffers(data=c_dbPattern, vLevel=9)
-
-        # Use start of packet address for DB sync delay
-        sopAddr = ((bui * N_chan_x) + ch) * corrPacketInfo.vis_packet_size          
-        dbSyncDelay = sopAddr
-        db_output.write_sync_delay(data=dbSyncDelay)
-        db_output.read_sync_delay()
-        # when dbSyncDelay=0 then the DB is written every sync
-        # else need to arm the DB using write access strobe to capture once at next sync + dbSyncDelay + latency(14)
-        if dbSyncDelay>0:
-            # Wait unit DB is ready to arm
-            tc.sleep(2)
-            db_output.write_arm_reg(data=1)
-        # Wait unit DB have been filled        
-        tc.sleep(2)
+# Verify visibility packet headers 
+corDb.verify_correlator_db_output_headers(beamletIndices, channelIndices)
         
-        # Read DB on all PN
-        dbOutputReadData = db_output.read_data_buffer(nofColumns=tc.nofCol)
-
-        ###############################################################################
-        # Verification per PN
-        for ni,nr in enumerate(tc.nodeNrs):  # loop PN
-            # Get read packet data
-            rdData = dbOutputReadData[ni]
-            
-            # Get packet info
-            unb = nr / nof_pn       # = range(N_band) = 0:15 = [0:127] / 8
-            pn  = nr % nof_pn       # = range(nof_pn) = 0:7 = [0:127] % 8
-            reportStr = 'unb = %2d, pn = %d, bui = %3d, ch = %2d : ' % (unb, pn, bui, ch)
-
-            ###################################################################
-            # Verify header fields
-            
-            # Beamlet mapping as specified in SP-062 table 6 (SC1), 9 (SC4):
-            #
-            #   u  bi  dest        bui
-            #   0   0    0    FN0    0    2  ...
-            #   1   0    1    FN1  256  258  ...
-            #   2   0    2    FN2  512  514  ...
-            #   3   0    3    FN3  768  770  ...
-            #   0   1    4    BN0    1    3  ...
-            #   1   1    5    BN1  257  259  ...
-            #   2   1    6    BN2  513  515  ...
-            #   3   1    7    BN3  769  771  ...
-            #
-            # Beamlet mapping as implemented in Apertif X due to reordered_sosi_arr wiring in node_apertif_unb1_correlator_mesh.vhd:
-            #
-            #   u  bi  dest        bui
-            #   0   0    0    FN0    0    2  ...
-            #   1   0    1    FN1    1    3  ...
-            #   2   0    2    FN2  256  258  ...
-            #   3   0    3    FN3  257  259  ...
-            #   0   1    4    BN0  512  514  ...
-            #   1   1    5    BN1  513  515  ...
-            #   2   1    6    BN2  768  770  ...
-            #   3   1    7    BN3  769  771  ...
-            expBeamlet = N_slot * unb                                                    # offset for UniBoard band
-            expBeamlet = expBeamlet + N_clk * (pn / Q_interleave) + (pn % Q_interleave)  # offset for Local PN
-            expBeamlet = expBeamlet + bui * Q_interleave                                 # offset serial beamlets
-            expChannel = cm.reverse_bits(ch, N_chan_x_w)
-            expChannel = cm.invert_msbit(expChannel, N_chan_x_w)
-
-            # Header fields
-            #    0 gap(16) + ETH dst mac hi(16)
-            #    1 ETH dst mac lo
-            #    2 ETH src mac hi
-            #    3 ETH src mac lo(16) + type(16)
-            #    4 IP
-            #    5 IP
-            #    6 IP
-            #    7 IP src addr
-            #    8 IP dst addr
-            #    9 UDP src port(16) + dst port(16)
-            #   10 UDP total len(16) + checksum(16)
-            #   11 ID marker(8) + version(8) + beamlet(16)
-            #   12 ID channel(16) + rsvd(16)
-            #   13 ID timestamp hi
-            #   14 ID timestamp lo
-            #   15 FLAG
-            #   16 FLAG
-            #   17 FLAG
-            #   18 FLAG
-            #   19 FLAG
-            #   20 FLAG
-            
-            # . dst mac as defined in central_commands.sh
-            if   unb< 4: expDstMac = c_baseDstMacEth01
-            elif unb< 8: expDstMac = c_baseDstMacEth01 + 1
-            elif unb<12: expDstMac = c_baseDstMacEth23
-            elif unb<16: expDstMac = c_baseDstMacEth23 + 1
-
-            rdDstMac  = cm.to_unsigned(rdData[0], 16) << 32
-            rdDstMac += cm.to_unsigned(rdData[1], 32)
-            if rdDstMac == expDstMac:
-                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'destination MAC = 0x%012X is OK' % rdDstMac)
-            else:
-                tc.append_log(tc.V_ERRORS, reportStr + 'read destination MAC is wrong (read 0x%012X != 0x%012X expected)' % (rdDstMac, expDstMac))
-                tc.set_result('FAILED')
-
-            # . src mac
-            expSrcMac = c_baseSrcMac + unb * 256 + pn
-            rdSrcMac  = cm.to_unsigned(rdData[2], 32) << 16
-            rdSrcMac += cm.to_unsigned(rdData[3], 32) >> 16
-            if rdSrcMac == expSrcMac:
-                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'source MAC = 0x%012X is OK' % rdSrcMac)
-            else:
-                tc.append_log(tc.V_ERRORS, reportStr + 'read source MAC is wrong (read 0x%012X != 0x%012X expected)' % (rdSrcMac, expSrcMac))
-                tc.set_result('FAILED')
 
-            # . ID marker
-            rdIdMarker = cm.to_unsigned(rdData[11], 32) >> 24
-            if rdIdMarker == c_expIdMarker:
-                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'ID marker = %d is OK' % rdIdMarker)
-            else:
-                tc.append_log(tc.V_ERRORS, reportStr + 'read ID marker is wrong (read %d != %d expected)' % (rdIdMarker, c_expIdMarker))
-                tc.set_result('FAILED')
-
-            # . ID version
-            rdIdVersion = cm.to_unsigned(rdData[11], 24) >> 16
-            if rdIdVersion == c_expIdVersion:
-                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'ID version = %d is OK' % rdIdVersion)
-            else:
-                tc.append_log(tc.V_ERRORS, reportStr + 'read ID version is wrong (read %d != %d expected)' % (rdIdVersion, c_expIdVersion))
-                tc.set_result('FAILED')
-                
-            # . beamlet
-            rdBeamlet = cm.to_unsigned(rdData[11], 16)
-            if rdBeamlet == expBeamlet:
-                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'beamlet = %d is OK' % rdBeamlet)
-            else:
-                tc.append_log(tc.V_ERRORS, reportStr + 'read beamlet is wrong (read %d != %d expected)' % (rdBeamlet, expBeamlet))
-                tc.set_result('FAILED')
-              
-            # . channel
-            rdChannel = cm.to_unsigned(rdData[12], 32) >> 16
-            if rdChannel == expChannel:
-                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'channel = %d is OK' % rdChannel)
-            else:
-                tc.append_log(tc.V_ERRORS, reportStr + 'read channel is wrong (read %d != %d expected)' % (rdChannel, expChannel))
-                tc.set_result('FAILED')
-    
-            # . timestamp (= bsn)
-            rdBsn  = cm.to_unsigned(rdData[13], 32) << 32
-            rdBsn += cm.to_unsigned(rdData[14], 32)
-            tc.append_log(tc.V_INFO_DETAILS, reportStr + 'read BSN = %d' % rdBsn)
+# Verify visibility payload data
+# 
+# A visibility packet contains all N_vis = 300 complex visibilities for one (beamlet, channel):
+# - The visibilities for which at least one TP is inactive must be zero, because for those TP the
+#   firmware forces the data to zero.
+# - If both TP in a visibility are active, then the visibility can be non-zero. The visibility 
+#   can still be zero if the BF weights for the corresponding beamlet are zero.
+#
+# Visibility (col, row) indices for
+#    0 vis (0,0)
+#    1 vis (0,1)   24 vis (1,1)
+#    2 vis (0,2)   25 vis (1,2)    47 vis (2,2)
+#    3 vis (0,3)   26 vis (1,3)    48 vis (2,3)
+#  ...                ...                 ...                  
+#   23 vis (0,23)  46 vis (1,23)   68 vis (2,23) ... 299 vis (23,23)
+#  
+#             24           + 23            + 22  ...            + 1 = 300 visibilities
+#
+for gn in tc.nodeNrs:  # loop active PN
+    unb, pn = apr.correlator_gn_to_unb_pn(gn)
+    for bui in beamletIndices:  # loop serial beamlets
+        for chi in channelIndices:   # loop serial channels
+            reportStr = 'unb = %2d, pn = %d, bui = %3d, chi = %2d : ' % (unb, pn, bui, chi)
 
-            ###################################################################
-            # Verify payload data
-            # 
-            # A visibility packet contains all N_vis = 300 visibilities for one (beamlet, channel):
-            # - The visibilities for which at least one TP is inactive must be zero, because for those TP the
-            #   firmware forces the data to zero.
-            # - If both TP in a visibility are active, then the visibility can be non-zero. The visibility 
-            #   can still be zero if the BF weights for the corresponding beamlet are zero.
-            #
-            # Visibility (col, row) indices for
-            #    0 vis (0,0)
-            #    1 vis (0,1)   24 vis (1,1)
-            #    2 vis (0,2)   25 vis (1,2)    47 vis (2,2)
-            #    3 vis (0,3)   26 vis (1,3)    48 vis (2,3)
-            #  ...                ...                 ...                  
-            #   23 vis (0,23)  46 vis (1,23)   68 vis (2,23) ... 299 vis (23,23)
-            #  
-            #             24           + 23            + 22  ...            + 1 = 300 visibilities
-            #
-            # The visibilities are stored in two 32b words: real, imag, so in total 600 words payload.
-            rdPayload = rdData[corrPacketInfo.vis_header_size:corrPacketInfo.vis_header_size+N_vis*cm.c_nof_complex]
-            rdVisibilities = []
-            even = True
-            for data in rdPayload:
-                dataNtoh = cm.reverse_word(data)    # change endianess
-                if even:
-                    visReal = cm.to_signed(dataNtoh, 32)
-                else:
-                    visImag = cm.to_signed(dataNtoh, 32)
-                    rdVisibilities.append(complex(visReal, visImag))
-                even = not even
+            rdVisibilities = corDb.dbOutputVisibilities[gn][bui][chi]
                     
             # Find the TP for all read visibilities that are non-zero
             nonZeroVisibilities = cm.find_indices_where_ne(rdVisibilities, 0)
@@ -319,9 +157,12 @@ for bui in beamlets:
             nonZeroPolarizations = apr.tp_to_pol_indices(nonZeroTp)
             nonZeroAutoVisibilities = apr.get_auto_visibilities(nonZeroTp)
 
-            # Verify that the active TP yield non-zero visibilities
+            # Verify that only the active TP yield non-zero visibilities
             # . Assume the BF weights have been set to select at least one ADC input per CB then the input (sky) noise at the ADC
             #   will contribute to all channels, so the visibilities for the TP pairs of active TP will then be none zero.
+            for vi in nonZeroVisibilities:
+                tc.append_log(tc.V_INFO_DETAILS, reportStr + 'Non zero correlation[%d] = %s' % (vi, rdVisibilities[vi]))
+              
             if activeTp == nonZeroTp:
                 tc.append_log(tc.V_INFO_DETAILS, reportStr + 'Non zero TP = %s : polarizations = %s and dishes = %s are OK' % (nonZeroTp, nonZeroPolarizations, nonZeroDishes))
             else:
@@ -333,7 +174,10 @@ for bui in beamlets:
             for ai in nonZeroAutoVisibilities:
                 atp2 = apr.visibilities_to_tp_pairs(ai)
                 atp = atp2[0][0]   # get tp from list with one tuple(tp, tp), both tp in tuple are the same index for auto correlation visibility
-                if not (rdVisibilities[ai].real>0 and rdVisibilities[ai].imag==0):
+                if rdVisibilities[ai].real>0 and rdVisibilities[ai].imag==0:
+                    tc.append_log(tc.V_INFO_DETAILS, reportStr + 'Auto correlation[%d] = %s' % (ai, rdVisibilities[ai]))
+                else:
+                    tc.append_log(tc.V_ERRORS, reportStr + 'Auto correlation[%d] = %s is wrong' % (ai, rdVisibilities[ai]))
                     atpFail.append(atp)
                 #print ai, atp2, atp, rdVisibilities[ai]
             if len(atpFail)==0:
@@ -348,3 +192,4 @@ for bui in beamlets:
 tc.set_section_id('')
 tc.append_log(tc.V_RESULT, '>>> Test result: %s' % tc.get_result())
 
+