diff --git a/src/constants.h b/src/constants.h
index fbc39cc04cb0bd3581a00ffd7807693b4fc9ebf6..bc4d7344d83bfdc87d038a76b96e57d5d6b101f8 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -52,5 +52,5 @@
 #define C_A_pn 6  // Number of dual polarization antennas per Processing Node (PN) FPGA.
 #define C_N_beamlets 976  // Number of beamlets per antenna band  488, 976
 
-
+#define C_200MHZ_1_CNT_NS 5  // Time of one cnt in nS
 #endif
diff --git a/src/fpga.cpp b/src/fpga.cpp
index 7b36d9c4cfc1afcdbe8296f0f9678e82df25bdb9..e447b5ba84b4e66a84e2ff5d1dadbbdd0b842f1e 100644
--- a/src/fpga.cpp
+++ b/src/fpga.cpp
@@ -54,9 +54,6 @@ Fpga::Fpga(list<class Node*>& nodelist)
     // Add points:
     vector<int> nodes = get_all_nodes();
 
-    pointMap->add_register("FPGA_mask_R",                                    "fpga/enable_mask",                            nodes.size(), 1, "RO", REG_FORMAT_BOOLEAN);
-    pointMap->add_register("FPGA_mask_RW",                                   "fpga/enable_mask",                            nodes.size(), 1, "RW", REG_FORMAT_BOOLEAN);
-    pointMap->add_register("FPGA_status_R",                                  "fpga/status",                                 nodes.size(), 1, "RO", REG_FORMAT_BOOLEAN);
     pointMap->add_register("FPGA_temp_R",                                    "fpga/temp",                                   nodes.size(), 1, "RO", REG_FORMAT_DOUBLE);
     pointMap->add_register("FPGA_firmware_version_R",                        "fpga/firmware_version",                       nodes.size(), 1, "RO", REG_FORMAT_STRING);
     pointMap->add_register("FPGA_hardware_version_R",                        "fpga/hardware_version",                       nodes.size(), 1, "RO", REG_FORMAT_STRING);
@@ -173,11 +170,48 @@ vector<bool> Fpga::get_all_enabled_nodes(void)
 {
     vector<bool> enabled;
     for (auto node : FPGA) {
-        enabled.push_back(node->GetEnabled());
+        enabled.push_back(node->isEnabled());
     }
     return enabled;
 }
 
+vector<bool> Fpga::get_all_masked_nodes(void)
+{
+    vector<bool> masked;
+    for (auto node : FPGA) {
+        masked.push_back(node->isMasked());
+    }
+    return masked;
+}
+
+vector<bool> Fpga::get_all_offline_nodes(void)
+{
+    vector<bool> online;
+    for (auto node : FPGA) {
+        online.push_back(!node->isOnline());
+    }
+    return online;
+}
+
+void Fpga::set_all_masked_nodes(std::vector<bool> masked)
+{
+    vector<int> nodes = get_all_nodes();
+    for (uint idx = 0; idx<nodes.size(); idx++) {
+        auto node = select_node(nodes[idx]);
+        node->setMasked(masked[idx]);
+    }
+}
+
+vector<uint32_t> Fpga::get_all_pps_offset_cnt(void)
+{
+    vector<uint32_t> pps_offset_cnt;
+    for (auto node : FPGA) {
+        pps_offset_cnt.push_back(node->ppsOffsetCnt());
+    }
+    return pps_offset_cnt;
+}
+
+
 uint Fpga::node_number(Node *node)
 {
     return (node->GetUniboardNr() * 4) + node->GetLocalNr();
diff --git a/src/fpga.h b/src/fpga.h
index cfa1ca83854e199b808d271100ba2e7b397b2c20..14f62ec1d3a79dc84cdcfaf38e264d6f10b14ba5 100644
--- a/src/fpga.h
+++ b/src/fpga.h
@@ -54,6 +54,11 @@ public:
     Node * select_node(const int nr);
     std::vector<int> get_all_nodes(void);
     std::vector<bool> get_all_enabled_nodes(void);
+    std::vector<bool> get_all_masked_nodes(void);
+    std::vector<bool> get_all_offline_nodes(void);
+    void set_all_masked_nodes(std::vector<bool> masked);
+    std::vector<uint32_t> get_all_pps_offset_cnt(void);
+
     CPointMap * get_pointMap(void);
     uint node_number(Node *node);
     bool is_fpga(const std::string addr);
diff --git a/src/map.cpp b/src/map.cpp
index eb605595025d9d4078909d2516c43027cbae2659..d584a8111c5feb41cb2abb7e2b36a51165a0b053 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -64,7 +64,6 @@ bool UniboardMap::read(TermOutput& termout,
     return retval;
 }
 
-
 bool UniboardMap::write(TermOutput& termout,
                         const string addr,
                         const unsigned int *data,
diff --git a/src/node.cpp b/src/node.cpp
index 270a87d483f4cfcfcdee8de50c5479372267267f..64cbe881e456f63fc982798ac02ed1c29832bb3a 100644
--- a/src/node.cpp
+++ b/src/node.cpp
@@ -161,13 +161,13 @@ Node::Node(const string ipaddr, const uint unb, const uint localnr, const string
            const string firmware, const uint firmware_version, const bool enabled)
 {
     // int ret;
-    myIPaddr = ipaddr;
-
-    periph_fpga = new Periph_fpga(ipaddr, firmware, firmware_version, enabled);
-
     UniboardNr = unb;
     LocalNr = localnr;
     GlobalNr = localnr + 4 * unb;
+    
+    myIPaddr = ipaddr;
+    periph_fpga = new Periph_fpga(GlobalNr, ipaddr, firmware, firmware_version, enabled);
+
 
     if (type != "pn") {
         throw runtime_error("invalid node type: \"" + type + "\"");
@@ -233,11 +233,31 @@ CMMap * Node::get_MMap(void)
     return periph_fpga->getMMap();
 }
 
-bool Node::GetEnabled(void)
+bool Node::isEnabled(void)
 {
-    return periph_fpga->getEnabled();
+    return periph_fpga->isEnabled();
 }
 
+bool Node::isOnline(void)
+{
+    return periph_fpga->isOnline();
+}
+
+bool Node::isMasked()
+{
+    return periph_fpga->isMasked();
+}
+
+void Node::setMasked(bool mask)
+{
+    periph_fpga->setMasked(mask);
+}
+
+uint32_t Node::ppsOffsetCnt(void) {
+    return periph_fpga->ppsOffsetCnt();
+}
+
+
 bool Node::exec_cmd(const char cmd, const string relative_addr,
                     const string type, const string instance, const unsigned int *data,
                     const int nvalues, const int format)
@@ -280,7 +300,7 @@ bool Node::exec_reply(TermOutput& termout)
     // cout << "received " << r->nof_vals << " bytes from worker node=" << GlobalNr 
     //      << " retval=" << r->retval << " datatype=" << r->datatype << endl;
 
-    if (GetEnabled() && !r->retval) {
+    if (isEnabled() && !r->retval) {
         //cout << "no retval" << endl;
         throw runtime_error("no retval");
     }
diff --git a/src/node.h b/src/node.h
index 52ce52b8b364a2bf0bff15f8a5fce39ffc4bc678..4006e2924fa4eb044d7c24013f3a68adf862a0d7 100644
--- a/src/node.h
+++ b/src/node.h
@@ -91,7 +91,11 @@ class Node {
   const uint GetGlobalNr()   { return GlobalNr; }
   const uint GetNr()         { return LocalNr; }
   const std::string GetType() { return Type; }
-  bool GetEnabled(void);
+  bool isEnabled(void);
+  bool isOnline(void);
+  bool isMasked();
+  void setMasked(bool mask);
+  uint32_t ppsOffsetCnt(void);
 
   bool monitor(TermOutput& t);
 
diff --git a/src/opcua/ua_server.cpp b/src/opcua/ua_server.cpp
index 8a9faecebf84098aecfabb55359bf07083e4dea2..e56d50cdb3dc39efee93ad82505412bf38a5a1fb 100644
--- a/src/opcua/ua_server.cpp
+++ b/src/opcua/ua_server.cpp
@@ -78,7 +78,9 @@ void ua_print_nodeid(UA_NodeId *nd)
 }
 
 
-
+/*
+* ua_read_DataSource(), this function is called by the opc-ua server to read a point.
+*/
 static UA_StatusCode ua_read_DataSource(UA_Server *server,
                 const UA_NodeId *sessionId, void *sessionContext,
                 const UA_NodeId *nodeId, void *nodeContext,
@@ -90,7 +92,7 @@ static UA_StatusCode ua_read_DataSource(UA_Server *server,
     regname[nodeId->identifier.string.length] = 0; // add end of string char
 
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
-                "ua_read_DataSource: from node %s",regname);
+                "ua_read_DataSource: reading from %s",regname);
 
     UA_NodeId currentNodeId = UA_NODEID_STRING(mUaLofarNameSpace, (char *)regname);
     UA_NodeId ntype;
@@ -109,24 +111,25 @@ static UA_StatusCode ua_read_DataSource(UA_Server *server,
     // weird...  must do -1
     // see: https://open62541.org/doc/current/types.html
 
-    // Translator regnames start with "TR_" others "FPGA_"
-    bool regname_is_for_translator = false;
-    if (strncmp(regname,"TR_", 3) == 0) {
-        regname_is_for_translator = true;
-        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"ua_read_DataSource: for translator class");
-    }
-
     TermOutput termout;
 
-    if (regname_is_for_translator) {
-        try {
+    // read values for point
+    try {
+        // Translator regnames start with "TR_" others "FPGA_"
+        if (strncmp(regname,"TR_", 3) == 0) {
+            // UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"ua_read_DataSource: for translator class");
             SD.tr->read(termout, regname, -1);
-        } catch (runtime_error& e) {
-            cerr << "ua_read_DataSource error: " << e.what() << endl;
         }
+        else {
+            // UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"ua_read_DataSource: for fpga class");
+            SD.unb->read(termout, regname, -1);
+        }
+    } catch (runtime_error& e) {
+        cerr << "ua_read_DataSource error: " << e.what() << endl;
+    }
 
-
-        // TR_* has only scalars
+    // if scalar (only TR_* points)
+    if (termout.nof_vals == 1) {
         switch (ntype.identifier.numeric - 1) {
             case UA_TYPES_STRING: {
                 char *ptr = (char *)termout.val;
@@ -193,14 +196,8 @@ static UA_StatusCode ua_read_DataSource(UA_Server *server,
             } break;
         }
     }
+    // else array
     else {
-        try {
-            SD.unb->read(termout, regname, -1);
-        } catch (runtime_error& e) {
-            cerr << "ua_read_DataSource: error: " << e.what() << endl;
-        }
-
-        // FPGA_* has only arrays
         switch (ntype.identifier.numeric - 1) {
             case UA_TYPES_STRING: {
                 UA_String *values = (UA_String *) UA_Array_new(termout.nof_vals, &UA_TYPES[UA_TYPES_STRING]);
@@ -297,6 +294,9 @@ static UA_StatusCode ua_read_DataSource(UA_Server *server,
     return UA_STATUSCODE_GOOD;
 }
 
+/*
+* ua_write_DataSource(), this function is called by the opc-ua server to write a point.
+*/
 static UA_StatusCode ua_write_DataSource(UA_Server *server,
                  const UA_NodeId *sessionId, void *sessionContext,
                  const UA_NodeId *nodeId, void *nodeContext,
@@ -314,207 +314,119 @@ static UA_StatusCode ua_write_DataSource(UA_Server *server,
     UA_NodeId ntype;
     UA_Server_readDataType(server, currentNodeId, &ntype);
 
+    // TODO: can this be removed.
+    // UA_Variant nv_dims;
+    // https://open62541.org/doc/0.2/server.html 
+    // UA_Server_readArrayDimensions(server, currentNodeId, &nv_dims);
+
+    // check if there is data to write, if not retval remains false.
+    if (data->hasValue && data->value.arrayLength > 0) {
+        //data.value is of type UA_Variant.
+        uint type = ntype.identifier.numeric - 1;
+        uint32_t *data_sdp;
+        uint32_t sz;
+        if ((type == UA_TYPES_DOUBLE) || (type == UA_TYPES_INT64) || (type == UA_TYPES_UINT64)) {
+            sz = data->value.arrayLength * 2;
+        } else if (type == UA_TYPES_STRING) {
+            sz = data->value.arrayLength * (SIZE1STRING / 4);
+        } else {
+            sz = data->value.arrayLength;
+        }
+        data_sdp = new uint32_t[sz];
+        memset((void *)data_sdp, 0, sizeof(uint32_t)*sz);
 
-    // Translator regnames start with "TR_" others "FPGA_"
-    bool regname_is_for_translator = false;
-    if (strncmp(regname,"TR_",3) == 0) {
-        regname_is_for_translator = true;
-        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"ua_write_DataSource: for translator class");
-    }
-
-    if (regname_is_for_translator) {
-        // TR_* has only scalars
-        if (data->hasValue) {
-            unsigned int array_length = 1;
-            uint32_t *data_sdp = new uint32_t[array_length];
-            
-            switch (ntype.identifier.numeric - 1) {
-                case UA_TYPES_STRING: {
-                    retval = false;
-                } break;
-                case UA_TYPES_FLOAT: {
-                    retval = false;
-                } break;
-                case UA_TYPES_DOUBLE: {
-                    retval = false;
-                } break;
-                case UA_TYPES_INT16: {
-                    retval = true;
-                    int16_t *dptr = (int16_t *)data->value.data;
-                    for (uint i=0; i<array_length; i++) {
-                      data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_UINT16: {
-                    retval = true;
-                    uint16_t *dptr = (uint16_t *)data->value.data;
-                    for (uint i=0; i<array_length; i++) {
-                      data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_INT32: {
-                    retval = true;
-                    int32_t *dptr = (int32_t *)data->value.data;
-                    for (uint i=0; i<array_length; i++) {
-                      data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_UINT32: {
-                    retval = true;
-                    uint32_t *dptr = (uint32_t *)data->value.data;
-                    for (uint i=0; i<array_length; i++) {
-                      data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_INT64: {
-                    retval = true;
-                    int64_t *dptr = (int64_t *)data->value.data;
-                    for (uint i=0; i<array_length; i++) {
-                      data_sdp[i] = (uint64_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_UINT64: {
-                    retval = true;
-                    uint64_t *dptr = (uint64_t *)data->value.data;
-                    for (uint i=0; i<array_length; i++) {
-                      data_sdp[i] = (uint64_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_BOOLEAN: {
-                    retval = true;
-                    bool *dptr = (bool *)data->value.data;
-                    for (unsigned int i=0; i<array_length; i++) {
-                      data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                default: {
-                    retval = false;
-                } break;
-            }
-            if (retval) {
-                TermOutput termout;
-
-                try {
-                    SD.tr->write(termout, regname, data_sdp, array_length);
-                } catch (runtime_error& e) {
-                    cerr << "write_TR_DataSource error: " << e.what() << endl;
-                    retval = false;
+        retval = true;
+        switch (ntype.identifier.numeric - 1) {
+            case UA_TYPES_STRING: {
+                UA_String *dptr = (UA_String *)data->value.data;
+                UA_String str_data;
+                for (int i=0; i<(int)data->value.arrayLength; i++){
+                    str_data = dptr[i];
+                    memcpy((void *)&(data_sdp[i*(SIZE1STRING/4)]), (void *)str_data.data, str_data.length);
                 }
-            }
-            delete[] data_sdp;
+            } break;
+            case UA_TYPES_FLOAT: {
+                memcpy(data_sdp, data->value.data, data->value.arrayLength*sizeof(UA_Float));
+            } break;
+            case UA_TYPES_DOUBLE: {
+                memcpy(data_sdp, data->value.data, data->value.arrayLength*sizeof(UA_Double));
+            } break;
+            case UA_TYPES_INT16: {
+                int16_t *dptr = (int16_t *)data->value.data;
+                for (uint i=0; i<data->value.arrayLength; i++) {
+                  data_sdp[i] = (uint32_t)dptr[i];
+                }
+            } break;
+            case UA_TYPES_UINT16: {
+                uint16_t *dptr = (uint16_t *)data->value.data;
+                for (uint i=0; i<data->value.arrayLength; i++) {
+                  data_sdp[i] = (uint32_t)dptr[i];
+                }
+            } break;
+            case UA_TYPES_INT32: {
+                int32_t *dptr = (int32_t *)data->value.data;
+                for (uint i=0; i<data->value.arrayLength; i++) {
+                    data_sdp[i] = (uint32_t)dptr[i];
+                }
+            } break;
+            case UA_TYPES_UINT32: {
+                uint32_t *dptr = (uint32_t *)data->value.data;
+                for (uint i=0; i<data->value.arrayLength; i++) {
+                    data_sdp[i] = (uint32_t)dptr[i];
+                }
+            } break;
+            case UA_TYPES_INT64: {  // TODO: 64b -> 32b, not used yet but should give an error
+                int64_t *dptr = (int64_t *)data->value.data;
+                for (uint i=0; i<data->value.arrayLength; i++) {
+                    data_sdp[i] = (uint64_t)dptr[i];
+                }
+            } break;
+            case UA_TYPES_UINT64: {  // TODO: 64b -> 32b, not used yet but should give an error
+                uint64_t *dptr = (uint64_t *)data->value.data;
+                for (uint i=0; i<data->value.arrayLength; i++) {
+                    data_sdp[i] = (uint64_t)dptr[i];
+                }
+            } break;
+            case UA_TYPES_BOOLEAN: {
+                bool *dptr = (bool *)data->value.data;
+                for (unsigned int i=0; i<data->value.arrayLength; i++) {
+                  data_sdp[i] = (uint32_t)dptr[i];
+                }
+            } break;
+            default: {
+                cout << "UA_TYPES_?: not implemented yet" << endl;
+                retval = false;
+            } break;
         }
-    }
-    else {
-        // FPGA_* has only arrays
-        UA_Variant nv_dims;
-        /*https://open62541.org/doc/0.2/server.html */
-        UA_Server_readArrayDimensions(server, currentNodeId, &nv_dims);
-
-        if (data->hasValue && data->value.arrayLength > 0) {
-            //data.value is a UA_Variant
-            uint type = ntype.identifier.numeric - 1;
-            uint32_t *data_sdp;
-            uint32_t sz;
-            if ((type == UA_TYPES_DOUBLE) || (type == UA_TYPES_INT64) || (type == UA_TYPES_UINT64)) {
-                sz = data->value.arrayLength * 2;
-            } else if (type == UA_TYPES_STRING) {
-                sz = data->value.arrayLength * (SIZE1STRING / 4);
-            } else {
-                sz = data->value.arrayLength;
-            }
-            data_sdp = new uint32_t[sz];
-            memset((void *)data_sdp, 0, sizeof(uint32_t)*sz);
-
-            switch (ntype.identifier.numeric - 1) {
-                case UA_TYPES_STRING: {
-                    UA_String *dptr = (UA_String *)data->value.data;
-                    UA_String str_data;
-                    for (int i=0; i<(int)data->value.arrayLength; i++){
-                        str_data = dptr[i];
-                        memcpy((void *)&(data_sdp[i*(SIZE1STRING/4)]), (void *)str_data.data, str_data.length);
-                    }
-                    retval = true;
-                } break;
-                case UA_TYPES_FLOAT: {
-                    retval = true;
-                    memcpy(data_sdp, data->value.data, data->value.arrayLength*sizeof(UA_Float));
-                } break;
-                case UA_TYPES_DOUBLE: {
-                    retval = true;
-                    memcpy(data_sdp, data->value.data, data->value.arrayLength*sizeof(UA_Double));
-                } break;
-                case UA_TYPES_INT16: {
-                    retval = true;
-                    int16_t *dptr = (int16_t *)data->value.data;
-                    for (uint i=0; i<data->value.arrayLength; i++) {
-                      data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_UINT16: {
-                    retval = true;
-                    uint16_t *dptr = (uint16_t *)data->value.data;
-                    for (uint i=0; i<data->value.arrayLength; i++) {
-                      data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_INT32: {
-                    retval = true;
-                    int32_t *dptr = (int32_t *)data->value.data;
-                    for (uint i=0; i<data->value.arrayLength; i++) {
-                        data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_UINT32: {
-                    retval = true;
-                    uint32_t *dptr = (uint32_t *)data->value.data;
-                    for (uint i=0; i<data->value.arrayLength; i++) {
-                        data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_INT64: {
-                    retval = true;
-                    int64_t *dptr = (int64_t *)data->value.data;
-                    for (uint i=0; i<data->value.arrayLength; i++) {
-                        data_sdp[i] = (uint64_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_UINT64: {
-                    retval = true;
-                    uint64_t *dptr = (uint64_t *)data->value.data;
-                    for (uint i=0; i<data->value.arrayLength; i++) {
-                        data_sdp[i] = (uint64_t)dptr[i];
-                    }
-                } break;
-                case UA_TYPES_BOOLEAN: {
-                    retval = true;
-                    bool *dptr = (bool *)data->value.data;
-                    for (unsigned int i=0; i<data->value.arrayLength; i++) {
-                      data_sdp[i] = (uint32_t)dptr[i];
-                    }
-                } break;
-                default: {
-                    cout << "UA_TYPES_?: not implemented yet" << endl;
-                    retval = false;
-                } break;
-            }
 
-            if (retval) {
-                TermOutput termout;
-                try {
+        if (retval) {
+            TermOutput termout;
+            try {
+                if (strncmp(regname,"TR_",3) == 0) {
+                    // UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"ua_write_DataSource: for translator class");
+                    SD.tr->write(termout, regname, data_sdp, data->value.arrayLength);
+                }
+                else {
+                    // UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"ua_write_DataSource: for fpga class");
                     SD.unb->write(termout, regname, data_sdp, data->value.arrayLength);
-                } catch (runtime_error& e) {
-                    cerr << "ua_write_DataSource error: " << e.what() << endl;
-                    retval = false;
                 }
+            } catch (runtime_error& e) {
+                cerr << "ua_write_DataSource error: " << e.what() << endl;
+                retval = false;
             }
-            delete[] data_sdp;
         }
+        delete[] data_sdp;
     }
 
     delete[] regname;
     return UA_STATUSCODE_GOOD;
 }
 
+
+/*
+* ua_add_Variable_[data_type]() functions to add a UA_Variant variabel (point) to the server
+*/
+
 static void ua_add_Variable_string(UA_Server *server, string regname, unsigned int size, string perm, bool is_scalar)
 {
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "ua_add_Variable_string: %s", (char *)regname.c_str());
@@ -1077,7 +989,14 @@ static void ua_add_Variable_boolean(UA_Server *server, string regname, unsigned
         cout << "Error adding node" << endl;
     }
 }
+/* end of ua_add_Variable_[data_type]() functions */
 
+
+/*
+*  ua_add_Variable(), main function to add variabel(point) for given format(data_type) to 
+*  the opc-ua server by calling an ua_add_Variable_[data_type]() functions.
+*  if data size is 1, its a scalar.
+*/
 static void ua_add_Variable(UA_Server *server, string regname, int format, unsigned int size, string perm)
 {
     cout << "regname=" << regname << " format=" << format << " size=" << size << endl;
@@ -1121,6 +1040,12 @@ static void ua_add_Variable(UA_Server *server, string regname, int format, unsig
     }
 }
 
+/*
+* ua_server_init(), get all FPGA_ and TR_ poins and add them to the opc-ua server.
+*
+* SD.unb->get_pointMap()     -->  FPGA_*  (points defined in src/fpga.cpp)
+* SD.tr->getTranslatorMap()  -->  TR_*    (points defined in src/tr.cpp)
+*/
 int ua_server_init(bool warm_start)
 {
     if (!warm_start) {
@@ -1154,6 +1079,9 @@ int ua_server_init(bool warm_start)
     return 0;
 }
 
+/*
+* ua_server(), start opc-ua server, called from sdptr.cpp
+*/
 int ua_server(void)
 {
     UA_StatusCode retval = UA_Server_run(mUaServer, &ServerRunning);
diff --git a/src/periph/fpga.cpp b/src/periph/fpga.cpp
index e49bed6b40e68204c6a78da092ec0d31bdae98fb..5c3a1c2c483d4d02b682ae37a882f5bab512d3d0 100644
--- a/src/periph/fpga.cpp
+++ b/src/periph/fpga.cpp
@@ -58,12 +58,14 @@ extern int debug;
 #define R_MEM 0  // read mem value
 #define R_UCP 1  // read value from hardware
 
-Periph_fpga::Periph_fpga(string ipaddr, string expected_design_name, uint expected_firmware_version, bool enabled):
+Periph_fpga::Periph_fpga(uint global_nr, string ipaddr, string expected_design_name, uint expected_firmware_version, bool enabled):
+    GlobalNr(global_nr),
     Enabled(enabled),
+    Masked(enabled),
+    Online(enabled),
     my_expected_design_name(expected_design_name),
     my_expected_firmware_version(expected_firmware_version),
     my_current_design_name("-"),
-    my_current_status("offline"),
     my_current_hw_version(0),
     my_current_fw_version("-.-"),
     my_bsn_input_sync_timeout(false),
@@ -75,15 +77,19 @@ Periph_fpga::Periph_fpga(string ipaddr, string expected_design_name, uint expect
     my_jesd_csr_dev_syncn {0},
     my_jesd_rx_err0 {0},
     my_jesd_rx_err1 {0},
-    my_xst_processing_enable(false)
+    my_xst_processing_enable(false),
+    my_pps_offset_cnt(0)
 {
     ucp = new UCP(ipaddr);
 
     if (Enabled) {
         mmap = new CMMap(read_reg_map());
+        cout << "mmap for node " << GlobalNr << endl;
+        mmap->print_screen();
     }
     else {
         mmap = new CMMap();
+        cout << "empty mmap for node " << GlobalNr << endl;
     }
 
     // Test FPGA by reading system info:
@@ -121,183 +127,30 @@ Periph_fpga::~Periph_fpga()
     if (mmap != NULL) delete mmap;
 }
 
-bool Periph_fpga::Read(const string addr_str, uint32_t *data_ptr, bool use_mask_shift=true)
-{
-    bool ret;
-    if (!Enabled) {
-        return false;
-        // throw runtime_error("disabled: " + addr_str);
-    }
-    if (!mmap->find_register(addr_str)) {
-        return false;
-    }
-
-    uint32_t addr = mmap->getAddr((addr_str));
-
-    mmap->getReadPermission((addr_str));
-
-    uint32_t nvalues = mmap->getSpan((addr_str));
-    bool isfifo = mmap->type_isfifo((addr_str));
-
-    ret = ucp->readRegister(addr, nvalues, data_ptr, isfifo);
-    if (use_mask_shift) {
-        for (uint32_t i=0; i<nvalues; i++) {
-            data_ptr[i] = mask_shift(addr_str, data_ptr[i]);
-        }
-    }
-    return ret;
-}
-
-bool Periph_fpga::Write(const string addr_str, uint32_t *data_ptr, bool use_shift_mask=true)
-{
-    if (!Enabled) {
-        return false;
-        // throw runtime_error("disabled " + addr_str);
-    }
-    if (!mmap->find_register(addr_str)) {
-        return false;
-    }
-    uint32_t addr = mmap->getValidAddr((addr_str), 1);
-    uint32_t span = mmap->getSpan((addr_str));
-    //cout << "addr=" << addr_str << " span=" << to_string(span) << endl;
-    mmap->getWritePermission((addr_str));
-
-    bool isfifo = mmap->type_isfifo((addr_str));
-    if (use_shift_mask) {
-        for (uint32_t i=0; i<span; i++) {
-            data_ptr[i] = shift_mask(addr_str, data_ptr[i]);
-        }
-    }
-    return ucp->writeRegister(addr, span, data_ptr, isfifo);
-}
-
-// for reading
-uint32_t Periph_fpga::mask_shift(const string addr_str, uint32_t data)
-{
-    uint32_t shift = mmap->getShift((addr_str));
-    uint32_t mask = mmap->getMask((addr_str));
-    uint32_t _data = data;
-
-    if (shift != 0 || mask != 0xffffffff) {
-        _data &= mask;
-        _data = _data >> shift;
-    }
-    return _data;
-}
-
-// for writing
-uint32_t Periph_fpga::shift_mask(const string addr_str, uint32_t data)
-{
-    uint32_t shift = mmap->getShift((addr_str));
-    uint32_t mask = mmap->getMask((addr_str));
-    uint32_t _data = data;
-
-    if (shift != 0 || mask != 0xffffffff) {
-        _data = _data << shift;
-        _data &= mask;
-    }
-    return _data;
-}
-
-
-bool Periph_fpga::read_fpga_status(TermOutput& termout, int format)
-{
-    if (my_current_status == "online") {
-        termout.val[0] = 1;
-    }
-    else {
-        termout.val[0] = 0;
-    }
-    termout.nof_vals = 1;
-    termout.datatype = format;
-    return true;
-}
-
-bool Periph_fpga::write_fpga_enable_mask(const char *data)
-{
-    Enabled = (bool)*data;
-    return true;
-}
-
-bool Periph_fpga::read_fpga_enable_mask(TermOutput& termout, int format)
-{
-    termout.val[0] = Enabled;
-    termout.nof_vals = 1;
-    termout.datatype = format;
-    return true;
-}
-
 /*
-"""Peripheral system_info
-   Register map:
-    31             24 23             16 15              8 7               0  wi
-   |-----------------|-----------------|-----------------|-----------------|
-   |                                                          use_phy[7:0] |  1
-   |-----------------------------------------------------------------------|
-   |                           system_info[31:0]                           |  0
-   |-----------------------------------------------------------------------|
-
-    system_info[23:20] = firmware version high[3:0]
-    system_info[19:16] = firmware version low[3:0]
-    system_info[10]    = cs_sim (= g_sim, 0 on HW, 1 in VHDL simulation)
-    system_info[9:8]   = hardware version [1:0] (= 0 for UniBoard 1A and 1B)
-    system_info[7:0]   = node id[7;0]
+* read(), this function is called from Node::worker() thread.
 */
-bool Periph_fpga::read_system_info(TermOutput& termout)
-{
-    // cout << "read system info" << endl;
-    uint32_t data;
-    bool retval = Read("mm/0/PIO_SYSTEM_INFO/info", &data);
-    if (retval == false) { return false; }
-
-    // string design_name = read_design_name();
-    my_current_design_name = read_design_name();
-
-    // FIXME: get rid of magic constants in masks, should be in CCFG:
-    uint firmware_version    = (data & 0x00F00000) >> 20;
-    uint firmware_subversion = (data & 0x000F0000) >> 16;
-    my_current_fw_version = to_string(firmware_version) + "." + to_string(firmware_subversion);
-
-    my_current_hw_version = (data & 0x0000300) >> 8;
-
-    if (my_current_design_name == my_expected_design_name && firmware_version >= my_expected_firmware_version) {
-        my_current_status = "online";
-        retval = true;
-    }
-    else {
-        retval = false;
-        cerr << "Warning: Node configuration mismatch!! (read_design_name/version=" << my_current_design_name
-                  << "/" << firmware_version << "), expected=" << my_expected_design_name
-                  << "/" << my_expected_firmware_version << ")" << endl;
-        //syslog(LOG_WARNING,"Node configuration mismatch!! (read_design_name/version=%s/%d, expected=%s/%d)\n",
-        //       my_current_design_name.c_str(), firmware_version,
-        //       my_expected_design_name.c_str(), my_expected_firmware_version);
-
-        my_current_status = "offline";
-        // registerMap->setAllPermission_NA();
-    }
-    return retval;
-}
-
 bool Periph_fpga::read(TermOutput& termout, const string addr,
                        const string type, char *data, const uint nvalues,
                        const int format)
 {
+    // cout << "node " << GlobalNr << " read: Enabled=" << Enabled << ", Online=" << Online << ", Masked=" << Masked << endl;
     bool retval = false;
 
     termout.datatype = format;
     termout.nof_vals = 0;
 
-    if (addr == "fpga/enable_mask") {
-        retval = read_fpga_enable_mask(termout, format);
-        return retval;
+    if (!Masked) {  // Not selected
+        return false;
+    }
+    if ((!Enabled) || (!Online)) {  // Selected but not possible
+        cout << "read() error node " << GlobalNr << " not enabled or not online" << endl;
+        return false;
     }
-
-    if (!Enabled) { return false; }
 
     if (type == "mm") {
         uint32_t *data_ptr = (uint32_t *)data;
-        retval = Read(addr, data_ptr);
+        retval = Read(addr, data_ptr, true);
         termout.datatype = format;
     }
     else { // "fpga/..."
@@ -331,12 +184,6 @@ bool Periph_fpga::read(TermOutput& termout, const string addr,
         else if (addr == "fpga/epcs_wait_busy") {
             retval = wait_while_epcs_busy(1000);
         }
-        else if (addr == "fpga/status") {
-            retval = read_fpga_status(termout, format);
-        }
-        else if (addr == "fpga/enable_mask") {
-            retval = read_fpga_enable_mask(termout, format);
-        }
         else if (addr == "fpga/scrap") {
             retval = read_fpga_scrap(termout, format);
         }
@@ -468,22 +315,27 @@ bool Periph_fpga::read(TermOutput& termout, const string addr,
     return retval;
 }
 
+/*
+* write(), this function is called from Node::worker() thread.
+*/
 bool Periph_fpga::write(TermOutput& termout, const string addr, const string type,
                         char *data, const uint nvalues, const int format)
 {
+    // cout << "node " << GlobalNr << " write: Enabled=" << Enabled << ", Online=" << Online << ", Masked=" << Masked << endl;
     bool retval = false;
 
-    if (addr == "fpga/enable_mask") {
-        retval = write_fpga_enable_mask(data);
-        return retval;
+    if (!Masked) {  // Not selected
+        return false;
+    }
+    if ((!Enabled) || (!Online)) {  // Selected but not possible
+        cout << "write() error node " << GlobalNr << " not enabled or not online" << endl;
+        return false;
     }
-
-    if (!Enabled) { return false; }
 
     uint32_t *data_ptr = (uint32_t *)data;
 
     if (type == "mm") {
-        retval = Write(addr, data_ptr);
+        retval = Write(addr, data_ptr, true);
     }
     else { // "fpga/..."
     //    if (addr == "fpga/rbf") {
@@ -515,7 +367,7 @@ bool Periph_fpga::write(TermOutput& termout, const string addr, const string typ
         }
         else if (addr == "fpga/epcs_mmdp_data") {
             // write to FIFO
-            retval = Write("mm/0/REG_MMDP_DATA/data", data_ptr);
+            retval = Write("mm/0/REG_MMDP_DATA/0/data", data_ptr, true);
         }
         else if (addr == "fpga/scrap") {
             retval = write_fpga_scrap(data_ptr);
@@ -611,28 +463,194 @@ bool Periph_fpga::write(TermOutput& termout, const string addr, const string typ
     return retval;
 }
 
+/*
+* monitor(), this function is called from Node::worker() thread.
+*/
 bool Periph_fpga::monitor(TermOutput& termout)
 {
     if (!Enabled) { return false; }
     // use tictoc, tic(start) toc(stop) to see how long this function takes
     // first 9 read functions on 4 nodes will take 6 msec.
-    tictoc.tic("periph.fpga.monitor");
+    string name = "periph.fpga.monitor node " + to_string(GlobalNr);
+    tictoc.tic(name.c_str());
     read_system_info(termout);
-    read_bsn_monitor_input_sync_timeout(termout, REG_FORMAT_INT64, R_UCP);
-    read_bsn_monitor_input_bsn(termout, REG_FORMAT_INT64, R_UCP);
-    read_bsn_monitor_input_nof_packets(termout, REG_FORMAT_INT32, R_UCP);
-    read_bsn_monitor_input_nof_valid(termout, REG_FORMAT_INT32, R_UCP);
-    read_bsn_monitor_input_nof_err(termout, REG_FORMAT_INT32, R_UCP);
-    read_jesd204b_csr_rbd_count(termout, REG_FORMAT_UINT32, R_UCP);
-    read_jesd204b_csr_dev_syncn(termout, REG_FORMAT_UINT32, R_UCP);
-    read_jesd204b_rx_err0(termout, REG_FORMAT_UINT32, R_UCP);
-    read_jesd204b_rx_err1(termout, REG_FORMAT_UINT32, R_UCP);
-
+    if (Online) {
+        read_time_since_last_pps(termout, REG_FORMAT_INT64, R_UCP);
+        read_bsn_monitor_input_sync_timeout(termout, REG_FORMAT_INT64, R_UCP);
+        read_bsn_monitor_input_bsn(termout, REG_FORMAT_INT64, R_UCP);
+        read_bsn_monitor_input_nof_packets(termout, REG_FORMAT_INT32, R_UCP);
+        read_bsn_monitor_input_nof_valid(termout, REG_FORMAT_INT32, R_UCP);
+        read_bsn_monitor_input_nof_err(termout, REG_FORMAT_INT32, R_UCP);
+        read_jesd204b_csr_rbd_count(termout, REG_FORMAT_UINT32, R_UCP);
+        read_jesd204b_csr_dev_syncn(termout, REG_FORMAT_UINT32, R_UCP);
+        read_jesd204b_rx_err0(termout, REG_FORMAT_UINT32, R_UCP);
+        read_jesd204b_rx_err1(termout, REG_FORMAT_UINT32, R_UCP);
+    }
     termout.clear();
     tictoc.toc();
     return true;
 }
 
+
+/************************************************
+ from here only intern (private) used functions
+************************************************/
+
+/* 
+* Read(), do a read to an UCP register
+* - check if addr_str is available.
+* - get information for reading.
+* - put read data in 'data_ptr' (masked and shifted on request).
+* - return true on succes else false.
+*/
+bool Periph_fpga::Read(const string addr_str, uint32_t *data_ptr, bool use_mask_shift=true)
+{
+    bool ret;
+    if ((!Enabled) || (!Masked)) {
+        return false;
+    }
+    if (!mmap->find_register(addr_str)) {
+        cout << addr_str << " not found" << endl;
+        return false;
+    }
+
+    uint32_t addr = mmap->getAddr((addr_str));
+
+    mmap->getReadPermission((addr_str));
+
+    uint32_t nvalues = mmap->getSpan((addr_str));
+    bool isfifo = mmap->type_isfifo((addr_str));
+
+    ret = ucp->readRegister(addr, nvalues, data_ptr, isfifo);
+    if (use_mask_shift) {
+        for (uint32_t i=0; i<nvalues; i++) {
+            data_ptr[i] = mask_shift(addr_str, data_ptr[i]);
+        }
+    }
+    return ret;
+}
+
+/* 
+* Write(), do a write to an UCP register
+* - check if addr_str is available.
+* - get information for writing.
+* - write data from 'data_ptr' (shifted and masked on request)
+* - return true on succes else false.
+*/
+bool Periph_fpga::Write(const string addr_str, uint32_t *data_ptr, bool use_shift_mask=true)
+{
+    if ((!Enabled) || (!Masked)) {
+        return false;
+    }
+    if (!mmap->find_register(addr_str)) {
+        return false;
+    }
+    uint32_t addr = mmap->getValidAddr((addr_str), 1);
+    uint32_t span = mmap->getSpan((addr_str));
+    //cout << "addr=" << addr_str << " span=" << to_string(span) << endl;
+    mmap->getWritePermission((addr_str));
+
+    bool isfifo = mmap->type_isfifo((addr_str));
+    if (use_shift_mask) {
+        for (uint32_t i=0; i<span; i++) {
+            data_ptr[i] = shift_mask(addr_str, data_ptr[i]);
+        }
+    }
+    return ucp->writeRegister(addr, span, data_ptr, isfifo);
+}
+
+/*
+* mask_shift (for reading)
+* mask data and shift bits using information from the mmap.
+*/
+uint32_t Periph_fpga::mask_shift(const string addr_str, uint32_t data)
+{
+    uint32_t shift = mmap->getShift((addr_str));
+    uint32_t mask = mmap->getMask((addr_str));
+    uint32_t _data = data;
+
+    if (shift != 0 || mask != 0xffffffff) {
+        _data &= mask;
+        _data = _data >> shift;
+    }
+    return _data;
+}
+
+/*
+* shift_mask (for writing)
+* shift data and mask bits using information from the mmap.
+*/
+uint32_t Periph_fpga::shift_mask(const string addr_str, uint32_t data)
+{
+    uint32_t shift = mmap->getShift((addr_str));
+    uint32_t mask = mmap->getMask((addr_str));
+    uint32_t _data = data;
+
+    if (shift != 0 || mask != 0xffffffff) {
+        _data = _data << shift;
+        _data &= mask;
+    }
+    return _data;
+}
+
+
+/*
+"""Peripheral system_info
+   Register map:
+    31             24 23             16 15              8 7               0  wi
+   |-----------------|-----------------|-----------------|-----------------|
+   |                                                          use_phy[7:0] |  1
+   |-----------------------------------------------------------------------|
+   |                           system_info[31:0]                           |  0
+   |-----------------------------------------------------------------------|
+
+    system_info[23:20] = firmware version high[3:0]
+    system_info[19:16] = firmware version low[3:0]
+    system_info[10]    = cs_sim (= g_sim, 0 on HW, 1 in VHDL simulation)
+    system_info[9:8]   = hardware version [1:0] (= 0 for UniBoard 1A and 1B)
+    system_info[7:0]   = node id[7;0]
+*/
+bool Periph_fpga::read_system_info(TermOutput& termout)
+{
+    // cout << "node " << GlobalNr << " read system info" << endl;
+    uint32_t data;
+    bool retval = Read("mm/0/PIO_SYSTEM_INFO/0/info", &data);
+    if (retval == false) {
+        cout << "node " << GlobalNr << " no response" << endl;
+        Online = false;
+        return false;
+    }
+
+    // string design_name = read_design_name();
+    my_current_design_name = read_design_name();
+    cout << "node " << GlobalNr << " design_name= " << my_current_design_name << endl;
+
+    // FIXME: get rid of magic constants in masks, should be in CCFG:
+    uint firmware_version    = (data & 0x00F00000) >> 20;
+    uint firmware_subversion = (data & 0x000F0000) >> 16;
+    my_current_fw_version = to_string(firmware_version) + "." + to_string(firmware_subversion);
+
+    my_current_hw_version = (data & 0x0000300) >> 8;
+
+    if (my_current_design_name == my_expected_design_name && firmware_version >= my_expected_firmware_version) {
+        Online = true;
+        retval = true;
+    }
+    else {
+        retval = false;
+        cerr << "Warning: Node configuration mismatch!! (read_design_name/version=" << my_current_design_name
+                  << "/" << firmware_version << "), expected=" << my_expected_design_name
+                  << "/" << my_expected_firmware_version << ")" << endl;
+        //syslog(LOG_WARNING,"Node configuration mismatch!! (read_design_name/version=%s/%d, expected=%s/%d)\n",
+        //       my_current_design_name.c_str(), firmware_version,
+        //       my_expected_design_name.c_str(), my_expected_firmware_version);
+
+        Online = false;
+        // registerMap->setAllPermission_NA();
+    }
+    return retval;
+}
+
 bool Periph_fpga::flash_prot(uint32_t *data)
 {
     bool retval = false;
@@ -640,10 +658,10 @@ bool Periph_fpga::flash_prot(uint32_t *data)
     uint32_t passphrase_unprotect = 0xBEDA221E;
 
     if (*data == 0) { // unprotect
-        retval = Write("mm/0/REG_EPCS/unprotect", &passphrase_unprotect);
+        retval = Write("mm/0/REG_EPCS/0/unprotect", &passphrase_unprotect);
     }
     else { // protect
-        retval = Write("mm/0/REG_EPCS/unprotect", &passphrase_protect);
+        retval = Write("mm/0/REG_EPCS/0/unprotect", &passphrase_protect);
     }
     return retval;
 }
@@ -655,14 +673,14 @@ bool Periph_fpga::flash_page(uint32_t *data)
     wait_while_epcs_busy(1);
     // write address
     uint32_t addr = Flash_page_start * Flash_page_size_bytes;
-    retval = Write("mm/0/REG_EPCS/addr", &addr);
+    retval = Write("mm/0/REG_EPCS/0/addr", &addr);
 
     // write to FIFO
-    retval = Write("mm/0/REG_MMDP_DATA/data", data);
+    retval = Write("mm/0/REG_MMDP_DATA/0/data", data);
 
     // write_write
     uint32_t d = 1;
-    retval = Write("mm/0/REG_EPCS/write", &d);
+    retval = Write("mm/0/REG_EPCS/0/write", &d);
 
     return retval;
 }
@@ -688,7 +706,7 @@ bool Periph_fpga::wait_while_epcs_busy(uint sleeptime)
     bool retval;
     //cout << "wait_while_epcs_busy:";
     for (int i=0; i<100; i++) {
-        retval = Read("mm/0/REG_EPCS/busy", &data);
+        retval = Read("mm/0/REG_EPCS/0/busy", &data);
         if (!retval) break;
         if (data == 0) break;
         usleep(sleeptime);
@@ -715,10 +733,10 @@ bool Periph_fpga::flash_erase_sector(uint32_t sector)
     for (uint i=0; i<s_list.size(); i++) {
         s = sector * 0x40000 + s_list[i];
         // write address
-        retval = Write("mm/0/REG_EPCS/addr", &s);
+        retval = Write("mm/0/REG_EPCS/0/addr", &s);
         // sector erase
         d = 1;
-        retval = Write("mm/0/REG_EPCS/sector_erase", &d);
+        retval = Write("mm/0/REG_EPCS/0/sector_erase", &d);
         wait_while_epcs_busy(50000);
     }
     return retval;
@@ -2280,6 +2298,22 @@ bool Periph_fpga::read_subband_weights(TermOutput& termout, int format) {
     return retval;
 }
 
+bool Periph_fpga::read_time_since_last_pps(TermOutput& termout, int format, int mode) {
+    bool retval = true;
+    if (mode == R_UCP) {
+        uint32_t data = 0;
+        retval = Read("mm/0/PIO_PPS/0/offset_cnt", &data);
+        my_pps_offset_cnt = data;
+    }
+
+    uint32_t *_ptr = (uint32_t *)termout.val;
+    *_ptr = my_pps_offset_cnt;
+    termout.nof_vals = 1;
+    termout.datatype = format;
+    return retval;
+}
+
+
 bool Periph_fpga::write_wdi_override(TermOutput& termout)
 {
     uint32_t data = 0xB007FAC7;
@@ -2319,7 +2353,7 @@ CMMap Periph_fpga::read_reg_map()
     char *str_ptr = (char *)data;
 
     string reg_map_str(str_ptr);
-    cout << "Periph_fpga::read_reg_map:\n" << reg_map_str << endl;
+    // cout << "Periph_fpga::read_reg_map:\n" << reg_map_str << endl;
     if (data != NULL) {
         delete[] data;
     }
diff --git a/src/periph/fpga.h b/src/periph/fpga.h
index b66d47d27acfbb8a7a2d15a32968330ecc21c762..eea3a2415f2c30c97388334cdfcaaa2c35d89466 100644
--- a/src/periph/fpga.h
+++ b/src/periph/fpga.h
@@ -46,12 +46,14 @@ private:
   Tictoc tictoc;  // used to get some timing information
   UCP *ucp;
   CMMap *mmap;
-
+  uint32_t GlobalNr;
   bool Enabled;
+  bool Masked;
+  bool Online;
+
   std::string my_expected_design_name;
   uint my_expected_firmware_version;
   std::string my_current_design_name;
-  std::string my_current_status;
   uint my_current_hw_version;
   std::string my_current_fw_version;
   
@@ -66,6 +68,7 @@ private:
   uint32_t my_jesd_rx_err0[C_S_pn];
   uint32_t my_jesd_rx_err1[C_S_pn];
   bool    my_xst_processing_enable;
+  uint32_t my_pps_offset_cnt;
 
   std::ofstream rbf_wf;
   uint32_t Flash_page_start, Flash_page_size_bytes, Flash_user_sector_start,
@@ -85,10 +88,6 @@ private:
   bool wait_while_epcs_busy(uint sleeptime);
   std::string read_design_name();
   std::string read_design_note();
-  bool read_fpga_status(TermOutput& termout, int format);
-
-  bool read_fpga_enable_mask(TermOutput& termout, int format);
-  bool write_fpga_enable_mask(const char *data);
 
   bool read_system_info(TermOutput& termout);
   bool read_hardware_version(TermOutput& termout, int format);
@@ -181,12 +180,13 @@ private:
 
   bool read_sdp_info_block_period(TermOutput& termout, int format);
 
+  bool read_time_since_last_pps(TermOutput& termout, int format, int mode);
   bool write_wdi_override(TermOutput& termout);
 
   CMMap read_reg_map();
 
 public:
-  Periph_fpga(std::string ipaddr, std::string expected_design_name, uint expected_firmware_version, bool enabled);
+  Periph_fpga(uint global_nr, std::string ipaddr, std::string expected_design_name, uint expected_firmware_version, bool enabled);
   ~Periph_fpga();
 
 
@@ -197,7 +197,11 @@ public:
   bool monitor(TermOutput& termout);
 
   CMMap* getMMap(void) { return mmap; };
-  bool getEnabled(void) { return Enabled; }
+  bool isEnabled(void) { return Enabled; }
+  bool isOnline(void) { return Online; }
+  bool isMasked(void) { return Masked; }
+  void setMasked(bool mask) { Masked = mask; }
+  uint32_t ppsOffsetCnt(void) { return my_pps_offset_cnt; }
 };
 
 #endif // __PERIPH_FPGA_H__
diff --git a/src/registers.cpp b/src/registers.cpp
index 124b3501950f586b65dfcbfd0a669724d4b5a6df..1639254c44243ee5e5189b33aa9d18ef636f67b5 100644
--- a/src/registers.cpp
+++ b/src/registers.cpp
@@ -99,7 +99,7 @@ bool CMMap::add_register(const string name, const uint32_t base, const uint32_t
                          const uint32_t shift, const string access,
                          const string type, const uint32_t peripheral_span, const uint32_t mm_port_span)
 {
-    cout << "CMMap::add_register: " << name << endl;
+    // cout << "CMMap::add_register: " << name << endl;
     if (find_register(name)) {
         cerr << "CMMap::add_register: " << name << " already exist!" << endl;
         return false;
@@ -114,7 +114,7 @@ bool CMMap::update_register(const string name, const uint32_t base, const uint32
                             const uint32_t shift, const string access,
                             const string type, const uint32_t peripheral_span, const uint32_t mm_port_span)
 {
-    cout << "CMMap::update_register: " << name << endl;
+    // cout << "CMMap::update_register: " << name << endl;
     if (!find_register(name)) {
         cerr << "CMMap::update_register: " << name << " not exist!" << endl;
         return false;
diff --git a/src/sdptr.cpp b/src/sdptr.cpp
index 80353e041c3fd22b90b785ccef91b39287004921..8357050624a378a51c520caa520653b3f0e8bdbb 100644
--- a/src/sdptr.cpp
+++ b/src/sdptr.cpp
@@ -62,6 +62,10 @@ void monitor()
     string line;
     string cmdname = "";
     TermOutput termout;
+    struct timeval current_time;
+    time_t secs;
+    struct tm * now;
+    char time_str[50];
 
     SD.timetick = SECONDS_PER_TICK;
     clock_gettime(CLOCK_REALTIME, (struct timespec *)&SD.t0);
@@ -69,21 +73,27 @@ void monitor()
     pthread_cond_init(&SD.newpoint_cond, NULL);
 
     SD.uptime = 0;
+    
+    gettimeofday(&current_time, NULL);
+    SD.start_time = current_time.tv_sec;
 
-    while (ServerRunning) {
-        struct timeval current_time;
 
+    while (ServerRunning) {
         SD.t0.tv_sec = SD.t0.tv_sec + SD.timetick;  // specify next position in time
         SD.t0.tv_nsec = 10000000L;  // 0..999999999 // offset 10ms in a new second
         pthread_mutex_lock(&SD.newpoint_lock);
         pthread_cond_timedwait(&SD.newpoint_cond, &SD.newpoint_lock, (const struct timespec *)&SD.t0);
         pthread_mutex_unlock(&SD.newpoint_lock);
-        SD.uptime++;
 
         gettimeofday(&current_time, NULL);
-        cout << "MONITOR THREAD. now=" << current_time.tv_sec << "." << current_time.tv_usec
-                  << " uptime=" << SD.uptime << " seconds" << endl;
         SD.tod = current_time.tv_sec;
+        SD.uptime++;
+
+        secs = current_time.tv_sec;
+        now = gmtime(&secs);
+        sprintf(time_str, "%02d:%02d:%02d.%06ld", now->tm_hour, now->tm_min, now->tm_sec, current_time.tv_usec);
+
+        cout << "[" << time_str << " (UTC)] " << "MONITOR THREAD: uptime=" << SD.uptime << " seconds" << endl;
 
         if (SD.unb != NULL) {
             // cout << "sdptr_monitor start" << endl;
@@ -229,7 +239,6 @@ int main (int argc, char* argv[])
         //openlog(basename(argv[0]), 0, LOG_LOCAL0);
         //syslog(LOG_INFO,"Starting server %s\n", SDPTR_VERSION);
 
-
         if (!nodaemon) {
             if (daemon(1, 0) < 0) cerr << "Error fork as daemon: " << strerror(errno) << endl;
         }
diff --git a/src/sdptr.h b/src/sdptr.h
index b112ea7be397b63987a9845d46cbbacefb36e6b4..ebfc4c3329de2567171f509e573c5cb2b7400963 100644
--- a/src/sdptr.h
+++ b/src/sdptr.h
@@ -56,6 +56,7 @@ public:
     pthread_mutex_t newpoint_lock;
 
     std::atomic<uint32_t> uptime;
+    std::atomic<uint32_t> start_time;
     std::atomic<uint32_t> tod;
     std::atomic<bool> busy;
 };
diff --git a/src/tools/mmap.cpp b/src/tools/mmap.cpp
index eed1055d5ecc9a67a0625edfa30f1b1d44483749..a14315eb00451ea62076cffa30885e2056ab7b2c 100644
--- a/src/tools/mmap.cpp
+++ b/src/tools/mmap.cpp
@@ -255,8 +255,8 @@ CMMap mmap_to_regmap(istringstream& iss)
         }
     }
 
-    cout << "regmap:" << endl;
-    regmap.print_screen();
+    // cout << "regmap:" << endl;
+    // regmap.print_screen();
 
     return regmap;
 }
diff --git a/src/tools/util.cpp b/src/tools/util.cpp
index a56aae66d4fa4569a16cffd1d621ae04bca45f52..538ec1d6d3860cb1dfc4499b0e95b1171bb24b33 100644
--- a/src/tools/util.cpp
+++ b/src/tools/util.cpp
@@ -29,6 +29,8 @@
 #include <cmath>
 #include <iostream>
 #include <algorithm>
+#include <ctime>
+#include <sys/time.h>
 
 #include "util.h"
 
@@ -94,10 +96,12 @@ vector<uint> unique_and_sort(vector<uint> v)
     return new_v;
 }
 
+#define TIME_STR_LEN 20
+
 void Tictoc::tic(string idstr)
 {
     name = idstr;
-    cout << "tic: start [" << name << "]" << endl;
+    // cout << "tic: start [" << name << "]" << endl;
     c_start = clock();
     t_start = chrono::high_resolution_clock::now();
 }
@@ -106,6 +110,15 @@ void Tictoc::toc(void)
 {
     clock_t c_end = clock();
     auto t_end = chrono::high_resolution_clock::now();
+    
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    time_t secs = tv.tv_sec;
+    struct tm * now = gmtime(&secs);
+    char time_str[TIME_STR_LEN];
+    sprintf(time_str, "%02d:%02d:%02d.%06ld", now->tm_hour, now->tm_min, now->tm_sec, tv.tv_usec);
+    
+    cout << "[" << time_str << " (UTC)] ";
     cout << "toc-tic (" << name << ") CPU time: " << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms. ";
     cout << "Real time: "
               << chrono::duration_cast<chrono::milliseconds>(t_end - t_start).count()
diff --git a/src/tr.cpp b/src/tr.cpp
index 9938d86b5650853bda298391d1ecfb20d31ffb77..f63ce90be75573e2d748e63547d180a69ca6cbd6 100644
--- a/src/tr.cpp
+++ b/src/tr.cpp
@@ -36,6 +36,7 @@
 
 #include "tr.h"
 #include "sdptr.h"
+#include "constants.h"
 
 using namespace std;
 
@@ -48,14 +49,17 @@ TranslatorMap::TranslatorMap()
 {
     translatorMap = new CPointMap();
 
-    translatorMap->add_register("TR_reload_RW",          "-", 1, 1, "RW", REG_FORMAT_BOOLEAN);
-    translatorMap->add_register("TR_software_version_R", "-", 1, 1, "RO", REG_FORMAT_STRING);
-    translatorMap->add_register("TR_busy_R",             "-", 1, 1, "RO", REG_FORMAT_BOOLEAN);
-    translatorMap->add_register("TR_tod_R",              "-", 1, 1, "RO", REG_FORMAT_UINT32);
-    translatorMap->add_register("TR_uptime_R",           "-", 1, 1, "RO", REG_FORMAT_UINT32);
+    translatorMap->add_register("TR_fpga_mask_R",                "-", C_N_pn, 1, "RO", REG_FORMAT_BOOLEAN);
+    translatorMap->add_register("TR_fpga_mask_RW",               "-", C_N_pn, 1, "RW", REG_FORMAT_BOOLEAN);
+    translatorMap->add_register("TR_software_version_R",         "-", 1,      1, "RO", REG_FORMAT_STRING);
+    translatorMap->add_register("TR_start_time_R",               "-", 1,      1, "RO", REG_FORMAT_UINT32);
+    // translatorMap->add_register("TR_tod_client_status_R",        "-", 1,      1, "RO", REG_FORMAT_BOOLEAN);  // TODO: PTP client is part of linux, can I get a status?
+    translatorMap->add_register("TR_tod_R",                      "-", 1,      1, "RO", REG_FORMAT_UINT32);
+    translatorMap->add_register("TR_tod_pps_delta_R",            "-", 1,      1, "RO", REG_FORMAT_DOUBLE);
+    translatorMap->add_register("TR_fpga_communication_error_R", "-", C_N_pn, 1, "RO", REG_FORMAT_BOOLEAN);
+    // translatorMap->add_register("TR_reload_RW",                  "-", 1,      1, "RW", REG_FORMAT_BOOLEAN);  // Maybe for future.
 }
 
-
 TranslatorMap::~TranslatorMap()
 {
     if (translatorMap != NULL) {
@@ -79,28 +83,78 @@ bool TranslatorMap::translator(TermOutput& termout, const char cmd, const string
     }
 
     int format = (int)translatorMap->getFormat(addr);
-    termout.nof_vals = translatorMap->getDataSize(addr);
+    uint32_t n_nodes = translatorMap->getNodesSize(addr);
+    uint32_t n_values = translatorMap->getDataSize(addr);
+    termout.nof_vals = n_nodes * n_values;
     termout.datatype = format;
 
     retval = true;
 
-    if (addr == "TR_reload_W") {
+    // reload is not used now, but maybe needed in future
+    // if (addr == "TR_reload_W") {
+    //     if (cmd == 'W') {
+    //         cout << "Tr: tr_reload_W: initiate RELOAD now" << endl;
+    //         raise(SIGHUP);
+    //     }
+    // }
+    // else 
+    if (addr == "TR_fpga_mask_R") {
+        bool *ptr_out = (bool *)termout.val;
+        vector<bool> masked = SD.unb->get_all_masked_nodes();
+        for (auto val : masked) {
+            *ptr_out = val;
+            ptr_out++;
+        }
+    }
+    else if (addr == "TR_fpga_mask_RW") {
         if (cmd == 'W') {
-            cout << "Tr: tr_reload_W: initiate RELOAD now" << endl;
-            raise(SIGHUP);
+            bool *ptr_in = (bool *)data;
+            vector<bool> masked;
+            for (uint i=0; i<n_nodes; i++) {
+                masked.push_back(*ptr_in);
+                ptr_in++;
+            }
+            SD.unb->set_all_masked_nodes(masked);
+        }
+        else {
+            bool *ptr_out = (bool *)termout.val;
+            vector<bool> masked = SD.unb->get_all_masked_nodes();
+            for (auto val : masked) {
+                *ptr_out = val;
+                ptr_out++;
+            }
         }
     }
     else if (addr == "TR_software_version_R") {
         strcpy(termout.val, SDPTR_VERSION);
     }
-    else if (addr == "TR_busy_R") {
-        bool *ptr_out = (bool *)termout.val;
-        *ptr_out = (bool)SD.busy;
-    }
     else if (addr == "TR_tod_R") {
         uint32_t *ptr_out = (uint32_t *)termout.val;
         *ptr_out = (uint32_t)SD.tod;
     }
+    else if (addr == "TR_tod_pps_delta_R") {
+        double *ptr_out = (double *)termout.val;
+        vector<uint32_t> pps_offset_cnt = SD.unb->get_all_pps_offset_cnt();
+        cout << "pps_offset_cnt=" << pps_offset_cnt[0] << endl;
+        double pps_delta = (double)(pps_offset_cnt[0] * C_200MHZ_1_CNT_NS);
+        *ptr_out = pps_delta;
+    }
+    else if (addr == "TR_start_time_R") {
+        uint32_t *ptr_out = (uint32_t *)termout.val;
+        *ptr_out = (uint32_t)SD.start_time;
+    }
+    else if (addr == "TR_fpga_communication_error_R") {
+        bool *ptr_out = (bool *)termout.val;
+        vector<bool> offline = SD.unb->get_all_offline_nodes();
+        for (auto val : offline) {
+            *ptr_out = val;
+            ptr_out++;
+        }
+    }
+    else if (addr == "TR_busy_R") {
+        bool *ptr_out = (bool *)termout.val;
+        *ptr_out = (bool)SD.busy;
+    }
     else if (addr == "TR_uptime_R") {
         uint32_t *ptr_out = (uint32_t *)termout.val;
         *ptr_out = (uint32_t)SD.uptime;
diff --git a/test/py/Client.py b/test/py/Client.py
index aa4e99d2fc65d7038bb8c1e53619614828397708..7a019e3920f5b0b67b15985563ec89ba73c56bd1 100644
--- a/test/py/Client.py
+++ b/test/py/Client.py
@@ -23,18 +23,6 @@
 # Purpose:
 # . script to test (control/monitor) opc-ua server
 # Description:
-# Client.py is used to connect (TCP/IP) to an opc-ua server and test (read/write)
-# opc-ua points (registers on a fpga).
-# For LTS the server is running on DOP36 port 4840, this client can not run
-# on DOP36 itself (too old system).
-#
-# In this version the following is working:
-# . --info,   read info from server, list all available points on the server.
-# . --all,    read all *_R point and print needed time.
-# . --wg,     turn off WG or setup for XST (to generate crosslets plot).
-# . --setup,  setup SST, BST and XST stream.
-# . --stream, turn off or on SST, BST or XST stream.
-#
 # . run ./Client.py -h for help
 # ##########################################################################
 
@@ -53,6 +41,12 @@ N_NODES = 16
 S_PN    = 12
 N_SUB   = 512
 
+
+#
+#  '2:' in child name is space it belongs to. 2: is "http://lofar.eu".
+#
+
+
 # timing decorator
 # put @timing for the function to time
 def timing(f):
@@ -132,6 +126,7 @@ def explore_temp(node):
 
 @timing
 def check_get_all_R_time(obj):
+    # '2:' = space 2 is "http://lofar.eu".
     info = []
     info.append(str(obj.get_child("2:FPGA_bsn_monitor_input_bsn_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_bsn_monitor_input_nof_err_R").get_value()))
@@ -143,7 +138,6 @@ def check_get_all_R_time(obj):
     info.append(str(obj.get_child("2:FPGA_jesd204b_csr_rbd_count_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_jesd204b_rx_err0_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_jesd204b_rx_err1_R").get_value()))
-    info.append(str(obj.get_child("2:FPGA_mask_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_processing_enable_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_sdp_info_antenna_band_index_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_sdp_info_block_period_R").get_value()))
@@ -170,7 +164,6 @@ def check_get_all_R_time(obj):
     info.append(str(obj.get_child("2:FPGA_xst_offload_hdr_eth_destination_mac_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_xst_offload_hdr_ip_destination_address_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_xst_offload_hdr_udp_destination_port_R").get_value()))
-    info.append(str(obj.get_child("2:FPGA_status_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_subband_weights_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_temp_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_weights_R").get_value()))
@@ -178,10 +171,12 @@ def check_get_all_R_time(obj):
     info.append(str(obj.get_child("2:FPGA_wg_enable_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_wg_frequency_R").get_value()))
     info.append(str(obj.get_child("2:FPGA_wg_phase_R").get_value()))
-    info.append(str(obj.get_child("2:TR_busy_R").get_value()))
+    info.append(str(obj.get_child("2:TR_fpga_mask_R").get_value()))
     info.append(str(obj.get_child("2:TR_software_version_R").get_value()))
+    info.append(str(obj.get_child("2:TR_start_time_R").get_value()))
     info.append(str(obj.get_child("2:TR_tod_R").get_value()))
-    info.append(str(obj.get_child("2:TR_uptime_R").get_value()))
+    info.append(str(obj.get_child("2:TR_tod_pps_delta_R").get_value()))
+    #info.append(str(obj.get_child("2:TR_uptime_R").get_value()))
     print(f"checked {len(info)} FPGA_*_R points")
     # print('\n'.join(info))
 
@@ -194,12 +189,12 @@ def write_fpga_mask(obj, nodes=None, mask=None):
         for node in list(nodes):
             enable_mask[node] = True
     #print(enable_mask)
-    var = obj.get_child("2:FPGA_mask_RW")
+    var = obj.get_child("2:TR_fpga_mask_RW")
     var.set_value(ua.Variant(value=list(enable_mask), varianttype=ua.VariantType.Boolean))
 
 
 def get_fpga_mask(obj):
-    var = obj.get_child("2:FPGA_mask_R")
+    var = obj.get_child("2:TR_fpga_mask_R")
     enable_mask = var.get_value()
     #print(enable_mask)
     return enable_mask
@@ -411,7 +406,7 @@ if __name__ == "__main__":
 
                 if args.wg_mode is not None:
                     if args.wg_mode == 'OFF':
-                        turn_wg_off(obj)
+                        turn_wg_off(Object)
                     elif args.wg_mode == 'XST':
                         setup_wg_xst_mode(Object)