diff --git a/src/Groundstation/Automated/ARPData.h b/src/Groundstation/Automated/ARPData.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2b85415c3f3b56276eb83bdea6819fe12128cc7
--- /dev/null
+++ b/src/Groundstation/Automated/ARPData.h
@@ -0,0 +1,56 @@
+/* Copyright (c) 2024 Skyward Experimental Rocketry
+ * Author: Nicolò Caruso
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include <iostream>
+#include <string>
+
+namespace Antennas
+{
+
+/**
+ * @brief Structure to handle the APR coordinates, a reduced set of the GPS
+ * data. ARP is assumed fixed, therefore do not move.
+ */
+struct ARPCoordinates
+{
+    uint64_t gpsTimestamp = 0;
+    float latitude        = 0;  // [deg]
+    float longitude       = 0;  // [deg]
+    float height          = 0;  // [m]
+    uint8_t satellites    = 0;  // [1]
+    uint8_t fix           = 0;  // 0 = no fix
+
+    static std::string header()
+    {
+        return "timestamp,latitude,longitude,height,satellites,fix\n";
+    }
+
+    void print(std::ostream& os) const
+    {
+        os << gpsTimestamp << "," << latitude << "," << longitude << ","
+           << height << "," << (int)satellites << "," << (int)fix << "\n";
+    }
+};
+}  // namespace Antennas
diff --git a/src/Groundstation/Automated/Hub.cpp b/src/Groundstation/Automated/Hub.cpp
index cb56f69e61033e8c783f165cca22e4264580bc78..c1917662ef24774d8bd9e4005c2eec162e0117d5 100644
--- a/src/Groundstation/Automated/Hub.cpp
+++ b/src/Groundstation/Automated/Hub.cpp
@@ -44,6 +44,7 @@ using namespace miosix;
 
 void Hub::dispatchOutgoingMsg(const mavlink_message_t& msg)
 {
+    TRACE("[info] Hub: Packet arrived from outgoing messages!!!\n");
     LyraGS::BoardStatus* status  = getModule<LyraGS::BoardStatus>();
     LyraGS::RadioMain* radioMain = getModule<LyraGS::RadioMain>();
 
@@ -242,11 +243,12 @@ void Hub::dispatchOutgoingMsg(const mavlink_message_t& msg)
     }
 
     // In case the message is spoofed from ethernet by another groundstation
-    if (msg.sysid == MAV_SYSID_MAIN)
+    if (msg.msgid == MAVLINK_MSG_ID_ROCKET_FLIGHT_TM ||
+        msg.msgid == MAVLINK_MSG_ID_ROCKET_STATS_TM)
     {
         TRACE(
-            "[info] Hub: A MAIN packet was received from ground packet (not "
-            "radio)\n");
+            "[info] Hub: A MAIN packet was received from ground packet "
+            "(ethernet probably and NOT radio)\n");
         /* The message received by ethernet (outgoing) in reality is not a
          * command but the telemetry spoofed, therefore is then used as incoming
          */
@@ -269,11 +271,17 @@ void Hub::dispatchIncomingMsg(const mavlink_message_t& msg)
         mavlink_rocket_flight_tm_t rocketTM;
         mavlink_msg_rocket_flight_tm_decode(&msg, &rocketTM);
         uint64_t timestamp = mavlink_msg_rocket_flight_tm_get_timestamp(&msg);
+        TRACE(
+            "[info] Hub: A FLIGHT_ROCKET_TM packet was received from ground "
+            "packet with ts %llu\n",
+            timestamp);
         /* Messages older and within the discard interval are treated as old
          * messages*/
         if (timestamp <= lastFlightTMTimestamp &&
             lastFlightTMTimestamp > timestamp + DISCARD_MSG_DELAY)
             return;
+        TRACE("[info] Hub: A FLIGHT_ROCKET_TM packet is valid with ts %llu\n",
+              timestamp);
         lastFlightTMTimestamp = timestamp;
         NASState nasState{
             mavlink_msg_rocket_flight_tm_get_timestamp(&msg),
@@ -293,15 +301,23 @@ void Hub::dispatchIncomingMsg(const mavlink_message_t& msg)
     {
         mavlink_rocket_stats_tm_t rocketST;
         mavlink_msg_rocket_stats_tm_decode(&msg, &rocketST);
+        TRACE(
+            "[info] Hub: A ROCKET_STAT_TM packet was received from ground "
+            "packet with ts %llu\n",
+            rocketST.timestamp);
         /* Messages older and within the discard interval are treated as old
          * messages*/
         if (rocketST.timestamp <= lastStatsTMTimestamp &&
             lastStatsTMTimestamp > rocketST.timestamp + DISCARD_MSG_DELAY)
             return;
+        TRACE("[info] Hub: A ROCKET_STAT_TM packet is valid, with ts %llu\n",
+              rocketST.timestamp);
         lastStatsTMTimestamp = rocketST.timestamp;
-        GPSData gpsState;
-        gpsState = getRocketOrigin();
 
+        // TODO: The origin should have its own struct since only timestamp and
+        // [lat, lon, alt] are needed
+        GPSData gpsState;
+        getRocketOrigin(gpsState);
         gpsState.gpsTimestamp = rocketST.timestamp;
         gpsState.latitude     = rocketST.ref_lat;
         gpsState.longitude    = rocketST.ref_lon;
@@ -326,25 +342,28 @@ void Hub::sendAck(const mavlink_message_t& msg)
     dispatchIncomingMsg(ackMsg);
 }
 
-GPSData Hub::getRocketOrigin()
+bool Hub::getRocketOrigin(Boardcore::GPSData& rocketOrigin)
 {
     Lock<FastMutex> lock(coordinatesMutex);
-    return lastRocketCoordinates;
+    rocketOrigin = lastRocketCoordinates;
+    return originReceived;
 }
 
-NASState Hub::getRocketNasState()
+bool Hub::getLastRocketNasState(Boardcore::NASState& nasState)
 {
     Lock<FastMutex> lock(nasStateMutex);
-    flagNasSet = false;
-    return lastRocketNasState;
+    nasState     = lastRocketNasState;
+    hasNewNasSet = false;
+    return rocketNasSet;
 }
 
-bool Hub::hasNasSet() { return flagNasSet; }
+bool Hub::hasNewNasState() { return hasNewNasSet; }
 
 void Hub::setRocketNasState(const NASState& newRocketNasState)
 {
     Lock<FastMutex> lock(nasStateMutex);
-    flagNasSet         = true;
+    hasNewNasSet       = true;
+    rocketNasSet       = true;
     lastRocketNasState = newRocketNasState;
 }
 
diff --git a/src/Groundstation/Automated/Hub.h b/src/Groundstation/Automated/Hub.h
index 5453eac7cb8e89f3339c2c1b099a54dfd075a12f..0828b3d2203ffa8ecb8ca051d6a37873147e9a65 100644
--- a/src/Groundstation/Automated/Hub.h
+++ b/src/Groundstation/Automated/Hub.h
@@ -79,14 +79,17 @@ public:
     /**
      * @brief Synchronized getter for the last rocket origin for NAS.
      */
-    Boardcore::GPSData getRocketOrigin();
+    bool getRocketOrigin(Boardcore::GPSData& rocketOrigin);
 
     /**
      * @brief Synchronized getter for the last rocket NAS state.
+     *
+     * @return true only if the rocket NAS state and is valid and the value is
+     * new (got from radio)
      */
-    Boardcore::NASState getRocketNasState();
+    bool getLastRocketNasState(Boardcore::NASState& nasState);
 
-    bool hasNasSet();
+    bool hasNewNasState();
 
 private:
     /**
@@ -100,10 +103,12 @@ private:
     void setRocketOrigin(const Boardcore::GPSData& newRocketCoordinates);
 
     Boardcore::GPSData lastRocketCoordinates;
+    bool originReceived = false;
     Boardcore::NASState lastRocketNasState;
+    bool rocketNasSet = false;
     miosix::FastMutex coordinatesMutex;
     miosix::FastMutex nasStateMutex;
-    bool flagNasSet = false;
+    bool hasNewNasSet = false;
     uint64_t lastFlightTMTimestamp;
     uint64_t lastStatsTMTimestamp;
 };
diff --git a/src/Groundstation/Common/Ports/EthernetBase.cpp b/src/Groundstation/Common/Ports/EthernetBase.cpp
index f183a9a64b728425db6bc3a3c2982159b5e80874..34ce2d8e658efefb2c7b0a1a896126d67fed2f08 100644
--- a/src/Groundstation/Common/Ports/EthernetBase.cpp
+++ b/src/Groundstation/Common/Ports/EthernetBase.cpp
@@ -82,7 +82,10 @@ bool EthernetBase::start(std::unique_ptr<Boardcore::Wiz5500> wiz5500)
         WizIp ip = IP_BASE;
         ip.d = 1 + ipOffset;  // Add to the ip the offset set on the dipswitch
         this->wiz5500->setSourceIp(ip);
-        this->wiz5500->setSourceMac(genNewRandomMac());
+        WizMac mac = MAC_BASE;
+        // Add to the mac address the offset set on the dipswitch
+        mac.c += 1 + ipOffset;
+        this->wiz5500->setSourceMac(mac);
     }
     else
     {
@@ -100,6 +103,18 @@ bool EthernetBase::start(std::unique_ptr<Boardcore::Wiz5500> wiz5500)
         return false;
     }
 
+    if (sniffOtherGs)
+    {
+        TRACE("[info] starting second UDP socket (inverse direction)\n");
+        if (!this->wiz5500->openUdp(1, SEND_PORT, {255, 255, 255, 255},
+                                    RECV_PORT, 500))
+
+        {
+            TRACE("[error] starting second UDP socket\n");
+            return false;
+        }
+    }
+
     auto mav_handler = [this](EthernetMavDriver* channel,
                               const mavlink_message_t& msg) { handleMsg(msg); };
 
@@ -119,12 +134,29 @@ void EthernetBase::handleMsg(const mavlink_message_t& msg)
 
 ssize_t EthernetBase::receive(uint8_t* pkt, size_t max_len)
 {
+    ssize_t size = 0;
     WizIp dst_ip;
     uint16_t dst_port;
-    return wiz5500->recvfrom(0, pkt, max_len, dst_ip, dst_port);
+    TRACE("Haloo\n");
+
+    if (!sniffOtherGs)
+        size = wiz5500->recvfrom(0, pkt, max_len, dst_ip, dst_port);
+    else
+        // In case of sniffing, there is a maximum waiting time for messages
+        // from the groundstation software to switch between one and other port
+        size = wiz5500->recvfrom(0, pkt, max_len, dst_ip, dst_port,
+                                 RECEIVE_PORT_TIMEOUT_MS);
+    if (size <= 0 && sniffOtherGs)
+    {
+        TRACE("Second sniff\n");
+        size = wiz5500->recvfrom(1, pkt, max_len, dst_ip, dst_port,
+                                 RECEIVE_PORT_TIMEOUT_MS);
+    }
+    return size;
 }
 
 bool EthernetBase::send(uint8_t* pkt, size_t len)
 {
     return wiz5500->send(0, pkt, len, 100);
+    // return true;
 }
diff --git a/src/Groundstation/Common/Ports/EthernetBase.h b/src/Groundstation/Common/Ports/EthernetBase.h
index 5f997f6caed74ecbfaf0dc24cc7353dcd116cd52..e2c1a830a628ed5a7e4d7965ac215b341ce03518 100644
--- a/src/Groundstation/Common/Ports/EthernetBase.h
+++ b/src/Groundstation/Common/Ports/EthernetBase.h
@@ -34,6 +34,9 @@
 namespace Groundstation
 {
 
+// Timeout for the port receive
+static constexpr uint16_t RECEIVE_PORT_TIMEOUT_MS = 200;
+
 Boardcore::WizIp genNewRandomIp();
 Boardcore::WizMac genNewRandomMac();
 
@@ -45,8 +48,8 @@ class EthernetBase : public Boardcore::Transceiver,
 {
 public:
     EthernetBase() {};
-    EthernetBase(bool randomIp, uint8_t ipOffset)
-        : randomIp{randomIp}, ipOffset{ipOffset} {};
+    EthernetBase(bool randomIp, uint8_t ipOffset, bool sniffing)
+        : randomIp{randomIp}, ipOffset{ipOffset}, sniffOtherGs{sniffing} {};
 
     void handleINTn();
 
@@ -56,6 +59,7 @@ public:
 
 protected:
     bool start(std::unique_ptr<Boardcore::Wiz5500> wiz5500);
+    std::unique_ptr<Boardcore::Wiz5500> wiz5500;
 
 private:
     /**
@@ -68,10 +72,10 @@ private:
     bool send(uint8_t* pkt, size_t len) override;
 
     bool started = false;
-    std::unique_ptr<Boardcore::Wiz5500> wiz5500;
     std::unique_ptr<EthernetMavDriver> mav_driver;
-    bool randomIp    = true;
-    uint8_t ipOffset = 0;
+    bool randomIp     = true;
+    uint8_t ipOffset  = 0;
+    bool sniffOtherGs = false;
 };
 
 }  // namespace Groundstation
diff --git a/src/Groundstation/LyraGS/Ports/Ethernet.h b/src/Groundstation/LyraGS/Ports/Ethernet.h
index a267a7abe1cef7bc863076659560f706ab3e3167..4899208287603a70beb44f845202a6c594a01523 100644
--- a/src/Groundstation/LyraGS/Ports/Ethernet.h
+++ b/src/Groundstation/LyraGS/Ports/Ethernet.h
@@ -36,7 +36,10 @@ class EthernetGS : public Boardcore::InjectableWithDeps<
 {
 public:
     EthernetGS() : Super{} {}
-    EthernetGS(bool randomIp, uint8_t ipOffset) : Super{randomIp, ipOffset} {}
+    EthernetGS(bool randomIp, uint8_t ipOffset, bool sniffing)
+        : Super{randomIp, ipOffset, sniffing}
+    {
+    }
     [[nodiscard]] bool start();
     void sendMsg(const mavlink_message_t& msg);
     void handleINTn();
diff --git a/src/Groundstation/LyraGS/lyra-gs-entry.cpp b/src/Groundstation/LyraGS/lyra-gs-entry.cpp
index 869328463a5abc32c44df6001423c7d8c5337773..26ab6211c476afe08739d64e5051cca61e677f1f 100644
--- a/src/Groundstation/LyraGS/lyra-gs-entry.cpp
+++ b/src/Groundstation/LyraGS/lyra-gs-entry.cpp
@@ -141,7 +141,7 @@ int main()
         new LyraGS::RadioMain(dipRead.mainHasBackup, dipRead.mainTXenable);
     LyraGS::BoardStatus* board_status = new LyraGS::BoardStatus(dipRead.isARP);
     LyraGS::EthernetGS* ethernet =
-        new LyraGS::EthernetGS(false, dipRead.ipConfig);
+        new LyraGS::EthernetGS(false, dipRead.ipConfig, true);
     LyraGS::RadioPayload* radio_payload = new LyraGS::RadioPayload(
         dipRead.payloadHasBackup, dipRead.payloadTXenable);
 
@@ -343,7 +343,7 @@ int main()
 
     // Check presence of radio and ethernet
 
-    if (board_status->isMainRadioPresent() && !dipRead.isARP)
+    if (board_status->isMainRadioPresent())
     {
         LOG_INFO(logger, "Main radio detected!\n");
         led1On();  //< GREEN led on (CU)
@@ -351,7 +351,7 @@ int main()
     else
         std::cout << "Main NOT detected" << std::endl;
 
-    if (board_status->isPayloadRadioPresent() && !dipRead.isARP)
+    if (board_status->isPayloadRadioPresent())
     {
         LOG_INFO(logger, "Payload radio detected!\n");
         led2On();  //< YELLOW led on (CU)
@@ -359,7 +359,7 @@ int main()
     else
         std::cout << "Payload NOT detected" << std::endl;
 
-    if (board_status->isEthernetPresent() && !dipRead.isARP)
+    if (board_status->isEthernetPresent())
     {
         LOG_INFO(logger, "Ethernet detected!\n");
         led4On();  //< ORANGE led on (CU)