From b1f8afb8e882bf0fd8aa144c00f7d1ec5d4dc379 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Mon, 1 Apr 2024 22:42:22 +0200 Subject: [PATCH 001/138] Add neighbor rate ratio calculation --- src/inet/linklayer/ieee8021as/Gptp.cc | 14 ++++++++++++-- src/inet/linklayer/ieee8021as/Gptp.h | 11 ++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index d2bc54095c9..7413b290c54 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -153,6 +153,7 @@ void Gptp::handleSelfMessage(cMessage *msg) case GPTP_SELF_MSG_PDELAY_REQ: // slaveport: sendPdelayReq(); //TODO on slaveports only + sendReqEndTimeStamp = clock->getClockTime(); scheduleClockEventAfter(pdelayInterval, selfMsgDelayReq); break; @@ -173,6 +174,10 @@ void Gptp::handleMessage(cMessage *msg) auto incomingNicId = packet->getTag()->getInterfaceId(); int incomingDomainNumber = gptp->getDomainNumber(); + // Get the time when the very first Pdelay_Req message is received + if (gptpMessageType == GPTPTYPE_PDELAY_REQ) + receiveReqStartTimestamp = clock->getClockTime(); // -> tr1 + if (incomingDomainNumber != domainNumber) { EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived with foreign domainNumber " << incomingDomainNumber << ", dropped\n"; PacketDropDetails details; @@ -327,12 +332,12 @@ void Gptp::sendPdelayReq() lastSentPdelayReqSequenceId = sequenceId++; gptp->setSequenceId(lastSentPdelayReqSequenceId); packet->insertAtFront(gptp); - pdelayReqEventEgressTimestamp = clock->getClockTime(); + pdelayReqEventEgressTimestamp = clock->getClockTime(); // -> ti1 rcvdPdelayResp = false; +// sendReqStartTimestamp = clock->getClockTime(); sendPacketToNIC(packet, slavePortId); } - void Gptp::processSync(Packet *packet, const GptpSync* gptp) { rcvdGptpSync = true; @@ -381,6 +386,10 @@ void Gptp::synchronize() emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(oldLocalTimeAtTimeSync) - now); + // Calculate the neighbor rate ratio + neighborRateRatio = (pdelayReqEventEgressTimestamp - sendReqStartTimestamp) / + (receiveReqEndTimestamp - receiveReqStartTimestamp); + /************** Time synchronization ***************************************** * Local time is adjusted using peer delay, correction field, residence time * * and packet transmission time based departure time of Sync message from GM * @@ -438,6 +447,7 @@ void Gptp::processPdelayReq(Packet *packet, const GptpPdelayReq* gptp) resp->setIngressTimestamp(packet->getTag()->getArrivalClockTime()); resp->setSourcePortIdentity(gptp->getSourcePortIdentity()); resp->setSequenceId(gptp->getSequenceId()); + receiveReqEndTimestamp = resp->getIngressTimestamp(); // TODO: check if this is correct scheduleClockEventAfter(pDelayReqProcessingTime, resp); } diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index af4ade20e8c..a5c48a2f9ed 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -48,8 +48,8 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener clocktime_t peerDelay; clocktime_t peerRequestReceiptTimestamp; // pdelayReqIngressTimestamp from peer (received in GptpPdelayResp) clocktime_t peerResponseOriginTimestamp; // pdelayRespEgressTimestamp from peer (received in GptpPdelayRespFollowUp) - clocktime_t pdelayRespEventIngressTimestamp; // receiving time of last GptpPdelayResp - clocktime_t pdelayReqEventEgressTimestamp; // sending time of last GptpPdelayReq + clocktime_t pdelayRespEventIngressTimestamp; // receiving time of last GptpPdelayResp -> ti2' + clocktime_t pdelayReqEventEgressTimestamp; // sending time of last GptpPdelayReq -> ti1' clocktime_t pDelayReqProcessingTime; // processing time between arrived PDelayReq and send of PDelayResp bool rcvdPdelayResp = false; @@ -70,6 +70,12 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener ClockEvent* selfMsgDelayReq = nullptr; ClockEvent* requestMsg = nullptr; + // Neighbor rate ratio calculation parameters + clocktime_t sendReqStartTimestamp; // ti1 + clocktime_t receiveReqStartTimestamp; // tr1 + clocktime_t receiveReqEndTimestamp; // tr1' + static double neighborRateRatio; + // Statistics information: // TODO remove, and replace with emit() calls static simsignal_t localTimeSignal; static simsignal_t timeDifferenceSignal; @@ -91,7 +97,6 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener virtual ~Gptp(); protected: void sendPacketToNIC(Packet *packet, int portId); - void sendSync(); void sendFollowUp(int portId, const GptpSync *sync, clocktime_t preciseOriginTimestamp); void sendPdelayReq(); From 7d68d015e0c4019a6d8aa2acfcc4b1c09e05b2f2 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Mon, 1 Apr 2024 22:43:01 +0200 Subject: [PATCH 002/138] Edit .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1fa8b133d9d..dc3ea77b709 100644 --- a/.gitignore +++ b/.gitignore @@ -93,3 +93,4 @@ xxx /tmp/ /.computed/ *.DS_Store +.idea/ From 3a79af006882cb12d1ba601c773597f06a785cd6 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Fri, 12 Apr 2024 10:00:17 +0200 Subject: [PATCH 003/138] added function calculate NRR and propagation delay --- src/inet/linklayer/ieee8021as/Gptp.cc | 982 +++++++++++++------------- src/inet/linklayer/ieee8021as/Gptp.h | 25 +- 2 files changed, 503 insertions(+), 504 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 7413b290c54..1ebb239a29a 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -22,547 +22,537 @@ namespace inet { -Define_Module(Gptp); + Define_Module(Gptp); -simsignal_t Gptp::localTimeSignal = cComponent::registerSignal("localTime"); -simsignal_t Gptp::timeDifferenceSignal = cComponent::registerSignal("timeDifference"); -simsignal_t Gptp::rateRatioSignal = cComponent::registerSignal("rateRatio"); -simsignal_t Gptp::peerDelaySignal = cComponent::registerSignal("peerDelay"); + simsignal_t Gptp::localTimeSignal = cComponent::registerSignal("localTime"); + simsignal_t Gptp::timeDifferenceSignal = cComponent::registerSignal("timeDifference"); + simsignal_t Gptp::rateRatioSignal = cComponent::registerSignal("rateRatio"); + simsignal_t Gptp::peerDelaySignal = cComponent::registerSignal("peerDelay"); // MAC address: // 01-80-C2-00-00-02 for TimeSync (ieee 802.1as-2020, 13.3.1.2) // 01-80-C2-00-00-0E for Announce and Signaling messages, for Sync, Follow_Up, Pdelay_Req, Pdelay_Resp, and Pdelay_Resp_Follow_Up messages -const MacAddress Gptp::GPTP_MULTICAST_ADDRESS("01:80:C2:00:00:0E"); + const MacAddress Gptp::GPTP_MULTICAST_ADDRESS("01:80:C2:00:00:0E"); // EtherType: // 0x8809 for TimeSync (ieee 802.1as-2020, 13.3.1.2) // 0x88F7 for Announce and Signaling messages, for Sync, Follow_Up, Pdelay_Req, Pdelay_Resp, and Pdelay_Resp_Follow_Up messages -Gptp::~Gptp() -{ - cancelAndDeleteClockEvent(selfMsgDelayReq); - cancelAndDeleteClockEvent(selfMsgSync); - cancelAndDeleteClockEvent(requestMsg); -} - -void Gptp::initialize(int stage) -{ - ClockUserModuleBase::initialize(stage); - - if (stage == INITSTAGE_LOCAL) { - gptpNodeType = static_cast(cEnum::get("GptpNodeType", "inet")->resolve(par("gptpNodeType"))); - domainNumber = par("domainNumber"); - syncInterval = par("syncInterval"); - pDelayReqProcessingTime = par("pDelayReqProcessingTime"); - std::hash strHash; - clockIdentity = strHash(getFullPath()); + Gptp::~Gptp() { + cancelAndDeleteClockEvent(selfMsgDelayReq); + cancelAndDeleteClockEvent(selfMsgSync); + cancelAndDeleteClockEvent(requestMsg); } - if (stage == INITSTAGE_LINK_LAYER) { - peerDelay = 0; - receivedTimeSync = CLOCKTIME_ZERO; - - interfaceTable.reference(this, "interfaceTableModule", true); - - const char *str = par("slavePort"); - if (*str) { - if (gptpNodeType == MASTER_NODE) - throw cRuntimeError("Parameter inconsistency: MASTER_NODE with slave port"); - auto nic = CHK(interfaceTable->findInterfaceByName(str)); - slavePortId = nic->getInterfaceId(); - nic->subscribe(transmissionEndedSignal, this); - nic->subscribe(receptionEndedSignal, this); + + void Gptp::initialize(int stage) { + ClockUserModuleBase::initialize(stage); + + if (stage == INITSTAGE_LOCAL) { + gptpNodeType = static_cast(cEnum::get("GptpNodeType", "inet")->resolve(par("gptpNodeType"))); + domainNumber = par("domainNumber"); + syncInterval = par("syncInterval"); + pDelayReqProcessingTime = par("pDelayReqProcessingTime"); + std::hash strHash; + clockIdentity = strHash(getFullPath()); } - else - if (gptpNodeType != MASTER_NODE) + if (stage == INITSTAGE_LINK_LAYER) { + peerDelay = 0; + receivedTimeSync = CLOCKTIME_ZERO; + + interfaceTable.reference(this, "interfaceTableModule", true); + + const char *str = par("slavePort"); + if (*str) { + if (gptpNodeType == MASTER_NODE) + throw cRuntimeError("Parameter inconsistency: MASTER_NODE with slave port"); + auto nic = CHK(interfaceTable->findInterfaceByName(str)); + slavePortId = nic->getInterfaceId(); + nic->subscribe(transmissionEndedSignal, this); + nic->subscribe(receptionEndedSignal, this); + } else if (gptpNodeType != MASTER_NODE) throw cRuntimeError("Parameter error: Missing slave port for %s", par("gptpNodeType").stringValue()); - auto v = check_and_cast(par("masterPorts").objectValue())->asStringVector(); - if (v.empty() and gptpNodeType != SLAVE_NODE) - throw cRuntimeError("Parameter error: Missing any master port for %s", par("gptpNodeType").stringValue()); - for (const auto& p : v) { - auto nic = CHK(interfaceTable->findInterfaceByName(p.c_str())); - int portId = nic->getInterfaceId(); - if (portId == slavePortId) - throw cRuntimeError("Parameter error: the port '%s' specified both master and slave port", p.c_str()); - masterPortIds.insert(portId); - nic->subscribe(transmissionEndedSignal, this); - nic->subscribe(receptionEndedSignal, this); - } + auto v = check_and_cast(par("masterPorts").objectValue())->asStringVector(); + if (v.empty() and gptpNodeType != SLAVE_NODE) + throw cRuntimeError("Parameter error: Missing any master port for %s", + par("gptpNodeType").stringValue()); + for (const auto &p: v) { + auto nic = CHK(interfaceTable->findInterfaceByName(p.c_str())); + int portId = nic->getInterfaceId(); + if (portId == slavePortId) + throw cRuntimeError("Parameter error: the port '%s' specified both master and slave port", + p.c_str()); + masterPortIds.insert(portId); + nic->subscribe(transmissionEndedSignal, this); + nic->subscribe(receptionEndedSignal, this); + } - if (slavePortId != -1) { - auto networkInterface = interfaceTable->getInterfaceById(slavePortId); - if (!networkInterface->matchesMulticastMacAddress(GPTP_MULTICAST_ADDRESS)) - networkInterface->addMulticastMacAddress(GPTP_MULTICAST_ADDRESS); - } - for (auto id: masterPortIds) { - auto networkInterface = interfaceTable->getInterfaceById(id); - if (!networkInterface->matchesMulticastMacAddress(GPTP_MULTICAST_ADDRESS)) - networkInterface->addMulticastMacAddress(GPTP_MULTICAST_ADDRESS); - } + if (slavePortId != -1) { + auto networkInterface = interfaceTable->getInterfaceById(slavePortId); + if (!networkInterface->matchesMulticastMacAddress(GPTP_MULTICAST_ADDRESS)) + networkInterface->addMulticastMacAddress(GPTP_MULTICAST_ADDRESS); + } + for (auto id: masterPortIds) { + auto networkInterface = interfaceTable->getInterfaceById(id); + if (!networkInterface->matchesMulticastMacAddress(GPTP_MULTICAST_ADDRESS)) + networkInterface->addMulticastMacAddress(GPTP_MULTICAST_ADDRESS); + } - correctionField = par("correctionField"); + correctionField = par("correctionField"); - gmRateRatio = 1.0; + gmRateRatio = 1.0; - registerProtocol(Protocol::gptp, gate("socketOut"), gate("socketIn")); + registerProtocol(Protocol::gptp, gate("socketOut"), gate("socketIn")); - /* Only grandmaster in the domain can initialize the synchronization message periodically - * so below condition checks whether it is grandmaster and then schedule first sync message */ - if(gptpNodeType == MASTER_NODE) - { - // Schedule Sync message to be sent - selfMsgSync = new ClockEvent("selfMsgSync", GPTP_SELF_MSG_SYNC); + /* Only grandmaster in the domain can initialize the synchronization message periodically + * so below condition checks whether it is grandmaster and then schedule first sync message */ + if (gptpNodeType == MASTER_NODE) { + // Schedule Sync message to be sent + selfMsgSync = new ClockEvent("selfMsgSync", GPTP_SELF_MSG_SYNC); - clocktime_t scheduleSync = par("syncInitialOffset"); - originTimestamp = clock->getClockTime() + scheduleSync; - scheduleClockEventAfter(scheduleSync, selfMsgSync); - } - if(slavePortId != -1) - { - requestMsg = new ClockEvent("requestToSendSync", GPTP_REQUEST_TO_SEND_SYNC); - - // Schedule Pdelay_Req message is sent by slave port - // without depending on node type which is grandmaster or bridge - selfMsgDelayReq = new ClockEvent("selfMsgPdelay", GPTP_SELF_MSG_PDELAY_REQ); - pdelayInterval = par("pdelayInterval"); - scheduleClockEventAfter(par("pdelayInitialOffset"), selfMsgDelayReq); + clocktime_t scheduleSync = par("syncInitialOffset"); + originTimestamp = clock->getClockTime() + scheduleSync; + scheduleClockEventAfter(scheduleSync, selfMsgSync); + } + if (slavePortId != -1) { + requestMsg = new ClockEvent("requestToSendSync", GPTP_REQUEST_TO_SEND_SYNC); + + // Schedule Pdelay_Req message is sent by slave port + // without depending on node type which is grandmaster or bridge + selfMsgDelayReq = new ClockEvent("selfMsgPdelay", GPTP_SELF_MSG_PDELAY_REQ); + pdelayInterval = par("pdelayInterval"); + scheduleClockEventAfter(par("pdelayInitialOffset"), selfMsgDelayReq); + } + WATCH(peerDelay); } - WATCH(peerDelay); } -} - -void Gptp::handleSelfMessage(cMessage *msg) -{ - switch(msg->getKind()) { - case GPTP_SELF_MSG_SYNC: - // masterport: - ASSERT(selfMsgSync == msg); - sendSync(); - - /* Schedule next Sync message at next sync interval - * Grand master always works at simulation time */ - scheduleClockEventAfter(syncInterval, selfMsgSync); - break; - - case GPTP_SELF_REQ_ANSWER_KIND: - // masterport: - sendPdelayResp(check_and_cast(msg)); - delete msg; - break; - - case GPTP_SELF_MSG_PDELAY_REQ: - // slaveport: - sendPdelayReq(); //TODO on slaveports only - sendReqEndTimeStamp = clock->getClockTime(); - scheduleClockEventAfter(pdelayInterval, selfMsgDelayReq); - break; - default: - throw cRuntimeError("Unknown self message (%s)%s, kind=%d", msg->getClassName(), msg->getName(), msg->getKind()); + void Gptp::handleSelfMessage(cMessage *msg) { + switch (msg->getKind()) { + case GPTP_SELF_MSG_SYNC: + // masterport: + ASSERT(selfMsgSync == msg); + sendSync(); + + /* Schedule next Sync message at next sync interval + * Grand master always works at simulation time */ + scheduleClockEventAfter(syncInterval, selfMsgSync); + break; + + case GPTP_SELF_REQ_ANSWER_KIND: + // masterport: + pdelayRespEventEgressTimestamp = clock->getClockTime(); //should this time information be included in the packet of pdelay_resp_followup? + sendPdelayResp(check_and_cast(msg)); + delete msg; + break; + + case GPTP_SELF_MSG_PDELAY_REQ: + // slaveport: + sendPdelayReq(); //TODO on slaveports only + sendReqEndTimeStamp = clock->getClockTime(); + scheduleClockEventAfter(pdelayInterval, selfMsgDelayReq); + break; + + default: + throw cRuntimeError("Unknown self message (%s)%s, kind=%d", msg->getClassName(), msg->getName(), + msg->getKind()); + } } -} -void Gptp::handleMessage(cMessage *msg) -{ - if (msg->isSelfMessage()) { - handleSelfMessage(msg); - } - else { - Packet *packet = check_and_cast(msg); - auto gptp = packet->peekAtFront(); - auto gptpMessageType = gptp->getMessageType(); - auto incomingNicId = packet->getTag()->getInterfaceId(); - int incomingDomainNumber = gptp->getDomainNumber(); - - // Get the time when the very first Pdelay_Req message is received - if (gptpMessageType == GPTPTYPE_PDELAY_REQ) - receiveReqStartTimestamp = clock->getClockTime(); // -> tr1 - - if (incomingDomainNumber != domainNumber) { - EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived with foreign domainNumber " << incomingDomainNumber << ", dropped\n"; - PacketDropDetails details; - details.setReason(NOT_ADDRESSED_TO_US); - emit(packetDroppedSignal, packet, &details); - } - else if (incomingNicId == slavePortId) { - // slave port - switch (gptpMessageType) { - case GPTPTYPE_SYNC: - processSync(packet, check_and_cast(gptp.get())); - break; - case GPTPTYPE_FOLLOW_UP: - processFollowUp(packet, check_and_cast(gptp.get())); - // Send a request to send Sync message - // through other gptp Ethernet interfaces - if(gptpNodeType == BRIDGE_NODE) - sendSync(); - break; - case GPTPTYPE_PDELAY_RESP: - processPdelayResp(packet, check_and_cast(gptp.get())); - break; - case GPTPTYPE_PDELAY_RESP_FOLLOW_UP: - processPdelayRespFollowUp(packet, check_and_cast(gptp.get())); - break; - default: - throw cRuntimeError("Unknown gPTP packet type: %d", (int)(gptpMessageType)); - } - } - else if (masterPortIds.find(incomingNicId) != masterPortIds.end()) { - // master port - if(gptpMessageType == GPTPTYPE_PDELAY_REQ) { - processPdelayReq(packet, check_and_cast(gptp.get())); - } - else { - throw cRuntimeError("Unaccepted gPTP type: %d", (int)(gptpMessageType)); + void Gptp::handleMessage(cMessage *msg) { + if (msg->isSelfMessage()) { + handleSelfMessage(msg); + } else { + Packet *packet = check_and_cast(msg); + auto gptp = packet->peekAtFront(); + auto gptpMessageType = gptp->getMessageType(); + auto incomingNicId = packet->getTag()->getInterfaceId(); + int incomingDomainNumber = gptp->getDomainNumber(); + + if (incomingDomainNumber != domainNumber) { + EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived with foreign domainNumber " + << incomingDomainNumber << ", dropped\n"; + PacketDropDetails details; + details.setReason(NOT_ADDRESSED_TO_US); + emit(packetDroppedSignal, packet, &details); + } else if (incomingNicId == slavePortId) { + // slave port + switch (gptpMessageType) { + case GPTPTYPE_SYNC: + processSync(packet, check_and_cast(gptp.get())); + break; + case GPTPTYPE_FOLLOW_UP: + processFollowUp(packet, check_and_cast(gptp.get())); + // Send a request to send Sync message + // through other gptp Ethernet interfaces + if (gptpNodeType == BRIDGE_NODE) + sendSync(); + break; + case GPTPTYPE_PDELAY_RESP: + + processPdelayResp(packet, check_and_cast(gptp.get())); + break; + case GPTPTYPE_PDELAY_RESP_FOLLOW_UP: + processPdelayRespFollowUp(packet, check_and_cast(gptp.get())); + break; + default: + throw cRuntimeError("Unknown gPTP packet type: %d", (int) (gptpMessageType)); + } + } else if (masterPortIds.find(incomingNicId) != masterPortIds.end()) { + // master port + if (gptpMessageType == GPTPTYPE_PDELAY_REQ) { + pdelayReqEventIngressTimestampLast = pdelayReqEventIngressTimestamp; // store the old value + pdelayReqEventIngressTimestamp = clock->getClockTime(); //should cover with the current time of master. Question: will "getColckTime()" function return the current time of master or slave? + processPdelayReq(packet, check_and_cast(gptp.get())); + } else { + throw cRuntimeError("Unaccepted gPTP type: %d", (int) (gptpMessageType)); + } + } else { + // passive port + EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived on passive port " << incomingNicId + << ", dropped\n"; } + delete msg; } - else { - // passive port - EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived on passive port " << incomingNicId << ", dropped\n"; + } + + void Gptp::sendPacketToNIC(Packet *packet, int portId) { + auto networkInterface = interfaceTable->getInterfaceById(portId); + EV_INFO << "Sending " << packet << " to output interface = " << networkInterface->getInterfaceName() << ".\n"; + packet->addTag()->setInterfaceId(portId); + packet->addTag()->setProtocol(&Protocol::gptp); + packet->addTag()->setProtocol(&Protocol::gptp); + auto protocol = networkInterface->getProtocol(); + if (protocol != nullptr) + packet->addTagIfAbsent()->setProtocol(protocol); + else + packet->removeTagIfPresent(); + send(packet, "socketOut"); + } + + void Gptp::sendSync() { + auto packet = new Packet("GptpSync"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + /* OriginTimestamp always get Sync departure time from grand master */ + if (gptpNodeType == MASTER_NODE) { + originTimestamp = clock->getClockTime(); } - delete msg; + //gptp->setOriginTimestamp(CLOCKTIME_ZERO); + gptp->setSequenceId(sequenceId++); + + sentTimeSyncSync = clock->getClockTime(); + packet->insertAtFront(gptp); + + for (auto port: masterPortIds) + sendPacketToNIC(packet->dup(), port); + delete packet; + + // The sendFollowUp(portId) called by receiveSignal(), when GptpSync sent } -} - -void Gptp::sendPacketToNIC(Packet *packet, int portId) -{ - auto networkInterface = interfaceTable->getInterfaceById(portId); - EV_INFO << "Sending " << packet << " to output interface = " << networkInterface->getInterfaceName() << ".\n"; - packet->addTag()->setInterfaceId(portId); - packet->addTag()->setProtocol(&Protocol::gptp); - packet->addTag()->setProtocol(&Protocol::gptp); - auto protocol = networkInterface->getProtocol(); - if (protocol != nullptr) - packet->addTagIfAbsent()->setProtocol(protocol); - else - packet->removeTagIfPresent(); - send(packet, "socketOut"); -} - -void Gptp::sendSync() -{ - auto packet = new Packet("GptpSync"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - /* OriginTimestamp always get Sync departure time from grand master */ - if (gptpNodeType == MASTER_NODE) { - originTimestamp = clock->getClockTime(); + + void Gptp::sendFollowUp(int portId, const GptpSync *sync, clocktime_t syncTxEndTimestamp) { + auto packet = new Packet("GptpFollowUp"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + gptp->setPreciseOriginTimestamp(originTimestamp); + gptp->setSequenceId(sync->getSequenceId()); + + if (gptpNodeType == MASTER_NODE) { + gptp->setCorrectionField(syncTxEndTimestamp - originTimestamp); + } else if (gptpNodeType == BRIDGE_NODE) { + /**************** Correction field calculation ********************************************* + * It is calculated by adding peer delay, residence time and packet transmission time * + * correctionField(i)=correctionField(i-1)+peerDelay+(timeReceivedSync-timeSentSync)*(1-f) * + *******************************************************************************************/ + gptp->setCorrectionField(syncTxEndTimestamp - originTimestamp); + } + gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); + packet->insertAtFront(gptp); + sendPacketToNIC(packet, portId); } - //gptp->setOriginTimestamp(CLOCKTIME_ZERO); - gptp->setSequenceId(sequenceId++); - - sentTimeSyncSync = clock->getClockTime(); - packet->insertAtFront(gptp); - - for (auto port: masterPortIds) - sendPacketToNIC(packet->dup(), port); - delete packet; - - // The sendFollowUp(portId) called by receiveSignal(), when GptpSync sent -} - -void Gptp::sendFollowUp(int portId, const GptpSync *sync, clocktime_t syncTxEndTimestamp) -{ - auto packet = new Packet("GptpFollowUp"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - gptp->setPreciseOriginTimestamp(originTimestamp); - gptp->setSequenceId(sync->getSequenceId()); - - if (gptpNodeType == MASTER_NODE) { - gptp->setCorrectionField(syncTxEndTimestamp - originTimestamp); + + void Gptp::sendPdelayResp(GptpReqAnswerEvent *req) { + int portId = req->getPortId(); + auto packet = new Packet("GptpPdelayResp"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + gptp->setRequestingPortIdentity(req->getSourcePortIdentity()); + gptp->setSequenceId(req->getSequenceId()); + gptp->setRequestReceiptTimestamp(req->getIngressTimestamp()); + packet->insertAtFront(gptp); + sendPacketToNIC(packet, portId); + // The sendPdelayRespFollowUp(portId) called by receiveSignal(), when GptpPdelayResp sent } - else if (gptpNodeType == BRIDGE_NODE) - { - /**************** Correction field calculation ********************************************* - * It is calculated by adding peer delay, residence time and packet transmission time * - * correctionField(i)=correctionField(i-1)+peerDelay+(timeReceivedSync-timeSentSync)*(1-f) * - *******************************************************************************************/ - gptp->setCorrectionField(syncTxEndTimestamp - originTimestamp); + + void Gptp::sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp) { + auto packet = new Packet("GptpPdelayRespFollowUp"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + auto now = clock->getClockTime(); + gptp->setDomainNumber(domainNumber); + gptp->setResponseOriginTimestamp(now); + gptp->setRequestingPortIdentity(resp->getRequestingPortIdentity()); + gptp->setSequenceId(resp->getSequenceId()); + packet->insertAtFront(gptp); + sendPacketToNIC(packet, portId); } - gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); - packet->insertAtFront(gptp); - sendPacketToNIC(packet, portId); -} - -void Gptp::sendPdelayResp(GptpReqAnswerEvent* req) -{ - int portId = req->getPortId(); - auto packet = new Packet("GptpPdelayResp"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - gptp->setRequestingPortIdentity(req->getSourcePortIdentity()); - gptp->setSequenceId(req->getSequenceId()); - gptp->setRequestReceiptTimestamp(req->getIngressTimestamp()); - packet->insertAtFront(gptp); - sendPacketToNIC(packet, portId); - // The sendPdelayRespFollowUp(portId) called by receiveSignal(), when GptpPdelayResp sent -} - -void Gptp::sendPdelayRespFollowUp(int portId, const GptpPdelayResp* resp) -{ - auto packet = new Packet("GptpPdelayRespFollowUp"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - auto now = clock->getClockTime(); - gptp->setDomainNumber(domainNumber); - gptp->setResponseOriginTimestamp(now); - gptp->setRequestingPortIdentity(resp->getRequestingPortIdentity()); - gptp->setSequenceId(resp->getSequenceId()); - packet->insertAtFront(gptp); - sendPacketToNIC(packet, portId); -} - -void Gptp::sendPdelayReq() -{ - ASSERT(slavePortId != -1); - auto packet = new Packet("GptpPdelayReq"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - gptp->setCorrectionField(CLOCKTIME_ZERO); - //save and send IDs - PortIdentity portId; - portId.clockIdentity = clockIdentity; - portId.portNumber = slavePortId; - gptp->setSourcePortIdentity(portId); - lastSentPdelayReqSequenceId = sequenceId++; - gptp->setSequenceId(lastSentPdelayReqSequenceId); - packet->insertAtFront(gptp); - pdelayReqEventEgressTimestamp = clock->getClockTime(); // -> ti1 - rcvdPdelayResp = false; + + void Gptp::sendPdelayReq() { + ASSERT(slavePortId != -1); + auto packet = new Packet("GptpPdelayReq"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + gptp->setCorrectionField(CLOCKTIME_ZERO); + //save and send IDs + PortIdentity portId; + portId.clockIdentity = clockIdentity; + portId.portNumber = slavePortId; + gptp->setSourcePortIdentity(portId); + lastSentPdelayReqSequenceId = sequenceId++; + gptp->setSequenceId(lastSentPdelayReqSequenceId); + packet->insertAtFront(gptp); + pdelayReqEventEgressTimestamp = clock->getClockTime(); + rcvdPdelayResp = false; // sendReqStartTimestamp = clock->getClockTime(); - sendPacketToNIC(packet, slavePortId); -} - -void Gptp::processSync(Packet *packet, const GptpSync* gptp) -{ - rcvdGptpSync = true; - lastReceivedGptpSyncSequenceId = gptp->getSequenceId(); - - // peerSentTimeSync = gptp->getOriginTimestamp(); // TODO this is unfilled in two-step mode - syncIngressTimestamp = packet->getTag()->getArrivalClockTime(); -} - -void Gptp::processFollowUp(Packet *packet, const GptpFollowUp* gptp) -{ - // check: is received the GptpSync for this GptpFollowUp? - if (!rcvdGptpSync) { - EV_WARN << "GptpFollowUp arrived without GptpSync, dropped"; - return; + sendPacketToNIC(packet, slavePortId); } - // verify IDs - if (gptp->getSequenceId() != lastReceivedGptpSyncSequenceId) { - EV_WARN << "GptpFollowUp arrived with invalid sequence ID, dropped"; - return; + + void Gptp::processSync(Packet *packet, const GptpSync *gptp) { + rcvdGptpSync = true; + lastReceivedGptpSyncSequenceId = gptp->getSequenceId(); + + // peerSentTimeSync = gptp->getOriginTimestamp(); // TODO this is unfilled in two-step mode + syncIngressTimestamp = packet->getTag()->getArrivalClockTime(); } - originTimestamp = gptp->getPreciseOriginTimestamp(); - correctionField = gptp->getCorrectionField(); - peerSentTimeSync = gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); - receivedRateRatio = gptp->getFollowUpInformationTLV().getRateRatio(); - - synchronize(); - - EV_INFO << "############## FOLLOW_UP ################################"<< endl; - EV_INFO << "RECEIVED TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; - EV_INFO << "ORIGIN TIME SYNC - " << originTimestamp << endl; - EV_INFO << "CORRECTION FIELD - " << correctionField << endl; - EV_INFO << "PROPAGATION DELAY - " << peerDelay << endl; - EV_INFO << "peerSentTimeSync - " << peerSentTimeSync << endl; - EV_INFO << "receivedRateRatio - " << receivedRateRatio << endl; - - rcvdGptpSync = false; -} - -void Gptp::synchronize() -{ - simtime_t now = simTime(); - clocktime_t oldLocalTimeAtTimeSync = clock->getClockTime(); - clocktime_t residenceTime = oldLocalTimeAtTimeSync - syncIngressTimestamp; - - emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(oldLocalTimeAtTimeSync) - now); - - // Calculate the neighbor rate ratio - neighborRateRatio = (pdelayReqEventEgressTimestamp - sendReqStartTimestamp) / - (receiveReqEndTimestamp - receiveReqStartTimestamp); - - /************** Time synchronization ***************************************** - * Local time is adjusted using peer delay, correction field, residence time * - * and packet transmission time based departure time of Sync message from GM * - *****************************************************************************/ - clocktime_t newTime = originTimestamp + correctionField + peerDelay + residenceTime; - - ASSERT(gptpNodeType != MASTER_NODE); - - // TODO validate the following expression with the standard - if (oldPeerSentTimeSync == -1) - gmRateRatio = 1; - else - gmRateRatio = (peerSentTimeSync - oldPeerSentTimeSync) / (syncIngressTimestamp - receivedTimeSync); - - auto settableClock = check_and_cast(clock.get()); - ppm newOscillatorCompensation = unit(gmRateRatio * (1 + unit(settableClock->getOscillatorCompensation()).get()) - 1); - settableClock->setClockTime(newTime, newOscillatorCompensation, true); - - newLocalTimeAtTimeSync = clock->getClockTime(); - timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; - receivedTimeSync = syncIngressTimestamp; - - // adjust local timestamps, too - adjustLocalTimestamp(pdelayRespEventIngressTimestamp); - adjustLocalTimestamp(pdelayReqEventEgressTimestamp); - adjustLocalTimestamp(receivedTimeSync); - - /************** Rate ratio calculation ************************************* - * It is calculated based on interval between two successive Sync messages * - ***************************************************************************/ - - EV_INFO << "############## SYNC #####################################"<< endl; - EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; - EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; - EV_INFO << "CURRENT SIMTIME - " << now << endl; - EV_INFO << "ORIGIN TIME SYNC - " << peerSentTimeSync << endl; - EV_INFO << "PREV ORIGIN TIME SYNC - " << oldPeerSentTimeSync << endl; - EV_INFO << "RESIDENCE TIME - " << residenceTime << endl; - EV_INFO << "CORRECTION FIELD - " << correctionField << endl; - EV_INFO << "PROPAGATION DELAY - " << peerDelay << endl; - EV_INFO << "TIME DIFFERENCE TO SIMTIME - " << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; - EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; - - oldPeerSentTimeSync = peerSentTimeSync; - - emit(rateRatioSignal, gmRateRatio); - emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); - emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); -} - -void Gptp::processPdelayReq(Packet *packet, const GptpPdelayReq* gptp) -{ - auto resp = new GptpReqAnswerEvent("selfMsgPdelayResp", GPTP_SELF_REQ_ANSWER_KIND); - resp->setPortId(packet->getTag()->getInterfaceId()); - resp->setIngressTimestamp(packet->getTag()->getArrivalClockTime()); - resp->setSourcePortIdentity(gptp->getSourcePortIdentity()); - resp->setSequenceId(gptp->getSequenceId()); - receiveReqEndTimestamp = resp->getIngressTimestamp(); // TODO: check if this is correct - - scheduleClockEventAfter(pDelayReqProcessingTime, resp); -} - -void Gptp::processPdelayResp(Packet *packet, const GptpPdelayResp* gptp) -{ - // verify IDs - if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || gptp->getRequestingPortIdentity().portNumber != slavePortId) { - EV_WARN << "GptpPdelayResp arrived with invalid PortIdentity, dropped"; - return; + void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) { + // check: is received the GptpSync for this GptpFollowUp? + if (!rcvdGptpSync) { + EV_WARN << "GptpFollowUp arrived without GptpSync, dropped"; + return; + } + // verify IDs + if (gptp->getSequenceId() != lastReceivedGptpSyncSequenceId) { + EV_WARN << "GptpFollowUp arrived with invalid sequence ID, dropped"; + return; + } + + originTimestamp = gptp->getPreciseOriginTimestamp(); + correctionField = gptp->getCorrectionField(); + peerSentTimeSync = gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); + receivedRateRatio = gptp->getFollowUpInformationTLV().getRateRatio(); + + synchronize(); + + EV_INFO << "############## FOLLOW_UP ################################" << endl; + EV_INFO << "RECEIVED TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; + EV_INFO << "ORIGIN TIME SYNC - " << originTimestamp << endl; + EV_INFO << "CORRECTION FIELD - " << correctionField << endl; + EV_INFO << "PROPAGATION DELAY - " << peerDelay << endl; + EV_INFO << "peerSentTimeSync - " << peerSentTimeSync << endl; + EV_INFO << "receivedRateRatio - " << receivedRateRatio << endl; + + rcvdGptpSync = false; } - if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { - EV_WARN << "GptpPdelayResp arrived with invalid sequence ID, dropped"; - return; + +//calculate Neighbor Rate Ratio +//called in processPdelayRespFollowUp() + void Gptp::calculateNRR() { + if (pdelayReqEventEgressTimestamp == -1) + neighborRateRatio = 1.0; + else + neighborRateRatio = (pdelayReqEventEgressTimestamp - pdelayReqEventEgressTimestampLast) / + (pdelayReqEventIngressTimestamp - pdelayReqEventIngressTimestampLast); } - rcvdPdelayResp = true; - pdelayRespEventIngressTimestamp = packet->getTag()->getArrivalClockTime(); - peerRequestReceiptTimestamp = gptp->getRequestReceiptTimestamp(); - peerResponseOriginTimestamp = CLOCKTIME_ZERO; -} - -void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp* gptp) -{ - if (!rcvdPdelayResp) { - EV_WARN << "GptpPdelayRespFollowUp arrived without GptpPdelayResp, dropped"; - return; +//calculate Propagation Delay (also named as Peer Delay by original author) + void Gptp::calculatePD() { + PD = ((pdelayRespEventIngressTimestamp - pdelayReqEventEgressTimestamp) / neighborRateRatio - + (pdelayRespEventEgressTimestamp - pdelayReqEventIngressTimestamp)) / 2 } - // verify IDs - if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || gptp->getRequestingPortIdentity().portNumber != slavePortId) { - EV_WARN << "GptpPdelayRespFollowUp arrived with invalid PortIdentity, dropped"; - return; + + + void Gptp::synchronize() { + simtime_t now = simTime(); + clocktime_t oldLocalTimeAtTimeSync = clock->getClockTime(); + clocktime_t residenceTime = oldLocalTimeAtTimeSync - syncIngressTimestamp; + + emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(oldLocalTimeAtTimeSync) - now); + + + /************** Time synchronization ***************************************** + * Local time is adjusted using peer delay, correction field, residence time * + * and packet transmission time based departure time of Sync message from GM * + *****************************************************************************/ + clocktime_t newTime = originTimestamp + correctionField + peerDelay + residenceTime; + + ASSERT(gptpNodeType != MASTER_NODE); + + auto settableClock = check_and_cast(clock.get()); + ppm newOscillatorCompensation = unit( + gmRateRatio * (1 + unit(settableClock->getOscillatorCompensation()).get()) - 1); + settableClock->setClockTime(newTime, newOscillatorCompensation, true); + + newLocalTimeAtTimeSync = clock->getClockTime(); + timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; + receivedTimeSync = syncIngressTimestamp; + + // adjust local timestamps, too + adjustLocalTimestamp(pdelayRespEventIngressTimestamp); +// adjustLocalTimestamp(pdelayReqEventEgressTimestamp); + adjustLocalTimestamp(receivedTimeSync); + + /************** Rate ratio calculation ************************************* + * It is calculated based on interval between two successive Sync messages * + ***************************************************************************/ + + EV_INFO << "############## SYNC #####################################" << endl; + EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; + EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; + EV_INFO << "CURRENT SIMTIME - " << now << endl; + EV_INFO << "ORIGIN TIME SYNC - " << peerSentTimeSync << endl; + EV_INFO << "PREV ORIGIN TIME SYNC - " << oldPeerSentTimeSync << endl; + EV_INFO << "RESIDENCE TIME - " << residenceTime << endl; + EV_INFO << "CORRECTION FIELD - " << correctionField << endl; + EV_INFO << "PROPAGATION DELAY - " << peerDelay << endl; + EV_INFO << "TIME DIFFERENCE TO SIMTIME - " << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; + EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; + + oldPeerSentTimeSync = peerSentTimeSync; + + emit(rateRatioSignal, gmRateRatio); + emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); + emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); } - if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { - EV_WARN << "GptpPdelayRespFollowUp arrived with invalid sequence ID, dropped"; - return; + + void Gptp::processPdelayReq(Packet *packet, const GptpPdelayReq *gptp) { + auto resp = new GptpReqAnswerEvent("selfMsgPdelayResp", GPTP_SELF_REQ_ANSWER_KIND); + resp->setPortId(packet->getTag()->getInterfaceId()); + resp->setIngressTimestamp(packet->getTag()->getArrivalClockTime()); + resp->setSourcePortIdentity(gptp->getSourcePortIdentity()); + resp->setSequenceId(gptp->getSequenceId()); + receiveReqEndTimestamp = resp->getIngressTimestamp(); // TODO: check if this is correct + + scheduleClockEventAfter(pDelayReqProcessingTime, resp); } - peerResponseOriginTimestamp = gptp->getResponseOriginTimestamp(); - - // computePropTime(): - peerDelay = (gmRateRatio * (pdelayRespEventIngressTimestamp - pdelayReqEventEgressTimestamp) - (peerResponseOriginTimestamp - peerRequestReceiptTimestamp)) / 2.0; - - EV_INFO << "RATE RATIO - " << gmRateRatio << endl; - EV_INFO << "pdelayReqEventEgressTimestamp - " << pdelayReqEventEgressTimestamp << endl; - EV_INFO << "peerResponseOriginTimestamp - " << peerResponseOriginTimestamp << endl; - EV_INFO << "pdelayRespEventIngressTimestamp - " << pdelayRespEventIngressTimestamp << endl; - EV_INFO << "peerRequestReceiptTimestamp - " << peerRequestReceiptTimestamp << endl; - EV_INFO << "PEER DELAY - " << peerDelay << endl; - - emit(peerDelaySignal, CLOCKTIME_AS_SIMTIME(peerDelay)); -} - -const GptpBase *Gptp::extractGptpHeader(Packet *packet) -{ - auto protocol = packet->getTag()->getProtocol(); - if (*protocol != Protocol::ethernetPhy) - return nullptr; - - const auto& ethPhyHeader = packet->peekAtFront(); - const auto& ethMacHeader = packet->peekDataAt(ethPhyHeader->getChunkLength()); - if (ethMacHeader->getTypeOrLength() != ETHERTYPE_GPTP) - return nullptr; - - b offset = ethPhyHeader->getChunkLength() + ethMacHeader->getChunkLength(); - return packet->peekDataAt(offset).get(); -} - -void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj, cObject *details) -{ - Enter_Method("%s", cComponent::getSignalName(simSignal)); - - if (simSignal != receptionEndedSignal && simSignal != transmissionEndedSignal) - return; - - auto ethernetSignal = check_and_cast(obj); - auto packet = check_and_cast_nullable(ethernetSignal->getEncapsulatedPacket()); - if (!packet) - return; - - auto gptp = extractGptpHeader(packet); - if (!gptp) - return; - - if (gptp->getDomainNumber() != domainNumber) - return; - - if (simSignal == receptionEndedSignal) - packet->addTagIfAbsent()->setArrivalClockTime(clock->getClockTime()); - else if (simSignal == transmissionEndedSignal) - handleDelayOrSendFollowUp(gptp, source); -} - -void Gptp::handleDelayOrSendFollowUp(const GptpBase *gptp, cComponent *source) -{ - int portId = getContainingNicModule(check_and_cast(source))->getInterfaceId(); - - switch (gptp->getMessageType()) { - case GPTPTYPE_PDELAY_RESP: { - auto gptpResp = check_and_cast(gptp); - sendPdelayRespFollowUp(portId, gptpResp); - break; + void Gptp::processPdelayResp(Packet *packet, const GptpPdelayResp *gptp) { + // verify IDs + if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || + gptp->getRequestingPortIdentity().portNumber != slavePortId) { + EV_WARN << "GptpPdelayResp arrived with invalid PortIdentity, dropped"; + return; + } + if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { + EV_WARN << "GptpPdelayResp arrived with invalid sequence ID, dropped"; + return; + } + + rcvdPdelayResp = true; + pdelayRespEventIngressTimestamp = packet->getTag()->getArrivalClockTime(); + peerRequestReceiptTimestamp = gptp->getRequestReceiptTimestamp(); + peerResponseOriginTimestamp = CLOCKTIME_ZERO; } - case GPTPTYPE_SYNC: { - auto gptpSync = check_and_cast(gptp); - sendFollowUp(portId, gptpSync, clock->getClockTime()); - break; + + void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp *gptp) { + if (!rcvdPdelayResp) { + EV_WARN << "GptpPdelayRespFollowUp arrived without GptpPdelayResp, dropped"; + return; + } + // verify IDs + if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || + gptp->getRequestingPortIdentity().portNumber != slavePortId) { + EV_WARN << "GptpPdelayRespFollowUp arrived with invalid PortIdentity, dropped"; + return; + } + if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { + EV_WARN << "GptpPdelayRespFollowUp arrived with invalid sequence ID, dropped"; + return; + } + + calculateNRR(); + calculatePD(); + + peerResponseOriginTimestamp = gptp->getResponseOriginTimestamp(); + + // computePropTime(): +// peerDelay = (gmRateRatio * (pdelayRespEventIngressTimestamp - pdelayReqEventEgressTimestamp) - (peerResponseOriginTimestamp - peerRequestReceiptTimestamp)) / 2.0; + + EV_INFO << "RATE RATIO - " << gmRateRatio << endl; + EV_INFO << "pdelayReqEventEgressTimestamp - " << pdelayReqEventEgressTimestamp << endl; + EV_INFO << "peerResponseOriginTimestamp - " << peerResponseOriginTimestamp << endl; + EV_INFO << "pdelayRespEventIngressTimestamp - " << pdelayRespEventIngressTimestamp << endl; + EV_INFO << "peerRequestReceiptTimestamp - " << peerRequestReceiptTimestamp << endl; + EV_INFO << "PEER DELAY - " << peerDelay << endl; + + emit(peerDelaySignal, CLOCKTIME_AS_SIMTIME(peerDelay)); } - case GPTPTYPE_PDELAY_REQ: - if (portId == slavePortId) - pdelayReqEventEgressTimestamp = clock->getClockTime(); - break; - default: - break; + + const GptpBase *Gptp::extractGptpHeader(Packet *packet) { + auto protocol = packet->getTag()->getProtocol(); + if (*protocol != Protocol::ethernetPhy) + return nullptr; + + const auto ðPhyHeader = packet->peekAtFront(); + const auto ðMacHeader = packet->peekDataAt(ethPhyHeader->getChunkLength()); + if (ethMacHeader->getTypeOrLength() != ETHERTYPE_GPTP) + return nullptr; + + b offset = ethPhyHeader->getChunkLength() + ethMacHeader->getChunkLength(); + return packet->peekDataAt(offset).get(); + } + + void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj, cObject *details) { + Enter_Method("%s", cComponent::getSignalName(simSignal)); + + if (simSignal != receptionEndedSignal && simSignal != transmissionEndedSignal) + return; + + auto ethernetSignal = check_and_cast(obj); + auto packet = check_and_cast_nullable(ethernetSignal->getEncapsulatedPacket()); + if (!packet) + return; + + auto gptp = extractGptpHeader(packet); + if (!gptp) + return; + + if (gptp->getDomainNumber() != domainNumber) + return; + + if (simSignal == receptionEndedSignal) + packet->addTagIfAbsent()->setArrivalClockTime(clock->getClockTime()); + else if (simSignal == transmissionEndedSignal) + handleDelayOrSendFollowUp(gptp, source); + } + + void Gptp::handleDelayOrSendFollowUp(const GptpBase *gptp, cComponent *source) { + int portId = getContainingNicModule(check_and_cast(source))->getInterfaceId(); + + switch (gptp->getMessageType()) { + case GPTPTYPE_PDELAY_RESP: { + auto gptpResp = check_and_cast(gptp); + sendPdelayRespFollowUp(portId, gptpResp); + break; + } + case GPTPTYPE_SYNC: { + auto gptpSync = check_and_cast(gptp); + sendFollowUp(portId, gptpSync, clock->getClockTime()); + break; + } + case GPTPTYPE_PDELAY_REQ: + if (portId == slavePortId) + pdelayReqEventEgressTimestampLast = pdelayReqEventEgressTimestamp; //store the old value + pdelayReqEventEgressTimestamp = clock->getClockTime(); // cover the value with new current time of slave + break; + default: + break; + } } -} } // namespace inet diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index a5c48a2f9ed..29777386e20 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -35,6 +35,7 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener double gmRateRatio = 1.0; double receivedRateRatio = 1.0; + clocktime_t originTimestamp; // last outgoing timestamp clocktime_t receivedTimeSync; @@ -43,13 +44,27 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener clocktime_t pdelayInterval; uint16_t sequenceId = 0; + + + /* Neighbor Rate Ratio Calculation */ + double neighborRateRatio = 1.0; + clocktime_t pdelayReqEventEgressTimestamp; // sending time of pdelay_req (SLAVE) + clocktime_t pdelayReqEventEgressTimestampLast; // sending time of last pdelay_req (SLAVE) + clocktime_t pdelayReqEventIngressTimestamp; // receiving time of pdelay_req (MASTER) + clocktime_t pdelayReqEventIngressTimestampLast; //receiving time of last pdelay_req (MASTER) + + /* Propagation Delay */ + clocktime_t PD; + clocktime_t pdelayRespEventEgressTimestamp; // sending time of pdelay_resp (MASTER) + clocktime_t pdelayRespEventIngressTimestamp;// receiving time of pdelay_resp (SLAVE) + + /* Slave port - Variables is used for Peer Delay Measurement */ uint16_t lastSentPdelayReqSequenceId = 0; clocktime_t peerDelay; clocktime_t peerRequestReceiptTimestamp; // pdelayReqIngressTimestamp from peer (received in GptpPdelayResp) clocktime_t peerResponseOriginTimestamp; // pdelayRespEgressTimestamp from peer (received in GptpPdelayRespFollowUp) - clocktime_t pdelayRespEventIngressTimestamp; // receiving time of last GptpPdelayResp -> ti2' - clocktime_t pdelayReqEventEgressTimestamp; // sending time of last GptpPdelayReq -> ti1' + clocktime_t pDelayReqProcessingTime; // processing time between arrived PDelayReq and send of PDelayResp bool rcvdPdelayResp = false; @@ -70,12 +85,6 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener ClockEvent* selfMsgDelayReq = nullptr; ClockEvent* requestMsg = nullptr; - // Neighbor rate ratio calculation parameters - clocktime_t sendReqStartTimestamp; // ti1 - clocktime_t receiveReqStartTimestamp; // tr1 - clocktime_t receiveReqEndTimestamp; // tr1' - static double neighborRateRatio; - // Statistics information: // TODO remove, and replace with emit() calls static simsignal_t localTimeSignal; static simsignal_t timeDifferenceSignal; From d50b2da3fe291ef6b574c3d16739bb57c5e7c775 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Mon, 15 Apr 2024 09:58:50 +0200 Subject: [PATCH 004/138] updated pdelayReqEventIngressTimestamp --- src/inet/linklayer/ieee8021as/Gptp.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 1ebb239a29a..154a2f8e951 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -201,8 +201,6 @@ namespace inet { } else if (masterPortIds.find(incomingNicId) != masterPortIds.end()) { // master port if (gptpMessageType == GPTPTYPE_PDELAY_REQ) { - pdelayReqEventIngressTimestampLast = pdelayReqEventIngressTimestamp; // store the old value - pdelayReqEventIngressTimestamp = clock->getClockTime(); //should cover with the current time of master. Question: will "getColckTime()" function return the current time of master or slave? processPdelayReq(packet, check_and_cast(gptp.get())); } else { throw cRuntimeError("Unaccepted gPTP type: %d", (int) (gptpMessageType)); @@ -436,7 +434,10 @@ namespace inet { resp->setIngressTimestamp(packet->getTag()->getArrivalClockTime()); resp->setSourcePortIdentity(gptp->getSourcePortIdentity()); resp->setSequenceId(gptp->getSequenceId()); - receiveReqEndTimestamp = resp->getIngressTimestamp(); // TODO: check if this is correct +// receiveReqEndTimestamp = resp->getIngressTimestamp(); // TODO: check if this is correct + + pdelayReqEventIngressTimestampLast = pdelayReqEventIngressTimestamp; // store the old value + pdelayReqEventIngressTimestamp = resp->getIngressTimestamp(); scheduleClockEventAfter(pDelayReqProcessingTime, resp); } From 06535722703141703e39f47939dd3083342fd9b4 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Mon, 15 Apr 2024 17:21:52 +0200 Subject: [PATCH 005/138] calculate correction field --- src/inet/linklayer/ieee8021as/Gptp.cc | 13 ++++++++----- src/inet/linklayer/ieee8021as/Gptp.h | 10 ++++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 154a2f8e951..e0cc8293e43 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -52,7 +52,7 @@ namespace inet { domainNumber = par("domainNumber"); syncInterval = par("syncInterval"); pDelayReqProcessingTime = par("pDelayReqProcessingTime"); - std::hash strHash; + std::hash strHash; clockIdentity = strHash(getFullPath()); } if (stage == INITSTAGE_LINK_LAYER) { @@ -366,14 +366,18 @@ namespace inet { else neighborRateRatio = (pdelayReqEventEgressTimestamp - pdelayReqEventEgressTimestampLast) / (pdelayReqEventIngressTimestamp - pdelayReqEventIngressTimestampLast); - } + + rateRatio = rateRatio * neighborRateRatio; +//rateRatio.insert(slavePortId, rateRatio); //calculate Propagation Delay (also named as Peer Delay by original author) - void Gptp::calculatePD() { PD = ((pdelayRespEventIngressTimestamp - pdelayReqEventEgressTimestamp) / neighborRateRatio - (pdelayRespEventEgressTimestamp - pdelayReqEventIngressTimestamp)) / 2 - } + //correction field + correctionFieldLast = correctionField; + correctionField = correctionFieldLast + PD + rateRatio * residenceTime; + } void Gptp::synchronize() { simtime_t now = simTime(); @@ -477,7 +481,6 @@ namespace inet { } calculateNRR(); - calculatePD(); peerResponseOriginTimestamp = gptp->getResponseOriginTimestamp(); diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 29777386e20..30398cc898e 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -18,6 +18,8 @@ #include "inet/networklayer/contract/IInterfaceTable.h" #include "inet/linklayer/ieee8021as/GptpPacket_m.h" +#include + namespace inet { class INET_API Gptp : public ClockUserModuleBase, public cListener @@ -48,16 +50,20 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener /* Neighbor Rate Ratio Calculation */ double neighborRateRatio = 1.0; +// std::vector rateRatio; + double rateRatio = 1.0; clocktime_t pdelayReqEventEgressTimestamp; // sending time of pdelay_req (SLAVE) clocktime_t pdelayReqEventEgressTimestampLast; // sending time of last pdelay_req (SLAVE) clocktime_t pdelayReqEventIngressTimestamp; // receiving time of pdelay_req (MASTER) clocktime_t pdelayReqEventIngressTimestampLast; //receiving time of last pdelay_req (MASTER) - /* Propagation Delay */ - clocktime_t PD; + clocktime_t PD; // Propagation Delay clocktime_t pdelayRespEventEgressTimestamp; // sending time of pdelay_resp (MASTER) clocktime_t pdelayRespEventIngressTimestamp;// receiving time of pdelay_resp (SLAVE) + clocktime_t correctionFieldLast; + clocktime_t correctionField; + /* Slave port - Variables is used for Peer Delay Measurement */ uint16_t lastSentPdelayReqSequenceId = 0; From ae3eb6b5f17ffb81de38db2fa5efaee0d82eab88 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Mon, 15 Apr 2024 18:49:43 +0200 Subject: [PATCH 006/138] calculate timeOffset --- src/inet/linklayer/ieee8021as/Gptp.cc | 24 ++++-- src/inet/linklayer/ieee8021as/Gptp.h | 110 +++++++++++++++++--------- 2 files changed, 89 insertions(+), 45 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index e0cc8293e43..f47b32b21ef 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -238,6 +238,7 @@ namespace inet { originTimestamp = clock->getClockTime(); } //gptp->setOriginTimestamp(CLOCKTIME_ZERO); + gptp->setSequenceId(sequenceId++); sentTimeSyncSync = clock->getClockTime(); @@ -326,6 +327,17 @@ namespace inet { // peerSentTimeSync = gptp->getOriginTimestamp(); // TODO this is unfilled in two-step mode syncIngressTimestamp = packet->getTag()->getArrivalClockTime(); + + //correction field + correctionFieldLast = correctionField; + correctionField = correctionFieldLast + PD + rateRatio * residenceTime; + + //relative time delay + //marked as theta in the paper and the definition is shown in header file + syncSentTimeFromMaster = gptp->getOriginTimestamp(); + timeOffset = syncIngressTimestamp - (syncSentTimeFromMaster + correctionField + PD) / rateRatio; + + } void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) { @@ -358,8 +370,8 @@ namespace inet { rcvdGptpSync = false; } -//calculate Neighbor Rate Ratio -//called in processPdelayRespFollowUp() + //calculate Neighbor Rate Ratio + //called in processPdelayRespFollowUp() void Gptp::calculateNRR() { if (pdelayReqEventEgressTimestamp == -1) neighborRateRatio = 1.0; @@ -368,15 +380,11 @@ namespace inet { (pdelayReqEventIngressTimestamp - pdelayReqEventIngressTimestampLast); rateRatio = rateRatio * neighborRateRatio; -//rateRatio.insert(slavePortId, rateRatio); + //rateRatio.insert(slavePortId, rateRatio); -//calculate Propagation Delay (also named as Peer Delay by original author) + //calculate Propagation Delay (also named as Peer Delay by original author) PD = ((pdelayRespEventIngressTimestamp - pdelayReqEventEgressTimestamp) / neighborRateRatio - (pdelayRespEventEgressTimestamp - pdelayReqEventIngressTimestamp)) / 2 - - //correction field - correctionFieldLast = correctionField; - correctionField = correctionFieldLast + PD + rateRatio * residenceTime; } void Gptp::synchronize() { diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 30398cc898e..337562553da 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -22,10 +22,11 @@ namespace inet { -class INET_API Gptp : public ClockUserModuleBase, public cListener -{ + class INET_API Gptp + + : public ClockUserModuleBase, public cListener { //parameters: - ModuleRefByPar interfaceTable; + ModuleRefByPar interfaceTable; GptpNodeType gptpNodeType; int domainNumber = -1; @@ -64,6 +65,10 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener clocktime_t correctionFieldLast; clocktime_t correctionField; + clocktime_t timeOffset; // time difference between "the slave's time when receives the sync packet" and "the projection of master's time onto slave's time axis when the sync packet is received by slave i". Should be 0 after synchronize + clocktime_t syncSentTimeFromMaster; // the time when the sync was sent from the Grand Master + + /* Slave port - Variables is used for Peer Delay Measurement */ uint16_t lastSentPdelayReqSequenceId = 0; @@ -87,48 +92,79 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener uint16_t lastReceivedGptpSyncSequenceId = 0xffff; // self timers: - ClockEvent* selfMsgSync = nullptr; - ClockEvent* selfMsgDelayReq = nullptr; - ClockEvent* requestMsg = nullptr; + ClockEvent *selfMsgSync = nullptr; + ClockEvent *selfMsgDelayReq = nullptr; + ClockEvent *requestMsg = nullptr; // Statistics information: // TODO remove, and replace with emit() calls static simsignal_t localTimeSignal; static simsignal_t timeDifferenceSignal; static simsignal_t rateRatioSignal; static simsignal_t peerDelaySignal; - public: + public: static const MacAddress GPTP_MULTICAST_ADDRESS; - protected: - virtual int numInitStages() const override { return NUM_INIT_STAGES; } - virtual void initialize(int stage) override; - virtual void handleMessage(cMessage *msg) override; - - virtual void handleSelfMessage(cMessage *msg); - void handleDelayOrSendFollowUp(const GptpBase *gptp, omnetpp::cComponent *source); - const GptpBase *extractGptpHeader(Packet *packet); - - public: - virtual ~Gptp(); - protected: - void sendPacketToNIC(Packet *packet, int portId); - void sendSync(); - void sendFollowUp(int portId, const GptpSync *sync, clocktime_t preciseOriginTimestamp); - void sendPdelayReq(); - void sendPdelayResp(GptpReqAnswerEvent *req); - void sendPdelayRespFollowUp(int portId, const GptpPdelayResp* resp); - - void processSync(Packet *packet, const GptpSync* gptp); - void processFollowUp(Packet *packet, const GptpFollowUp* gptp); - void processPdelayReq(Packet *packet, const GptpPdelayReq* gptp); - void processPdelayResp(Packet *packet, const GptpPdelayResp* gptp); - void processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp* gptp); - - clocktime_t getCalculatedDrift(IClock *clock, clocktime_t value) { return CLOCKTIME_ZERO; } - void synchronize(); - inline void adjustLocalTimestamp(clocktime_t& time) { time += timeDiffAtTimeSync; } - - virtual void receiveSignal(cComponent *source, simsignal_t signal, cObject *obj, cObject *details) override; + protected: + + virtual int numInitStages() const + + override { + return + NUM_INIT_STAGES; +} + +virtual void initialize(int stage) + +override; + +virtual void handleMessage(cMessage *msg) + +override; + +virtual void handleSelfMessage(cMessage *msg); + +void handleDelayOrSendFollowUp(const GptpBase *gptp, omnetpp::cComponent *source); + +const GptpBase *extractGptpHeader(Packet *packet); + +public: +virtual ~ + +Gptp(); + +protected: + +void sendPacketToNIC(Packet *packet, int portId); + +void sendSync(); + +void sendFollowUp(int portId, const GptpSync *sync, clocktime_t preciseOriginTimestamp); + +void sendPdelayReq(); + +void sendPdelayResp(GptpReqAnswerEvent *req); + +void sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp); + +void processSync(Packet *packet, const GptpSync *gptp); + +void processFollowUp(Packet *packet, const GptpFollowUp *gptp); + +void processPdelayReq(Packet *packet, const GptpPdelayReq *gptp); + +void processPdelayResp(Packet *packet, const GptpPdelayResp *gptp); + +void processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp *gptp); + +clocktime_t getCalculatedDrift(IClock *clock, clocktime_t value) { return CLOCKTIME_ZERO; } + +void synchronize(); + +inline void adjustLocalTimestamp(clocktime_t &time) { time += timeDiffAtTimeSync; } + +virtual void receiveSignal(cComponent *source, simsignal_t signal, cObject *obj, cObject *details) + +override; }; } From a0e916a60a78f27df633e940565832ca3fc949cb Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 16 Apr 2024 15:36:23 +0200 Subject: [PATCH 007/138] correction field update --- src/inet/linklayer/ieee8021as/Gptp.cc | 11 ++++++----- src/inet/linklayer/ieee8021as/Gptp.h | 5 ++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index f47b32b21ef..0650b7bfb2d 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -268,7 +268,7 @@ namespace inet { *******************************************************************************************/ gptp->setCorrectionField(syncTxEndTimestamp - originTimestamp); } - gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); + gptp->getFollowUpInformationTLVForUpdate().setRateRatio(rateRatio); packet->insertAtFront(gptp); sendPacketToNIC(packet, portId); } @@ -335,7 +335,7 @@ namespace inet { //relative time delay //marked as theta in the paper and the definition is shown in header file syncSentTimeFromMaster = gptp->getOriginTimestamp(); - timeOffset = syncIngressTimestamp - (syncSentTimeFromMaster + correctionField + PD) / rateRatio; + timeOffset = syncIngressTimestamp - (syncSentTimeFromMaster + correctionField + PD) * rateRatio; } @@ -376,11 +376,12 @@ namespace inet { if (pdelayReqEventEgressTimestamp == -1) neighborRateRatio = 1.0; else - neighborRateRatio = (pdelayReqEventEgressTimestamp - pdelayReqEventEgressTimestampLast) / - (pdelayReqEventIngressTimestamp - pdelayReqEventIngressTimestampLast); + neighborRateRatio = (pdelayReqEventIngressTimestamp - pdelayReqEventIngressTimestampLast) / (pdelayReqEventEgressTimestamp - pdelayReqEventEgressTimestampLast); - rateRatio = rateRatio * neighborRateRatio; + + rateRatio = receivedRateRatio * neighborRateRatio; //rateRatio.insert(slavePortId, rateRatio); + // //calculate Propagation Delay (also named as Peer Delay by original author) PD = ((pdelayRespEventIngressTimestamp - pdelayReqEventEgressTimestamp) / neighborRateRatio - diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 337562553da..47714c386c5 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -32,7 +32,6 @@ namespace inet { int domainNumber = -1; int slavePortId = -1; // interface ID of slave port std::set masterPortIds; // interface IDs of master ports - clocktime_t correctionField; uint64_t clockIdentity = 0; double gmRateRatio = 1.0; @@ -63,9 +62,9 @@ namespace inet { clocktime_t pdelayRespEventIngressTimestamp;// receiving time of pdelay_resp (SLAVE) clocktime_t correctionFieldLast; - clocktime_t correctionField; + clocktime_t correctionField = CLOCKTIME_ZERO; - clocktime_t timeOffset; // time difference between "the slave's time when receives the sync packet" and "the projection of master's time onto slave's time axis when the sync packet is received by slave i". Should be 0 after synchronize + clocktime_t timeOffset; // time difference between "the slave's time when receives the sync packet" and "the projection of master's time onto slave's time axis when the sync packet is received by slave". Should be 0 after synchronize clocktime_t syncSentTimeFromMaster; // the time when the sync was sent from the Grand Master From ab6f4376d9e9b2edf88911d5d3bb5c040b616a92 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 16 Apr 2024 19:57:06 +0200 Subject: [PATCH 008/138] Cleanup and tidy and stuff --- src/inet/linklayer/ieee8021as/Gptp.cc | 183 ++++++++++++----------- src/inet/linklayer/ieee8021as/Gptp.h | 202 +++++++++++++------------- 2 files changed, 195 insertions(+), 190 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 0650b7bfb2d..d818ac20f8c 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -56,8 +56,8 @@ namespace inet { clockIdentity = strHash(getFullPath()); } if (stage == INITSTAGE_LINK_LAYER) { - peerDelay = 0; - receivedTimeSync = CLOCKTIME_ZERO; + meanLinkDelay = 0; + syncIngressTimestampLast = CLOCKTIME_ZERO; interfaceTable.reference(this, "interfaceTableModule", true); @@ -111,7 +111,7 @@ namespace inet { selfMsgSync = new ClockEvent("selfMsgSync", GPTP_SELF_MSG_SYNC); clocktime_t scheduleSync = par("syncInitialOffset"); - originTimestamp = clock->getClockTime() + scheduleSync; + preciseOriginTimestamp = clock->getClockTime() + scheduleSync; scheduleClockEventAfter(scheduleSync, selfMsgSync); } if (slavePortId != -1) { @@ -123,7 +123,7 @@ namespace inet { pdelayInterval = par("pdelayInterval"); scheduleClockEventAfter(par("pdelayInitialOffset"), selfMsgDelayReq); } - WATCH(peerDelay); + WATCH(meanLinkDelay); } } @@ -141,7 +141,6 @@ namespace inet { case GPTP_SELF_REQ_ANSWER_KIND: // masterport: - pdelayRespEventEgressTimestamp = clock->getClockTime(); //should this time information be included in the packet of pdelay_resp_followup? sendPdelayResp(check_and_cast(msg)); delete msg; break; @@ -149,7 +148,6 @@ namespace inet { case GPTP_SELF_MSG_PDELAY_REQ: // slaveport: sendPdelayReq(); //TODO on slaveports only - sendReqEndTimeStamp = clock->getClockTime(); scheduleClockEventAfter(pdelayInterval, selfMsgDelayReq); break; @@ -235,13 +233,15 @@ namespace inet { gptp->setDomainNumber(domainNumber); /* OriginTimestamp always get Sync departure time from grand master */ if (gptpNodeType == MASTER_NODE) { - originTimestamp = clock->getClockTime(); + preciseOriginTimestamp = clock->getClockTime(); } //gptp->setOriginTimestamp(CLOCKTIME_ZERO); gptp->setSequenceId(sequenceId++); - - sentTimeSyncSync = clock->getClockTime(); + // Correction field for Sync message is zero for two-step mode + // See Table 11-6 in IEEE 802.1AS-2020 + // Change when implementing CMLDS + gptp->setCorrectionField(CLOCKTIME_ZERO); packet->insertAtFront(gptp); for (auto port: masterPortIds) @@ -251,24 +251,23 @@ namespace inet { // The sendFollowUp(portId) called by receiveSignal(), when GptpSync sent } - void Gptp::sendFollowUp(int portId, const GptpSync *sync, clocktime_t syncTxEndTimestamp) { + void Gptp::sendFollowUp(int portId, const GptpSync *sync, clocktime_t syncEgressTimestampOwn) { auto packet = new Packet("GptpFollowUp"); packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); auto gptp = makeShared(); gptp->setDomainNumber(domainNumber); - gptp->setPreciseOriginTimestamp(originTimestamp); + gptp->setPreciseOriginTimestamp(preciseOriginTimestamp); gptp->setSequenceId(sync->getSequenceId()); if (gptpNodeType == MASTER_NODE) { - gptp->setCorrectionField(syncTxEndTimestamp - originTimestamp); + gptp->setCorrectionField(syncEgressTimestampOwn - preciseOriginTimestamp); } else if (gptpNodeType == BRIDGE_NODE) { - /**************** Correction field calculation ********************************************* - * It is calculated by adding peer delay, residence time and packet transmission time * - * correctionField(i)=correctionField(i-1)+peerDelay+(timeReceivedSync-timeSentSync)*(1-f) * - *******************************************************************************************/ - gptp->setCorrectionField(syncTxEndTimestamp - originTimestamp); + // TODO: Check which rate ratio we should use here + auto residenceTime = syncEgressTimestampOwn - syncIngressTimestamp; + auto newCorrectionField = correctionField + meanLinkDelay + gmRateRatio * residenceTime; + gptp->setCorrectionField(newCorrectionField); } - gptp->getFollowUpInformationTLVForUpdate().setRateRatio(rateRatio); + gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); packet->insertAtFront(gptp); sendPacketToNIC(packet, portId); } @@ -278,6 +277,12 @@ namespace inet { auto packet = new Packet("GptpPdelayResp"); packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); auto gptp = makeShared(); + + // Correction field for Pdelay_Resp contains fractional nanoseconds + // according to the standard, we do not need this in INET. + // See Table 11-6 in IEEE 802.1AS-2020 + gptp->setCorrectionField(CLOCKTIME_ZERO); + gptp->setDomainNumber(domainNumber); gptp->setRequestingPortIdentity(req->getSourcePortIdentity()); gptp->setSequenceId(req->getSequenceId()); @@ -291,9 +296,19 @@ namespace inet { auto packet = new Packet("GptpPdelayRespFollowUp"); packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); auto gptp = makeShared(); - auto now = clock->getClockTime(); gptp->setDomainNumber(domainNumber); + + // Correction field for Pdelay_Resp contains fractional nanoseconds + // according to the standard, we do not need this in INET. + // See Table 11-6 in IEEE 802.1AS-2020 + gptp->setCorrectionField(CLOCKTIME_ZERO); + + // We can set this to now, because this function is called directly + // after the transmissionEnded signal for the pdelayResp packet + // is received + auto now = clock->getClockTime(); gptp->setResponseOriginTimestamp(now); + gptp->setRequestingPortIdentity(resp->getRequestingPortIdentity()); gptp->setSequenceId(resp->getSequenceId()); packet->insertAtFront(gptp); @@ -306,7 +321,11 @@ namespace inet { packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); auto gptp = makeShared(); gptp->setDomainNumber(domainNumber); + + // Correction field for Pdelay_Req message is zero for two-step mode + // See Table 11-6 in IEEE 802.1AS-2020 gptp->setCorrectionField(CLOCKTIME_ZERO); + //save and send IDs PortIdentity portId; portId.clockIdentity = clockIdentity; @@ -315,7 +334,6 @@ namespace inet { lastSentPdelayReqSequenceId = sequenceId++; gptp->setSequenceId(lastSentPdelayReqSequenceId); packet->insertAtFront(gptp); - pdelayReqEventEgressTimestamp = clock->getClockTime(); rcvdPdelayResp = false; // sendReqStartTimestamp = clock->getClockTime(); sendPacketToNIC(packet, slavePortId); @@ -327,17 +345,6 @@ namespace inet { // peerSentTimeSync = gptp->getOriginTimestamp(); // TODO this is unfilled in two-step mode syncIngressTimestamp = packet->getTag()->getArrivalClockTime(); - - //correction field - correctionFieldLast = correctionField; - correctionField = correctionFieldLast + PD + rateRatio * residenceTime; - - //relative time delay - //marked as theta in the paper and the definition is shown in header file - syncSentTimeFromMaster = gptp->getOriginTimestamp(); - timeOffset = syncIngressTimestamp - (syncSentTimeFromMaster + correctionField + PD) * rateRatio; - - } void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) { @@ -352,42 +359,25 @@ namespace inet { return; } - originTimestamp = gptp->getPreciseOriginTimestamp(); + preciseOriginTimestamp = gptp->getPreciseOriginTimestamp(); correctionField = gptp->getCorrectionField(); - peerSentTimeSync = gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); + syncEgressTimestampMaster = gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); receivedRateRatio = gptp->getFollowUpInformationTLV().getRateRatio(); synchronize(); EV_INFO << "############## FOLLOW_UP ################################" << endl; - EV_INFO << "RECEIVED TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; - EV_INFO << "ORIGIN TIME SYNC - " << originTimestamp << endl; - EV_INFO << "CORRECTION FIELD - " << correctionField << endl; - EV_INFO << "PROPAGATION DELAY - " << peerDelay << endl; - EV_INFO << "peerSentTimeSync - " << peerSentTimeSync << endl; - EV_INFO << "receivedRateRatio - " << receivedRateRatio << endl; + EV_INFO << "RECEIVED TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; + EV_INFO << "ORIGIN TIME SYNC - " << preciseOriginTimestamp + << endl; + EV_INFO << "CORRECTION FIELD - " << correctionField << endl; + EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; + EV_INFO << "syncEgressTimestampMaster - " << syncEgressTimestampMaster << endl; + EV_INFO << "receivedRateRatio - " << receivedRateRatio << endl; rcvdGptpSync = false; } - //calculate Neighbor Rate Ratio - //called in processPdelayRespFollowUp() - void Gptp::calculateNRR() { - if (pdelayReqEventEgressTimestamp == -1) - neighborRateRatio = 1.0; - else - neighborRateRatio = (pdelayReqEventIngressTimestamp - pdelayReqEventIngressTimestampLast) / (pdelayReqEventEgressTimestamp - pdelayReqEventEgressTimestampLast); - - - rateRatio = receivedRateRatio * neighborRateRatio; - //rateRatio.insert(slavePortId, rateRatio); - // - - //calculate Propagation Delay (also named as Peer Delay by original author) - PD = ((pdelayRespEventIngressTimestamp - pdelayReqEventEgressTimestamp) / neighborRateRatio - - (pdelayRespEventEgressTimestamp - pdelayReqEventIngressTimestamp)) / 2 - } - void Gptp::synchronize() { simtime_t now = simTime(); clocktime_t oldLocalTimeAtTimeSync = clock->getClockTime(); @@ -400,10 +390,17 @@ namespace inet { * Local time is adjusted using peer delay, correction field, residence time * * and packet transmission time based departure time of Sync message from GM * *****************************************************************************/ - clocktime_t newTime = originTimestamp + correctionField + peerDelay + residenceTime; + // TODO: This should be the offset calculation and should include the rate ratio + clocktime_t newTime = preciseOriginTimestamp + correctionField + meanLinkDelay + residenceTime; ASSERT(gptpNodeType != MASTER_NODE); + // TODO validate the following expression with the standard + if (syncEgressTimestampMasterLast == -1) + gmRateRatio = 1; + else + gmRateRatio = (syncEgressTimestampMaster - syncEgressTimestampMasterLast) / (syncIngressTimestamp - syncIngressTimestampLast); + auto settableClock = check_and_cast(clock.get()); ppm newOscillatorCompensation = unit( gmRateRatio * (1 + unit(settableClock->getOscillatorCompensation()).get()) - 1); @@ -411,12 +408,15 @@ namespace inet { newLocalTimeAtTimeSync = clock->getClockTime(); timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; - receivedTimeSync = syncIngressTimestamp; + syncIngressTimestampLast = syncIngressTimestamp; + // TODO: What does this do, what does it mean? + // TODO: Check which timestamps we really need to adjust. // adjust local timestamps, too - adjustLocalTimestamp(pdelayRespEventIngressTimestamp); + //adjustLocalTimestamp(pdelayRespEventIngressTimestamp); // adjustLocalTimestamp(pdelayReqEventEgressTimestamp); - adjustLocalTimestamp(receivedTimeSync); + //adjustLocalTimestamp(syncIngressTimestampLast); + /************** Rate ratio calculation ************************************* * It is calculated based on interval between two successive Sync messages * @@ -426,15 +426,15 @@ namespace inet { EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; EV_INFO << "CURRENT SIMTIME - " << now << endl; - EV_INFO << "ORIGIN TIME SYNC - " << peerSentTimeSync << endl; - EV_INFO << "PREV ORIGIN TIME SYNC - " << oldPeerSentTimeSync << endl; + EV_INFO << "ORIGIN TIME SYNC - " << syncEgressTimestampMaster << endl; + EV_INFO << "PREV ORIGIN TIME SYNC - " << syncEgressTimestampMasterLast << endl; EV_INFO << "RESIDENCE TIME - " << residenceTime << endl; EV_INFO << "CORRECTION FIELD - " << correctionField << endl; - EV_INFO << "PROPAGATION DELAY - " << peerDelay << endl; + EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; EV_INFO << "TIME DIFFERENCE TO SIMTIME - " << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; - oldPeerSentTimeSync = peerSentTimeSync; + syncEgressTimestampMasterLast = syncEgressTimestampMaster; emit(rateRatioSignal, gmRateRatio); emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); @@ -447,10 +447,6 @@ namespace inet { resp->setIngressTimestamp(packet->getTag()->getArrivalClockTime()); resp->setSourcePortIdentity(gptp->getSourcePortIdentity()); resp->setSequenceId(gptp->getSequenceId()); -// receiveReqEndTimestamp = resp->getIngressTimestamp(); // TODO: check if this is correct - - pdelayReqEventIngressTimestampLast = pdelayReqEventIngressTimestamp; // store the old value - pdelayReqEventIngressTimestamp = resp->getIngressTimestamp(); scheduleClockEventAfter(pDelayReqProcessingTime, resp); } @@ -468,9 +464,10 @@ namespace inet { } rcvdPdelayResp = true; - pdelayRespEventIngressTimestamp = packet->getTag()->getArrivalClockTime(); - peerRequestReceiptTimestamp = gptp->getRequestReceiptTimestamp(); - peerResponseOriginTimestamp = CLOCKTIME_ZERO; + pDelayRespIngressTimestamp = packet->getTag()->getArrivalClockTime(); + pDelayReqIngressTimestamp = gptp->getRequestReceiptTimestamp(); + pDelayRespEgressTimestampLast = pDelayRespEgressTimestamp; + pDelayRespEgressTimestamp = -1; } void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp *gptp) { @@ -489,21 +486,35 @@ namespace inet { return; } - calculateNRR(); + pDelayRespEgressTimestamp = gptp->getResponseOriginTimestamp(); + + // Note, that the standard defines the usage of the correction field + // for the following two calculations. + // However, these contain fractional nanoseconds, which we do not + // use in INET. + // See 11.2.19.3.3 computePdelayRateRatio() in IEEE 802.1AS-2020 + if (pDelayRespEgressTimestampLast == -1 || pDelayRespIngressTimestampLast == -1) + neighborRateRatio = 1.0; + else + neighborRateRatio = (pDelayRespEgressTimestamp - pDelayRespEgressTimestampLast) / (pDelayRespIngressTimestamp - pDelayRespIngressTimestampLast); + + // See 11.2.19.3.4 computePropTime() and Figure11-1 in IEEE 802.1AS-2020 + auto t4 = pDelayRespIngressTimestamp; + auto t1 = pDelayReqEgressTimestamp; - peerResponseOriginTimestamp = gptp->getResponseOriginTimestamp(); + auto t2 = pDelayReqIngressTimestamp; + auto t3 = pDelayRespEgressTimestamp; - // computePropTime(): -// peerDelay = (gmRateRatio * (pdelayRespEventIngressTimestamp - pdelayReqEventEgressTimestamp) - (peerResponseOriginTimestamp - peerRequestReceiptTimestamp)) / 2.0; + meanLinkDelay = (neighborRateRatio * (t4 - t1) - (t3 - t2)) / 2; - EV_INFO << "RATE RATIO - " << gmRateRatio << endl; - EV_INFO << "pdelayReqEventEgressTimestamp - " << pdelayReqEventEgressTimestamp << endl; - EV_INFO << "peerResponseOriginTimestamp - " << peerResponseOriginTimestamp << endl; - EV_INFO << "pdelayRespEventIngressTimestamp - " << pdelayRespEventIngressTimestamp << endl; - EV_INFO << "peerRequestReceiptTimestamp - " << peerRequestReceiptTimestamp << endl; - EV_INFO << "PEER DELAY - " << peerDelay << endl; + EV_INFO << "RATE RATIO - " << gmRateRatio << endl; + EV_INFO << "pDelayReqEgressTimestamp - " << pDelayReqEgressTimestamp << endl; + EV_INFO << "pDelayReqIngressTimestamp - " << pDelayReqIngressTimestamp << endl; + EV_INFO << "pDelayRespEgressTimestamp - " << pDelayRespEgressTimestamp << endl; + EV_INFO << "pDelayRespIngressTimestamp - " << pDelayRespIngressTimestamp << endl; + EV_INFO << "PEER DELAY - " << meanLinkDelay << endl; - emit(peerDelaySignal, CLOCKTIME_AS_SIMTIME(peerDelay)); + emit(peerDelaySignal, CLOCKTIME_AS_SIMTIME(meanLinkDelay)); } const GptpBase *Gptp::extractGptpHeader(Packet *packet) { @@ -559,9 +570,9 @@ namespace inet { break; } case GPTPTYPE_PDELAY_REQ: - if (portId == slavePortId) - pdelayReqEventEgressTimestampLast = pdelayReqEventEgressTimestamp; //store the old value - pdelayReqEventEgressTimestamp = clock->getClockTime(); // cover the value with new current time of slave + if (portId == slavePortId) { + pDelayReqEgressTimestamp = clock->getClockTime(); + } break; default: break; diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 47714c386c5..f98d38fc106 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -8,165 +8,159 @@ #ifndef __INET_GPTP_H #define __INET_GPTP_H -#include "inet/clock/contract/ClockTime.h" +#include + #include "inet/clock/common/ClockTime.h" +#include "inet/clock/contract/ClockTime.h" #include "inet/clock/model/SettableClock.h" #include "inet/common/INETDefs.h" #include "inet/common/ModuleRefByPar.h" #include "inet/common/clock/ClockUserModuleBase.h" #include "inet/common/packet/Packet.h" -#include "inet/networklayer/contract/IInterfaceTable.h" #include "inet/linklayer/ieee8021as/GptpPacket_m.h" - -#include +#include "inet/networklayer/contract/IInterfaceTable.h" namespace inet { - class INET_API Gptp +class INET_API Gptp : public ClockUserModuleBase, public cListener { + // parameters: + ModuleRefByPar interfaceTable; - : public ClockUserModuleBase, public cListener { - //parameters: - ModuleRefByPar interfaceTable; + // Configuration + GptpNodeType gptpNodeType; + int domainNumber = -1; + int slavePortId = -1; // interface ID of slave port + std::set masterPortIds; // interface IDs of master ports + uint64_t clockIdentity = 0; + clocktime_t syncInterval; + clocktime_t pdelayInterval; + clocktime_t pDelayReqProcessingTime; // processing time between arrived + // PDelayReq and send of PDelayResp - GptpNodeType gptpNodeType; - int domainNumber = -1; - int slavePortId = -1; // interface ID of slave port - std::set masterPortIds; // interface IDs of master ports - uint64_t clockIdentity = 0; + // Rate Ratios + double gmRateRatio = 1.0; + double receivedRateRatio = 1.0; + double neighborRateRatio = 1.0; // the rate ratio to the neighbor - double gmRateRatio = 1.0; - double receivedRateRatio = 1.0; + uint16_t sequenceId = 0; + // == Propagation Delay Measurement Procedure == + // Timestamps corresponding to the PDelayRequest and PDelayResponse mechanism + clocktime_t pDelayReqEgressTimestamp = -1; // egress time of pdelay_req at initiator (this node) + clocktime_t pDelayReqIngressTimestamp = -1; // ingress time of pdelay_req at responder + clocktime_t pDelayRespEgressTimestamp = -1; // egress time of pdelay_resp at responder (received in PDelayRespFollowUp) + clocktime_t pDelayRespEgressTimestampLast = -1; // egress time of previous pdelay_resp at responder (received in PDelayRespFollowUp) + clocktime_t pDelayRespIngressTimestamp = -1; // ingress time of pdelay_resp at initiator (this node) + clocktime_t pDelayRespIngressTimestampLast = -1; // ingress time of previous pdelay_resp at initiator (this node) - clocktime_t originTimestamp; // last outgoing timestamp + bool rcvdPdelayResp = false; + uint16_t lastSentPdelayReqSequenceId = 0; - clocktime_t receivedTimeSync; + clocktime_t meanLinkDelay = CLOCKTIME_ZERO; // mean propagation delay between this node and the responder - clocktime_t syncInterval; - clocktime_t pdelayInterval; - uint16_t sequenceId = 0; + // == Sync Procedure == + // Holds the (received) correction field value, used for the next FollowUp message + // Unsure why this is also configurable with a parameter (TODO: Check) + clocktime_t correctionField = CLOCKTIME_ZERO; - /* Neighbor Rate Ratio Calculation */ - double neighborRateRatio = 1.0; -// std::vector rateRatio; - double rateRatio = 1.0; - clocktime_t pdelayReqEventEgressTimestamp; // sending time of pdelay_req (SLAVE) - clocktime_t pdelayReqEventEgressTimestampLast; // sending time of last pdelay_req (SLAVE) - clocktime_t pdelayReqEventIngressTimestamp; // receiving time of pdelay_req (MASTER) - clocktime_t pdelayReqEventIngressTimestampLast; //receiving time of last pdelay_req (MASTER) + clocktime_t preciseOriginTimestamp; // timestamp when the last sync message was generated at the GM - clocktime_t PD; // Propagation Delay - clocktime_t pdelayRespEventEgressTimestamp; // sending time of pdelay_resp (MASTER) - clocktime_t pdelayRespEventIngressTimestamp;// receiving time of pdelay_resp (SLAVE) + clocktime_t syncEgressTimestampMaster = -1; // egress time of Sync at master + clocktime_t syncEgressTimestampMasterLast = -1; // egress time of previous Sync at master + clocktime_t syncIngressTimestamp = -1; // ingress time of Sync at slave (this node) + clocktime_t syncIngressTimestampLast = -1; // ingress time of previous Sync at slave (this node) - clocktime_t correctionFieldLast; - clocktime_t correctionField = CLOCKTIME_ZERO; + bool rcvdGptpSync = false; + uint16_t lastReceivedGptpSyncSequenceId = 0xffff; - clocktime_t timeOffset; // time difference between "the slave's time when receives the sync packet" and "the projection of master's time onto slave's time axis when the sync packet is received by slave". Should be 0 after synchronize - clocktime_t syncSentTimeFromMaster; // the time when the sync was sent from the Grand Master + clocktime_t newLocalTimeAtTimeSync; + clocktime_t timeDiffAtTimeSync; // new local time - old local time + // self timers: + ClockEvent *selfMsgSync = nullptr; + ClockEvent *selfMsgDelayReq = nullptr; + ClockEvent *requestMsg = nullptr; - /* Slave port - Variables is used for Peer Delay Measurement */ - uint16_t lastSentPdelayReqSequenceId = 0; - clocktime_t peerDelay; - clocktime_t peerRequestReceiptTimestamp; // pdelayReqIngressTimestamp from peer (received in GptpPdelayResp) - clocktime_t peerResponseOriginTimestamp; // pdelayRespEgressTimestamp from peer (received in GptpPdelayRespFollowUp) + // Statistics information: // TODO remove, and replace with emit() calls + static simsignal_t localTimeSignal; + static simsignal_t timeDifferenceSignal; + static simsignal_t rateRatioSignal; + static simsignal_t peerDelaySignal; - clocktime_t pDelayReqProcessingTime; // processing time between arrived PDelayReq and send of PDelayResp - bool rcvdPdelayResp = false; + public: + static const MacAddress GPTP_MULTICAST_ADDRESS; - clocktime_t sentTimeSyncSync; + protected: + virtual int numInitStages() const - /* Slave port - Variables is used for Rate Ratio. All times are drifted based on constant drift */ - clocktime_t newLocalTimeAtTimeSync; - clocktime_t timeDiffAtTimeSync; // new local time - old local time - clocktime_t peerSentTimeSync; // sending time of last received GptpSync - clocktime_t oldPeerSentTimeSync = -1; // sending time of previous received GptpSync - clocktime_t syncIngressTimestamp; // receiving time of last incoming GptpSync + override { + return NUM_INIT_STAGES; + } - bool rcvdGptpSync = false; - uint16_t lastReceivedGptpSyncSequenceId = 0xffff; + virtual void initialize(int stage) - // self timers: - ClockEvent *selfMsgSync = nullptr; - ClockEvent *selfMsgDelayReq = nullptr; - ClockEvent *requestMsg = nullptr; + override; - // Statistics information: // TODO remove, and replace with emit() calls - static simsignal_t localTimeSignal; - static simsignal_t timeDifferenceSignal; - static simsignal_t rateRatioSignal; - static simsignal_t peerDelaySignal; - public: - static const MacAddress GPTP_MULTICAST_ADDRESS; + virtual void handleMessage(cMessage *msg) - protected: + override; - virtual int numInitStages() const + virtual void handleSelfMessage(cMessage *msg); - override { - return - NUM_INIT_STAGES; -} + void handleDelayOrSendFollowUp(const GptpBase *gptp, + omnetpp::cComponent *source); -virtual void initialize(int stage) + const GptpBase *extractGptpHeader(Packet *packet); -override; + public: + virtual ~ -virtual void handleMessage(cMessage *msg) + Gptp(); -override; + protected: + void sendPacketToNIC(Packet *packet, int portId); -virtual void handleSelfMessage(cMessage *msg); + void sendSync(); -void handleDelayOrSendFollowUp(const GptpBase *gptp, omnetpp::cComponent *source); + void sendFollowUp(int portId, const GptpSync *sync, + clocktime_t syncEgressTimestampOwn); -const GptpBase *extractGptpHeader(Packet *packet); + void sendPdelayReq(); -public: -virtual ~ + void sendPdelayResp(GptpReqAnswerEvent *req); -Gptp(); + void sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp); -protected: + void processSync(Packet *packet, const GptpSync *gptp); -void sendPacketToNIC(Packet *packet, int portId); + void processFollowUp(Packet *packet, const GptpFollowUp *gptp); -void sendSync(); + void processPdelayReq(Packet *packet, const GptpPdelayReq *gptp); -void sendFollowUp(int portId, const GptpSync *sync, clocktime_t preciseOriginTimestamp); + void processPdelayResp(Packet *packet, const GptpPdelayResp *gptp); -void sendPdelayReq(); + void processPdelayRespFollowUp(Packet *packet, + const GptpPdelayRespFollowUp *gptp); -void sendPdelayResp(GptpReqAnswerEvent *req); + clocktime_t getCalculatedDrift(IClock *clock, clocktime_t value) { + return CLOCKTIME_ZERO; + } -void sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp); + void synchronize(); -void processSync(Packet *packet, const GptpSync *gptp); + inline void adjustLocalTimestamp(clocktime_t &time) { + time += timeDiffAtTimeSync; + } -void processFollowUp(Packet *packet, const GptpFollowUp *gptp); + virtual void receiveSignal(cComponent *source, simsignal_t signal, + cObject *obj, cObject *details) -void processPdelayReq(Packet *packet, const GptpPdelayReq *gptp); - -void processPdelayResp(Packet *packet, const GptpPdelayResp *gptp); - -void processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp *gptp); - -clocktime_t getCalculatedDrift(IClock *clock, clocktime_t value) { return CLOCKTIME_ZERO; } - -void synchronize(); - -inline void adjustLocalTimestamp(clocktime_t &time) { time += timeDiffAtTimeSync; } - -virtual void receiveSignal(cComponent *source, simsignal_t signal, cObject *obj, cObject *details) - -override; + override; }; -} +} // namespace inet #endif - From 139aa45897918c625fa373058fae537ab9072aaa Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 16 Apr 2024 21:37:33 +0200 Subject: [PATCH 009/138] Changed formatting to match configuration of existing code more closely --- .clang-format | 40 +- src/inet/linklayer/ieee8021as/Gptp.cc | 1093 +++++++++++++------------ 2 files changed, 611 insertions(+), 522 deletions(-) diff --git a/.clang-format b/.clang-format index 86629fa4bae..10bf5188e8c 100644 --- a/.clang-format +++ b/.clang-format @@ -1,21 +1,31 @@ -BasedOnStyle: None +BasedOnStyle: InheritParentConfig + +SortIncludes: CaseSensitive +IncludeBlocks: Regroup +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterFunction: true + BeforeElse: true + AfterControlStatement: MultiLine +TabWidth: 4 +IndentWidth: 4 +ColumnLimit: 0 -SortIncludes: true -IncludeBlocks: Regroup IncludeIsMainRegex: '([-_](test|unittest))?$' IncludeIsMainSourceRegex: '' IncludeCategories: - - Regex: '^' - Priority: 2 - SortPriority: 0 - - Regex: '^<.*\.h>' - Priority: 1 - SortPriority: 0 - - Regex: '^<.*' - Priority: 2 - SortPriority: 0 - - Regex: '.*' - Priority: 3 - SortPriority: 0 + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index d818ac20f8c..f0171c18a2d 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -8,7 +8,6 @@ #include "Gptp.h" #include "GptpPacket_m.h" - #include "inet/clock/model/SettableClock.h" #include "inet/common/IProtocolRegistrationListener.h" #include "inet/common/clock/ClockUserModuleBase.h" @@ -22,561 +21,641 @@ namespace inet { - Define_Module(Gptp); +Define_Module(Gptp); - simsignal_t Gptp::localTimeSignal = cComponent::registerSignal("localTime"); - simsignal_t Gptp::timeDifferenceSignal = cComponent::registerSignal("timeDifference"); - simsignal_t Gptp::rateRatioSignal = cComponent::registerSignal("rateRatio"); - simsignal_t Gptp::peerDelaySignal = cComponent::registerSignal("peerDelay"); +simsignal_t Gptp::localTimeSignal = cComponent::registerSignal("localTime"); +simsignal_t Gptp::timeDifferenceSignal = + cComponent::registerSignal("timeDifference"); +simsignal_t Gptp::rateRatioSignal = cComponent::registerSignal("rateRatio"); +simsignal_t Gptp::peerDelaySignal = cComponent::registerSignal("peerDelay"); // MAC address: // 01-80-C2-00-00-02 for TimeSync (ieee 802.1as-2020, 13.3.1.2) -// 01-80-C2-00-00-0E for Announce and Signaling messages, for Sync, Follow_Up, Pdelay_Req, Pdelay_Resp, and Pdelay_Resp_Follow_Up messages - const MacAddress Gptp::GPTP_MULTICAST_ADDRESS("01:80:C2:00:00:0E"); +// 01-80-C2-00-00-0E for Announce and Signaling messages, for Sync, Follow_Up, +// Pdelay_Req, Pdelay_Resp, and Pdelay_Resp_Follow_Up messages +const MacAddress Gptp::GPTP_MULTICAST_ADDRESS("01:80:C2:00:00:0E"); // EtherType: // 0x8809 for TimeSync (ieee 802.1as-2020, 13.3.1.2) -// 0x88F7 for Announce and Signaling messages, for Sync, Follow_Up, Pdelay_Req, Pdelay_Resp, and Pdelay_Resp_Follow_Up messages - - Gptp::~Gptp() { - cancelAndDeleteClockEvent(selfMsgDelayReq); - cancelAndDeleteClockEvent(selfMsgSync); - cancelAndDeleteClockEvent(requestMsg); +// 0x88F7 for Announce and Signaling messages, for Sync, Follow_Up, +// Pdelay_Req, Pdelay_Resp, and Pdelay_Resp_Follow_Up messages + +Gptp::~Gptp() +{ + cancelAndDeleteClockEvent(selfMsgDelayReq); + cancelAndDeleteClockEvent(selfMsgSync); + cancelAndDeleteClockEvent(requestMsg); +} + +void Gptp::initialize(int stage) +{ + ClockUserModuleBase::initialize(stage); + + if (stage == INITSTAGE_LOCAL) { + gptpNodeType = static_cast( + cEnum::get("GptpNodeType", "inet")->resolve(par("gptpNodeType"))); + domainNumber = par("domainNumber"); + syncInterval = par("syncInterval"); + pDelayReqProcessingTime = par("pDelayReqProcessingTime"); + std::hash strHash; + clockIdentity = strHash(getFullPath()); } - - void Gptp::initialize(int stage) { - ClockUserModuleBase::initialize(stage); - - if (stage == INITSTAGE_LOCAL) { - gptpNodeType = static_cast(cEnum::get("GptpNodeType", "inet")->resolve(par("gptpNodeType"))); - domainNumber = par("domainNumber"); - syncInterval = par("syncInterval"); - pDelayReqProcessingTime = par("pDelayReqProcessingTime"); - std::hash strHash; - clockIdentity = strHash(getFullPath()); + if (stage == INITSTAGE_LINK_LAYER) { + meanLinkDelay = 0; + syncIngressTimestampLast = CLOCKTIME_ZERO; + + interfaceTable.reference(this, "interfaceTableModule", true); + + const char *str = par("slavePort"); + if (*str) { + if (gptpNodeType == MASTER_NODE) + throw cRuntimeError( + "Parameter inconsistency: MASTER_NODE with slave port"); + auto nic = CHK(interfaceTable->findInterfaceByName(str)); + slavePortId = nic->getInterfaceId(); + nic->subscribe(transmissionEndedSignal, this); + nic->subscribe(receptionEndedSignal, this); + } + else if (gptpNodeType != MASTER_NODE) + throw cRuntimeError("Parameter error: Missing slave port for %s", + par("gptpNodeType").stringValue()); + + auto v = check_and_cast(par("masterPorts").objectValue()) + ->asStringVector(); + if (v.empty() and gptpNodeType != SLAVE_NODE) + throw cRuntimeError("Parameter error: Missing any master port for %s", + par("gptpNodeType").stringValue()); + for (const auto &p : v) { + auto nic = CHK(interfaceTable->findInterfaceByName(p.c_str())); + int portId = nic->getInterfaceId(); + if (portId == slavePortId) + throw cRuntimeError("Parameter error: the port '%s' specified both " + "master and slave port", + p.c_str()); + masterPortIds.insert(portId); + nic->subscribe(transmissionEndedSignal, this); + nic->subscribe(receptionEndedSignal, this); } - if (stage == INITSTAGE_LINK_LAYER) { - meanLinkDelay = 0; - syncIngressTimestampLast = CLOCKTIME_ZERO; - - interfaceTable.reference(this, "interfaceTableModule", true); - - const char *str = par("slavePort"); - if (*str) { - if (gptpNodeType == MASTER_NODE) - throw cRuntimeError("Parameter inconsistency: MASTER_NODE with slave port"); - auto nic = CHK(interfaceTable->findInterfaceByName(str)); - slavePortId = nic->getInterfaceId(); - nic->subscribe(transmissionEndedSignal, this); - nic->subscribe(receptionEndedSignal, this); - } else if (gptpNodeType != MASTER_NODE) - throw cRuntimeError("Parameter error: Missing slave port for %s", par("gptpNodeType").stringValue()); - - auto v = check_and_cast(par("masterPorts").objectValue())->asStringVector(); - if (v.empty() and gptpNodeType != SLAVE_NODE) - throw cRuntimeError("Parameter error: Missing any master port for %s", - par("gptpNodeType").stringValue()); - for (const auto &p: v) { - auto nic = CHK(interfaceTable->findInterfaceByName(p.c_str())); - int portId = nic->getInterfaceId(); - if (portId == slavePortId) - throw cRuntimeError("Parameter error: the port '%s' specified both master and slave port", - p.c_str()); - masterPortIds.insert(portId); - nic->subscribe(transmissionEndedSignal, this); - nic->subscribe(receptionEndedSignal, this); - } - if (slavePortId != -1) { - auto networkInterface = interfaceTable->getInterfaceById(slavePortId); - if (!networkInterface->matchesMulticastMacAddress(GPTP_MULTICAST_ADDRESS)) - networkInterface->addMulticastMacAddress(GPTP_MULTICAST_ADDRESS); - } - for (auto id: masterPortIds) { - auto networkInterface = interfaceTable->getInterfaceById(id); - if (!networkInterface->matchesMulticastMacAddress(GPTP_MULTICAST_ADDRESS)) - networkInterface->addMulticastMacAddress(GPTP_MULTICAST_ADDRESS); - } + if (slavePortId != -1) { + auto networkInterface = interfaceTable->getInterfaceById(slavePortId); + if (!networkInterface->matchesMulticastMacAddress(GPTP_MULTICAST_ADDRESS)) + networkInterface->addMulticastMacAddress(GPTP_MULTICAST_ADDRESS); + } + for (auto id : masterPortIds) { + auto networkInterface = interfaceTable->getInterfaceById(id); + if (!networkInterface->matchesMulticastMacAddress(GPTP_MULTICAST_ADDRESS)) + networkInterface->addMulticastMacAddress(GPTP_MULTICAST_ADDRESS); + } - correctionField = par("correctionField"); + correctionField = par("correctionField"); - gmRateRatio = 1.0; + gmRateRatio = 1.0; - registerProtocol(Protocol::gptp, gate("socketOut"), gate("socketIn")); + registerProtocol(Protocol::gptp, gate("socketOut"), gate("socketIn")); - /* Only grandmaster in the domain can initialize the synchronization message periodically - * so below condition checks whether it is grandmaster and then schedule first sync message */ - if (gptpNodeType == MASTER_NODE) { - // Schedule Sync message to be sent - selfMsgSync = new ClockEvent("selfMsgSync", GPTP_SELF_MSG_SYNC); + /* Only grandmaster in the domain can initialize the synchronization message + * periodically so below condition checks whether it is grandmaster and then + * schedule first sync message */ + if (gptpNodeType == MASTER_NODE) { + // Schedule Sync message to be sent + selfMsgSync = new ClockEvent("selfMsgSync", GPTP_SELF_MSG_SYNC); - clocktime_t scheduleSync = par("syncInitialOffset"); - preciseOriginTimestamp = clock->getClockTime() + scheduleSync; - scheduleClockEventAfter(scheduleSync, selfMsgSync); - } - if (slavePortId != -1) { - requestMsg = new ClockEvent("requestToSendSync", GPTP_REQUEST_TO_SEND_SYNC); - - // Schedule Pdelay_Req message is sent by slave port - // without depending on node type which is grandmaster or bridge - selfMsgDelayReq = new ClockEvent("selfMsgPdelay", GPTP_SELF_MSG_PDELAY_REQ); - pdelayInterval = par("pdelayInterval"); - scheduleClockEventAfter(par("pdelayInitialOffset"), selfMsgDelayReq); - } - WATCH(meanLinkDelay); + clocktime_t scheduleSync = par("syncInitialOffset"); + preciseOriginTimestamp = clock->getClockTime() + scheduleSync; + scheduleClockEventAfter(scheduleSync, selfMsgSync); + } + if (slavePortId != -1) { + requestMsg = + new ClockEvent("requestToSendSync", GPTP_REQUEST_TO_SEND_SYNC); + + // Schedule Pdelay_Req message is sent by slave port + // without depending on node type which is grandmaster or bridge + selfMsgDelayReq = + new ClockEvent("selfMsgPdelay", GPTP_SELF_MSG_PDELAY_REQ); + pdelayInterval = par("pdelayInterval"); + scheduleClockEventAfter(par("pdelayInitialOffset"), selfMsgDelayReq); } + WATCH(meanLinkDelay); } +} + +void Gptp::handleSelfMessage(cMessage *msg) +{ + switch (msg->getKind()) { + case GPTP_SELF_MSG_SYNC: + // masterport: + ASSERT(selfMsgSync == msg); + sendSync(); + + /* Schedule next Sync message at next sync interval + * Grand master always works at simulation time */ + scheduleClockEventAfter(syncInterval, selfMsgSync); + break; + + case GPTP_SELF_REQ_ANSWER_KIND: + // masterport: + sendPdelayResp(check_and_cast(msg)); + delete msg; + break; + + case GPTP_SELF_MSG_PDELAY_REQ: + // slaveport: + sendPdelayReq(); // TODO on slaveports only + scheduleClockEventAfter(pdelayInterval, selfMsgDelayReq); + break; + + default: + throw cRuntimeError("Unknown self message (%s)%s, kind=%d", + msg->getClassName(), msg->getName(), msg->getKind()); + } +} - void Gptp::handleSelfMessage(cMessage *msg) { - switch (msg->getKind()) { - case GPTP_SELF_MSG_SYNC: - // masterport: - ASSERT(selfMsgSync == msg); - sendSync(); - - /* Schedule next Sync message at next sync interval - * Grand master always works at simulation time */ - scheduleClockEventAfter(syncInterval, selfMsgSync); +void Gptp::handleMessage(cMessage *msg) +{ + if (msg->isSelfMessage()) { + handleSelfMessage(msg); + } + else { + Packet *packet = check_and_cast(msg); + auto gptp = packet->peekAtFront(); + auto gptpMessageType = gptp->getMessageType(); + auto incomingNicId = packet->getTag()->getInterfaceId(); + int incomingDomainNumber = gptp->getDomainNumber(); + + if (incomingDomainNumber != domainNumber) { + EV_ERROR << "Message " << msg->getClassAndFullName() + << " arrived with foreign domainNumber " << incomingDomainNumber + << ", dropped\n"; + PacketDropDetails details; + details.setReason(NOT_ADDRESSED_TO_US); + emit(packetDroppedSignal, packet, &details); + } + else if (incomingNicId == slavePortId) { + // slave port + switch (gptpMessageType) { + case GPTPTYPE_SYNC: + processSync(packet, check_and_cast(gptp.get())); break; - - case GPTP_SELF_REQ_ANSWER_KIND: - // masterport: - sendPdelayResp(check_and_cast(msg)); - delete msg; + case GPTPTYPE_FOLLOW_UP: + processFollowUp(packet, + check_and_cast(gptp.get())); + // Send a request to send Sync message + // through other gptp Ethernet interfaces + if (gptpNodeType == BRIDGE_NODE) + sendSync(); break; + case GPTPTYPE_PDELAY_RESP: - case GPTP_SELF_MSG_PDELAY_REQ: - // slaveport: - sendPdelayReq(); //TODO on slaveports only - scheduleClockEventAfter(pdelayInterval, selfMsgDelayReq); + processPdelayResp(packet, + check_and_cast(gptp.get())); + break; + case GPTPTYPE_PDELAY_RESP_FOLLOW_UP: + processPdelayRespFollowUp( + packet, check_and_cast(gptp.get())); break; - default: - throw cRuntimeError("Unknown self message (%s)%s, kind=%d", msg->getClassName(), msg->getName(), - msg->getKind()); + throw cRuntimeError("Unknown gPTP packet type: %d", + (int)(gptpMessageType)); + } } - } - - void Gptp::handleMessage(cMessage *msg) { - if (msg->isSelfMessage()) { - handleSelfMessage(msg); - } else { - Packet *packet = check_and_cast(msg); - auto gptp = packet->peekAtFront(); - auto gptpMessageType = gptp->getMessageType(); - auto incomingNicId = packet->getTag()->getInterfaceId(); - int incomingDomainNumber = gptp->getDomainNumber(); - - if (incomingDomainNumber != domainNumber) { - EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived with foreign domainNumber " - << incomingDomainNumber << ", dropped\n"; - PacketDropDetails details; - details.setReason(NOT_ADDRESSED_TO_US); - emit(packetDroppedSignal, packet, &details); - } else if (incomingNicId == slavePortId) { - // slave port - switch (gptpMessageType) { - case GPTPTYPE_SYNC: - processSync(packet, check_and_cast(gptp.get())); - break; - case GPTPTYPE_FOLLOW_UP: - processFollowUp(packet, check_and_cast(gptp.get())); - // Send a request to send Sync message - // through other gptp Ethernet interfaces - if (gptpNodeType == BRIDGE_NODE) - sendSync(); - break; - case GPTPTYPE_PDELAY_RESP: - - processPdelayResp(packet, check_and_cast(gptp.get())); - break; - case GPTPTYPE_PDELAY_RESP_FOLLOW_UP: - processPdelayRespFollowUp(packet, check_and_cast(gptp.get())); - break; - default: - throw cRuntimeError("Unknown gPTP packet type: %d", (int) (gptpMessageType)); - } - } else if (masterPortIds.find(incomingNicId) != masterPortIds.end()) { - // master port - if (gptpMessageType == GPTPTYPE_PDELAY_REQ) { - processPdelayReq(packet, check_and_cast(gptp.get())); - } else { - throw cRuntimeError("Unaccepted gPTP type: %d", (int) (gptpMessageType)); - } - } else { - // passive port - EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived on passive port " << incomingNicId - << ", dropped\n"; + else if (masterPortIds.find(incomingNicId) != masterPortIds.end()) { + // master port + if (gptpMessageType == GPTPTYPE_PDELAY_REQ) { + processPdelayReq(packet, + check_and_cast(gptp.get())); + } + else { + throw cRuntimeError("Unaccepted gPTP type: %d", (int)(gptpMessageType)); } - delete msg; } - } - - void Gptp::sendPacketToNIC(Packet *packet, int portId) { - auto networkInterface = interfaceTable->getInterfaceById(portId); - EV_INFO << "Sending " << packet << " to output interface = " << networkInterface->getInterfaceName() << ".\n"; - packet->addTag()->setInterfaceId(portId); - packet->addTag()->setProtocol(&Protocol::gptp); - packet->addTag()->setProtocol(&Protocol::gptp); - auto protocol = networkInterface->getProtocol(); - if (protocol != nullptr) - packet->addTagIfAbsent()->setProtocol(protocol); - else - packet->removeTagIfPresent(); - send(packet, "socketOut"); - } - - void Gptp::sendSync() { - auto packet = new Packet("GptpSync"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - /* OriginTimestamp always get Sync departure time from grand master */ - if (gptpNodeType == MASTER_NODE) { - preciseOriginTimestamp = clock->getClockTime(); + else { + // passive port + EV_ERROR << "Message " << msg->getClassAndFullName() + << " arrived on passive port " << incomingNicId << ", dropped\n"; } - //gptp->setOriginTimestamp(CLOCKTIME_ZERO); - - gptp->setSequenceId(sequenceId++); - // Correction field for Sync message is zero for two-step mode - // See Table 11-6 in IEEE 802.1AS-2020 - // Change when implementing CMLDS - gptp->setCorrectionField(CLOCKTIME_ZERO); - packet->insertAtFront(gptp); - - for (auto port: masterPortIds) - sendPacketToNIC(packet->dup(), port); - delete packet; - - // The sendFollowUp(portId) called by receiveSignal(), when GptpSync sent + delete msg; } - - void Gptp::sendFollowUp(int portId, const GptpSync *sync, clocktime_t syncEgressTimestampOwn) { - auto packet = new Packet("GptpFollowUp"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - gptp->setPreciseOriginTimestamp(preciseOriginTimestamp); - gptp->setSequenceId(sync->getSequenceId()); - - if (gptpNodeType == MASTER_NODE) { - gptp->setCorrectionField(syncEgressTimestampOwn - preciseOriginTimestamp); - } else if (gptpNodeType == BRIDGE_NODE) { - // TODO: Check which rate ratio we should use here - auto residenceTime = syncEgressTimestampOwn - syncIngressTimestamp; - auto newCorrectionField = correctionField + meanLinkDelay + gmRateRatio * residenceTime; - gptp->setCorrectionField(newCorrectionField); - } - gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); - packet->insertAtFront(gptp); - sendPacketToNIC(packet, portId); +} + +void Gptp::sendPacketToNIC(Packet *packet, int portId) +{ + auto networkInterface = interfaceTable->getInterfaceById(portId); + EV_INFO << "Sending " << packet + << " to output interface = " << networkInterface->getInterfaceName() + << ".\n"; + packet->addTag()->setInterfaceId(portId); + packet->addTag()->setProtocol(&Protocol::gptp); + packet->addTag()->setProtocol(&Protocol::gptp); + auto protocol = networkInterface->getProtocol(); + if (protocol != nullptr) + packet->addTagIfAbsent()->setProtocol(protocol); + else + packet->removeTagIfPresent(); + send(packet, "socketOut"); +} + +void Gptp::sendSync() +{ + auto packet = new Packet("GptpSync"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + /* OriginTimestamp always get Sync departure time from grand master */ + if (gptpNodeType == MASTER_NODE) { + preciseOriginTimestamp = clock->getClockTime(); } - - void Gptp::sendPdelayResp(GptpReqAnswerEvent *req) { - int portId = req->getPortId(); - auto packet = new Packet("GptpPdelayResp"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - - // Correction field for Pdelay_Resp contains fractional nanoseconds - // according to the standard, we do not need this in INET. - // See Table 11-6 in IEEE 802.1AS-2020 - gptp->setCorrectionField(CLOCKTIME_ZERO); - - gptp->setDomainNumber(domainNumber); - gptp->setRequestingPortIdentity(req->getSourcePortIdentity()); - gptp->setSequenceId(req->getSequenceId()); - gptp->setRequestReceiptTimestamp(req->getIngressTimestamp()); - packet->insertAtFront(gptp); - sendPacketToNIC(packet, portId); - // The sendPdelayRespFollowUp(portId) called by receiveSignal(), when GptpPdelayResp sent + // gptp->setOriginTimestamp(CLOCKTIME_ZERO); + + gptp->setSequenceId(sequenceId++); + // Correction field for Sync message is zero for two-step mode + // See Table 11-6 in IEEE 802.1AS-2020 + // Change when implementing CMLDS + gptp->setCorrectionField(CLOCKTIME_ZERO); + packet->insertAtFront(gptp); + + for (auto port : masterPortIds) + sendPacketToNIC(packet->dup(), port); + delete packet; + + // The sendFollowUp(portId) called by receiveSignal(), when GptpSync sent +} + +void Gptp::sendFollowUp(int portId, const GptpSync *sync, + clocktime_t syncEgressTimestampOwn) +{ + auto packet = new Packet("GptpFollowUp"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + gptp->setPreciseOriginTimestamp(preciseOriginTimestamp); + gptp->setSequenceId(sync->getSequenceId()); + + if (gptpNodeType == MASTER_NODE) { + gptp->setCorrectionField(syncEgressTimestampOwn - preciseOriginTimestamp); } - - void Gptp::sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp) { - auto packet = new Packet("GptpPdelayRespFollowUp"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - - // Correction field for Pdelay_Resp contains fractional nanoseconds - // according to the standard, we do not need this in INET. - // See Table 11-6 in IEEE 802.1AS-2020 - gptp->setCorrectionField(CLOCKTIME_ZERO); - - // We can set this to now, because this function is called directly - // after the transmissionEnded signal for the pdelayResp packet - // is received - auto now = clock->getClockTime(); - gptp->setResponseOriginTimestamp(now); - - gptp->setRequestingPortIdentity(resp->getRequestingPortIdentity()); - gptp->setSequenceId(resp->getSequenceId()); - packet->insertAtFront(gptp); - sendPacketToNIC(packet, portId); + else if (gptpNodeType == BRIDGE_NODE) { + // TODO: Check which rate ratio we should use here + auto residenceTime = syncEgressTimestampOwn - syncIngressTimestamp; + auto newCorrectionField = + correctionField + meanLinkDelay + gmRateRatio * residenceTime; + gptp->setCorrectionField(newCorrectionField); } - - void Gptp::sendPdelayReq() { - ASSERT(slavePortId != -1); - auto packet = new Packet("GptpPdelayReq"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - - // Correction field for Pdelay_Req message is zero for two-step mode - // See Table 11-6 in IEEE 802.1AS-2020 - gptp->setCorrectionField(CLOCKTIME_ZERO); - - //save and send IDs - PortIdentity portId; - portId.clockIdentity = clockIdentity; - portId.portNumber = slavePortId; - gptp->setSourcePortIdentity(portId); - lastSentPdelayReqSequenceId = sequenceId++; - gptp->setSequenceId(lastSentPdelayReqSequenceId); - packet->insertAtFront(gptp); - rcvdPdelayResp = false; -// sendReqStartTimestamp = clock->getClockTime(); - sendPacketToNIC(packet, slavePortId); + gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); + packet->insertAtFront(gptp); + sendPacketToNIC(packet, portId); +} + +void Gptp::sendPdelayResp(GptpReqAnswerEvent *req) +{ + int portId = req->getPortId(); + auto packet = new Packet("GptpPdelayResp"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + + // Correction field for Pdelay_Resp contains fractional nanoseconds + // according to the standard, we do not need this in INET. + // See Table 11-6 in IEEE 802.1AS-2020 + gptp->setCorrectionField(CLOCKTIME_ZERO); + + gptp->setDomainNumber(domainNumber); + gptp->setRequestingPortIdentity(req->getSourcePortIdentity()); + gptp->setSequenceId(req->getSequenceId()); + gptp->setRequestReceiptTimestamp(req->getIngressTimestamp()); + packet->insertAtFront(gptp); + sendPacketToNIC(packet, portId); + // The sendPdelayRespFollowUp(portId) called by receiveSignal(), when + // GptpPdelayResp sent +} + +void Gptp::sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp) +{ + auto packet = new Packet("GptpPdelayRespFollowUp"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + + // Correction field for Pdelay_Resp contains fractional nanoseconds + // according to the standard, we do not need this in INET. + // See Table 11-6 in IEEE 802.1AS-2020 + gptp->setCorrectionField(CLOCKTIME_ZERO); + + // We can set this to now, because this function is called directly + // after the transmissionEnded signal for the pdelayResp packet + // is received + auto now = clock->getClockTime(); + gptp->setResponseOriginTimestamp(now); + + gptp->setRequestingPortIdentity(resp->getRequestingPortIdentity()); + gptp->setSequenceId(resp->getSequenceId()); + packet->insertAtFront(gptp); + sendPacketToNIC(packet, portId); +} + +void Gptp::sendPdelayReq() +{ + ASSERT(slavePortId != -1); + auto packet = new Packet("GptpPdelayReq"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + + // Correction field for Pdelay_Req message is zero for two-step mode + // See Table 11-6 in IEEE 802.1AS-2020 + gptp->setCorrectionField(CLOCKTIME_ZERO); + + // save and send IDs + PortIdentity portId; + portId.clockIdentity = clockIdentity; + portId.portNumber = slavePortId; + gptp->setSourcePortIdentity(portId); + lastSentPdelayReqSequenceId = sequenceId++; + gptp->setSequenceId(lastSentPdelayReqSequenceId); + packet->insertAtFront(gptp); + rcvdPdelayResp = false; + // sendReqStartTimestamp = clock->getClockTime(); + sendPacketToNIC(packet, slavePortId); +} + +void Gptp::processSync(Packet *packet, const GptpSync *gptp) +{ + rcvdGptpSync = true; + lastReceivedGptpSyncSequenceId = gptp->getSequenceId(); + + // peerSentTimeSync = gptp->getOriginTimestamp(); + // TODO this is unfilled in two-step mode + syncIngressTimestamp = + packet->getTag()->getArrivalClockTime(); +} + +void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) +{ + // check: is received the GptpSync for this GptpFollowUp? + if (!rcvdGptpSync) { + EV_WARN << "GptpFollowUp arrived without GptpSync, dropped"; + return; } - - void Gptp::processSync(Packet *packet, const GptpSync *gptp) { - rcvdGptpSync = true; - lastReceivedGptpSyncSequenceId = gptp->getSequenceId(); - - // peerSentTimeSync = gptp->getOriginTimestamp(); // TODO this is unfilled in two-step mode - syncIngressTimestamp = packet->getTag()->getArrivalClockTime(); + // verify IDs + if (gptp->getSequenceId() != lastReceivedGptpSyncSequenceId) { + EV_WARN << "GptpFollowUp arrived with invalid sequence ID, dropped"; + return; } - void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) { - // check: is received the GptpSync for this GptpFollowUp? - if (!rcvdGptpSync) { - EV_WARN << "GptpFollowUp arrived without GptpSync, dropped"; - return; - } - // verify IDs - if (gptp->getSequenceId() != lastReceivedGptpSyncSequenceId) { - EV_WARN << "GptpFollowUp arrived with invalid sequence ID, dropped"; - return; - } - - preciseOriginTimestamp = gptp->getPreciseOriginTimestamp(); - correctionField = gptp->getCorrectionField(); - syncEgressTimestampMaster = gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); - receivedRateRatio = gptp->getFollowUpInformationTLV().getRateRatio(); - - synchronize(); - - EV_INFO << "############## FOLLOW_UP ################################" << endl; - EV_INFO << "RECEIVED TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; - EV_INFO << "ORIGIN TIME SYNC - " << preciseOriginTimestamp - << endl; - EV_INFO << "CORRECTION FIELD - " << correctionField << endl; - EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; - EV_INFO << "syncEgressTimestampMaster - " << syncEgressTimestampMaster << endl; - EV_INFO << "receivedRateRatio - " << receivedRateRatio << endl; - - rcvdGptpSync = false; - } - - void Gptp::synchronize() { - simtime_t now = simTime(); - clocktime_t oldLocalTimeAtTimeSync = clock->getClockTime(); - clocktime_t residenceTime = oldLocalTimeAtTimeSync - syncIngressTimestamp; - - emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(oldLocalTimeAtTimeSync) - now); - - - /************** Time synchronization ***************************************** - * Local time is adjusted using peer delay, correction field, residence time * - * and packet transmission time based departure time of Sync message from GM * - *****************************************************************************/ - // TODO: This should be the offset calculation and should include the rate ratio - clocktime_t newTime = preciseOriginTimestamp + correctionField + meanLinkDelay + residenceTime; - - ASSERT(gptpNodeType != MASTER_NODE); + preciseOriginTimestamp = gptp->getPreciseOriginTimestamp(); + correctionField = gptp->getCorrectionField(); + syncEgressTimestampMaster = + gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); + receivedRateRatio = gptp->getFollowUpInformationTLV().getRateRatio(); + + synchronize(); + + EV_INFO << "############## FOLLOW_UP ################################" + << endl; + EV_INFO << "RECEIVED TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; + EV_INFO << "ORIGIN TIME SYNC - " << preciseOriginTimestamp << endl; + EV_INFO << "CORRECTION FIELD - " << correctionField << endl; + EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; + EV_INFO << "syncEgressTimestampMaster - " << syncEgressTimestampMaster + << endl; + EV_INFO << "receivedRateRatio - " << receivedRateRatio << endl; + + rcvdGptpSync = false; +} + +void Gptp::synchronize() +{ + simtime_t now = simTime(); + clocktime_t oldLocalTimeAtTimeSync = clock->getClockTime(); + clocktime_t residenceTime = oldLocalTimeAtTimeSync - syncIngressTimestamp; + + emit(timeDifferenceSignal, + CLOCKTIME_AS_SIMTIME(oldLocalTimeAtTimeSync) - now); + + /************** Time synchronization ***************************************** + * Local time is adjusted using peer delay, correction field, residence time * + * and packet transmission time based departure time of Sync message from GM * + *****************************************************************************/ + // TODO: This should be the offset calculation and should include the rate + // ratio + clocktime_t newTime = + preciseOriginTimestamp + correctionField + meanLinkDelay + residenceTime; + + ASSERT(gptpNodeType != MASTER_NODE); // TODO validate the following expression with the standard if (syncEgressTimestampMasterLast == -1) gmRateRatio = 1; else - gmRateRatio = (syncEgressTimestampMaster - syncEgressTimestampMasterLast) / (syncIngressTimestamp - syncIngressTimestampLast); - - auto settableClock = check_and_cast(clock.get()); - ppm newOscillatorCompensation = unit( - gmRateRatio * (1 + unit(settableClock->getOscillatorCompensation()).get()) - 1); - settableClock->setClockTime(newTime, newOscillatorCompensation, true); - - newLocalTimeAtTimeSync = clock->getClockTime(); - timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; - syncIngressTimestampLast = syncIngressTimestamp; - - // TODO: What does this do, what does it mean? - // TODO: Check which timestamps we really need to adjust. - // adjust local timestamps, too - //adjustLocalTimestamp(pdelayRespEventIngressTimestamp); -// adjustLocalTimestamp(pdelayReqEventEgressTimestamp); - //adjustLocalTimestamp(syncIngressTimestampLast); - - - /************** Rate ratio calculation ************************************* - * It is calculated based on interval between two successive Sync messages * - ***************************************************************************/ - - EV_INFO << "############## SYNC #####################################" << endl; - EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; - EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; - EV_INFO << "CURRENT SIMTIME - " << now << endl; - EV_INFO << "ORIGIN TIME SYNC - " << syncEgressTimestampMaster << endl; - EV_INFO << "PREV ORIGIN TIME SYNC - " << syncEgressTimestampMasterLast << endl; - EV_INFO << "RESIDENCE TIME - " << residenceTime << endl; - EV_INFO << "CORRECTION FIELD - " << correctionField << endl; - EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; - EV_INFO << "TIME DIFFERENCE TO SIMTIME - " << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; - EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; - - syncEgressTimestampMasterLast = syncEgressTimestampMaster; - - emit(rateRatioSignal, gmRateRatio); - emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); - emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); + gmRateRatio = (syncEgressTimestampMaster - syncEgressTimestampMasterLast) / + (syncIngressTimestamp - syncIngressTimestampLast); + + auto settableClock = check_and_cast(clock.get()); + ppm newOscillatorCompensation = + unit(gmRateRatio * + (1 + unit(settableClock->getOscillatorCompensation()).get()) - + 1); + settableClock->setClockTime(newTime, newOscillatorCompensation, true); + + newLocalTimeAtTimeSync = clock->getClockTime(); + timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; + syncIngressTimestampLast = syncIngressTimestamp; + + // TODO: What does this do, what does it mean? + // TODO: Check which timestamps we really need to adjust. + // adjust local timestamps, too + // adjustLocalTimestamp(pdelayRespEventIngressTimestamp); + // adjustLocalTimestamp(pdelayReqEventEgressTimestamp); + // adjustLocalTimestamp(syncIngressTimestampLast); + + /************** Rate ratio calculation ************************************* + * It is calculated based on interval between two successive Sync messages * + ***************************************************************************/ + + EV_INFO << "############## SYNC #####################################" + << endl; + EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; + EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; + EV_INFO << "CURRENT SIMTIME - " << now << endl; + EV_INFO << "ORIGIN TIME SYNC - " << syncEgressTimestampMaster + << endl; + EV_INFO << "PREV ORIGIN TIME SYNC - " << syncEgressTimestampMasterLast + << endl; + EV_INFO << "RESIDENCE TIME - " << residenceTime << endl; + EV_INFO << "CORRECTION FIELD - " << correctionField << endl; + EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; + EV_INFO << "TIME DIFFERENCE TO SIMTIME - " + << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; + EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; + + syncEgressTimestampMasterLast = syncEgressTimestampMaster; + + emit(rateRatioSignal, gmRateRatio); + emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); + emit(timeDifferenceSignal, + CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); +} + +void Gptp::processPdelayReq(Packet *packet, const GptpPdelayReq *gptp) +{ + auto resp = + new GptpReqAnswerEvent("selfMsgPdelayResp", GPTP_SELF_REQ_ANSWER_KIND); + resp->setPortId(packet->getTag()->getInterfaceId()); + resp->setIngressTimestamp( + packet->getTag()->getArrivalClockTime()); + resp->setSourcePortIdentity(gptp->getSourcePortIdentity()); + resp->setSequenceId(gptp->getSequenceId()); + + scheduleClockEventAfter(pDelayReqProcessingTime, resp); +} + +void Gptp::processPdelayResp(Packet *packet, const GptpPdelayResp *gptp) +{ + // verify IDs + if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || + gptp->getRequestingPortIdentity().portNumber != slavePortId) { + EV_WARN << "GptpPdelayResp arrived with invalid PortIdentity, dropped"; + return; } - - void Gptp::processPdelayReq(Packet *packet, const GptpPdelayReq *gptp) { - auto resp = new GptpReqAnswerEvent("selfMsgPdelayResp", GPTP_SELF_REQ_ANSWER_KIND); - resp->setPortId(packet->getTag()->getInterfaceId()); - resp->setIngressTimestamp(packet->getTag()->getArrivalClockTime()); - resp->setSourcePortIdentity(gptp->getSourcePortIdentity()); - resp->setSequenceId(gptp->getSequenceId()); - - scheduleClockEventAfter(pDelayReqProcessingTime, resp); + if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { + EV_WARN << "GptpPdelayResp arrived with invalid sequence ID, dropped"; + return; } - void Gptp::processPdelayResp(Packet *packet, const GptpPdelayResp *gptp) { - // verify IDs - if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || - gptp->getRequestingPortIdentity().portNumber != slavePortId) { - EV_WARN << "GptpPdelayResp arrived with invalid PortIdentity, dropped"; - return; - } - if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { - EV_WARN << "GptpPdelayResp arrived with invalid sequence ID, dropped"; - return; - } - - rcvdPdelayResp = true; - pDelayRespIngressTimestamp = packet->getTag()->getArrivalClockTime(); - pDelayReqIngressTimestamp = gptp->getRequestReceiptTimestamp(); - pDelayRespEgressTimestampLast = pDelayRespEgressTimestamp; - pDelayRespEgressTimestamp = -1; + rcvdPdelayResp = true; + pDelayRespIngressTimestamp = + packet->getTag()->getArrivalClockTime(); + pDelayReqIngressTimestamp = gptp->getRequestReceiptTimestamp(); + pDelayRespEgressTimestampLast = pDelayRespEgressTimestamp; + pDelayRespEgressTimestamp = -1; +} + +void Gptp::processPdelayRespFollowUp(Packet *packet, + const GptpPdelayRespFollowUp *gptp) +{ + if (!rcvdPdelayResp) { + EV_WARN << "GptpPdelayRespFollowUp arrived without GptpPdelayResp, dropped"; + return; } - - void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp *gptp) { - if (!rcvdPdelayResp) { - EV_WARN << "GptpPdelayRespFollowUp arrived without GptpPdelayResp, dropped"; - return; - } - // verify IDs - if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || - gptp->getRequestingPortIdentity().portNumber != slavePortId) { - EV_WARN << "GptpPdelayRespFollowUp arrived with invalid PortIdentity, dropped"; - return; - } - if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { - EV_WARN << "GptpPdelayRespFollowUp arrived with invalid sequence ID, dropped"; - return; - } - - pDelayRespEgressTimestamp = gptp->getResponseOriginTimestamp(); - - // Note, that the standard defines the usage of the correction field - // for the following two calculations. - // However, these contain fractional nanoseconds, which we do not - // use in INET. - // See 11.2.19.3.3 computePdelayRateRatio() in IEEE 802.1AS-2020 - if (pDelayRespEgressTimestampLast == -1 || pDelayRespIngressTimestampLast == -1) - neighborRateRatio = 1.0; - else - neighborRateRatio = (pDelayRespEgressTimestamp - pDelayRespEgressTimestampLast) / (pDelayRespIngressTimestamp - pDelayRespIngressTimestampLast); - - // See 11.2.19.3.4 computePropTime() and Figure11-1 in IEEE 802.1AS-2020 - auto t4 = pDelayRespIngressTimestamp; - auto t1 = pDelayReqEgressTimestamp; - - auto t2 = pDelayReqIngressTimestamp; - auto t3 = pDelayRespEgressTimestamp; - - meanLinkDelay = (neighborRateRatio * (t4 - t1) - (t3 - t2)) / 2; - - EV_INFO << "RATE RATIO - " << gmRateRatio << endl; - EV_INFO << "pDelayReqEgressTimestamp - " << pDelayReqEgressTimestamp << endl; - EV_INFO << "pDelayReqIngressTimestamp - " << pDelayReqIngressTimestamp << endl; - EV_INFO << "pDelayRespEgressTimestamp - " << pDelayRespEgressTimestamp << endl; - EV_INFO << "pDelayRespIngressTimestamp - " << pDelayRespIngressTimestamp << endl; - EV_INFO << "PEER DELAY - " << meanLinkDelay << endl; - - emit(peerDelaySignal, CLOCKTIME_AS_SIMTIME(meanLinkDelay)); + // verify IDs + if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || + gptp->getRequestingPortIdentity().portNumber != slavePortId) { + EV_WARN + << "GptpPdelayRespFollowUp arrived with invalid PortIdentity, dropped"; + return; } - - const GptpBase *Gptp::extractGptpHeader(Packet *packet) { - auto protocol = packet->getTag()->getProtocol(); - if (*protocol != Protocol::ethernetPhy) - return nullptr; - - const auto ðPhyHeader = packet->peekAtFront(); - const auto ðMacHeader = packet->peekDataAt(ethPhyHeader->getChunkLength()); - if (ethMacHeader->getTypeOrLength() != ETHERTYPE_GPTP) - return nullptr; - - b offset = ethPhyHeader->getChunkLength() + ethMacHeader->getChunkLength(); - return packet->peekDataAt(offset).get(); + if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { + EV_WARN + << "GptpPdelayRespFollowUp arrived with invalid sequence ID, dropped"; + return; } - void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj, cObject *details) { - Enter_Method("%s", cComponent::getSignalName(simSignal)); - - if (simSignal != receptionEndedSignal && simSignal != transmissionEndedSignal) - return; - - auto ethernetSignal = check_and_cast(obj); - auto packet = check_and_cast_nullable(ethernetSignal->getEncapsulatedPacket()); - if (!packet) - return; - - auto gptp = extractGptpHeader(packet); - if (!gptp) - return; - - if (gptp->getDomainNumber() != domainNumber) - return; + pDelayRespEgressTimestamp = gptp->getResponseOriginTimestamp(); - if (simSignal == receptionEndedSignal) - packet->addTagIfAbsent()->setArrivalClockTime(clock->getClockTime()); - else if (simSignal == transmissionEndedSignal) - handleDelayOrSendFollowUp(gptp, source); + // Note, that the standard defines the usage of the correction field + // for the following two calculations. + // However, these contain fractional nanoseconds, which we do not + // use in INET. + // See 11.2.19.3.3 computePdelayRateRatio() in IEEE 802.1AS-2020 + if (pDelayRespEgressTimestampLast == -1 || + pDelayRespIngressTimestampLast == -1) + neighborRateRatio = 1.0; + else + neighborRateRatio = + (pDelayRespEgressTimestamp - pDelayRespEgressTimestampLast) / + (pDelayRespIngressTimestamp - pDelayRespIngressTimestampLast); + + // See 11.2.19.3.4 computePropTime() and Figure11-1 in IEEE 802.1AS-2020 + auto t4 = pDelayRespIngressTimestamp; + auto t1 = pDelayReqEgressTimestamp; + + auto t2 = pDelayReqIngressTimestamp; + auto t3 = pDelayRespEgressTimestamp; + + meanLinkDelay = (neighborRateRatio * (t4 - t1) - (t3 - t2)) / 2; + + EV_INFO << "RATE RATIO - " << gmRateRatio << endl; + EV_INFO << "pDelayReqEgressTimestamp - " << pDelayReqEgressTimestamp + << endl; + EV_INFO << "pDelayReqIngressTimestamp - " << pDelayReqIngressTimestamp + << endl; + EV_INFO << "pDelayRespEgressTimestamp - " << pDelayRespEgressTimestamp + << endl; + EV_INFO << "pDelayRespIngressTimestamp - " << pDelayRespIngressTimestamp + << endl; + EV_INFO << "PEER DELAY - " << meanLinkDelay << endl; + + emit(peerDelaySignal, CLOCKTIME_AS_SIMTIME(meanLinkDelay)); +} + +const GptpBase *Gptp::extractGptpHeader(Packet *packet) +{ + auto protocol = packet->getTag()->getProtocol(); + if (*protocol != Protocol::ethernetPhy) + return nullptr; + + const auto ðPhyHeader = + packet->peekAtFront(); + const auto ðMacHeader = + packet->peekDataAt(ethPhyHeader->getChunkLength()); + if (ethMacHeader->getTypeOrLength() != ETHERTYPE_GPTP) + return nullptr; + + b offset = ethPhyHeader->getChunkLength() + ethMacHeader->getChunkLength(); + return packet->peekDataAt(offset).get(); +} + +void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, + cObject *obj, cObject *details) +{ + Enter_Method("%s", cComponent::getSignalName(simSignal)); + + if (simSignal != receptionEndedSignal && simSignal != transmissionEndedSignal) + return; + + auto ethernetSignal = check_and_cast(obj); + auto packet = check_and_cast_nullable( + ethernetSignal->getEncapsulatedPacket()); + if (!packet) + return; + + auto gptp = extractGptpHeader(packet); + if (!gptp) + return; + + if (gptp->getDomainNumber() != domainNumber) + return; + + if (simSignal == receptionEndedSignal) + packet->addTagIfAbsent()->setArrivalClockTime( + clock->getClockTime()); + else if (simSignal == transmissionEndedSignal) + handleDelayOrSendFollowUp(gptp, source); +} + +void Gptp::handleDelayOrSendFollowUp(const GptpBase *gptp, cComponent *source) +{ + int portId = getContainingNicModule(check_and_cast(source)) + ->getInterfaceId(); + + switch (gptp->getMessageType()) { + case GPTPTYPE_PDELAY_RESP: { + auto gptpResp = check_and_cast(gptp); + sendPdelayRespFollowUp(portId, gptpResp); + break; } - - void Gptp::handleDelayOrSendFollowUp(const GptpBase *gptp, cComponent *source) { - int portId = getContainingNicModule(check_and_cast(source))->getInterfaceId(); - - switch (gptp->getMessageType()) { - case GPTPTYPE_PDELAY_RESP: { - auto gptpResp = check_and_cast(gptp); - sendPdelayRespFollowUp(portId, gptpResp); - break; - } - case GPTPTYPE_SYNC: { - auto gptpSync = check_and_cast(gptp); - sendFollowUp(portId, gptpSync, clock->getClockTime()); - break; - } - case GPTPTYPE_PDELAY_REQ: - if (portId == slavePortId) { - pDelayReqEgressTimestamp = clock->getClockTime(); - } - break; - default: - break; + case GPTPTYPE_SYNC: { + auto gptpSync = check_and_cast(gptp); + sendFollowUp(portId, gptpSync, clock->getClockTime()); + break; + } + case GPTPTYPE_PDELAY_REQ: + if (portId == slavePortId) { + pDelayReqEgressTimestamp = clock->getClockTime(); } + break; + default: + break; } +} } // namespace inet From 85458dff66372e5a1e31f7157be06a6463995b75 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 16 Apr 2024 22:35:10 +0200 Subject: [PATCH 010/138] Changed formatting to match configuration of existing code more closely --- src/inet/linklayer/ieee8021as/Gptp.h | 194 ++++++++++++++------------- 1 file changed, 98 insertions(+), 96 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index f98d38fc106..ab5380eff93 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -22,145 +22,147 @@ namespace inet { -class INET_API Gptp : public ClockUserModuleBase, public cListener { - // parameters: - ModuleRefByPar interfaceTable; +class INET_API Gptp : public ClockUserModuleBase, public cListener +{ + // parameters: + ModuleRefByPar interfaceTable; - // Configuration - GptpNodeType gptpNodeType; - int domainNumber = -1; - int slavePortId = -1; // interface ID of slave port - std::set masterPortIds; // interface IDs of master ports - uint64_t clockIdentity = 0; - clocktime_t syncInterval; - clocktime_t pdelayInterval; - clocktime_t pDelayReqProcessingTime; // processing time between arrived - // PDelayReq and send of PDelayResp + // Configuration + GptpNodeType gptpNodeType; + int domainNumber = -1; + int slavePortId = -1; // interface ID of slave port + std::set masterPortIds; // interface IDs of master ports + uint64_t clockIdentity = 0; + clocktime_t syncInterval; + clocktime_t pdelayInterval; + clocktime_t pDelayReqProcessingTime; // processing time between arrived + // PDelayReq and send of PDelayResp - // Rate Ratios - double gmRateRatio = 1.0; - double receivedRateRatio = 1.0; - double neighborRateRatio = 1.0; // the rate ratio to the neighbor + // Rate Ratios + double gmRateRatio = 1.0; + double receivedRateRatio = 1.0; + double neighborRateRatio = 1.0; // the rate ratio to the neighbor - uint16_t sequenceId = 0; + uint16_t sequenceId = 0; - // == Propagation Delay Measurement Procedure == - // Timestamps corresponding to the PDelayRequest and PDelayResponse mechanism - clocktime_t pDelayReqEgressTimestamp = -1; // egress time of pdelay_req at initiator (this node) - clocktime_t pDelayReqIngressTimestamp = -1; // ingress time of pdelay_req at responder - clocktime_t pDelayRespEgressTimestamp = -1; // egress time of pdelay_resp at responder (received in PDelayRespFollowUp) - clocktime_t pDelayRespEgressTimestampLast = -1; // egress time of previous pdelay_resp at responder (received in PDelayRespFollowUp) - clocktime_t pDelayRespIngressTimestamp = -1; // ingress time of pdelay_resp at initiator (this node) - clocktime_t pDelayRespIngressTimestampLast = -1; // ingress time of previous pdelay_resp at initiator (this node) + // == Propagation Delay Measurement Procedure == + // Timestamps corresponding to the PDelayRequest and PDelayResponse mechanism + clocktime_t pDelayReqEgressTimestamp = -1; // egress time of pdelay_req at initiator (this node) + clocktime_t pDelayReqIngressTimestamp = -1; // ingress time of pdelay_req at responder + clocktime_t pDelayRespEgressTimestamp = -1; // egress time of pdelay_resp at responder (received in PDelayRespFollowUp) + clocktime_t pDelayRespEgressTimestampLast = -1; // egress time of previous pdelay_resp at responder (received in PDelayRespFollowUp) + clocktime_t pDelayRespIngressTimestamp = -1; // ingress time of pdelay_resp at initiator (this node) + clocktime_t pDelayRespIngressTimestampLast = -1; // ingress time of previous pdelay_resp at initiator (this node) - bool rcvdPdelayResp = false; - uint16_t lastSentPdelayReqSequenceId = 0; + bool rcvdPdelayResp = false; + uint16_t lastSentPdelayReqSequenceId = 0; - clocktime_t meanLinkDelay = CLOCKTIME_ZERO; // mean propagation delay between this node and the responder + clocktime_t meanLinkDelay = CLOCKTIME_ZERO; // mean propagation delay between this node and the responder + // == Sync Procedure == - // == Sync Procedure == + // Holds the (received) correction field value, used for the next FollowUp message + // Unsure why this is also configurable with a parameter (TODO: Check) + clocktime_t correctionField = CLOCKTIME_ZERO; - // Holds the (received) correction field value, used for the next FollowUp message - // Unsure why this is also configurable with a parameter (TODO: Check) - clocktime_t correctionField = CLOCKTIME_ZERO; + clocktime_t preciseOriginTimestamp; // timestamp when the last sync message was generated at the GM - clocktime_t preciseOriginTimestamp; // timestamp when the last sync message was generated at the GM + clocktime_t syncEgressTimestampMaster = -1; // egress time of Sync at master + clocktime_t syncEgressTimestampMasterLast = -1; // egress time of previous Sync at master + clocktime_t syncIngressTimestamp = -1; // ingress time of Sync at slave (this node) + clocktime_t syncIngressTimestampLast = -1; // ingress time of previous Sync at slave (this node) - clocktime_t syncEgressTimestampMaster = -1; // egress time of Sync at master - clocktime_t syncEgressTimestampMasterLast = -1; // egress time of previous Sync at master - clocktime_t syncIngressTimestamp = -1; // ingress time of Sync at slave (this node) - clocktime_t syncIngressTimestampLast = -1; // ingress time of previous Sync at slave (this node) + bool rcvdGptpSync = false; + uint16_t lastReceivedGptpSyncSequenceId = 0xffff; - bool rcvdGptpSync = false; - uint16_t lastReceivedGptpSyncSequenceId = 0xffff; + clocktime_t newLocalTimeAtTimeSync; + clocktime_t timeDiffAtTimeSync; // new local time - old local time - clocktime_t newLocalTimeAtTimeSync; - clocktime_t timeDiffAtTimeSync; // new local time - old local time + // self timers: + ClockEvent *selfMsgSync = nullptr; + ClockEvent *selfMsgDelayReq = nullptr; + ClockEvent *requestMsg = nullptr; + // Statistics information: // TODO remove, and replace with emit() calls + static simsignal_t localTimeSignal; + static simsignal_t timeDifferenceSignal; + static simsignal_t rateRatioSignal; + static simsignal_t peerDelaySignal; - // self timers: - ClockEvent *selfMsgSync = nullptr; - ClockEvent *selfMsgDelayReq = nullptr; - ClockEvent *requestMsg = nullptr; + public: + static const MacAddress GPTP_MULTICAST_ADDRESS; - // Statistics information: // TODO remove, and replace with emit() calls - static simsignal_t localTimeSignal; - static simsignal_t timeDifferenceSignal; - static simsignal_t rateRatioSignal; - static simsignal_t peerDelaySignal; + protected: + virtual int numInitStages() const - public: - static const MacAddress GPTP_MULTICAST_ADDRESS; + override + { + return NUM_INIT_STAGES; + } - protected: - virtual int numInitStages() const + virtual void initialize(int stage) - override { - return NUM_INIT_STAGES; - } + override; - virtual void initialize(int stage) + virtual void handleMessage(cMessage *msg) - override; + override; - virtual void handleMessage(cMessage *msg) + virtual void handleSelfMessage(cMessage *msg); - override; + void handleDelayOrSendFollowUp(const GptpBase *gptp, + omnetpp::cComponent *source); - virtual void handleSelfMessage(cMessage *msg); + const GptpBase *extractGptpHeader(Packet *packet); - void handleDelayOrSendFollowUp(const GptpBase *gptp, - omnetpp::cComponent *source); + public: + virtual ~ - const GptpBase *extractGptpHeader(Packet *packet); + Gptp(); - public: - virtual ~ + protected: + void sendPacketToNIC(Packet *packet, int portId); - Gptp(); + void sendSync(); - protected: - void sendPacketToNIC(Packet *packet, int portId); + void sendFollowUp(int portId, const GptpSync *sync, + clocktime_t syncEgressTimestampOwn); - void sendSync(); + void sendPdelayReq(); - void sendFollowUp(int portId, const GptpSync *sync, - clocktime_t syncEgressTimestampOwn); + void sendPdelayResp(GptpReqAnswerEvent *req); - void sendPdelayReq(); + void sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp); - void sendPdelayResp(GptpReqAnswerEvent *req); + void processSync(Packet *packet, const GptpSync *gptp); - void sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp); + void processFollowUp(Packet *packet, const GptpFollowUp *gptp); - void processSync(Packet *packet, const GptpSync *gptp); + void processPdelayReq(Packet *packet, const GptpPdelayReq *gptp); - void processFollowUp(Packet *packet, const GptpFollowUp *gptp); + void processPdelayResp(Packet *packet, const GptpPdelayResp *gptp); - void processPdelayReq(Packet *packet, const GptpPdelayReq *gptp); + void processPdelayRespFollowUp(Packet *packet, + const GptpPdelayRespFollowUp *gptp); - void processPdelayResp(Packet *packet, const GptpPdelayResp *gptp); + clocktime_t getCalculatedDrift(IClock *clock, clocktime_t value) + { + return CLOCKTIME_ZERO; + } - void processPdelayRespFollowUp(Packet *packet, - const GptpPdelayRespFollowUp *gptp); + void synchronize(); - clocktime_t getCalculatedDrift(IClock *clock, clocktime_t value) { - return CLOCKTIME_ZERO; - } + inline void adjustLocalTimestamp(clocktime_t &time) + { + time += timeDiffAtTimeSync; + } - void synchronize(); + virtual void receiveSignal(cComponent *source, simsignal_t signal, + cObject *obj, cObject *details) - inline void adjustLocalTimestamp(clocktime_t &time) { - time += timeDiffAtTimeSync; - } - - virtual void receiveSignal(cComponent *source, simsignal_t signal, - cObject *obj, cObject *details) - - override; + override; }; -} // namespace inet +} // namespace inet #endif From 52b8942146922653bde4cd609c08d33cd891bb21 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 17 Apr 2024 10:02:25 +0200 Subject: [PATCH 011/138] Adjusted logs --- src/inet/linklayer/ieee8021as/Gptp.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index f0171c18a2d..e417ce2702f 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -62,7 +62,7 @@ void Gptp::initialize(int stage) } if (stage == INITSTAGE_LINK_LAYER) { meanLinkDelay = 0; - syncIngressTimestampLast = CLOCKTIME_ZERO; + syncIngressTimestampLast = -1; interfaceTable.reference(this, "interfaceTableModule", true); @@ -573,7 +573,8 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, meanLinkDelay = (neighborRateRatio * (t4 - t1) - (t3 - t2)) / 2; - EV_INFO << "RATE RATIO - " << gmRateRatio << endl; + EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; + EV_INFO << "NEIGHBOR RATE RATIO - " << gmRateRatio << endl; EV_INFO << "pDelayReqEgressTimestamp - " << pDelayReqEgressTimestamp << endl; EV_INFO << "pDelayReqIngressTimestamp - " << pDelayReqIngressTimestamp From 057c1adf15d7fba820939584ec5711789033d99f Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 17 Apr 2024 13:00:44 +0200 Subject: [PATCH 012/138] Reformatting --- .clang-format | 2 +- src/inet/linklayer/ieee8021as/Gptp.cc | 161 +++++++++----------------- src/inet/linklayer/ieee8021as/Gptp.h | 30 ++--- 3 files changed, 68 insertions(+), 125 deletions(-) diff --git a/.clang-format b/.clang-format index 10bf5188e8c..bdd0d7d0674 100644 --- a/.clang-format +++ b/.clang-format @@ -10,7 +10,7 @@ BraceWrapping: AfterControlStatement: MultiLine TabWidth: 4 IndentWidth: 4 -ColumnLimit: 0 +ColumnLimit: 120 IncludeIsMainRegex: '([-_](test|unittest))?$' diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index e417ce2702f..f5cd02ecb30 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -24,8 +24,7 @@ namespace inet { Define_Module(Gptp); simsignal_t Gptp::localTimeSignal = cComponent::registerSignal("localTime"); -simsignal_t Gptp::timeDifferenceSignal = - cComponent::registerSignal("timeDifference"); +simsignal_t Gptp::timeDifferenceSignal = cComponent::registerSignal("timeDifference"); simsignal_t Gptp::rateRatioSignal = cComponent::registerSignal("rateRatio"); simsignal_t Gptp::peerDelaySignal = cComponent::registerSignal("peerDelay"); @@ -52,8 +51,7 @@ void Gptp::initialize(int stage) ClockUserModuleBase::initialize(stage); if (stage == INITSTAGE_LOCAL) { - gptpNodeType = static_cast( - cEnum::get("GptpNodeType", "inet")->resolve(par("gptpNodeType"))); + gptpNodeType = static_cast(cEnum::get("GptpNodeType", "inet")->resolve(par("gptpNodeType"))); domainNumber = par("domainNumber"); syncInterval = par("syncInterval"); pDelayReqProcessingTime = par("pDelayReqProcessingTime"); @@ -69,22 +67,18 @@ void Gptp::initialize(int stage) const char *str = par("slavePort"); if (*str) { if (gptpNodeType == MASTER_NODE) - throw cRuntimeError( - "Parameter inconsistency: MASTER_NODE with slave port"); + throw cRuntimeError("Parameter inconsistency: MASTER_NODE with slave port"); auto nic = CHK(interfaceTable->findInterfaceByName(str)); slavePortId = nic->getInterfaceId(); nic->subscribe(transmissionEndedSignal, this); nic->subscribe(receptionEndedSignal, this); } else if (gptpNodeType != MASTER_NODE) - throw cRuntimeError("Parameter error: Missing slave port for %s", - par("gptpNodeType").stringValue()); + throw cRuntimeError("Parameter error: Missing slave port for %s", par("gptpNodeType").stringValue()); - auto v = check_and_cast(par("masterPorts").objectValue()) - ->asStringVector(); + auto v = check_and_cast(par("masterPorts").objectValue())->asStringVector(); if (v.empty() and gptpNodeType != SLAVE_NODE) - throw cRuntimeError("Parameter error: Missing any master port for %s", - par("gptpNodeType").stringValue()); + throw cRuntimeError("Parameter error: Missing any master port for %s", par("gptpNodeType").stringValue()); for (const auto &p : v) { auto nic = CHK(interfaceTable->findInterfaceByName(p.c_str())); int portId = nic->getInterfaceId(); @@ -126,13 +120,11 @@ void Gptp::initialize(int stage) scheduleClockEventAfter(scheduleSync, selfMsgSync); } if (slavePortId != -1) { - requestMsg = - new ClockEvent("requestToSendSync", GPTP_REQUEST_TO_SEND_SYNC); + requestMsg = new ClockEvent("requestToSendSync", GPTP_REQUEST_TO_SEND_SYNC); // Schedule Pdelay_Req message is sent by slave port // without depending on node type which is grandmaster or bridge - selfMsgDelayReq = - new ClockEvent("selfMsgPdelay", GPTP_SELF_MSG_PDELAY_REQ); + selfMsgDelayReq = new ClockEvent("selfMsgPdelay", GPTP_SELF_MSG_PDELAY_REQ); pdelayInterval = par("pdelayInterval"); scheduleClockEventAfter(par("pdelayInitialOffset"), selfMsgDelayReq); } @@ -166,8 +158,8 @@ void Gptp::handleSelfMessage(cMessage *msg) break; default: - throw cRuntimeError("Unknown self message (%s)%s, kind=%d", - msg->getClassName(), msg->getName(), msg->getKind()); + throw cRuntimeError("Unknown self message (%s)%s, kind=%d", msg->getClassName(), msg->getName(), + msg->getKind()); } } @@ -184,9 +176,8 @@ void Gptp::handleMessage(cMessage *msg) int incomingDomainNumber = gptp->getDomainNumber(); if (incomingDomainNumber != domainNumber) { - EV_ERROR << "Message " << msg->getClassAndFullName() - << " arrived with foreign domainNumber " << incomingDomainNumber - << ", dropped\n"; + EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived with foreign domainNumber " + << incomingDomainNumber << ", dropped\n"; PacketDropDetails details; details.setReason(NOT_ADDRESSED_TO_US); emit(packetDroppedSignal, packet, &details); @@ -198,8 +189,7 @@ void Gptp::handleMessage(cMessage *msg) processSync(packet, check_and_cast(gptp.get())); break; case GPTPTYPE_FOLLOW_UP: - processFollowUp(packet, - check_and_cast(gptp.get())); + processFollowUp(packet, check_and_cast(gptp.get())); // Send a request to send Sync message // through other gptp Ethernet interfaces if (gptpNodeType == BRIDGE_NODE) @@ -207,23 +197,19 @@ void Gptp::handleMessage(cMessage *msg) break; case GPTPTYPE_PDELAY_RESP: - processPdelayResp(packet, - check_and_cast(gptp.get())); + processPdelayResp(packet, check_and_cast(gptp.get())); break; case GPTPTYPE_PDELAY_RESP_FOLLOW_UP: - processPdelayRespFollowUp( - packet, check_and_cast(gptp.get())); + processPdelayRespFollowUp(packet, check_and_cast(gptp.get())); break; default: - throw cRuntimeError("Unknown gPTP packet type: %d", - (int)(gptpMessageType)); + throw cRuntimeError("Unknown gPTP packet type: %d", (int)(gptpMessageType)); } } else if (masterPortIds.find(incomingNicId) != masterPortIds.end()) { // master port if (gptpMessageType == GPTPTYPE_PDELAY_REQ) { - processPdelayReq(packet, - check_and_cast(gptp.get())); + processPdelayReq(packet, check_and_cast(gptp.get())); } else { throw cRuntimeError("Unaccepted gPTP type: %d", (int)(gptpMessageType)); @@ -231,8 +217,8 @@ void Gptp::handleMessage(cMessage *msg) } else { // passive port - EV_ERROR << "Message " << msg->getClassAndFullName() - << " arrived on passive port " << incomingNicId << ", dropped\n"; + EV_ERROR << "Message " << msg->getClassAndFullName() << " arrived on passive port " << incomingNicId + << ", dropped\n"; } delete msg; } @@ -241,9 +227,7 @@ void Gptp::handleMessage(cMessage *msg) void Gptp::sendPacketToNIC(Packet *packet, int portId) { auto networkInterface = interfaceTable->getInterfaceById(portId); - EV_INFO << "Sending " << packet - << " to output interface = " << networkInterface->getInterfaceName() - << ".\n"; + EV_INFO << "Sending " << packet << " to output interface = " << networkInterface->getInterfaceName() << ".\n"; packet->addTag()->setInterfaceId(portId); packet->addTag()->setProtocol(&Protocol::gptp); packet->addTag()->setProtocol(&Protocol::gptp); @@ -281,8 +265,7 @@ void Gptp::sendSync() // The sendFollowUp(portId) called by receiveSignal(), when GptpSync sent } -void Gptp::sendFollowUp(int portId, const GptpSync *sync, - clocktime_t syncEgressTimestampOwn) +void Gptp::sendFollowUp(int portId, const GptpSync *sync, const clocktime_t& syncEgressTimestampOwn) { auto packet = new Packet("GptpFollowUp"); packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); @@ -297,8 +280,7 @@ void Gptp::sendFollowUp(int portId, const GptpSync *sync, else if (gptpNodeType == BRIDGE_NODE) { // TODO: Check which rate ratio we should use here auto residenceTime = syncEgressTimestampOwn - syncIngressTimestamp; - auto newCorrectionField = - correctionField + meanLinkDelay + gmRateRatio * residenceTime; + auto newCorrectionField = correctionField + meanLinkDelay + gmRateRatio * residenceTime; gptp->setCorrectionField(newCorrectionField); } gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); @@ -384,8 +366,7 @@ void Gptp::processSync(Packet *packet, const GptpSync *gptp) // peerSentTimeSync = gptp->getOriginTimestamp(); // TODO this is unfilled in two-step mode - syncIngressTimestamp = - packet->getTag()->getArrivalClockTime(); + syncIngressTimestamp = packet->getTag()->getArrivalClockTime(); } void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) @@ -403,20 +384,17 @@ void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) preciseOriginTimestamp = gptp->getPreciseOriginTimestamp(); correctionField = gptp->getCorrectionField(); - syncEgressTimestampMaster = - gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); + syncEgressTimestampMaster = gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); receivedRateRatio = gptp->getFollowUpInformationTLV().getRateRatio(); synchronize(); - EV_INFO << "############## FOLLOW_UP ################################" - << endl; + EV_INFO << "############## FOLLOW_UP ################################" << endl; EV_INFO << "RECEIVED TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; EV_INFO << "ORIGIN TIME SYNC - " << preciseOriginTimestamp << endl; EV_INFO << "CORRECTION FIELD - " << correctionField << endl; EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; - EV_INFO << "syncEgressTimestampMaster - " << syncEgressTimestampMaster - << endl; + EV_INFO << "syncEgressTimestampMaster - " << syncEgressTimestampMaster << endl; EV_INFO << "receivedRateRatio - " << receivedRateRatio << endl; rcvdGptpSync = false; @@ -428,8 +406,7 @@ void Gptp::synchronize() clocktime_t oldLocalTimeAtTimeSync = clock->getClockTime(); clocktime_t residenceTime = oldLocalTimeAtTimeSync - syncIngressTimestamp; - emit(timeDifferenceSignal, - CLOCKTIME_AS_SIMTIME(oldLocalTimeAtTimeSync) - now); + emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(oldLocalTimeAtTimeSync) - now); /************** Time synchronization ***************************************** * Local time is adjusted using peer delay, correction field, residence time * @@ -437,8 +414,7 @@ void Gptp::synchronize() *****************************************************************************/ // TODO: This should be the offset calculation and should include the rate // ratio - clocktime_t newTime = - preciseOriginTimestamp + correctionField + meanLinkDelay + residenceTime; + clocktime_t newTime = preciseOriginTimestamp + correctionField + meanLinkDelay + residenceTime; ASSERT(gptpNodeType != MASTER_NODE); @@ -451,9 +427,7 @@ void Gptp::synchronize() auto settableClock = check_and_cast(clock.get()); ppm newOscillatorCompensation = - unit(gmRateRatio * - (1 + unit(settableClock->getOscillatorCompensation()).get()) - - 1); + unit(gmRateRatio * (1 + unit(settableClock->getOscillatorCompensation()).get()) - 1); settableClock->setClockTime(newTime, newOscillatorCompensation, true); newLocalTimeAtTimeSync = clock->getClockTime(); @@ -471,37 +445,30 @@ void Gptp::synchronize() * It is calculated based on interval between two successive Sync messages * ***************************************************************************/ - EV_INFO << "############## SYNC #####################################" - << endl; + EV_INFO << "############## SYNC #####################################" << endl; EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; EV_INFO << "CURRENT SIMTIME - " << now << endl; - EV_INFO << "ORIGIN TIME SYNC - " << syncEgressTimestampMaster - << endl; - EV_INFO << "PREV ORIGIN TIME SYNC - " << syncEgressTimestampMasterLast - << endl; + EV_INFO << "ORIGIN TIME SYNC - " << syncEgressTimestampMaster << endl; + EV_INFO << "PREV ORIGIN TIME SYNC - " << syncEgressTimestampMasterLast << endl; EV_INFO << "RESIDENCE TIME - " << residenceTime << endl; EV_INFO << "CORRECTION FIELD - " << correctionField << endl; EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; - EV_INFO << "TIME DIFFERENCE TO SIMTIME - " - << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; + EV_INFO << "TIME DIFFERENCE TO SIMTIME - " << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; syncEgressTimestampMasterLast = syncEgressTimestampMaster; emit(rateRatioSignal, gmRateRatio); emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); - emit(timeDifferenceSignal, - CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); + emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); } void Gptp::processPdelayReq(Packet *packet, const GptpPdelayReq *gptp) { - auto resp = - new GptpReqAnswerEvent("selfMsgPdelayResp", GPTP_SELF_REQ_ANSWER_KIND); + auto resp = new GptpReqAnswerEvent("selfMsgPdelayResp", GPTP_SELF_REQ_ANSWER_KIND); resp->setPortId(packet->getTag()->getInterfaceId()); - resp->setIngressTimestamp( - packet->getTag()->getArrivalClockTime()); + resp->setIngressTimestamp(packet->getTag()->getArrivalClockTime()); resp->setSourcePortIdentity(gptp->getSourcePortIdentity()); resp->setSequenceId(gptp->getSequenceId()); @@ -512,7 +479,8 @@ void Gptp::processPdelayResp(Packet *packet, const GptpPdelayResp *gptp) { // verify IDs if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || - gptp->getRequestingPortIdentity().portNumber != slavePortId) { + gptp->getRequestingPortIdentity().portNumber != slavePortId) + { EV_WARN << "GptpPdelayResp arrived with invalid PortIdentity, dropped"; return; } @@ -522,15 +490,13 @@ void Gptp::processPdelayResp(Packet *packet, const GptpPdelayResp *gptp) } rcvdPdelayResp = true; - pDelayRespIngressTimestamp = - packet->getTag()->getArrivalClockTime(); + pDelayRespIngressTimestamp = packet->getTag()->getArrivalClockTime(); pDelayReqIngressTimestamp = gptp->getRequestReceiptTimestamp(); pDelayRespEgressTimestampLast = pDelayRespEgressTimestamp; pDelayRespEgressTimestamp = -1; } -void Gptp::processPdelayRespFollowUp(Packet *packet, - const GptpPdelayRespFollowUp *gptp) +void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp *gptp) { if (!rcvdPdelayResp) { EV_WARN << "GptpPdelayRespFollowUp arrived without GptpPdelayResp, dropped"; @@ -538,14 +504,13 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, } // verify IDs if (gptp->getRequestingPortIdentity().clockIdentity != clockIdentity || - gptp->getRequestingPortIdentity().portNumber != slavePortId) { - EV_WARN - << "GptpPdelayRespFollowUp arrived with invalid PortIdentity, dropped"; + gptp->getRequestingPortIdentity().portNumber != slavePortId) + { + EV_WARN << "GptpPdelayRespFollowUp arrived with invalid PortIdentity, dropped"; return; } if (gptp->getSequenceId() != lastSentPdelayReqSequenceId) { - EV_WARN - << "GptpPdelayRespFollowUp arrived with invalid sequence ID, dropped"; + EV_WARN << "GptpPdelayRespFollowUp arrived with invalid sequence ID, dropped"; return; } @@ -556,13 +521,11 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, // However, these contain fractional nanoseconds, which we do not // use in INET. // See 11.2.19.3.3 computePdelayRateRatio() in IEEE 802.1AS-2020 - if (pDelayRespEgressTimestampLast == -1 || - pDelayRespIngressTimestampLast == -1) + if (pDelayRespEgressTimestampLast == -1 || pDelayRespIngressTimestampLast == -1) neighborRateRatio = 1.0; else - neighborRateRatio = - (pDelayRespEgressTimestamp - pDelayRespEgressTimestampLast) / - (pDelayRespIngressTimestamp - pDelayRespIngressTimestampLast); + neighborRateRatio = (pDelayRespEgressTimestamp - pDelayRespEgressTimestampLast) / + (pDelayRespIngressTimestamp - pDelayRespIngressTimestampLast); // See 11.2.19.3.4 computePropTime() and Figure11-1 in IEEE 802.1AS-2020 auto t4 = pDelayRespIngressTimestamp; @@ -575,14 +538,10 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; EV_INFO << "NEIGHBOR RATE RATIO - " << gmRateRatio << endl; - EV_INFO << "pDelayReqEgressTimestamp - " << pDelayReqEgressTimestamp - << endl; - EV_INFO << "pDelayReqIngressTimestamp - " << pDelayReqIngressTimestamp - << endl; - EV_INFO << "pDelayRespEgressTimestamp - " << pDelayRespEgressTimestamp - << endl; - EV_INFO << "pDelayRespIngressTimestamp - " << pDelayRespIngressTimestamp - << endl; + EV_INFO << "pDelayReqEgressTimestamp - " << pDelayReqEgressTimestamp << endl; + EV_INFO << "pDelayReqIngressTimestamp - " << pDelayReqIngressTimestamp << endl; + EV_INFO << "pDelayRespEgressTimestamp - " << pDelayRespEgressTimestamp << endl; + EV_INFO << "pDelayRespIngressTimestamp - " << pDelayRespIngressTimestamp << endl; EV_INFO << "PEER DELAY - " << meanLinkDelay << endl; emit(peerDelaySignal, CLOCKTIME_AS_SIMTIME(meanLinkDelay)); @@ -594,10 +553,8 @@ const GptpBase *Gptp::extractGptpHeader(Packet *packet) if (*protocol != Protocol::ethernetPhy) return nullptr; - const auto ðPhyHeader = - packet->peekAtFront(); - const auto ðMacHeader = - packet->peekDataAt(ethPhyHeader->getChunkLength()); + const auto ðPhyHeader = packet->peekAtFront(); + const auto ðMacHeader = packet->peekDataAt(ethPhyHeader->getChunkLength()); if (ethMacHeader->getTypeOrLength() != ETHERTYPE_GPTP) return nullptr; @@ -605,8 +562,7 @@ const GptpBase *Gptp::extractGptpHeader(Packet *packet) return packet->peekDataAt(offset).get(); } -void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, - cObject *obj, cObject *details) +void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj, cObject *details) { Enter_Method("%s", cComponent::getSignalName(simSignal)); @@ -614,8 +570,7 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, return; auto ethernetSignal = check_and_cast(obj); - auto packet = check_and_cast_nullable( - ethernetSignal->getEncapsulatedPacket()); + auto packet = check_and_cast_nullable(ethernetSignal->getEncapsulatedPacket()); if (!packet) return; @@ -627,16 +582,14 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, return; if (simSignal == receptionEndedSignal) - packet->addTagIfAbsent()->setArrivalClockTime( - clock->getClockTime()); + packet->addTagIfAbsent()->setArrivalClockTime(clock->getClockTime()); else if (simSignal == transmissionEndedSignal) handleDelayOrSendFollowUp(gptp, source); } void Gptp::handleDelayOrSendFollowUp(const GptpBase *gptp, cComponent *source) { - int portId = getContainingNicModule(check_and_cast(source)) - ->getInterfaceId(); + int portId = getContainingNicModule(check_and_cast(source))->getInterfaceId(); switch (gptp->getMessageType()) { case GPTPTYPE_PDELAY_RESP: { diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index ab5380eff93..ba4b6d48233 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -47,10 +47,10 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener // == Propagation Delay Measurement Procedure == // Timestamps corresponding to the PDelayRequest and PDelayResponse mechanism - clocktime_t pDelayReqEgressTimestamp = -1; // egress time of pdelay_req at initiator (this node) - clocktime_t pDelayReqIngressTimestamp = -1; // ingress time of pdelay_req at responder - clocktime_t pDelayRespEgressTimestamp = -1; // egress time of pdelay_resp at responder (received in PDelayRespFollowUp) - clocktime_t pDelayRespEgressTimestampLast = -1; // egress time of previous pdelay_resp at responder (received in PDelayRespFollowUp) + clocktime_t pDelayReqEgressTimestamp = -1; // egress time of pdelay_req at initiator (this node) + clocktime_t pDelayReqIngressTimestamp = -1; // ingress time of pdelay_req at responder + clocktime_t pDelayRespEgressTimestamp = -1; // egress time of pdelay_resp at responder (received in PDelayRespFollowUp) + clocktime_t pDelayRespEgressTimestampLast = -1; // egress time of previous pdelay_resp at responder (received in PDelayRespFollowUp) clocktime_t pDelayRespIngressTimestamp = -1; // ingress time of pdelay_resp at initiator (this node) clocktime_t pDelayRespIngressTimestampLast = -1; // ingress time of previous pdelay_resp at initiator (this node) @@ -110,8 +110,7 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener virtual void handleSelfMessage(cMessage *msg); - void handleDelayOrSendFollowUp(const GptpBase *gptp, - omnetpp::cComponent *source); + void handleDelayOrSendFollowUp(const GptpBase *gptp, omnetpp::cComponent *source); const GptpBase *extractGptpHeader(Packet *packet); @@ -125,8 +124,7 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener void sendSync(); - void sendFollowUp(int portId, const GptpSync *sync, - clocktime_t syncEgressTimestampOwn); + void sendFollowUp(int portId, const GptpSync *sync, const clocktime_t &syncEgressTimestampOwn); void sendPdelayReq(); @@ -142,23 +140,15 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener void processPdelayResp(Packet *packet, const GptpPdelayResp *gptp); - void processPdelayRespFollowUp(Packet *packet, - const GptpPdelayRespFollowUp *gptp); + void processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowUp *gptp); - clocktime_t getCalculatedDrift(IClock *clock, clocktime_t value) - { - return CLOCKTIME_ZERO; - } + clocktime_t getCalculatedDrift(IClock *clock, clocktime_t value) { return CLOCKTIME_ZERO; } void synchronize(); - inline void adjustLocalTimestamp(clocktime_t &time) - { - time += timeDiffAtTimeSync; - } + inline void adjustLocalTimestamp(clocktime_t &time) { time += timeDiffAtTimeSync; } - virtual void receiveSignal(cComponent *source, simsignal_t signal, - cObject *obj, cObject *details) + virtual void receiveSignal(cComponent *source, simsignal_t signal, cObject *obj, cObject *details) override; }; From de61db14064a68ffb42bb2a0d1bad6b5680e17d2 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 17 Apr 2024 17:22:43 +0200 Subject: [PATCH 013/138] Fix rate ratios (not working) --- .cproject | 48 +- .../timesynchronization/gptp/GptpShowcase.anf | 1312 ++++++++++++++++- .../tsn/timesynchronization/gptp/omnetpp.ini | 5 +- src/inet/linklayer/ieee8021as/Gptp.cc | 47 +- src/inet/linklayer/ieee8021as/Gptp.h | 5 +- 5 files changed, 1347 insertions(+), 70 deletions(-) diff --git a/.cproject b/.cproject index cf2c5ad4ffd..da12842285f 100644 --- a/.cproject +++ b/.cproject @@ -55,24 +55,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + @@ -128,24 +128,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 03d358bc039..352aeeff348 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -9,8 +9,8 @@ - - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index c35098afacc..e0ea40e45cf 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -1,5 +1,5 @@ [General] -sim-time-limit = 1s +sim-time-limit = 10s description = "abstract" # enable time synchronization in all network nodes @@ -7,7 +7,8 @@ description = "abstract" # all oscillators have a constant drift rate (specified with a random distribution for each one) # except for the master clocks, which have a random drift rate -**.tsnClock*.clock.oscillator.typename = "RandomDriftOscillator" +**.tsnClock*.clock.oscillator.typename = "IdealOscillator" +**.oscillator.typename = "IdealOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) **.oscillator.driftRateChange = uniform(-1ppm, 1ppm) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index f5cd02ecb30..a676ee31c05 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -278,9 +278,11 @@ void Gptp::sendFollowUp(int portId, const GptpSync *sync, const clocktime_t& syn gptp->setCorrectionField(syncEgressTimestampOwn - preciseOriginTimestamp); } else if (gptpNodeType == BRIDGE_NODE) { - // TODO: Check which rate ratio we should use here auto residenceTime = syncEgressTimestampOwn - syncIngressTimestamp; - auto newCorrectionField = correctionField + meanLinkDelay + gmRateRatio * residenceTime; + // meanLinkDelay and residence time are in the local time base + // In the correctionField we need to express it in the grandmaster's time base + // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio + auto newCorrectionField = correctionField + gmRateRatio * (meanLinkDelay + residenceTime); gptp->setCorrectionField(newCorrectionField); } gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); @@ -363,9 +365,6 @@ void Gptp::processSync(Packet *packet, const GptpSync *gptp) { rcvdGptpSync = true; lastReceivedGptpSyncSequenceId = gptp->getSequenceId(); - - // peerSentTimeSync = gptp->getOriginTimestamp(); - // TODO this is unfilled in two-step mode syncIngressTimestamp = packet->getTag()->getArrivalClockTime(); } @@ -384,7 +383,6 @@ void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) preciseOriginTimestamp = gptp->getPreciseOriginTimestamp(); correctionField = gptp->getCorrectionField(); - syncEgressTimestampMaster = gptp->getPreciseOriginTimestamp() + gptp->getCorrectionField(); receivedRateRatio = gptp->getFollowUpInformationTLV().getRateRatio(); synchronize(); @@ -394,7 +392,6 @@ void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) EV_INFO << "ORIGIN TIME SYNC - " << preciseOriginTimestamp << endl; EV_INFO << "CORRECTION FIELD - " << correctionField << endl; EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; - EV_INFO << "syncEgressTimestampMaster - " << syncEgressTimestampMaster << endl; EV_INFO << "receivedRateRatio - " << receivedRateRatio << endl; rcvdGptpSync = false; @@ -412,19 +409,20 @@ void Gptp::synchronize() * Local time is adjusted using peer delay, correction field, residence time * * and packet transmission time based departure time of Sync message from GM * *****************************************************************************/ - // TODO: This should be the offset calculation and should include the rate - // ratio - clocktime_t newTime = preciseOriginTimestamp + correctionField + meanLinkDelay + residenceTime; + // preciseOriginTimestamp and correctionField are in the grandmaster's time base + // meanLinkDelay and residence time are in the local time base + // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio + clocktime_t newTime = preciseOriginTimestamp + correctionField + gmRateRatio * (meanLinkDelay + residenceTime); ASSERT(gptpNodeType != MASTER_NODE); - // TODO validate the following expression with the standard - if (syncEgressTimestampMasterLast == -1) + if (preciseOriginTimestampLast == -1 || syncIngressTimestampLast == -1) gmRateRatio = 1; else - gmRateRatio = (syncEgressTimestampMaster - syncEgressTimestampMasterLast) / + gmRateRatio = (preciseOriginTimestamp - preciseOriginTimestampLast) / (syncIngressTimestamp - syncIngressTimestampLast); + // TODO: Add a clock servo model to INET in the future auto settableClock = check_and_cast(clock.get()); ppm newOscillatorCompensation = unit(gmRateRatio * (1 + unit(settableClock->getOscillatorCompensation()).get()) - 1); @@ -432,14 +430,18 @@ void Gptp::synchronize() newLocalTimeAtTimeSync = clock->getClockTime(); timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; - syncIngressTimestampLast = syncIngressTimestamp; // TODO: What does this do, what does it mean? // TODO: Check which timestamps we really need to adjust. // adjust local timestamps, too - // adjustLocalTimestamp(pdelayRespEventIngressTimestamp); - // adjustLocalTimestamp(pdelayReqEventEgressTimestamp); - // adjustLocalTimestamp(syncIngressTimestampLast); + //adjustLocalTimestamp(pDelayReqEgressTimestamp); + //adjustLocalTimestamp(pDelayReqIngressTimestamp); + //adjustLocalTimestamp(pDelayRespEgressTimestamp); + //adjustLocalTimestamp(pDelayRespEgressTimestampLast); + //adjustLocalTimestamp(pDelayRespIngressTimestamp); + //adjustLocalTimestamp(pDelayRespIngressTimestampLast); + //adjustLocalTimestamp(syncIngressTimestampLast); + //adjustLocalTimestamp() /************** Rate ratio calculation ************************************* * It is calculated based on interval between two successive Sync messages * @@ -449,15 +451,18 @@ void Gptp::synchronize() EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; EV_INFO << "CURRENT SIMTIME - " << now << endl; - EV_INFO << "ORIGIN TIME SYNC - " << syncEgressTimestampMaster << endl; - EV_INFO << "PREV ORIGIN TIME SYNC - " << syncEgressTimestampMasterLast << endl; + EV_INFO << "ORIGIN TIME SYNC - " << preciseOriginTimestamp << endl; + EV_INFO << "PREV ORIGIN TIME SYNC - " << preciseOriginTimestampLast << endl; + EV_INFO << "SYNC INGRESS TIME - " << syncIngressTimestamp << endl; + EV_INFO << "SYNC INGRESS TIME LAST - " << syncIngressTimestampLast << endl; EV_INFO << "RESIDENCE TIME - " << residenceTime << endl; EV_INFO << "CORRECTION FIELD - " << correctionField << endl; EV_INFO << "PROPAGATION DELAY - " << meanLinkDelay << endl; EV_INFO << "TIME DIFFERENCE TO SIMTIME - " << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; - syncEgressTimestampMasterLast = syncEgressTimestampMaster; + syncIngressTimestampLast = syncIngressTimestamp; + preciseOriginTimestampLast = preciseOriginTimestamp; emit(rateRatioSignal, gmRateRatio); emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); @@ -521,7 +526,7 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU // However, these contain fractional nanoseconds, which we do not // use in INET. // See 11.2.19.3.3 computePdelayRateRatio() in IEEE 802.1AS-2020 - if (pDelayRespEgressTimestampLast == -1 || pDelayRespIngressTimestampLast == -1) + if (true || pDelayRespEgressTimestampLast == -1 || pDelayRespIngressTimestampLast == -1) neighborRateRatio = 1.0; else neighborRateRatio = (pDelayRespEgressTimestamp - pDelayRespEgressTimestampLast) / diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index ba4b6d48233..de05961cb68 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -65,10 +65,9 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener // Unsure why this is also configurable with a parameter (TODO: Check) clocktime_t correctionField = CLOCKTIME_ZERO; - clocktime_t preciseOriginTimestamp; // timestamp when the last sync message was generated at the GM + clocktime_t preciseOriginTimestamp = -1; // timestamp when the last sync message was generated at the GM + clocktime_t preciseOriginTimestampLast = -1; // timestamp when the last sync message was generated at the GM - clocktime_t syncEgressTimestampMaster = -1; // egress time of Sync at master - clocktime_t syncEgressTimestampMasterLast = -1; // egress time of previous Sync at master clocktime_t syncIngressTimestamp = -1; // ingress time of Sync at slave (this node) clocktime_t syncIngressTimestampLast = -1; // ingress time of previous Sync at slave (this node) From 0faa28bb7b4858c18abfac770ba9ec72d2c56ac8 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 17 Apr 2024 18:51:36 +0200 Subject: [PATCH 014/138] Adjust timestamp, newTime calculation is correct now. --- src/inet/linklayer/ieee8021as/Gptp.cc | 52 +++++++++++++++------------ 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index a676ee31c05..271ecec311a 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -265,7 +265,7 @@ void Gptp::sendSync() // The sendFollowUp(portId) called by receiveSignal(), when GptpSync sent } -void Gptp::sendFollowUp(int portId, const GptpSync *sync, const clocktime_t& syncEgressTimestampOwn) +void Gptp::sendFollowUp(int portId, const GptpSync *sync, const clocktime_t &syncEgressTimestampOwn) { auto packet = new Packet("GptpFollowUp"); packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); @@ -409,18 +409,19 @@ void Gptp::synchronize() * Local time is adjusted using peer delay, correction field, residence time * * and packet transmission time based departure time of Sync message from GM * *****************************************************************************/ - // preciseOriginTimestamp and correctionField are in the grandmaster's time base - // meanLinkDelay and residence time are in the local time base - // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio - clocktime_t newTime = preciseOriginTimestamp + correctionField + gmRateRatio * (meanLinkDelay + residenceTime); ASSERT(gptpNodeType != MASTER_NODE); - if (preciseOriginTimestampLast == -1 || syncIngressTimestampLast == -1) + if (true || preciseOriginTimestampLast == -1 || syncIngressTimestampLast == -1) gmRateRatio = 1; else - gmRateRatio = (preciseOriginTimestamp - preciseOriginTimestampLast) / - (syncIngressTimestamp - syncIngressTimestampLast); + gmRateRatio = + (preciseOriginTimestamp - preciseOriginTimestampLast) / (syncIngressTimestamp - syncIngressTimestampLast); + + // preciseOriginTimestamp and correctionField are in the grandmaster's time base + // meanLinkDelay and residence time are in the local time base + // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio + clocktime_t newTime = preciseOriginTimestamp + correctionField + gmRateRatio * (meanLinkDelay + residenceTime); // TODO: Add a clock servo model to INET in the future auto settableClock = check_and_cast(clock.get()); @@ -431,18 +432,6 @@ void Gptp::synchronize() newLocalTimeAtTimeSync = clock->getClockTime(); timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; - // TODO: What does this do, what does it mean? - // TODO: Check which timestamps we really need to adjust. - // adjust local timestamps, too - //adjustLocalTimestamp(pDelayReqEgressTimestamp); - //adjustLocalTimestamp(pDelayReqIngressTimestamp); - //adjustLocalTimestamp(pDelayRespEgressTimestamp); - //adjustLocalTimestamp(pDelayRespEgressTimestampLast); - //adjustLocalTimestamp(pDelayRespIngressTimestamp); - //adjustLocalTimestamp(pDelayRespIngressTimestampLast); - //adjustLocalTimestamp(syncIngressTimestampLast); - //adjustLocalTimestamp() - /************** Rate ratio calculation ************************************* * It is calculated based on interval between two successive Sync messages * ***************************************************************************/ @@ -461,6 +450,22 @@ void Gptp::synchronize() EV_INFO << "TIME DIFFERENCE TO SIMTIME - " << CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now << endl; EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; + // TODO: What does this do, what does it mean? + // TODO: Check which timestamps we really need to adjust. + // TODO: What to do with -1 timestamps? + // adjust local timestamps, too + adjustLocalTimestamp(syncIngressTimestamp); + //adjustLocalTimestamp(pDelayReqEgressTimestamp); + //adjustLocalTimestamp(pDelayReqIngressTimestamp); + //adjustLocalTimestamp(pDelayRespEgressTimestamp); + //adjustLocalTimestamp(pDelayRespEgressTimestampLast); + //adjustLocalTimestamp(pDelayRespIngressTimestamp); + //adjustLocalTimestamp(pDelayRespIngressTimestampLast); + //adjustLocalTimestamp(syncIngressTimestamp); + //adjustLocalTimestamp(syncIngressTimestampLast); + //adjustLocalTimestamp(preciseOriginTimestamp); + //adjustLocalTimestamp(preciseOriginTimestampLast); + syncIngressTimestampLast = syncIngressTimestamp; preciseOriginTimestampLast = preciseOriginTimestamp; @@ -539,10 +544,13 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU auto t2 = pDelayReqIngressTimestamp; auto t3 = pDelayRespEgressTimestamp; - meanLinkDelay = (neighborRateRatio * (t4 - t1) - (t3 - t2)) / 2; + auto meanLinkDelayInResponderTimebase = (neighborRateRatio * (t4 - t1) - (t3 - t2)) / 2; + + // Regarding NOTE 1 in 11.2.19.3.4, we need to device by the nrr to get the meanLinkDelay in the current time base + meanLinkDelay = meanLinkDelayInResponderTimebase / neighborRateRatio; EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; - EV_INFO << "NEIGHBOR RATE RATIO - " << gmRateRatio << endl; + EV_INFO << "NEIGHBOR RATE RATIO - " << neighborRateRatio << endl; EV_INFO << "pDelayReqEgressTimestamp - " << pDelayReqEgressTimestamp << endl; EV_INFO << "pDelayReqIngressTimestamp - " << pDelayReqIngressTimestamp << endl; EV_INFO << "pDelayRespEgressTimestamp - " << pDelayRespEgressTimestamp << endl; From fa0534f556e7dd58e536c564a8d013e83dddbd73 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 17 Apr 2024 19:14:42 +0200 Subject: [PATCH 015/138] NRR is weird --- .../timesynchronization/gptp/GptpShowcase.anf | 42 +++++++++---------- .../tsn/timesynchronization/gptp/omnetpp.ini | 1 - src/inet/linklayer/ieee8021as/Gptp.cc | 30 +++++++------ src/inet/linklayer/ieee8021as/Gptp.h | 9 +++- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 352aeeff348..6eb219c27bb 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -9,8 +9,8 @@ - - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index b3691df0505..086e56024f3 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -276,11 +276,15 @@ void Gptp::sendFollowUp(int portId, const GptpSync *sync, const clocktime_t &syn gptp->setPreciseOriginTimestamp(preciseOriginTimestamp); gptp->setSequenceId(sync->getSequenceId()); + clocktime_t residenceTime; if (gptpNodeType == MASTER_NODE) { - gptp->setCorrectionField(syncEgressTimestampOwn - preciseOriginTimestamp); + residenceTime = syncEgressTimestampOwn - preciseOriginTimestamp; + gptp->setCorrectionField(residenceTime); } else if (gptpNodeType == BRIDGE_NODE) { - auto residenceTime = syncEgressTimestampOwn - syncIngressTimestamp; + // TODO: Residence time calculation does not work correctly (maybe wrong timestamps) + // See event #406 and #508 + residenceTime = syncEgressTimestampOwn - syncIngressTimestamp; // meanLinkDelay and residence time are in the local time base // In the correctionField we need to express it in the grandmaster's time base // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio @@ -289,6 +293,13 @@ void Gptp::sendFollowUp(int portId, const GptpSync *sync, const clocktime_t &syn } gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); packet->insertAtFront(gptp); + + EV_INFO << "############## SEND FOLLOW_UP ################################" << endl; + EV_INFO << "Correction Field - " << gptp->getCorrectionField() << endl; + EV_INFO << "gmRateRatio - " << gmRateRatio << endl; + EV_INFO << "meanLinkDelay - " << meanLinkDelay << endl; + EV_INFO << "residenceTime - " << residenceTime << endl; + sendPacketToNIC(packet, portId); } @@ -511,7 +522,6 @@ void Gptp::processPdelayResp(Packet *packet, const GptpPdelayResp *gptp) if (pDelayRespIngressTimestampLast == -1) { pDelayRespIngressTimestampLast = pDelayRespIngressTimestamp; // t4 last } - pDelayRespIngressTimestampLast = pDelayRespIngressTimestamp; //t4 last pDelayRespIngressTimestamp = packet->getTag()->getArrivalClockTime(); //t4 now pDelayReqIngressTimestamp = gptp->getRequestReceiptTimestamp(); //t2 } diff --git a/src/inet/linklayer/ieee8021as/Gptp.ned b/src/inet/linklayer/ieee8021as/Gptp.ned index f2db02cff42..55625066977 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.ned +++ b/src/inet/linklayer/ieee8021as/Gptp.ned @@ -33,7 +33,7 @@ simple Gptp like IGptp // following parameters are used to schedule follow_up and pdelay_resp messages. // These numbers must be enough large to prevent creating queue in MAC layer. - // It means it should be large than transmission time of message sent before + // It means it should be larger than transmission time of message sent before double pDelayReqProcessingTime @unit(s) = default(8us); // processing time between arrived PDelayReq and send of PDelayResp double followUpInterval @unit(s) = default(7us); From e96af21cc383bdf3e5d83733337f7d3186654c95 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Wed, 24 Apr 2024 00:15:42 +0200 Subject: [PATCH 022/138] Revise the NRR's calculation --- src/inet/linklayer/ieee8021as/Gptp.cc | 41 +++++++++++++++++++++++---- src/inet/linklayer/ieee8021as/Gptp.h | 4 ++- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index b3691df0505..de1477a1a40 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -59,6 +59,8 @@ void Gptp::initialize(int stage) pDelayReqProcessingTime = par("pDelayReqProcessingTime"); std::hash strHash; clockIdentity = strHash(getFullPath()); + pDelayRespEgressTimestampIntervals.resize(timeInterval, -1); + pDelayRespIngressTimestampIntervals.resize(timeInterval, -1); } if (stage == INITSTAGE_LINK_LAYER) { meanLinkDelay = 0; @@ -508,10 +510,17 @@ void Gptp::processPdelayResp(Packet *packet, const GptpPdelayResp *gptp) } rcvdPdelayResp = true; - if (pDelayRespIngressTimestampLast == -1) { - pDelayRespIngressTimestampLast = pDelayRespIngressTimestamp; // t4 last + + pDelayRespIngressTimestampIntervals.pop_back(); + pDelayRespIngressTimestampIntervals.insert(pDelayRespIngressTimestampIntervals.begin(), pDelayRespIngressTimestamp); + EV_INFO << "***** Ingress Time Stamp Intervals Vector *****" << endl; + for (auto val: pDelayRespIngressTimestampIntervals){ + EV_INFO << val << endl; } - pDelayRespIngressTimestampLast = pDelayRespIngressTimestamp; //t4 last +// if (pDelayRespIngressTimestampLast == -1) { +// pDelayRespIngressTimestampLast = pDelayRespIngressTimestamp; // t4 last +// } + pDelayRespIngressTimestamp = packet->getTag()->getArrivalClockTime(); //t4 now pDelayReqIngressTimestamp = gptp->getRequestReceiptTimestamp(); //t2 } @@ -534,9 +543,16 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU return; } - if (pDelayRespEgressTimestampLast == -1) { - pDelayRespEgressTimestampLast = pDelayRespEgressTimestamp; //t3 last + pDelayRespEgressTimestampIntervals.pop_back(); + pDelayRespEgressTimestampIntervals.insert(pDelayRespEgressTimestampIntervals.begin(), pDelayRespEgressTimestamp); + EV_INFO << "***** Egress Time Stamp Intervals Vector *****" << endl; + for (auto val: pDelayRespEgressTimestampIntervals){ + EV_INFO << val << endl; } +// if (pDelayRespEgressTimestampLast == -1) { +// pDelayRespEgressTimestampLast = pDelayRespEgressTimestamp; //t3 last +// } + pDelayRespEgressTimestamp = gptp->getResponseOriginTimestamp(); //t3 now // Note, that the standard defines the usage of the correction field @@ -544,11 +560,24 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU // However, these contain fractional nanoseconds, which we do not // use in INET. // See 11.2.19.3.3 computePdelayRateRatio() in IEEE 802.1AS-2020 + for (int last = timeInterval - 1; last >= 0; last--){ + if(pDelayRespIngressTimestampIntervals[last] != -1 && pDelayRespEgressTimestampIntervals[last] != -1){ + pDelayRespIngressTimestampLast = pDelayRespIngressTimestampIntervals[last]; + pDelayRespEgressTimestampLast = pDelayRespEgressTimestampIntervals[last]; + break; + } + else{ + pDelayRespIngressTimestampLast = -1; + pDelayRespEgressTimestampLast = -1; + } + } + if (pDelayRespEgressTimestampLast == -1 || pDelayRespIngressTimestampLast == -1) neighborRateRatio = 1.0; - else + else { neighborRateRatio = (pDelayRespEgressTimestamp - pDelayRespEgressTimestampLast) / (pDelayRespIngressTimestamp - pDelayRespIngressTimestampLast); + } // TODO: Check why nrr is always 1 diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 04e076d0a4f..6528862a5d8 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -44,7 +44,9 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener double neighborRateRatio = 1.0; // the rate ratio to the neighbor uint16_t sequenceId = 0; - + int timeInterval = 10; // This should be dynamically set as needed + std::vector pDelayRespIngressTimestampIntervals; + std::vector pDelayRespEgressTimestampIntervals; // == Propagation Delay Measurement Procedure == // Timestamps corresponding to the PDelayRequest and PDelayResponse mechanism clocktime_t pDelayReqEgressTimestamp = -1; // egress time of pdelay_req at initiator (this node) From 69ff19269c08b1d950cae007b4e447addb54b93f Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 24 Apr 2024 16:31:06 +0200 Subject: [PATCH 023/138] First working version, need to check with old implemention for accuracy --- .cproject | 48 +- .../timesynchronization/gptp/GptpShowcase.anf | 1950 ++++++++++++++++- .../tsn/timesynchronization/gptp/omnetpp.ini | 4 +- src/inet/linklayer/ieee8021as/Gptp.cc | 104 +- src/inet/linklayer/ieee8021as/Gptp.h | 12 +- 5 files changed, 2007 insertions(+), 111 deletions(-) diff --git a/.cproject b/.cproject index bf352c0c7d8..76264c3cdd9 100644 --- a/.cproject +++ b/.cproject @@ -55,24 +55,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + @@ -128,24 +128,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 7a470a6f205..a21661a2b78 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -9,8 +9,8 @@ - - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 40e52aa0875..ef552b792ce 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -15,6 +15,8 @@ description = "abstract" **.oscillator.driftRateChangeUpperLimit = 100ppm **.oscillator.driftRateChangeLowerLimit = -100ppm +**.kp=8 +**.kp=7 **.offsetThreshold = 0s # all Ethernet interfaces have 100 Mbps speed @@ -63,7 +65,7 @@ description = "Basic tree topology with one master clock" extends=OneMasterClock description="Study effect of kp and ki" **.kp = ${kp=0,5,6,7,8,9,10,11,12,13,14,15,16,17,18} -**.ki = ${ki=0,2,4,6,8,8,10,11,12} +**.ki = ${ki=0,2,4,6,7,8,9,10,11,12,14,16} [Config PrimaryAndHotStandbyMasterClocks] network = TwoMasterClocksTreeGptpShowcase From fd7fce263cfa971c601467c5000a9990f2b1d363 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 21 May 2024 14:21:57 +0200 Subject: [PATCH 037/138] change kp ki parameter range --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 10 +++++----- src/inet/clock/model/PiClock.cc | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index ef552b792ce..b3162f92a02 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -8,7 +8,7 @@ description = "abstract" # all oscillators have a constant drift rate (specified with a random distribution for each one) # except for the master clocks, which have a random drift rate -**.tsnClock*.clock.oscillator.typename = "RandomDriftOscillator" +**.clock.oscillator.typename = "RandomDriftOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) **.oscillator.driftRateChange = uniform(-1ppm, 1ppm) @@ -16,8 +16,8 @@ description = "abstract" **.oscillator.driftRateChangeLowerLimit = -100ppm **.kp=8 -**.kp=7 -**.offsetThreshold = 0s +**.ki=7 +**.offsetThreshold = 1s # all Ethernet interfaces have 100 Mbps speed *.*.eth[*].bitrate = 100Mbps @@ -64,8 +64,8 @@ description = "Basic tree topology with one master clock" [ParameterStudies] extends=OneMasterClock description="Study effect of kp and ki" -**.kp = ${kp=0,5,6,7,8,9,10,11,12,13,14,15,16,17,18} -**.ki = ${ki=0,2,4,6,7,8,9,10,11,12,14,16} +**.kp = ${kp=0,7.0,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8} +**.ki = ${ki=0,2,3,4,5,6,7,8,9,10} [Config PrimaryAndHotStandbyMasterClocks] network = TwoMasterClocksTreeGptpShowcase diff --git a/src/inet/clock/model/PiClock.cc b/src/inet/clock/model/PiClock.cc index f1c1fb1f4d3..efd48cdfbae 100644 --- a/src/inet/clock/model/PiClock.cc +++ b/src/inet/clock/model/PiClock.cc @@ -70,7 +70,7 @@ clocktime_t PiClock::setClockTime(clocktime_t newClockTime) Enter_Method("setClockTime"); clocktime_t oldClockTime = getClockTime(); - if (newClockTime != oldClockTime) { + if (newClockTime != oldClockTime){ emit(timeChangedSignal, oldClockTime.asSimTime()); clocktime_t offsetNow = newClockTime - oldClockTime; From 520cdd656130e91829e6ea1bdbb3b9a035bfbca2 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 21 May 2024 14:48:27 +0200 Subject: [PATCH 038/138] Added Stepping Clock Scenario --- .../timesynchronization/gptp/GptpShowcase.ned | 1 + .../gptp/SteppingClock.anf | 645 ++++++++++++++++++ .../tsn/timesynchronization/gptp/omnetpp.ini | 11 +- .../gptp/scenario-stepping-master.xml | 8 + 4 files changed, 664 insertions(+), 1 deletion(-) create mode 100644 showcases/tsn/timesynchronization/gptp/SteppingClock.anf create mode 100644 showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.ned b/showcases/tsn/timesynchronization/gptp/GptpShowcase.ned index d8f9d32eaf3..ae1ffb09719 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.ned +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.ned @@ -14,6 +14,7 @@ import inet.node.tsn.TsnSwitch; network OneMasterClockGptpShowcase extends TsnNetworkBase { submodules: + scenarioManager: ScenarioManager; tsnClock: TsnClock { @display("p=500,150"); } diff --git a/showcases/tsn/timesynchronization/gptp/SteppingClock.anf b/showcases/tsn/timesynchronization/gptp/SteppingClock.anf new file mode 100644 index 00000000000..20f35659643 --- /dev/null +++ b/showcases/tsn/timesynchronization/gptp/SteppingClock.anf @@ -0,0 +1,645 @@ + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index b3162f92a02..9902bb34752 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -17,7 +17,6 @@ description = "abstract" **.kp=8 **.ki=7 -**.offsetThreshold = 1s # all Ethernet interfaces have 100 Mbps speed *.*.eth[*].bitrate = 100Mbps @@ -67,6 +66,16 @@ description="Study effect of kp and ki" **.kp = ${kp=0,7.0,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8} **.ki = ${ki=0,2,3,4,5,6,7,8,9,10} +[SteppingClock] +extends=OneMasterClock +description="See how the clock behaves when the master clock steps 1us" +*.scenarioManager.script = xmldoc("scenario-stepping-master.xml") +**.tsnClock.clock.typename = "SettableClock" +**.tsnClock*.clock.oscillator.typename = "IdealOscillator" +**.tsnSwitch*.clock.oscillator.typename = "ConstantDriftOscillator" +**.tsnDevice*.clock.oscillator.typename = "ConstantDriftOscillator" + + [Config PrimaryAndHotStandbyMasterClocks] network = TwoMasterClocksTreeGptpShowcase description = "Extended tree topology with two master clocks" diff --git a/showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml b/showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml new file mode 100644 index 00000000000..c73c2b82e50 --- /dev/null +++ b/showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml @@ -0,0 +1,8 @@ + + + + + + + + From 28be456c0a448ac46a3955dafb9403b1b268a439 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 21 May 2024 15:05:56 +0200 Subject: [PATCH 039/138] Bug with kp=7.0 at 1.7s --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index b3162f92a02..bea7d092572 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -1,5 +1,5 @@ [General] -seed-set = 0 +seed-set = -123 sim-time-limit = 20s description = "abstract" @@ -8,7 +8,9 @@ description = "abstract" # all oscillators have a constant drift rate (specified with a random distribution for each one) # except for the master clocks, which have a random drift rate -**.clock.oscillator.typename = "RandomDriftOscillator" +#**.tsnClock*.clock.oscillator.typename = "RandomDriftOscillator" +**.clock.oscillator.typename="RandomDriftOscillator" + **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) **.oscillator.driftRateChange = uniform(-1ppm, 1ppm) @@ -17,7 +19,7 @@ description = "abstract" **.kp=8 **.ki=7 -**.offsetThreshold = 1s +**.offsetThreshold =1us # all Ethernet interfaces have 100 Mbps speed *.*.eth[*].bitrate = 100Mbps From 08c8952510398245449183020c76710634a1fdfa Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 21 May 2024 16:26:46 +0200 Subject: [PATCH 040/138] Fixed bug when no refefrence clock is set --- .../tsn/timesynchronization/gptp/omnetpp.ini | 4 +- src/inet/clock/model/MultiClock.ned | 2 +- src/inet/clock/model/PiClock.cc | 39 +------------------ src/inet/clock/model/PiClock.h | 24 ++---------- src/inet/clock/model/PiClock.ned | 3 +- src/inet/linklayer/ieee8021as/Gptp.cc | 17 +++++--- 6 files changed, 20 insertions(+), 69 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 9902bb34752..bbfa0cfa6df 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -80,8 +80,8 @@ description="See how the clock behaves when the master clock steps 1us" network = TwoMasterClocksTreeGptpShowcase description = "Extended tree topology with two master clocks" -# TSN clock2 has a settable clock -*.tsnClock2.clock.typename = "SettableClock" +# TSN clock2 has a pi clock +*.tsnClock2.clock.typename = "PiClock" *.tsnClock1.gptp.typename = "Gptp" *.tsnClock1.gptp.clockModule = "tsnClock1.clock" diff --git a/src/inet/clock/model/MultiClock.ned b/src/inet/clock/model/MultiClock.ned index 8ebcb6cc76b..b120c932d4e 100644 --- a/src/inet/clock/model/MultiClock.ned +++ b/src/inet/clock/model/MultiClock.ned @@ -27,7 +27,7 @@ module MultiClock like IClock @signal[timeChanged](type=simtime_t); @statistic[timeChanged](title="Clock time"; source=localSignal(timeChanged); record=vector; interpolationmode=linear); submodules: - clock[numClocks]: like IClock { + clock[numClocks]: like IClock { @display("p=200,200,row,200"); } } diff --git a/src/inet/clock/model/PiClock.cc b/src/inet/clock/model/PiClock.cc index efd48cdfbae..25cc54ccba6 100644 --- a/src/inet/clock/model/PiClock.cc +++ b/src/inet/clock/model/PiClock.cc @@ -20,7 +20,7 @@ simsignal_t PiClock::driftSignal = cComponent::registerSignal("drift"); void PiClock::initialize(int stage) { - OscillatorBasedClock::initialize(stage); + SettableClock::initialize(stage); if (stage == INITSTAGE_LOCAL) { offsetThreshold = &par("offsetThreshold"); kp = par("kp"); @@ -39,32 +39,6 @@ void PiClock::initialize(int stage) } } -OverdueClockEventHandlingMode PiClock::getOverdueClockEventHandlingMode(ClockEvent *event) const -{ - auto mode = event->getOverdueClockEventHandlingMode(); - if (mode == UNSPECIFIED) - return defaultOverdueClockEventHandlingMode; - else - return mode; -} - -simtime_t PiClock::handleOverdueClockEvent(ClockEvent *event, simtime_t t) -{ - switch (getOverdueClockEventHandlingMode(event)) { - case EXECUTE: - EV_WARN << "Scheduling overdue clock event " << event->getName() << " to current simulation time.\n"; - return t; - case SKIP: - EV_WARN << "Skipping overdue clock event " << event->getName() << ".\n"; - cancelClockEvent(event); - return -1; - case ERROR: - throw cRuntimeError("Clock event is overdue"); - default: - throw cRuntimeError("Unknown overdue clock event handling mode"); - } -} - clocktime_t PiClock::setClockTime(clocktime_t newClockTime) { Enter_Method("setClockTime"); @@ -143,15 +117,4 @@ clocktime_t PiClock::setClockTime(clocktime_t newClockTime) return getClockTime(); } -void PiClock::processCommand(const cXMLElement &node) -{ - Enter_Method("processCommand"); - if (!strcmp(node.getTagName(), "set-clock")) { - clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); - setClockTime(time); - } - else - throw cRuntimeError("Invalid command: %s", node.getTagName()); -} - } // namespace inet diff --git a/src/inet/clock/model/PiClock.h b/src/inet/clock/model/PiClock.h index d0b62142b0b..89854ac2597 100644 --- a/src/inet/clock/model/PiClock.h +++ b/src/inet/clock/model/PiClock.h @@ -4,24 +4,21 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // -#ifndef __INET_PICONTROLCLOCK_H -#define __INET_PICONTROLCLOCK_H +#ifndef __INET_PICLOCK_H +#define __INET_PICLOCK_H #include "inet/clock/model/OscillatorBasedClock.h" +#include "inet/clock/model/SettableClock.h" #include "inet/common/scenario/IScriptable.h" namespace inet { -class INET_API PiClock : public OscillatorBasedClock, public IScriptable +class INET_API PiClock : public SettableClock { protected: static simsignal_t driftSignal; static simsignal_t kpSignal; OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; - ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value - // means the clock compensates 100 microseconds for every second in clock time - // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) - // than the actual tick length measured in clock time int phase = 0; clocktime_t offset[2]; @@ -31,34 +28,21 @@ class INET_API PiClock : public OscillatorBasedClock, public IScriptable double kp; // proportional gain double ki; // integral gain - double kd = 0; ppm kpTerm = ppm(0); ppm kiTerm = ppm(0); - ppm kdTerm = ppm(0); ppm kpTermMax = ppm(100); ppm kpTermMin = ppm(-100); ppm kiTermMax = ppm(100); ppm kiTermMin = ppm(-100); - ppm kdTermMax = ppm(100); - ppm kdTermMin = ppm(-100); ppm drift = ppm(0); protected: virtual void initialize(int stage) override; - virtual OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event) const; - - virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); - - // IScriptable implementation - virtual void processCommand(const cXMLElement &node) override; - public: - virtual ppm getOscillatorCompensation() const override { return oscillatorCompensation; } - /** * Sets the clock time immediately to the given value. Greater than 1 oscillator * compensation factor means the clock measures time faster. diff --git a/src/inet/clock/model/PiClock.ned b/src/inet/clock/model/PiClock.ned index cf86bc3f7e5..aaf2a80a027 100644 --- a/src/inet/clock/model/PiClock.ned +++ b/src/inet/clock/model/PiClock.ned @@ -14,10 +14,9 @@ package inet.clock.model; // // @see ~ScenarioManager // -module PiClock extends OscillatorBasedClock +module PiClock extends SettableClock { parameters: - string defaultOverdueClockEventHandlingMode @enum("execute","skip","error") = default("error"); double offsetThreshold @unit(s) = default(0s); // Clock will perform a time jump if the offset is greater than this value, 0.0 disables this feature double kp = default(8); double ki = default(4); diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 05b987e66e3..45f636c24a0 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -425,6 +425,7 @@ void Gptp::synchronize() ASSERT(gptpNodeType != MASTER_NODE); gmRateRatio = receivedRateRatio * neighborRateRatio; + gmRateRatio = 1.0; // preciseOriginTimestamp and correctionField are in the grandmaster's time base // meanLinkDelay and residence time are in the local time base @@ -451,9 +452,8 @@ void Gptp::synchronize() newLocalTimeAtTimeSync = clock->getClockTime(); // new=5 - old=4 = +1 timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; - auto referenceClockTime = piControlClock->referenceClockModule->getClockTime(); - auto diffReferenceToOldLocal = oldLocalTimeAtTimeSync - referenceClockTime; - auto diffReferenceToNewTime = newTime - referenceClockTime; + + /************** Rate ratio calculation ************************************* * It is calculated based on interval between two successive Sync messages * @@ -463,9 +463,14 @@ void Gptp::synchronize() EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; EV_INFO << "CALCULATED NEW TIME - " << newTime << endl; - EV_INFO << "REFERENCE CLOCK TIME - " << referenceClockTime << endl; - EV_INFO << "DIFF REFERENCE TO OLD TIME - " << diffReferenceToOldLocal << endl; - EV_INFO << "DIFF REFERENCE TO NEW TIME - " << diffReferenceToNewTime << endl; + if (piControlClock->referenceClockModule != nullptr) { + auto referenceClockTime = piControlClock->referenceClockModule->getClockTime(); + auto diffReferenceToOldLocal = oldLocalTimeAtTimeSync - referenceClockTime; + auto diffReferenceToNewTime = newTime - referenceClockTime; + EV_INFO << "REFERENCE CLOCK TIME - " << referenceClockTime << endl; + EV_INFO << "DIFF REFERENCE TO OLD TIME - " << diffReferenceToOldLocal << endl; + EV_INFO << "DIFF REFERENCE TO NEW TIME - " << diffReferenceToNewTime << endl; + } EV_INFO << "CURRENT SIMTIME - " << now << endl; EV_INFO << "ORIGIN TIME SYNC - " << preciseOriginTimestamp << endl; EV_INFO << "PREV ORIGIN TIME SYNC - " << preciseOriginTimestampLast << endl; From 41fc12c7695324132e883a3ea92623610adeaf60 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 21 May 2024 16:51:41 +0200 Subject: [PATCH 041/138] residence time bug? --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 4 ++-- src/inet/linklayer/ieee8021as/Gptp.cc | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index bea7d092572..9f1bac04db0 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -17,8 +17,8 @@ description = "abstract" **.oscillator.driftRateChangeUpperLimit = 100ppm **.oscillator.driftRateChangeLowerLimit = -100ppm -**.kp=8 -**.ki=7 +**.kp=7 +**.ki=0 **.offsetThreshold =1us # all Ethernet interfaces have 100 Mbps speed diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 05b987e66e3..2b71bbd4a2d 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -425,6 +425,7 @@ void Gptp::synchronize() ASSERT(gptpNodeType != MASTER_NODE); gmRateRatio = receivedRateRatio * neighborRateRatio; + gmRateRatio = 1.0; // preciseOriginTimestamp and correctionField are in the grandmaster's time base // meanLinkDelay and residence time are in the local time base From 18c7b8108993af344f6242538ebe18673c925b63 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 21 May 2024 17:42:18 +0200 Subject: [PATCH 042/138] minor modify with ki to reveal the bug --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 2 +- src/inet/clock/model/PiClock.cc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 9f1bac04db0..73ce9002837 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -18,7 +18,7 @@ description = "abstract" **.oscillator.driftRateChangeLowerLimit = -100ppm **.kp=7 -**.ki=0 +**.ki=2 **.offsetThreshold =1us # all Ethernet interfaces have 100 Mbps speed diff --git a/src/inet/clock/model/PiClock.cc b/src/inet/clock/model/PiClock.cc index efd48cdfbae..71f767db051 100644 --- a/src/inet/clock/model/PiClock.cc +++ b/src/inet/clock/model/PiClock.cc @@ -102,6 +102,7 @@ clocktime_t PiClock::setClockTime(clocktime_t newClockTime) localNsPrev = local[0].inUnit(SIMTIME_NS); localNs = local[1].inUnit(SIMTIME_NS); +// drift = ppm(0); drift += ppm(1e6 * (offsetNsPrev - offsetNs) / (localNsPrev - localNs)); EV_INFO << "Drift: " << drift << "\n"; From 3d4ec2f53197ac92b80f7b869f7d262eb94b925e Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 21 May 2024 17:46:43 +0200 Subject: [PATCH 043/138] Residencetime statistic --- .../timesynchronization/gptp/GptpShowcase.anf | 50 +++++++++---------- src/inet/linklayer/ieee8021as/Gptp.cc | 17 ++++--- src/inet/linklayer/ieee8021as/Gptp.h | 3 +- src/inet/linklayer/ieee8021as/Gptp.ned | 2 + 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 99542207acf..0a9804629cf 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -9,8 +9,8 @@ - - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 73ce9002837..9f1bac04db0 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -18,7 +18,7 @@ description = "abstract" **.oscillator.driftRateChangeLowerLimit = -100ppm **.kp=7 -**.ki=2 +**.ki=0 **.offsetThreshold =1us # all Ethernet interfaces have 100 Mbps speed diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 6a19032d8ad..4750714c2e0 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -31,6 +31,8 @@ simsignal_t Gptp::receivedRateRatioSignal = cComponent::registerSignal("received simsignal_t Gptp::neighborRateRatioSignal = cComponent::registerSignal("neighborRateRatio"); simsignal_t Gptp::peerDelaySignal = cComponent::registerSignal("peerDelay"); simsignal_t Gptp::residenceTimeSignal = cComponent::registerSignal("residenceTime"); +simsignal_t Gptp::correctionFieldIngressSignal = cComponent::registerSignal("correctionFieldIngress"); +simsignal_t Gptp::correctionFieldEgressSignal = cComponent::registerSignal("correctionFieldEgress"); // MAC address: // 01-80-C2-00-00-02 for TimeSync (ieee 802.1as-2020, 13.3.1.2) @@ -74,7 +76,7 @@ void Gptp::initialize(int stage) throw cRuntimeError("Parameter inconsistency: MASTER_NODE with slave port"); auto nic = CHK(interfaceTable->findInterfaceByName(str)); slavePortId = nic->getInterfaceId(); - nic->subscribe(transmissionEndedSignal, this); + nic->subscribe(transmissionStartedSignal, this); nic->subscribe(receptionEndedSignal, this); } else if (gptpNodeType != MASTER_NODE) @@ -91,7 +93,7 @@ void Gptp::initialize(int stage) "master and slave port", p.c_str()); masterPortIds.insert(portId); - nic->subscribe(transmissionEndedSignal, this); + nic->subscribe(transmissionStartedSignal, this); nic->subscribe(receptionEndedSignal, this); } @@ -292,6 +294,7 @@ void Gptp::sendFollowUp(int portId, const GptpSync *sync, const clocktime_t &syn gptp->setCorrectionField(newCorrectionField); } emit(residenceTimeSignal, residenceTime.asSimTime()); + emit(correctionFieldEgressSignal, gptp->getCorrectionField().asSimTime()); gptp->getFollowUpInformationTLVForUpdate().setRateRatio(gmRateRatio); packet->insertAtFront(gptp); @@ -399,6 +402,8 @@ void Gptp::processFollowUp(Packet *packet, const GptpFollowUp *gptp) correctionField = gptp->getCorrectionField(); receivedRateRatio = gptp->getFollowUpInformationTLV().getRateRatio(); + emit(correctionFieldIngressSignal, correctionField.asSimTime()); + synchronize(); EV_INFO << "############## FOLLOW_UP ################################" << endl; @@ -645,7 +650,7 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj { Enter_Method("%s", cComponent::getSignalName(simSignal)); - if (simSignal != receptionEndedSignal && simSignal != transmissionEndedSignal) + if (simSignal != receptionEndedSignal && simSignal != transmissionStartedSignal) return; auto ethernetSignal = check_and_cast(obj); @@ -662,11 +667,11 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj if (simSignal == receptionEndedSignal) packet->addTagIfAbsent()->setArrivalClockTime(clock->getClockTime()); - else if (simSignal == transmissionEndedSignal) - handleTransmissionEndedSignal(gptp, source); + else if (simSignal == transmissionStartedSignal) + handleTransmissionStartedSignal(gptp, source); } -void Gptp::handleTransmissionEndedSignal(const GptpBase *gptp, cComponent *source) +void Gptp::handleTransmissionStartedSignal(const GptpBase *gptp, cComponent *source) { int portId = getContainingNicModule(check_and_cast(source))->getInterfaceId(); diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index f0fb2de195d..7e9e9cb6cbb 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -92,6 +92,8 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener static simsignal_t neighborRateRatioSignal; static simsignal_t peerDelaySignal; static simsignal_t residenceTimeSignal; + static simsignal_t correctionFieldIngressSignal; + static simsignal_t correctionFieldEgressSignal; public: static const MacAddress GPTP_MULTICAST_ADDRESS; @@ -114,7 +116,7 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener virtual void handleSelfMessage(cMessage *msg); - void handleTransmissionEndedSignal(const GptpBase *gptp, omnetpp::cComponent *source); + void handleTransmissionStartedSignal(const GptpBase *gptp, omnetpp::cComponent *source); const GptpBase *extractGptpHeader(Packet *packet); diff --git a/src/inet/linklayer/ieee8021as/Gptp.ned b/src/inet/linklayer/ieee8021as/Gptp.ned index 446db5c983e..9f68f8dc3fe 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.ned +++ b/src/inet/linklayer/ieee8021as/Gptp.ned @@ -35,7 +35,6 @@ simple Gptp like IGptp // These numbers must be enough large to prevent creating queue in MAC layer. // It means it should be larger than transmission time of message sent before double pDelayReqProcessingTime @unit(s) = default(8us); // processing time between arrived PDelayReq and send of PDelayResp - double followUpInterval @unit(s) = default(7us); @display("i=block/timer"); @signal[localTime](type=simtime_t); // as clocktime_t @@ -45,6 +44,8 @@ simple Gptp like IGptp @signal[neighborRateRatio](type=double); @signal[peerDelay](type=simtime_t); @signal[residenceTime](type=simtime_t); + @signal[correctionFieldIngress](type=simtime_t); + @signal[correctionFieldEgress](type=simtime_t); @statistic[localTime](record=vector; interpolationmode=linear); @statistic[timeDifference](record=vector; interpolationmode=linear); @statistic[gmRateRatio](record=vector; interpolationmode=sample-hold); @@ -52,6 +53,8 @@ simple Gptp like IGptp @statistic[neighborRateRatio](record=vector; interpolationmode=sample-hold); @statistic[peerDelay](record=vector; interpolationmode=sample-hold); @statistic[residenceTime](record=vector;interpolationmode=none); + @statistic[correctionFieldIngress](record=vector; interpolationmode=none); + @statistic[correctionFieldEgress](record=vector; interpolationmode=none); @selfMessageKinds(inet::GptpSelfMsgKind); gates: From 2c1e94f16fd14cccc872dea15ac81122eda71da3 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 22 May 2024 10:31:57 +0200 Subject: [PATCH 045/138] Use EthernetStreamingPhyLayer for time sync --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 2 +- src/inet/linklayer/ieee8021as/Gptp.cc | 11 ++++++----- src/inet/node/tsn/TsnClock.ned | 2 +- src/inet/node/tsn/TsnDevice.ned | 2 +- src/inet/node/tsn/TsnSwitch.ned | 2 +- .../transceiver/base/PacketTransmitterBase.cc | 1 + 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 9f1bac04db0..b22f6baa0f0 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -9,7 +9,7 @@ description = "abstract" # all oscillators have a constant drift rate (specified with a random distribution for each one) # except for the master clocks, which have a random drift rate #**.tsnClock*.clock.oscillator.typename = "RandomDriftOscillator" -**.clock.oscillator.typename="RandomDriftOscillator" +**.clock.oscillator.typename="IdealOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 4750714c2e0..aff0c27f91a 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -77,7 +77,7 @@ void Gptp::initialize(int stage) auto nic = CHK(interfaceTable->findInterfaceByName(str)); slavePortId = nic->getInterfaceId(); nic->subscribe(transmissionStartedSignal, this); - nic->subscribe(receptionEndedSignal, this); + nic->subscribe(receptionStartedSignal, this); } else if (gptpNodeType != MASTER_NODE) throw cRuntimeError("Parameter error: Missing slave port for %s", par("gptpNodeType").stringValue()); @@ -94,7 +94,7 @@ void Gptp::initialize(int stage) p.c_str()); masterPortIds.insert(portId); nic->subscribe(transmissionStartedSignal, this); - nic->subscribe(receptionEndedSignal, this); + nic->subscribe(receptionStartedSignal, this); } if (slavePortId != -1) { @@ -342,7 +342,7 @@ void Gptp::sendPdelayRespFollowUp(int portId, const GptpPdelayResp *resp) gptp->setCorrectionField(CLOCKTIME_ZERO); // We can set this to now, because this function is called directly - // after the transmissionEnded signal for the pdelayResp packet + // after the transmissionStarted signal for the pdelayResp packet // is received auto now = clock->getClockTime(); gptp->setResponseOriginTimestamp(now); // t3 @@ -650,7 +650,7 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj { Enter_Method("%s", cComponent::getSignalName(simSignal)); - if (simSignal != receptionEndedSignal && simSignal != transmissionStartedSignal) + if (simSignal != receptionStartedSignal && simSignal != transmissionStartedSignal) return; auto ethernetSignal = check_and_cast(obj); @@ -665,8 +665,9 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj if (gptp->getDomainNumber() != domainNumber) return; - if (simSignal == receptionEndedSignal) + if (simSignal == receptionStartedSignal) { packet->addTagIfAbsent()->setArrivalClockTime(clock->getClockTime()); + } else if (simSignal == transmissionStartedSignal) handleTransmissionStartedSignal(gptp, source); } diff --git a/src/inet/node/tsn/TsnClock.ned b/src/inet/node/tsn/TsnClock.ned index 83c789ffdc7..9a099d031f7 100644 --- a/src/inet/node/tsn/TsnClock.ned +++ b/src/inet/node/tsn/TsnClock.ned @@ -21,6 +21,6 @@ module TsnClock extends GptpMaster clock.typename = default("OscillatorBasedClock"); // master clocks cannot be set ethernet.typename = default("EthernetLayer"); // use Ethernet protocol layer outside of network interfaces eth[*].typename = default("LayeredEthernetInterface"); // switch to modular Ethernet interface - eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamingPhyLayer" : "EthernetPhyLayer"); // use packet streaming when cut-through switching is enabled + eth[*].phyLayer.typename = default("EthernetStreamingPhyLayer"); // we need packet streaming to use the receptionStartedSignal @display("i=device/card"); // change icon to emphasise hardware device } diff --git a/src/inet/node/tsn/TsnDevice.ned b/src/inet/node/tsn/TsnDevice.ned index d34e8978ca7..cdb01e22488 100644 --- a/src/inet/node/tsn/TsnDevice.ned +++ b/src/inet/node/tsn/TsnDevice.ned @@ -35,7 +35,7 @@ module TsnDevice extends StandardHost eth[*].typename = default("LayeredEthernetInterface"); // switch to modular Ethernet interface eth[*].macLayer.typename = default(hasFramePreemption ? "EthernetPreemptingMacLayer" : "EthernetMacLayer"); eth[*].macLayer.queue.typename = default(hasEgressTrafficShaping ? "Ieee8021qTimeAwareShaper" : (hasFramePreemption ? "" : "PacketQueue")); // use priority queue having multiple subqueues controlled by separate gates - eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamingPhyLayer" : (hasFramePreemption ? "EthernetPreemptingPhyLayer" : "EthernetPhyLayer")); // use packet streaming when cut-through switching is enabled + eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamThroughPhyLayer" : (hasFramePreemption ? "EthernetPreemptingPhyLayer" : (hasTimeSynchronization ? "EthernetStreamingPhyLayer" : "EthernetPhyLayer"))); // use packet streaming when cut-through switching is enabled bridging.typename = default(hasBridging ? "BridgingLayer" : ""); // switch to modular bridging bridging.interfaceRelay.typename = default(""); // disable frame relaying bridging.streamIdentifier.typename = default(hasOutgoingStreams || hasStreamRedundancy ? "StreamIdentifierLayer" : ""); // enable stream identification when stream redundancy is enabled diff --git a/src/inet/node/tsn/TsnSwitch.ned b/src/inet/node/tsn/TsnSwitch.ned index ade4d8f0a91..45af40eb1d6 100644 --- a/src/inet/node/tsn/TsnSwitch.ned +++ b/src/inet/node/tsn/TsnSwitch.ned @@ -35,7 +35,7 @@ module TsnSwitch extends EthernetSwitch eth[*].typename = default(hasCutthroughSwitching ? "EthernetCutthroughInterface" : "LayeredEthernetInterface"); // switch to modular Ethernet interface eth[*].macLayer.typename = default(hasFramePreemption ? "EthernetPreemptingMacLayer" : "EthernetMacLayer"); eth[*].macLayer.queue.typename = default(hasEgressTrafficShaping ? "Ieee8021qTimeAwareShaper" : "PacketQueue"); // use compound priority queue having multiple subqueues controlled by separate gates when egress traffic shaping is enabled - eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamThroughPhyLayer" : (hasFramePreemption ? "EthernetPreemptingPhyLayer" : "EthernetPhyLayer")); // use packet streaming when cut-through switching is enabled + eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamThroughPhyLayer" : (hasFramePreemption ? "EthernetPreemptingPhyLayer" : (hasTimeSynchronization ? "EthernetStreamingPhyLayer" : "EthernetPhyLayer"))); // use packet streaming when cut-through switching is enabled bridging.typename = default("BridgingLayer"); // switch to modular bridging bridging.directionReverser.cutthroughBarrier.typename = default(hasCutthroughSwitching ? "EthernetCutthroughBarrier" : ""); // enable cut-through barrier when cut-through switching is enabled bridging.streamIdentifier.typename = default(hasOutgoingStreams || hasStreamRedundancy ? "StreamIdentifierLayer" : ""); // enable stream identification when stream redundancy is enabled diff --git a/src/inet/protocolelement/transceiver/base/PacketTransmitterBase.cc b/src/inet/protocolelement/transceiver/base/PacketTransmitterBase.cc index c7b52fee95c..8b0b765c731 100644 --- a/src/inet/protocolelement/transceiver/base/PacketTransmitterBase.cc +++ b/src/inet/protocolelement/transceiver/base/PacketTransmitterBase.cc @@ -7,6 +7,7 @@ #include "inet/protocolelement/transceiver/base/PacketTransmitterBase.h" +#include "inet/linklayer/ieee8021as/GptpPacket_m.h" #include "inet/common/ModuleAccess.h" #include "inet/common/PacketEventTag.h" #include "inet/common/ProtocolTag_m.h" From caa5e66c279a2f3a21717878b8036ed779571624 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 28 May 2024 14:25:41 +0200 Subject: [PATCH 046/138] transmissionId --- src/inet/linklayer/ieee8021as/Gptp.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index aff0c27f91a..b4a40c1b166 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -78,6 +78,7 @@ void Gptp::initialize(int stage) slavePortId = nic->getInterfaceId(); nic->subscribe(transmissionStartedSignal, this); nic->subscribe(receptionStartedSignal, this); + nic->subscribe(receptionEndedSignal, this); } else if (gptpNodeType != MASTER_NODE) throw cRuntimeError("Parameter error: Missing slave port for %s", par("gptpNodeType").stringValue()); @@ -95,6 +96,8 @@ void Gptp::initialize(int stage) masterPortIds.insert(portId); nic->subscribe(transmissionStartedSignal, this); nic->subscribe(receptionStartedSignal, this); + nic->subscribe(receptionEndedSignal, this); + } if (slavePortId != -1) { @@ -650,7 +653,7 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj { Enter_Method("%s", cComponent::getSignalName(simSignal)); - if (simSignal != receptionStartedSignal && simSignal != transmissionStartedSignal) + if (simSignal != receptionStartedSignal && simSignal != transmissionStartedSignal && simSignal != receptionEndedSignal) return; auto ethernetSignal = check_and_cast(obj); @@ -666,7 +669,14 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj return; if (simSignal == receptionStartedSignal) { - packet->addTagIfAbsent()->setArrivalClockTime(clock->getClockTime()); + auto sequenceId = ethernetSignal->getTransmissionId(); + + // Save ingress time in map + } else if (simSignal == receptionEndedSignal) { + auto sequenceId = gptp->getSequenceId(); + + // Read ingress time from map + // Ad tag to packet } else if (simSignal == transmissionStartedSignal) handleTransmissionStartedSignal(gptp, source); From 311e72995cf4b28d45f4bc30e4b2016c07c71647 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 28 May 2024 15:10:08 +0200 Subject: [PATCH 047/138] use reception start signal instead of reception end signal --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 2 +- src/inet/linklayer/ieee8021as/Gptp.cc | 9 +++++---- src/inet/linklayer/ieee8021as/Gptp.h | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index b22f6baa0f0..9f1bac04db0 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -9,7 +9,7 @@ description = "abstract" # all oscillators have a constant drift rate (specified with a random distribution for each one) # except for the master clocks, which have a random drift rate #**.tsnClock*.clock.oscillator.typename = "RandomDriftOscillator" -**.clock.oscillator.typename="IdealOscillator" +**.clock.oscillator.typename="RandomDriftOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index b4a40c1b166..feed2489085 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -669,12 +669,13 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj return; if (simSignal == receptionStartedSignal) { - auto sequenceId = ethernetSignal->getTransmissionId(); - + auto transmissionId = ethernetSignal->getTransmissionId(); + auto ingressTime = clock->getClockTime(); + ingressTimeMap[transmissionId] = ingressTime; // Save ingress time in map } else if (simSignal == receptionEndedSignal) { - auto sequenceId = gptp->getSequenceId(); - + packet->addTagIfAbsent()->setArrivalClockTime(ingressTimeMap[ethernetSignal->getTransmissionId()]); + ingressTimeMap.erase(ethernetSignal->getTransmissionId()); // Read ingress time from map // Ad tag to packet } diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 7e9e9cb6cbb..3cb61bd6614 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -95,6 +95,9 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener static simsignal_t correctionFieldIngressSignal; static simsignal_t correctionFieldEgressSignal; + // Packet receive signals: + std::map ingressTimeMap; // Date: Tue, 4 Jun 2024 13:04:56 +0200 Subject: [PATCH 048/138] Multiple TSN Devices scenario --- .cproject | 48 +++++++++---------- .../gptp/Gptp_accumulation_error.ned | 44 +++++++++++++++++ .../tsn/timesynchronization/gptp/omnetpp.ini | 37 ++++++++++++++ src/inet/linklayer/ieee8021as/Gptp.cc | 2 +- 4 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned diff --git a/.cproject b/.cproject index 4d4e842402b..e32a5855aad 100644 --- a/.cproject +++ b/.cproject @@ -55,24 +55,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + @@ -128,24 +128,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + diff --git a/showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned b/showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned new file mode 100644 index 00000000000..60a5d152e3d --- /dev/null +++ b/showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned @@ -0,0 +1,44 @@ +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +package inet.showcases.tsn.timesynchronization.gptp; + +import inet.common.scenario.ScenarioManager; +import inet.networks.base.TsnNetworkBase; +import inet.node.ethernet.EthernetLink; +import inet.node.tsn.TsnClock; +import inet.node.tsn.TsnDevice; +import inet.node.tsn.TsnSwitch; + +network OneMasterClockGptpAccumulationError extends TsnNetworkBase +{ + parameters: + int numDevices; + submodules: + scenarioManager: ScenarioManager; + tsnClock: TsnClock { + @display("p=500,150"); + } + tsnSwitch: TsnSwitch { + @display("p=500,300"); + } + tsnDevice1[numDevices]: TsnDevice { + @display("p=400,450,col,100"); + } + tsnDevice2[numDevices]: TsnDevice { + @display("p=600,450,col,100"); + } + connections: + tsnClock.ethg++ <--> EthernetLink <--> tsnSwitch.ethg++; + + tsnSwitch.ethg++ <--> EthernetLink <--> tsnDevice1[0].ethg++; + for i=0..sizeof(tsnDevice1)-2 { + tsnDevice1[i].ethg++ <--> EthernetLink <--> tsnDevice1[i+1].ethg++; + } + + tsnSwitch.ethg++ <--> EthernetLink <--> tsnDevice2[0].ethg++; + for i=0..sizeof(tsnDevice2)-2 { + tsnDevice2[i].ethg++ <--> EthernetLink <--> tsnDevice2[i+1].ethg++; + } +} diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 4eac506eb9e..55d2a9ab984 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -18,6 +18,7 @@ description = "abstract" **.kp=7 **.ki=0 **.offsetThreshold =1us +**.numDevices=5 # all Ethernet interfaces have 100 Mbps speed *.*.eth[*].bitrate = 100Mbps @@ -25,6 +26,42 @@ description = "abstract" *.visualizer.typename = "IntegratedMultiCanvasVisualizer" *.visualizer.infoVisualizer[*].displayInfos = true +[Config OneMasterClockAccumulationError] +network = OneMasterClockGptpAccumulationError +description = "Basic tree topology with one master clock and multiple devices" + +# Uncomment these lines to test out of band time synchronization using the SimpleClockSynchronizer instead of gPTP +#*.tsnClock.gptp.typename = "" +#*.*.gptp.typename = "SimpleClockSynchronizer" +#*.*.gptp.synchronizationInterval = 0.125s +#*.*.gptp.masterClockModule = "^.^.tsnClock.clock" +#*.*.gptp.slaveClockModule = "^.clock" + +# TSN clock gPTP master ports +*.tsnClock.gptp.masterPorts = ["eth0"] + +# TSN switch gPTP bridge master ports +*.tsnSwitch.gptp.masterPorts = ["eth1", "eth2"] + +# Set all reference clocks to master clock so the time difference can be visualized +**.referenceClock = "tsnClock.clock" + +# data link visualizer displays gPTP time synchronization packets +*.visualizer.dataLinkVisualizer[0].displayLinks = true +*.visualizer.dataLinkVisualizer[0].activityLevel = "protocol" +*.visualizer.dataLinkVisualizer[0].packetFilter = "GptpSync" +*.visualizer.dataLinkVisualizer[0].lineColor = "blue2" + +*.visualizer.numInfoVisualizers = 3 +*.visualizer.infoVisualizer[0].modules = "*.tsnClock.clock" +*.tsnClock.clock.displayStringTextFormat = "time: %T" +*.visualizer.infoVisualizer[1].modules = "*.tsnSwitch.clock" +*.visualizer.infoVisualizer[1].placementHint = "topLeft" +*.visualizer.infoVisualizer[2].modules = "*.tsnDevice*.clock" +*.visualizer.infoVisualizer[2].placementHint = "bottom" +*.tsnDevice*.clock.displayStringTextFormat = "diff: %d" +*.tsnSwitch.clock.displayStringTextFormat = "diff: %d" + [Config OneMasterClock] network = OneMasterClockGptpShowcase description = "Basic tree topology with one master clock" diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 2c75cb7ab41..f2317c0cf6c 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -612,7 +612,7 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU nrrCalculationSetCurrent++; } - neighborRateRatio = 1; +// neighborRateRatio = 1; // See 11.2.19.3.4 computePropTime() and Figure11-1 in IEEE 802.1AS-2020 auto t4 = pDelayRespIngressTimestamp; From 924d819be5acb85cf7189b9e50bca21307b06e69 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Tue, 4 Jun 2024 14:44:18 +0200 Subject: [PATCH 049/138] Revise multiple switches scenario --- .../timesynchronization/gptp/GptpShowcase.anf | 638 +++++++++++++++++- .../gptp/Gptp_accumulation_error.ned | 23 +- .../tsn/timesynchronization/gptp/omnetpp.ini | 11 +- 3 files changed, 626 insertions(+), 46 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 09c633dfd69..0149daccc4c 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -7,10 +7,12 @@ + + - - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned b/showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned index 60a5d152e3d..cc2b28c5335 100644 --- a/showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned +++ b/showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned @@ -14,31 +14,22 @@ import inet.node.tsn.TsnSwitch; network OneMasterClockGptpAccumulationError extends TsnNetworkBase { parameters: - int numDevices; + int numSwitch; submodules: scenarioManager: ScenarioManager; tsnClock: TsnClock { @display("p=500,150"); } - tsnSwitch: TsnSwitch { - @display("p=500,300"); - } - tsnDevice1[numDevices]: TsnDevice { + tsnSwitch[numSwitch]: TsnSwitch { @display("p=400,450,col,100"); } - tsnDevice2[numDevices]: TsnDevice { + tsnDevice: TsnDevice { @display("p=600,450,col,100"); } connections: - tsnClock.ethg++ <--> EthernetLink <--> tsnSwitch.ethg++; - - tsnSwitch.ethg++ <--> EthernetLink <--> tsnDevice1[0].ethg++; - for i=0..sizeof(tsnDevice1)-2 { - tsnDevice1[i].ethg++ <--> EthernetLink <--> tsnDevice1[i+1].ethg++; - } - - tsnSwitch.ethg++ <--> EthernetLink <--> tsnDevice2[0].ethg++; - for i=0..sizeof(tsnDevice2)-2 { - tsnDevice2[i].ethg++ <--> EthernetLink <--> tsnDevice2[i+1].ethg++; + tsnClock.ethg++ <--> EthernetLink <--> tsnSwitch[0].ethg++; + for i=0..sizeof(tsnSwitch)-2 { + tsnSwitch[i].ethg++ <--> EthernetLink <--> tsnSwitch[i+1].ethg++; } + tsnSwitch[numSwitch-1].ethg++ <--> EthernetLink <--> tsnDevice.ethg++; } diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 55d2a9ab984..6e6e9a7a8e4 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -8,6 +8,7 @@ description = "abstract" # all oscillators have a constant drift rate (specified with a random distribution for each one) # except for the master clocks, which have a random drift rate +#**.clock.oscillator.typename = "RandomDriftOscillator" **.clock.oscillator.typename = "RandomDriftOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) @@ -15,10 +16,10 @@ description = "abstract" **.oscillator.driftRateChangeUpperLimit = 100ppm **.oscillator.driftRateChangeLowerLimit = -100ppm -**.kp=7 -**.ki=0 +**.kp=8 +**.ki=7 **.offsetThreshold =1us -**.numDevices=5 + # all Ethernet interfaces have 100 Mbps speed *.*.eth[*].bitrate = 100Mbps @@ -37,11 +38,13 @@ description = "Basic tree topology with one master clock and multiple devices" #*.*.gptp.masterClockModule = "^.^.tsnClock.clock" #*.*.gptp.slaveClockModule = "^.clock" +**.numSwitch=100 + # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] # TSN switch gPTP bridge master ports -*.tsnSwitch.gptp.masterPorts = ["eth1", "eth2"] +*.tsnSwitch[*].gptp.masterPorts = ["eth1"] # Set all reference clocks to master clock so the time difference can be visualized **.referenceClock = "tsnClock.clock" From 670d2f5ab78d803931234eae24b277a2cce165d4 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Sun, 9 Jun 2024 15:00:50 +0200 Subject: [PATCH 050/138] added iSettableClock.h --- .cproject | 48 +++++++++---------- .../tsn/timesynchronization/gptp/omnetpp.ini | 4 +- src/inet/clock/model/iSettableClock.h | 19 ++++++++ 3 files changed, 45 insertions(+), 26 deletions(-) create mode 100644 src/inet/clock/model/iSettableClock.h diff --git a/.cproject b/.cproject index e32a5855aad..4abad82050f 100644 --- a/.cproject +++ b/.cproject @@ -55,24 +55,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + @@ -128,24 +128,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 6e6e9a7a8e4..bbebbadc245 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -1,5 +1,5 @@ [General] -seed-set = 0 +seed-set = -123 sim-time-limit = 20s description = "abstract" @@ -38,7 +38,7 @@ description = "Basic tree topology with one master clock and multiple devices" #*.*.gptp.masterClockModule = "^.^.tsnClock.clock" #*.*.gptp.slaveClockModule = "^.clock" -**.numSwitch=100 +**.numSwitch=10 # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] diff --git a/src/inet/clock/model/iSettableClock.h b/src/inet/clock/model/iSettableClock.h new file mode 100644 index 00000000000..c88049b6c58 --- /dev/null +++ b/src/inet/clock/model/iSettableClock.h @@ -0,0 +1,19 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_SETTABLECLOCK_H +#define __INET_SETTABLECLOCK_H + +#include "inet/clock/model/OscillatorBasedClock.h" +#include "inet/common/scenario/IScriptable.h" + +namespace inet { + +} // namespace inet + +#endif + From 0fa6e1c11bbb6cd446af7c614af9eec394b05b6e Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Sun, 9 Jun 2024 15:05:39 +0200 Subject: [PATCH 051/138] Create a parent class --- src/inet/clock/model/ISettableClock.cc | 29 ++++++++++++++++++++++++++ src/inet/clock/model/ISettableClock.h | 29 ++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/inet/clock/model/ISettableClock.cc create mode 100644 src/inet/clock/model/ISettableClock.h diff --git a/src/inet/clock/model/ISettableClock.cc b/src/inet/clock/model/ISettableClock.cc new file mode 100644 index 00000000000..ab74cb6c48b --- /dev/null +++ b/src/inet/clock/model/ISettableClock.cc @@ -0,0 +1,29 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#include "ISettableClock.h" + +namespace inet { + +ISettableClock::ISettableClock() { + // TODO Auto-generated constructor stub + +} + +ISettableClock::~ISettableClock() { + // TODO Auto-generated destructor stub +} + +} /* namespace inet */ diff --git a/src/inet/clock/model/ISettableClock.h b/src/inet/clock/model/ISettableClock.h new file mode 100644 index 00000000000..18c2bc294e4 --- /dev/null +++ b/src/inet/clock/model/ISettableClock.h @@ -0,0 +1,29 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#ifndef INET_CLOCK_MODEL_ISETTABLECLOCK_H_ +#define INET_CLOCK_MODEL_ISETTABLECLOCK_H_ + +namespace inet { + +class ISettableClock { +public: + ISettableClock(); + virtual ~ISettableClock(); +}; + +} /* namespace inet */ + +#endif /* INET_CLOCK_MODEL_ISETTABLECLOCK_H_ */ From 3e4218c956d9016c52f0c7841024d3450de2f220 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Sun, 9 Jun 2024 15:08:24 +0200 Subject: [PATCH 052/138] Merge conflicts --- src/inet/clock/model/iSettableClock.h | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/inet/clock/model/iSettableClock.h diff --git a/src/inet/clock/model/iSettableClock.h b/src/inet/clock/model/iSettableClock.h deleted file mode 100644 index c88049b6c58..00000000000 --- a/src/inet/clock/model/iSettableClock.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (C) 2020 OpenSim Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - - -#ifndef __INET_SETTABLECLOCK_H -#define __INET_SETTABLECLOCK_H - -#include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/common/scenario/IScriptable.h" - -namespace inet { - -} // namespace inet - -#endif - From d40f8f6d8fbd259002de7c252f0e0f00eba8856d Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Sun, 9 Jun 2024 15:37:51 +0200 Subject: [PATCH 053/138] added iSettableClock.h and *.cc file --- src/inet/clock/model/ISettableClock.cc | 41 +++++++++++++------------- src/inet/clock/model/ISettableClock.h | 36 ++++++++++------------ src/inet/clock/model/PiClock.h | 6 ++-- src/inet/clock/model/SettableClock.h | 5 ++-- 4 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/inet/clock/model/ISettableClock.cc b/src/inet/clock/model/ISettableClock.cc index ab74cb6c48b..8aa3e852db1 100644 --- a/src/inet/clock/model/ISettableClock.cc +++ b/src/inet/clock/model/ISettableClock.cc @@ -1,29 +1,30 @@ // -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// -#include "ISettableClock.h" +#include "inet/clock/model/ISettableClock.h" namespace inet { -ISettableClock::ISettableClock() { - // TODO Auto-generated constructor stub -} + Define_Module(ISettableClock); -ISettableClock::~ISettableClock() { - // TODO Auto-generated destructor stub -} + void ISettableClock::initialize(int stage) + { + OscillatorBasedClock::initialize(stage); + if (stage == INITSTAGE_LOCAL) { + const char *text = par("defaultOverdueClockEventHandlingMode"); + if (!strcmp(text, "execute")) + defaultOverdueClockEventHandlingMode = EXECUTE; + else if (!strcmp(text, "skip")) + defaultOverdueClockEventHandlingMode = SKIP; + else if (!strcmp(text, "error")) + defaultOverdueClockEventHandlingMode = ERROR; + else + throw cRuntimeError("Unknown defaultOverdueClockEventHandlingMode parameter value"); + } + } } /* namespace inet */ diff --git a/src/inet/clock/model/ISettableClock.h b/src/inet/clock/model/ISettableClock.h index 18c2bc294e4..5080e96b3bb 100644 --- a/src/inet/clock/model/ISettableClock.h +++ b/src/inet/clock/model/ISettableClock.h @@ -1,29 +1,25 @@ // -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// -#ifndef INET_CLOCK_MODEL_ISETTABLECLOCK_H_ -#define INET_CLOCK_MODEL_ISETTABLECLOCK_H_ +#ifndef __INET_ISETTABLECLOCK_H +#define __INET_ISETTABLECLOCK_H + +#include "inet/clock/model/OscillatorBasedClock.h" +#include "inet/common/scenario/IScriptable.h" namespace inet { -class ISettableClock { -public: - ISettableClock(); - virtual ~ISettableClock(); + class INET_API ISettableClock : public OscillatorBasedClock, public IScriptable { + protected: + virtual void initialize(int stage) override; + + public: + virtual void setClockTime(clocktime_t time); }; } /* namespace inet */ -#endif /* INET_CLOCK_MODEL_ISETTABLECLOCK_H_ */ +#endif diff --git a/src/inet/clock/model/PiClock.h b/src/inet/clock/model/PiClock.h index 89854ac2597..e5cbbbd09f7 100644 --- a/src/inet/clock/model/PiClock.h +++ b/src/inet/clock/model/PiClock.h @@ -7,13 +7,11 @@ #ifndef __INET_PICLOCK_H #define __INET_PICLOCK_H -#include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/clock/model/SettableClock.h" -#include "inet/common/scenario/IScriptable.h" +#include "inet/clock/model/ISettableClock.h" namespace inet { -class INET_API PiClock : public SettableClock +class INET_API PiClock : public ISettableClock { protected: static simsignal_t driftSignal; diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index ee407263321..aaf181d5fad 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -8,12 +8,11 @@ #ifndef __INET_SETTABLECLOCK_H #define __INET_SETTABLECLOCK_H -#include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/common/scenario/IScriptable.h" +#include "inet/clock/model/ISettableClock.h" namespace inet { -class INET_API SettableClock : public OscillatorBasedClock, public IScriptable +class INET_API SettableClock : public ISettableClock { protected: OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; From ab7d934e95d381f466685c9316eed8258ad10f21 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Sun, 9 Jun 2024 15:40:30 +0200 Subject: [PATCH 054/138] Update ISettableClock --- src/inet/clock/model/ISettableClock.cc | 8 +------- src/inet/clock/model/ISettableClock.h | 9 ++++++--- src/inet/clock/model/SettableClock.h | 5 +++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/inet/clock/model/ISettableClock.cc b/src/inet/clock/model/ISettableClock.cc index ab74cb6c48b..fa8103fe5af 100644 --- a/src/inet/clock/model/ISettableClock.cc +++ b/src/inet/clock/model/ISettableClock.cc @@ -17,13 +17,7 @@ namespace inet { -ISettableClock::ISettableClock() { - // TODO Auto-generated constructor stub + void setClockTime(clocktime_t newClockTime){} -} - -ISettableClock::~ISettableClock() { - // TODO Auto-generated destructor stub -} } /* namespace inet */ diff --git a/src/inet/clock/model/ISettableClock.h b/src/inet/clock/model/ISettableClock.h index 18c2bc294e4..5175b467fb4 100644 --- a/src/inet/clock/model/ISettableClock.h +++ b/src/inet/clock/model/ISettableClock.h @@ -16,12 +16,15 @@ #ifndef INET_CLOCK_MODEL_ISETTABLECLOCK_H_ #define INET_CLOCK_MODEL_ISETTABLECLOCK_H_ +#include "inet/clock/base/ClockBase.h" + namespace inet { -class ISettableClock { +class ISettableClock : public ClockBase { + public: - ISettableClock(); - virtual ~ISettableClock(); + + virtual void setClockTime(clocktime_t newClockTime) = 0; }; } /* namespace inet */ diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index ee407263321..9239f79c838 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -10,10 +10,11 @@ #include "inet/clock/model/OscillatorBasedClock.h" #include "inet/common/scenario/IScriptable.h" +#include "inet/clock/model/ISettableClock.h" namespace inet { -class INET_API SettableClock : public OscillatorBasedClock, public IScriptable +class INET_API SettableClock : public OscillatorBasedClock, public IScriptable, public ISettableClock { protected: OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; @@ -36,7 +37,7 @@ class INET_API SettableClock : public OscillatorBasedClock, public IScriptable * Sets the clock time immediately to the given value. Greater than 1 oscillator * compensation factor means the clock measures time faster. */ - virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator); + virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator) override; }; } // namespace inet From cae5e71e476497d14db41c0abb7a9e0521939eb9 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Sun, 9 Jun 2024 16:50:24 +0200 Subject: [PATCH 055/138] Update ISettableClock and other related files --- .cproject | 48 +++++++++++++------------- src/inet/clock/model/ISettableClock.cc | 10 +++++- src/inet/clock/model/ISettableClock.h | 20 +++++++++-- src/inet/clock/model/PiClock.cc | 2 +- src/inet/clock/model/SettableClock.h | 4 +-- src/inet/linklayer/ieee8021as/Gptp.cc | 3 +- 6 files changed, 54 insertions(+), 33 deletions(-) diff --git a/.cproject b/.cproject index 4abad82050f..79281f18da5 100644 --- a/.cproject +++ b/.cproject @@ -55,24 +55,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + @@ -128,24 +128,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + diff --git a/src/inet/clock/model/ISettableClock.cc b/src/inet/clock/model/ISettableClock.cc index 84fb553868d..78cc4a02e19 100644 --- a/src/inet/clock/model/ISettableClock.cc +++ b/src/inet/clock/model/ISettableClock.cc @@ -12,12 +12,20 @@ namespace inet { void ISettableClock::initialize(int stage) { - ISettableClock::initialize(stage); + OscillatorBasedClock::initialize(stage); if (stage == INITSTAGE_LOCAL) { } } + OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event){} + + simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t){} + + void processCommand(const cXMLElement& node){} + + virtual ppm getOscillatorCompensation() const {} void setClockTime(clocktime_t newClockTime){} + void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator){} } /* namespace inet */ diff --git a/src/inet/clock/model/ISettableClock.h b/src/inet/clock/model/ISettableClock.h index 88bb0399293..cdf29218855 100644 --- a/src/inet/clock/model/ISettableClock.h +++ b/src/inet/clock/model/ISettableClock.h @@ -10,17 +10,31 @@ #include "inet/clock/model/OscillatorBasedClock.h" #include "inet/common/scenario/IScriptable.h" -#include "inet/clock/base/ClockBase.h" - namespace inet { -class ISettableClock : public ClockBase { +class ISettableClock : public OscillatorBasedClock, public IScriptable { protected: + OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; + ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time + // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time + + protected: virtual void initialize(int stage) override; + virtual OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event) const = 0; + virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t) = 0; + + // IScriptable implementation + virtual void processCommand(const cXMLElement& node) override; + + public: + virtual ppm getOscillatorCompensation() const override { return oscillatorCompensation; } + + public: virtual void setClockTime(clocktime_t newClockTime) = 0; + virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator) = 0; }; } /* namespace inet */ diff --git a/src/inet/clock/model/PiClock.cc b/src/inet/clock/model/PiClock.cc index c8f6b2e4c43..71a061142f9 100644 --- a/src/inet/clock/model/PiClock.cc +++ b/src/inet/clock/model/PiClock.cc @@ -20,7 +20,7 @@ simsignal_t PiClock::driftSignal = cComponent::registerSignal("drift"); void PiClock::initialize(int stage) { - SettableClock::initialize(stage); + ISettableClock::initialize(stage); if (stage == INITSTAGE_LOCAL) { offsetThreshold = &par("offsetThreshold"); kp = par("kp"); diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index 9239f79c838..89d1e24f4e7 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -8,13 +8,11 @@ #ifndef __INET_SETTABLECLOCK_H #define __INET_SETTABLECLOCK_H -#include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/common/scenario/IScriptable.h" #include "inet/clock/model/ISettableClock.h" namespace inet { -class INET_API SettableClock : public OscillatorBasedClock, public IScriptable, public ISettableClock +class INET_API SettableClock : public ISettableClock { protected: OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index f2317c0cf6c..584cfd870d2 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -10,6 +10,7 @@ #include "GptpPacket_m.h" #include "inet/clock/model/PiClock.h" #include "inet/clock/model/SettableClock.h" +#include "inet/clock/model/ISettableClock.h" #include "inet/common/IProtocolRegistrationListener.h" #include "inet/common/clock/ClockUserModuleBase.h" #include "inet/linklayer/common/InterfaceTag_m.h" @@ -442,7 +443,7 @@ void Gptp::synchronize() // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio clocktime_t newTime = preciseOriginTimestamp + correctionField + gmRateRatio * (meanLinkDelay + residenceTime); - auto piControlClock = check_and_cast(clock.get()); + auto piControlClock = check_and_cast(clock.get()); // Only change the oscillator if we have new information about our nrr // TODO: We should change this to a clock servo model in the future anyways! From 4192593cf845b540c6b21f22323dfc631084e850 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Sun, 9 Jun 2024 18:10:02 +0200 Subject: [PATCH 056/138] Update ISettableClock and other related files with Define_Module bug --- src/inet/clock/model/ISettableClock.cc | 17 ++++++----------- src/inet/clock/model/ISettableClock.h | 15 +++------------ src/inet/clock/model/ISettableClock.ned | 22 ++++++++++++++++++++++ src/inet/clock/model/PiClock.h | 2 +- src/inet/clock/model/SettableClock.cc | 1 - src/inet/clock/model/SettableClock.h | 6 +++--- 6 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 src/inet/clock/model/ISettableClock.ned diff --git a/src/inet/clock/model/ISettableClock.cc b/src/inet/clock/model/ISettableClock.cc index 78cc4a02e19..e14fd146853 100644 --- a/src/inet/clock/model/ISettableClock.cc +++ b/src/inet/clock/model/ISettableClock.cc @@ -5,27 +5,22 @@ // #include "inet/clock/model/ISettableClock.h" - -Define_Module(ISettableClock); +#include "inet/clock/oscillator/ConstantDriftOscillator.h" +#include "inet/common/XMLUtils.h" namespace inet { +Define_Module(ISettableClock); + void ISettableClock::initialize(int stage) { - OscillatorBasedClock::initialize(stage); - if (stage == INITSTAGE_LOCAL) { - } } - OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event){} - simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t){} - void processCommand(const cXMLElement& node){} - virtual ppm getOscillatorCompensation() const {} - void setClockTime(clocktime_t newClockTime){} - void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator){} + clocktime_t setClockTime(clocktime_t newClockTime){} + } /* namespace inet */ diff --git a/src/inet/clock/model/ISettableClock.h b/src/inet/clock/model/ISettableClock.h index cdf29218855..d1092551d55 100644 --- a/src/inet/clock/model/ISettableClock.h +++ b/src/inet/clock/model/ISettableClock.h @@ -19,22 +19,13 @@ class ISettableClock : public OscillatorBasedClock, public IScriptable { ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time - protected: +protected: virtual void initialize(int stage) override; - virtual OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event) const = 0; - virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t) = 0; - - // IScriptable implementation - virtual void processCommand(const cXMLElement& node) override; - - public: - virtual ppm getOscillatorCompensation() const override { return oscillatorCompensation; } - public: - virtual void setClockTime(clocktime_t newClockTime) = 0; - virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator) = 0; + + virtual clocktime_t setClockTime(clocktime_t newClockTime) = 0; }; } /* namespace inet */ diff --git a/src/inet/clock/model/ISettableClock.ned b/src/inet/clock/model/ISettableClock.ned new file mode 100644 index 00000000000..53d176fe692 --- /dev/null +++ b/src/inet/clock/model/ISettableClock.ned @@ -0,0 +1,22 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +package inet.clock.model; + +// +// Models a clock which can be set to a different clock time. The clock time +// can be set from C++ or using a command +// in a `ScenarioManager` script. +// +// @see ~ScenarioManager +// +module ISettableClock extends OscillatorBasedClock +{ + parameters: + @class(ISettableClock); +} + diff --git a/src/inet/clock/model/PiClock.h b/src/inet/clock/model/PiClock.h index e5cbbbd09f7..c855b1a5d15 100644 --- a/src/inet/clock/model/PiClock.h +++ b/src/inet/clock/model/PiClock.h @@ -50,7 +50,7 @@ class INET_API PiClock : public ISettableClock * - newClockTime, if the servo is in the initial phase * - the old clock time, if the servo is in the PI phase (only the oscillator compensation is updated) */ - clocktime_t setClockTime(clocktime_t newClockTime); + clocktime_t setClockTime(clocktime_t newClockTime) override; }; } // namespace inet diff --git a/src/inet/clock/model/SettableClock.cc b/src/inet/clock/model/SettableClock.cc index 95123a15c3c..e9eeab9cce2 100644 --- a/src/inet/clock/model/SettableClock.cc +++ b/src/inet/clock/model/SettableClock.cc @@ -6,7 +6,6 @@ #include "inet/clock/model/SettableClock.h" - #include "inet/clock/oscillator/ConstantDriftOscillator.h" #include "inet/common/XMLUtils.h" diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index 89d1e24f4e7..660ddc66308 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -22,8 +22,8 @@ class INET_API SettableClock : public ISettableClock protected: virtual void initialize(int stage) override; - virtual OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event) const; - virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); + virtual OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event) const ; + virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t) ; // IScriptable implementation virtual void processCommand(const cXMLElement& node) override; @@ -35,7 +35,7 @@ class INET_API SettableClock : public ISettableClock * Sets the clock time immediately to the given value. Greater than 1 oscillator * compensation factor means the clock measures time faster. */ - virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator) override; + virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator); }; } // namespace inet From 5d504a7a08976ee8118b97ed10c6d33850efcf3c Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Sun, 9 Jun 2024 18:12:40 +0200 Subject: [PATCH 057/138] polished iSettableClock.h and *.cc file --- src/inet/clock/model/ISettableClock.cc | 9 ++------- src/inet/clock/model/ISettableClock.h | 12 +++++++----- src/inet/clock/model/PiClock.cc | 2 +- src/inet/clock/model/SettableClock.h | 15 ++++++--------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/inet/clock/model/ISettableClock.cc b/src/inet/clock/model/ISettableClock.cc index 84fb553868d..a45821352c4 100644 --- a/src/inet/clock/model/ISettableClock.cc +++ b/src/inet/clock/model/ISettableClock.cc @@ -6,18 +6,13 @@ #include "inet/clock/model/ISettableClock.h" -Define_Module(ISettableClock); namespace inet { + Define_Module(ISettableClock); void ISettableClock::initialize(int stage) { - ISettableClock::initialize(stage); - if (stage == INITSTAGE_LOCAL) { - - } + OscillatorBasedClock::initialize(stage); } - void setClockTime(clocktime_t newClockTime){} - } /* namespace inet */ diff --git a/src/inet/clock/model/ISettableClock.h b/src/inet/clock/model/ISettableClock.h index 88bb0399293..6809942de15 100644 --- a/src/inet/clock/model/ISettableClock.h +++ b/src/inet/clock/model/ISettableClock.h @@ -10,17 +10,19 @@ #include "inet/clock/model/OscillatorBasedClock.h" #include "inet/common/scenario/IScriptable.h" -#include "inet/clock/base/ClockBase.h" - namespace inet { -class ISettableClock : public ClockBase { +class ISettableClock : public OscillatorBasedClock, public IScriptable { + +protected: + OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; + ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time + // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time + protected: virtual void initialize(int stage) override; -public: - virtual void setClockTime(clocktime_t newClockTime) = 0; }; } /* namespace inet */ diff --git a/src/inet/clock/model/PiClock.cc b/src/inet/clock/model/PiClock.cc index c8f6b2e4c43..ff98a8a71d0 100644 --- a/src/inet/clock/model/PiClock.cc +++ b/src/inet/clock/model/PiClock.cc @@ -20,7 +20,7 @@ simsignal_t PiClock::driftSignal = cComponent::registerSignal("drift"); void PiClock::initialize(int stage) { - SettableClock::initialize(stage); +// ISettableClock::initialize(stage); if (stage == INITSTAGE_LOCAL) { offsetThreshold = &par("offsetThreshold"); kp = par("kp"); diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index 9239f79c838..36eda8a133e 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -8,18 +8,16 @@ #ifndef __INET_SETTABLECLOCK_H #define __INET_SETTABLECLOCK_H -#include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/common/scenario/IScriptable.h" #include "inet/clock/model/ISettableClock.h" namespace inet { -class INET_API SettableClock : public OscillatorBasedClock, public IScriptable, public ISettableClock +class INET_API SettableClock : public ISettableClock { - protected: - OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; - ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time - // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time +// protected: +// OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; +// ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time +// // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time protected: virtual void initialize(int stage) override; @@ -37,10 +35,9 @@ class INET_API SettableClock : public OscillatorBasedClock, public IScriptable, * Sets the clock time immediately to the given value. Greater than 1 oscillator * compensation factor means the clock measures time faster. */ - virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator) override; + virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator); }; } // namespace inet #endif - From 6fbbd1f86495851a44eeb641676e9b051f9dcd91 Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Tue, 11 Jun 2024 14:59:58 +0200 Subject: [PATCH 058/138] ini file example --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index bbebbadc245..cd0d9e8cff8 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -39,6 +39,7 @@ description = "Basic tree topology with one master clock and multiple devices" #*.*.gptp.slaveClockModule = "^.clock" **.numSwitch=10 +**.clock.typename = "SettableClock" # clock type # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] @@ -122,7 +123,7 @@ network = TwoMasterClocksTreeGptpShowcase description = "Extended tree topology with two master clocks" # TSN clock2 has a pi clock -*.tsnClock2.clock.typename = "PiClock" +**.clock.typename = "PiClock" *.tsnClock1.gptp.typename = "Gptp" *.tsnClock1.gptp.clockModule = "tsnClock1.clock" From 07b53c1f2175177ac349d106a3ec61065f7769ba Mon Sep 17 00:00:00 2001 From: DanYuDE Date: Tue, 18 Jun 2024 10:49:29 +0200 Subject: [PATCH 059/138] Refactor setClockTime to adjustClockTime and update related functionality --- .cproject | 48 +++++++------- .../tsn/timesynchronization/gptp/omnetpp.ini | 2 +- .../clock/SimpleClockSynchronizer.cc | 2 +- src/inet/clock/model/PiClock.cc | 4 +- src/inet/clock/model/ServoClockBase.cc | 39 +++++++++++ src/inet/clock/model/ServoClockBase.h | 14 ++-- src/inet/clock/model/SettableClock.cc | 66 +++++++++++-------- src/inet/clock/model/SettableClock.h | 6 +- src/inet/linklayer/ieee8021as/Gptp.cc | 7 +- 9 files changed, 119 insertions(+), 69 deletions(-) create mode 100644 src/inet/clock/model/ServoClockBase.cc diff --git a/.cproject b/.cproject index 4abad82050f..60adfe7f229 100644 --- a/.cproject +++ b/.cproject @@ -55,24 +55,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + @@ -128,24 +128,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index cd0d9e8cff8..de18e1f1e0a 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -39,7 +39,7 @@ description = "Basic tree topology with one master clock and multiple devices" #*.*.gptp.slaveClockModule = "^.clock" **.numSwitch=10 -**.clock.typename = "SettableClock" # clock type +**.clock.typename = "PiClock" # clock type # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] diff --git a/src/inet/applications/clock/SimpleClockSynchronizer.cc b/src/inet/applications/clock/SimpleClockSynchronizer.cc index 4969a5e6da4..613053a909f 100644 --- a/src/inet/applications/clock/SimpleClockSynchronizer.cc +++ b/src/inet/applications/clock/SimpleClockSynchronizer.cc @@ -57,7 +57,7 @@ void SimpleClockSynchronizer::synchronizeSlaveClock() ppm oscillatorCompensation = unit(getCurrentRelativeTickLength(slaveClock.get()) / getCurrentRelativeTickLength(masterClock.get()) * (1 + unit(masterOscillatorBasedClock->getOscillatorCompensation()).get()) * (1 + unit(ppm(synchronizationOscillatorCompensationErrorParameter->doubleValue())).get()) - 1); - slaveClock->setClockTime(clockTime, oscillatorCompensation, true); +// slaveClock->setClockTime(clockTime, oscillatorCompensation, true); } void SimpleClockSynchronizer::scheduleSynchronizationTimer() diff --git a/src/inet/clock/model/PiClock.cc b/src/inet/clock/model/PiClock.cc index 6caf267e944..e9b013ea34f 100644 --- a/src/inet/clock/model/PiClock.cc +++ b/src/inet/clock/model/PiClock.cc @@ -39,7 +39,7 @@ void PiClock::initialize(int stage) } } -clocktime_t PiClock::setClockTime(clocktime_t newClockTime) +void PiClock::adjustClockTime(clocktime_t newClockTime) { Enter_Method("setClockTime"); clocktime_t oldClockTime = getClockTime(); @@ -115,7 +115,7 @@ clocktime_t PiClock::setClockTime(clocktime_t newClockTime) } } emit(driftSignal, drift.get()); - return getClockTime(); + getClockTime(); } } // namespace inet diff --git a/src/inet/clock/model/ServoClockBase.cc b/src/inet/clock/model/ServoClockBase.cc new file mode 100644 index 00000000000..526c9493b2e --- /dev/null +++ b/src/inet/clock/model/ServoClockBase.cc @@ -0,0 +1,39 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +#include "inet/clock/model/ServoClockBase.h" +#include "inet/clock/oscillator/ConstantDriftOscillator.h" + +namespace inet { + +Define_Module(ServoClockBase); + +void ServoClockBase::setOscillatorCompensation(ppm oscillatorCompensation) { + this->oscillatorCompensation = oscillatorCompensation; +} + +void ServoClockBase::resetOscillatorOffset() const{ + if (auto constantDriftOscillator = dynamic_cast(oscillator)) + constantDriftOscillator->setTickOffset(0); +} + +void ServoClockBase::rescheduleClockEvents(inet::clocktime_t oldClockTime, inet::clocktime_t newClockTime) { + clocktime_t clockDelta = newClockTime - oldClockTime; + simtime_t currentSimTime = simTime(); + for (auto event : events) { + if (event->getRelative()) + // NOTE: the simulation time of event execution is not affected + event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); + else { + clocktime_t arrivalClockTime = event->getArrivalClockTime(); + bool isOverdue = arrivalClockTime < newClockTime; + simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); + } + } +} + +} + diff --git a/src/inet/clock/model/ServoClockBase.h b/src/inet/clock/model/ServoClockBase.h index 83a563d9670..4e8a78d9599 100644 --- a/src/inet/clock/model/ServoClockBase.h +++ b/src/inet/clock/model/ServoClockBase.h @@ -8,11 +8,11 @@ #define __INET_SERVOCLOCKBASE_H #include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/common/scenario/IScriptable.h" +//#include "inet/common/scenario/IScriptable.h" namespace inet { -class ServoClockBase : public OscillatorBasedClock, public IScriptable { +class ServoClockBase : public OscillatorBasedClock { protected: OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; @@ -20,11 +20,11 @@ class ServoClockBase : public OscillatorBasedClock, public IScriptable { // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time -protected: - virtual void adjustClockTime(clocktime_t newClockTime) = 0; - virtual void setOscillatorCompensation(ppm oscillatorCompensation) const; - virtual void resetOscillator(bool resetOscillator) const; - virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) const; +public: + virtual void adjustClockTime(clocktime_t newClockTime) {}; + virtual void setOscillatorCompensation(ppm oscillatorCompensation); + virtual void resetOscillatorOffset() const; + virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime); }; diff --git a/src/inet/clock/model/SettableClock.cc b/src/inet/clock/model/SettableClock.cc index 95123a15c3c..51d4a228daa 100644 --- a/src/inet/clock/model/SettableClock.cc +++ b/src/inet/clock/model/SettableClock.cc @@ -56,42 +56,51 @@ simtime_t SettableClock::handleOverdueClockEvent(ClockEvent *event, simtime_t t) } } -void SettableClock::setClockTime(clocktime_t newClockTime, ppm oscillatorCompensation, bool resetOscillator) +void SettableClock::adjustClockTime(clocktime_t newClockTime) { - Enter_Method("setClockTime"); + Enter_Method("adjustClockTime"); clocktime_t oldClockTime = getClockTime(); - auto diff = newClockTime - oldClockTime; if (newClockTime != oldClockTime) { emit(timeChangedSignal, oldClockTime.asSimTime()); - if (resetOscillator) { - if (auto constantDriftOscillator = dynamic_cast(oscillator)) - constantDriftOscillator->setTickOffset(0); + + if(resetOscillator){ + resetOscillatorOffset(); } + simtime_t currentSimTime = simTime(); - EV_DEBUG << "Setting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " << currentSimTime << ".\n"; + EV_DEBUG << "Adjusting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " << currentSimTime << ".\n"; + originSimulationTime = simTime(); originClockTime = newClockTime; - this->oscillatorCompensation = oscillatorCompensation; + setOscillatorCompensation(oscillatorCompensation); + ASSERT(newClockTime == getClockTime()); - clocktime_t clockDelta = newClockTime - oldClockTime; - for (auto event : events) { - if (event->getRelative()) - // NOTE: the simulation time of event execution is not affected - event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); - else { - clocktime_t arrivalClockTime = event->getArrivalClockTime(); - bool isOverdue = arrivalClockTime < newClockTime; - simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); - if (isOverdue || arrivalSimTime < currentSimTime) - arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); - if (event->isScheduled()) { - cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); - cContextSwitcher contextSwitcher(targetModule); - targetModule->rescheduleAt(arrivalSimTime, event); - } + + rescheduleClockEvents(oldClockTime, newClockTime); + + emit(timeChangedSignal, newClockTime.asSimTime()); + } +} + +void SettableClock::rescheduleClockEvents(inet::clocktime_t oldClockTime, inet::clocktime_t newClockTime) { + clocktime_t clockDelta = newClockTime - oldClockTime; + simtime_t currentSimTime = simTime(); + for (auto event : events) { + if (event->getRelative()) + // NOTE: the simulation time of event execution is not affected + event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); + else { + clocktime_t arrivalClockTime = event->getArrivalClockTime(); + bool isOverdue = arrivalClockTime < newClockTime; + simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); + if (isOverdue || arrivalSimTime < currentSimTime) + arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); + if (event->isScheduled()) { + cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); + cContextSwitcher contextSwitcher(targetModule); + targetModule->rescheduleAt(arrivalSimTime, event); } } - emit(timeChangedSignal, newClockTime.asSimTime()); } } @@ -100,13 +109,12 @@ void SettableClock::processCommand(const cXMLElement& node) Enter_Method("processCommand"); if (!strcmp(node.getTagName(), "set-clock")) { clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); - ppm oscillatorCompensation = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); - bool resetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); - setClockTime(time, oscillatorCompensation, resetOscillator); + oscillatorCompensation = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); + resetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); + adjustClockTime(time); } else throw cRuntimeError("Invalid command: %s", node.getTagName()); } } // namespace inet - diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index 693330e06d0..505a2a7ff47 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -9,16 +9,19 @@ #define __INET_SETTABLECLOCK_H #include "inet/clock/model/ServoClockBase.h" +#include "inet/common/scenario/IScriptable.h" namespace inet { -class INET_API SettableClock : public ServoClockBase +class INET_API SettableClock : public ServoClockBase, public IScriptable { // protected: // OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; // ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time // // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time + protected: + bool resetOscillator = false; protected: virtual void initialize(int stage) override; @@ -37,6 +40,7 @@ class INET_API SettableClock : public ServoClockBase */ // virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator) override; virtual void adjustClockTime(clocktime_t newClockTime) override; + virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) override; }; } // namespace inet diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index f2317c0cf6c..cb14f937908 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -9,7 +9,7 @@ #include "GptpPacket_m.h" #include "inet/clock/model/PiClock.h" -#include "inet/clock/model/SettableClock.h" +#include "inet/clock/model/ServoClockBase.h" #include "inet/common/IProtocolRegistrationListener.h" #include "inet/common/clock/ClockUserModuleBase.h" #include "inet/linklayer/common/InterfaceTag_m.h" @@ -442,7 +442,7 @@ void Gptp::synchronize() // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio clocktime_t newTime = preciseOriginTimestamp + correctionField + gmRateRatio * (meanLinkDelay + residenceTime); - auto piControlClock = check_and_cast(clock.get()); + auto piControlClock = check_and_cast(clock.get()); // Only change the oscillator if we have new information about our nrr // TODO: We should change this to a clock servo model in the future anyways! @@ -455,7 +455,7 @@ void Gptp::synchronize() // unit(gmRateRatio * (1 + unit(piControlClock->getOscillatorCompensation()).get()) - 1); // hasNewRateRatioForOscillatorCompensation = false; // } - piControlClock->setClockTime(newTime); + piControlClock->adjustClockTime(newTime); // EV_INFO << "newOscillatorCompensation " << newOscillatorCompensation << endl; @@ -463,7 +463,6 @@ void Gptp::synchronize() timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; - /************** Rate ratio calculation ************************************* * It is calculated based on interval between two successive Sync messages * ***************************************************************************/ From 42256eb1610be01e59136bca707302d6b436b94a Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 18 Jun 2024 14:45:12 +0200 Subject: [PATCH 060/138] refactor SettableClock and PiClock with ServoClockBase --- .../clock/SimpleClockSynchronizer.cc | 7 +- src/inet/clock/model/PiClock.cc | 23 +-- src/inet/clock/model/PiClock.h | 1 - src/inet/clock/model/PiClock.ned | 2 +- src/inet/clock/model/ServoClockBase.h | 33 ----- src/inet/clock/model/SettableClock.cc | 138 +++++++++++------- src/inet/clock/model/SettableClock.h | 12 +- src/inet/clock/model/SettableClock.ned | 2 +- 8 files changed, 113 insertions(+), 105 deletions(-) delete mode 100644 src/inet/clock/model/ServoClockBase.h diff --git a/src/inet/applications/clock/SimpleClockSynchronizer.cc b/src/inet/applications/clock/SimpleClockSynchronizer.cc index 4969a5e6da4..5b107321ec3 100644 --- a/src/inet/applications/clock/SimpleClockSynchronizer.cc +++ b/src/inet/applications/clock/SimpleClockSynchronizer.cc @@ -57,7 +57,12 @@ void SimpleClockSynchronizer::synchronizeSlaveClock() ppm oscillatorCompensation = unit(getCurrentRelativeTickLength(slaveClock.get()) / getCurrentRelativeTickLength(masterClock.get()) * (1 + unit(masterOscillatorBasedClock->getOscillatorCompensation()).get()) * (1 + unit(ppm(synchronizationOscillatorCompensationErrorParameter->doubleValue())).get()) - 1); - slaveClock->setClockTime(clockTime, oscillatorCompensation, true); +// slaveClock->setClockTime(clockTime, oscillatorCompensation, true); + slaveClock->adjustClockTime(clockTime); + slaveClock->resetOscillator(); + slaveClock->setOscillatorCompensation(oscillatorCompensation); + + } void SimpleClockSynchronizer::scheduleSynchronizationTimer() diff --git a/src/inet/clock/model/PiClock.cc b/src/inet/clock/model/PiClock.cc index 6caf267e944..7981a6cdeb8 100644 --- a/src/inet/clock/model/PiClock.cc +++ b/src/inet/clock/model/PiClock.cc @@ -5,11 +5,8 @@ // #include "inet/clock/model/PiClock.h" - #include -#include "inet/clock/oscillator/ConstantDriftOscillator.h" -#include "inet/common/XMLUtils.h" namespace inet { @@ -20,7 +17,7 @@ simsignal_t PiClock::driftSignal = cComponent::registerSignal("drift"); void PiClock::initialize(int stage) { -// ServoClockBase::initialize(stage); +// OscillatorBasedClock::initialize(stage); if (stage == INITSTAGE_LOCAL) { offsetThreshold = &par("offsetThreshold"); kp = par("kp"); @@ -39,9 +36,8 @@ void PiClock::initialize(int stage) } } -clocktime_t PiClock::setClockTime(clocktime_t newClockTime) -{ - Enter_Method("setClockTime"); +void PiClock::adjustClockTime(clocktime_t newClockTime) { + Enter_Method("adjustClockTime"); clocktime_t oldClockTime = getClockTime(); if (newClockTime != oldClockTime){ @@ -83,7 +79,11 @@ clocktime_t PiClock::setClockTime(clocktime_t newClockTime) originSimulationTime = simTime(); originClockTime = newClockTime; - this->oscillatorCompensation = drift; + ASSERT(newClockTime == getClockTime()); + rescheduleClockEvents(oldClockTime, newClockTime); +// this->oscillatorCompensation = drift; + setOscillatorCompensation(drift); + emit(timeChangedSignal, newClockTime.asSimTime()); phase = 2; break; @@ -107,15 +107,18 @@ clocktime_t PiClock::setClockTime(clocktime_t newClockTime) EV_INFO << "kpTerm: " << kpTerm << " kiTerm: " << kiTerm << " offsetUs: " << offsetUs << " drift: " << drift << "\n"; - this->oscillatorCompensation = kpTerm + kiTerm + drift; +// this->oscillatorCompensation = kpTerm + kiTerm + drift; + setOscillatorCompensation(kpTerm + kiTerm + drift); + drift += kiTerm; emit(kpSignal, kpTerm.get()); break; } } + emit(driftSignal, drift.get()); - return getClockTime(); +// return getClockTime(); } } // namespace inet diff --git a/src/inet/clock/model/PiClock.h b/src/inet/clock/model/PiClock.h index 1eae1670033..5ddbc803031 100644 --- a/src/inet/clock/model/PiClock.h +++ b/src/inet/clock/model/PiClock.h @@ -16,7 +16,6 @@ class INET_API PiClock : public ServoClockBase protected: static simsignal_t driftSignal; static simsignal_t kpSignal; - OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; int phase = 0; clocktime_t offset[2]; diff --git a/src/inet/clock/model/PiClock.ned b/src/inet/clock/model/PiClock.ned index aaf2a80a027..75f59c92a72 100644 --- a/src/inet/clock/model/PiClock.ned +++ b/src/inet/clock/model/PiClock.ned @@ -14,7 +14,7 @@ package inet.clock.model; // // @see ~ScenarioManager // -module PiClock extends SettableClock +module PiClock extends ServoClockBase { parameters: double offsetThreshold @unit(s) = default(0s); // Clock will perform a time jump if the offset is greater than this value, 0.0 disables this feature diff --git a/src/inet/clock/model/ServoClockBase.h b/src/inet/clock/model/ServoClockBase.h deleted file mode 100644 index 83a563d9670..00000000000 --- a/src/inet/clock/model/ServoClockBase.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (C) 2020 OpenSim Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - -#ifndef __INET_SERVOCLOCKBASE_H -#define __INET_SERVOCLOCKBASE_H - -#include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/common/scenario/IScriptable.h" - -namespace inet { - -class ServoClockBase : public OscillatorBasedClock, public IScriptable { - -protected: - OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; - ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time - // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time - - -protected: - virtual void adjustClockTime(clocktime_t newClockTime) = 0; - virtual void setOscillatorCompensation(ppm oscillatorCompensation) const; - virtual void resetOscillator(bool resetOscillator) const; - virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) const; - -}; - -} /* namespace inet */ - -#endif diff --git a/src/inet/clock/model/SettableClock.cc b/src/inet/clock/model/SettableClock.cc index 95123a15c3c..464df95dea6 100644 --- a/src/inet/clock/model/SettableClock.cc +++ b/src/inet/clock/model/SettableClock.cc @@ -7,8 +7,8 @@ #include "inet/clock/model/SettableClock.h" -#include "inet/clock/oscillator/ConstantDriftOscillator.h" -#include "inet/common/XMLUtils.h" +//#include "inet/clock/oscillator/ConstantDriftOscillator.h" +//#include "inet/common/XMLUtils.h" namespace inet { @@ -30,79 +30,107 @@ void SettableClock::initialize(int stage) } } -OverdueClockEventHandlingMode SettableClock::getOverdueClockEventHandlingMode(ClockEvent *event) const -{ - auto mode = event->getOverdueClockEventHandlingMode(); - if (mode == UNSPECIFIED) - return defaultOverdueClockEventHandlingMode; - else - return mode; -} +//OverdueClockEventHandlingMode SettableClock::getOverdueClockEventHandlingMode(ClockEvent *event) const +//{ +// auto mode = event->getOverdueClockEventHandlingMode(); +// if (mode == UNSPECIFIED) +// return defaultOverdueClockEventHandlingMode; +// else +// return mode; +//} -simtime_t SettableClock::handleOverdueClockEvent(ClockEvent *event, simtime_t t) -{ - switch (getOverdueClockEventHandlingMode(event)) { - case EXECUTE: - EV_WARN << "Scheduling overdue clock event " << event->getName() << " to current simulation time.\n"; - return t; - case SKIP: - EV_WARN << "Skipping overdue clock event " << event->getName() << ".\n"; - cancelClockEvent(event); - return -1; - case ERROR: - throw cRuntimeError("Clock event is overdue"); - default: - throw cRuntimeError("Unknown overdue clock event handling mode"); - } -} +//simtime_t SettableClock::handleOverdueClockEvent(ClockEvent *event, simtime_t t) +//{ +// switch (getOverdueClockEventHandlingMode(event)) { +// case EXECUTE: +// EV_WARN << "Scheduling overdue clock event " << event->getName() << " to current simulation time.\n"; +// return t; +// case SKIP: +// EV_WARN << "Skipping overdue clock event " << event->getName() << ".\n"; +// cancelClockEvent(event); +// return -1; +// case ERROR: +// throw cRuntimeError("Clock event is overdue"); +// default: +// throw cRuntimeError("Unknown overdue clock event handling mode"); +// } +//} -void SettableClock::setClockTime(clocktime_t newClockTime, ppm oscillatorCompensation, bool resetOscillator) +void SettableClock::adjustClockTime(clocktime_t newClockTime) { - Enter_Method("setClockTime"); + Enter_Method("adjustClockTime"); clocktime_t oldClockTime = getClockTime(); - auto diff = newClockTime - oldClockTime; + clocktime_t diff = newClockTime - oldClockTime; + if (newClockTime != oldClockTime) { emit(timeChangedSignal, oldClockTime.asSimTime()); - if (resetOscillator) { - if (auto constantDriftOscillator = dynamic_cast(oscillator)) - constantDriftOscillator->setTickOffset(0); - } +// resetOscillator(); + simtime_t currentSimTime = simTime(); EV_DEBUG << "Setting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " << currentSimTime << ".\n"; originSimulationTime = simTime(); originClockTime = newClockTime; - this->oscillatorCompensation = oscillatorCompensation; +// setOscillatorCompensation(oscillatorCompensationValue); + ASSERT(newClockTime == getClockTime()); - clocktime_t clockDelta = newClockTime - oldClockTime; - for (auto event : events) { - if (event->getRelative()) - // NOTE: the simulation time of event execution is not affected - event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); - else { - clocktime_t arrivalClockTime = event->getArrivalClockTime(); - bool isOverdue = arrivalClockTime < newClockTime; - simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); - if (isOverdue || arrivalSimTime < currentSimTime) - arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); - if (event->isScheduled()) { - cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); - cContextSwitcher contextSwitcher(targetModule); - targetModule->rescheduleAt(arrivalSimTime, event); - } - } - } + rescheduleClockEvents(oldClockTime, newClockTime); emit(timeChangedSignal, newClockTime.asSimTime()); + } + } + + +//void SettableClock::setClockTime(clocktime_t newClockTime, ppm oscillatorCompensation, bool resetOscillator) +//{ +// Enter_Method("setClockTime"); +// clocktime_t oldClockTime = getClockTime(); +// auto diff = newClockTime - oldClockTime; +// if (newClockTime != oldClockTime) { +// emit(timeChangedSignal, oldClockTime.asSimTime()); +// if (resetOscillator) { +// if (auto constantDriftOscillator = dynamic_cast(oscillator)) +// constantDriftOscillator->setTickOffset(0); +// } +// simtime_t currentSimTime = simTime(); +// EV_DEBUG << "Setting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " << currentSimTime << ".\n"; +// originSimulationTime = simTime(); +// originClockTime = newClockTime; +// this->oscillatorCompensation = oscillatorCompensation; +// +// ASSERT(newClockTime == getClockTime()); +// clocktime_t clockDelta = newClockTime - oldClockTime; +// for (auto event : events) { +// if (event->getRelative()) +// // NOTE: the simulation time of event execution is not affected +// event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); +// else { +// clocktime_t arrivalClockTime = event->getArrivalClockTime(); +// bool isOverdue = arrivalClockTime < newClockTime; +// simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); +// if (isOverdue || arrivalSimTime < currentSimTime) +// arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); +// if (event->isScheduled()) { +// cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); +// cContextSwitcher contextSwitcher(targetModule); +// targetModule->rescheduleAt(arrivalSimTime, event); +// } +// } +// } +// emit(timeChangedSignal, newClockTime.asSimTime()); +// } +//} + void SettableClock::processCommand(const cXMLElement& node) { Enter_Method("processCommand"); if (!strcmp(node.getTagName(), "set-clock")) { clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); - ppm oscillatorCompensation = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); - bool resetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); - setClockTime(time, oscillatorCompensation, resetOscillator); + this->oscillatorCompensationValue = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); + this->isResetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); + adjustClockTime(time); +// setClockTime(time, oscillatorCompensation, resetOscillator); } else throw cRuntimeError("Invalid command: %s", node.getTagName()); diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index 693330e06d0..1d158f5b99f 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -9,6 +9,8 @@ #define __INET_SETTABLECLOCK_H #include "inet/clock/model/ServoClockBase.h" +#include "inet/common/XMLUtils.h" + namespace inet { @@ -22,14 +24,17 @@ class INET_API SettableClock : public ServoClockBase protected: virtual void initialize(int stage) override; - virtual OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event) const; - virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); +// virtual OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event) const; +// virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); // IScriptable implementation virtual void processCommand(const cXMLElement& node) override; + ppm oscillatorCompensationValue = ppm(0); + + public: - virtual ppm getOscillatorCompensation() const override { return oscillatorCompensation; } +// virtual ppm getOscillatorCompensation() const override { return oscillatorCompensation; } /** * Sets the clock time immediately to the given value. Greater than 1 oscillator @@ -37,6 +42,7 @@ class INET_API SettableClock : public ServoClockBase */ // virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator) override; virtual void adjustClockTime(clocktime_t newClockTime) override; + }; } // namespace inet diff --git a/src/inet/clock/model/SettableClock.ned b/src/inet/clock/model/SettableClock.ned index 0e2a18fad30..21c58052155 100644 --- a/src/inet/clock/model/SettableClock.ned +++ b/src/inet/clock/model/SettableClock.ned @@ -14,7 +14,7 @@ package inet.clock.model; // // @see ~ScenarioManager // -module SettableClock extends OscillatorBasedClock +module SettableClock extends ServoClockBase { parameters: string defaultOverdueClockEventHandlingMode @enum("execute","skip","error") = default("error"); From e9d24888c664e20e0a8f98865f2cd1177b0f9b42 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 18 Jun 2024 14:46:39 +0200 Subject: [PATCH 061/138] refactor SettableClock and PiClock with ServoClockBase --- src/inet/clock/model/ServoClockBase.cc | 90 +++++++++++++++++++++++++ src/inet/clock/model/ServoClockBase.h | 38 +++++++++++ src/inet/clock/model/ServoClockBase.ned | 23 +++++++ 3 files changed, 151 insertions(+) create mode 100644 src/inet/clock/model/ServoClockBase.cc create mode 100644 src/inet/clock/model/ServoClockBase.h create mode 100644 src/inet/clock/model/ServoClockBase.ned diff --git a/src/inet/clock/model/ServoClockBase.cc b/src/inet/clock/model/ServoClockBase.cc new file mode 100644 index 00000000000..08e6204c128 --- /dev/null +++ b/src/inet/clock/model/ServoClockBase.cc @@ -0,0 +1,90 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/clock/model/ServoClockBase.h" + +namespace inet { + + Define_Module(ServoClockBase); + void ServoClockBase::initialize(int stage) + { + OscillatorBasedClock::initialize(stage); + if (stage == INITSTAGE_LOCAL) { + const char *text = par("defaultOverdueClockEventHandlingMode"); + if (!strcmp(text, "execute")) + defaultOverdueClockEventHandlingMode = EXECUTE; + else if (!strcmp(text, "skip")) + defaultOverdueClockEventHandlingMode = SKIP; + else if (!strcmp(text, "error")) + defaultOverdueClockEventHandlingMode = ERROR; + else + throw cRuntimeError("Unknown defaultOverdueClockEventHandlingMode parameter value"); + } + } + + + void ServoClockBase::setOscillatorCompensation(ppm oscillatorCompensationValue) + { + this->oscillatorCompensation = oscillatorCompensationValue; + } + + void ServoClockBase::resetOscillator() const + { + Enter_Method("resetOscillator"); + if (auto constantDriftOscillator = dynamic_cast(oscillator)) + constantDriftOscillator->setTickOffset(0); + + } + + void ServoClockBase::rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) const + { + clocktime_t clockDelta = newClockTime - oldClockTime; + simtime_t currentSimTime = simTime(); + for (auto event : this->events) { + if (event->getRelative()) + // NOTE: the simulation time of event execution is not affected + event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); + else { + clocktime_t arrivalClockTime = event->getArrivalClockTime(); + bool isOverdue = arrivalClockTime < newClockTime; + simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); + if (isOverdue || arrivalSimTime < currentSimTime) + arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); + if (event->isScheduled()) { + cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); + cContextSwitcher contextSwitcher(targetModule); + targetModule->rescheduleAt(arrivalSimTime, event); + } + } + } + + } + + simtime_t ServoClockBase::handleOverdueClockEvent(ClockEvent *event, simtime_t t) + { + auto mode = event->getOverdueClockEventHandlingMode(); + if (mode == UNSPECIFIED) + mode = defaultOverdueClockEventHandlingMode; + + switch (mode) { + case EXECUTE: + EV_WARN << "Scheduling overdue clock event " << event->getName() << " to current simulation time.\n"; + return t; + case SKIP: + EV_WARN << "Skipping overdue clock event " << event->getName() << ".\n"; + cancelClockEvent(event); + return -1; + case ERROR: + throw cRuntimeError("Clock event is overdue"); + default: + throw cRuntimeError("Unknown overdue clock event handling mode"); + } + } + + +} // namespace inet + diff --git a/src/inet/clock/model/ServoClockBase.h b/src/inet/clock/model/ServoClockBase.h new file mode 100644 index 00000000000..34c62c3fbe6 --- /dev/null +++ b/src/inet/clock/model/ServoClockBase.h @@ -0,0 +1,38 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +#ifndef __INET_SERVOCLOCKBASE_H +#define __INET_SERVOCLOCKBASE_H + +#include "inet/clock/model/OscillatorBasedClock.h" +#include "inet/common/scenario/IScriptable.h" + +#include "inet/clock/oscillator/ConstantDriftOscillator.h" + +namespace inet { + +class ServoClockBase : public OscillatorBasedClock, public IScriptable { + +protected: + OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; + ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time + // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time + +protected: + virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) const; + virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); + virtual void initialize(int stage) override; +public: + virtual void adjustClockTime(clocktime_t newClockTime) = 0; + virtual void setOscillatorCompensation(ppm oscillatorCompensationValue); + virtual void resetOscillator() const; + + +}; + +} /* namespace inet */ + +#endif diff --git a/src/inet/clock/model/ServoClockBase.ned b/src/inet/clock/model/ServoClockBase.ned new file mode 100644 index 00000000000..d13ce10afd9 --- /dev/null +++ b/src/inet/clock/model/ServoClockBase.ned @@ -0,0 +1,23 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +package inet.clock.model; + +// +// Models a clock which can be set to a different clock time. The clock time +// can be set from C++ or using a command +// in a `ScenarioManager` script. +// +// @see ~ScenarioManager +// +module ServoClockBase extends OscillatorBasedClock +{ + parameters: + string defaultOverdueClockEventHandlingMode @enum("execute","skip","error") = default("error"); + @class(ServoClockBase); +} + From e347b41e09d48078c8e539542a0f303a5b72ca49 Mon Sep 17 00:00:00 2001 From: ChunzhiGuo <18818201750@163.com> Date: Tue, 18 Jun 2024 15:22:39 +0200 Subject: [PATCH 062/138] make functions public and non-const --- .cproject | 48 +++++++++++----------- src/inet/clock/model/ServoClockBase.cc | 23 ++++++++++- src/inet/clock/model/ServoClockBase.h | 4 +- src/inet/clock/model/SettableClock.cc | 56 +++++++++++++------------- src/inet/clock/model/SettableClock.h | 4 +- 5 files changed, 78 insertions(+), 57 deletions(-) diff --git a/.cproject b/.cproject index 60adfe7f229..29d37099621 100644 --- a/.cproject +++ b/.cproject @@ -55,24 +55,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + @@ -128,24 +128,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + diff --git a/src/inet/clock/model/ServoClockBase.cc b/src/inet/clock/model/ServoClockBase.cc index 08e6204c128..459a387aa02 100644 --- a/src/inet/clock/model/ServoClockBase.cc +++ b/src/inet/clock/model/ServoClockBase.cc @@ -10,6 +10,7 @@ namespace inet { Define_Module(ServoClockBase); + void ServoClockBase::initialize(int stage) { OscillatorBasedClock::initialize(stage); @@ -26,7 +27,6 @@ namespace inet { } } - void ServoClockBase::setOscillatorCompensation(ppm oscillatorCompensationValue) { this->oscillatorCompensation = oscillatorCompensationValue; @@ -40,7 +40,7 @@ namespace inet { } - void ServoClockBase::rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) const + void ServoClockBase::rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) { clocktime_t clockDelta = newClockTime - oldClockTime; simtime_t currentSimTime = simTime(); @@ -85,6 +85,25 @@ namespace inet { } } + void ServoClockBase::processCommand(const cXMLElement& node) + { + Enter_Method("processCommand"); + if (!strcmp(node.getTagName(), "adjust-clock")) { + clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); + ppm oscillatorCompensation = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); + bool isResetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); + + adjustClockTime(time); + if (isResetOscillator) + resetOscillator(); + setOscillatorCompensation(oscillatorCompensation); + +// setClockTime(time, oscillatorCompensation, resetOscillator); + } + else + throw cRuntimeError("Invalid command: %s", node.getTagName()); + } + } // namespace inet diff --git a/src/inet/clock/model/ServoClockBase.h b/src/inet/clock/model/ServoClockBase.h index 34c62c3fbe6..1f966a88d8b 100644 --- a/src/inet/clock/model/ServoClockBase.h +++ b/src/inet/clock/model/ServoClockBase.h @@ -9,6 +9,7 @@ #include "inet/clock/model/OscillatorBasedClock.h" #include "inet/common/scenario/IScriptable.h" +#include "inet/common/XMLUtils.h" #include "inet/clock/oscillator/ConstantDriftOscillator.h" @@ -22,9 +23,10 @@ class ServoClockBase : public OscillatorBasedClock, public IScriptable { // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time protected: - virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) const; + virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime); virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); virtual void initialize(int stage) override; + virtual void processCommand(const cXMLElement& node) override; public: virtual void adjustClockTime(clocktime_t newClockTime) = 0; virtual void setOscillatorCompensation(ppm oscillatorCompensationValue); diff --git a/src/inet/clock/model/SettableClock.cc b/src/inet/clock/model/SettableClock.cc index 464df95dea6..b68c475b8ae 100644 --- a/src/inet/clock/model/SettableClock.cc +++ b/src/inet/clock/model/SettableClock.cc @@ -14,21 +14,21 @@ namespace inet { Define_Module(SettableClock); -void SettableClock::initialize(int stage) -{ - OscillatorBasedClock::initialize(stage); - if (stage == INITSTAGE_LOCAL) { - const char *text = par("defaultOverdueClockEventHandlingMode"); - if (!strcmp(text, "execute")) - defaultOverdueClockEventHandlingMode = EXECUTE; - else if (!strcmp(text, "skip")) - defaultOverdueClockEventHandlingMode = SKIP; - else if (!strcmp(text, "error")) - defaultOverdueClockEventHandlingMode = ERROR; - else - throw cRuntimeError("Unknown defaultOverdueClockEventHandlingMode parameter value"); - } -} +//void SettableClock::initialize(int stage) +//{ +// OscillatorBasedClock::initialize(stage); +// if (stage == INITSTAGE_LOCAL) { +// const char *text = par("defaultOverdueClockEventHandlingMode"); +// if (!strcmp(text, "execute")) +// defaultOverdueClockEventHandlingMode = EXECUTE; +// else if (!strcmp(text, "skip")) +// defaultOverdueClockEventHandlingMode = SKIP; +// else if (!strcmp(text, "error")) +// defaultOverdueClockEventHandlingMode = ERROR; +// else +// throw cRuntimeError("Unknown defaultOverdueClockEventHandlingMode parameter value"); +// } +//} //OverdueClockEventHandlingMode SettableClock::getOverdueClockEventHandlingMode(ClockEvent *event) const //{ @@ -122,19 +122,19 @@ void SettableClock::adjustClockTime(clocktime_t newClockTime) // } //} -void SettableClock::processCommand(const cXMLElement& node) -{ - Enter_Method("processCommand"); - if (!strcmp(node.getTagName(), "set-clock")) { - clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); - this->oscillatorCompensationValue = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); - this->isResetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); - adjustClockTime(time); -// setClockTime(time, oscillatorCompensation, resetOscillator); - } - else - throw cRuntimeError("Invalid command: %s", node.getTagName()); -} +//void SettableClock::processCommand(const cXMLElement& node) +//{ +// Enter_Method("processCommand"); +// if (!strcmp(node.getTagName(), "set-clock")) { +// clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); +// this->oscillatorCompensationValue = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); +// this->isResetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); +// adjustClockTime(time); +//// setClockTime(time, oscillatorCompensation, resetOscillator); +// } +// else +// throw cRuntimeError("Invalid command: %s", node.getTagName()); +//} } // namespace inet diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index 1d158f5b99f..aed4383bc46 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -9,7 +9,7 @@ #define __INET_SETTABLECLOCK_H #include "inet/clock/model/ServoClockBase.h" -#include "inet/common/XMLUtils.h" + namespace inet { @@ -28,7 +28,7 @@ class INET_API SettableClock : public ServoClockBase // virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); // IScriptable implementation - virtual void processCommand(const cXMLElement& node) override; +// virtual void processCommand(const cXMLElement& node) override; ppm oscillatorCompensationValue = ppm(0); From af403b7e9adff5c9db8c4ead54df884d6275a29b Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 18 Jun 2024 18:00:23 +0200 Subject: [PATCH 063/138] Fix and Cleanup --- src/inet/clock/model/PiClock.h | 10 --- src/inet/clock/model/ServoClockBase.cc | 2 +- src/inet/clock/model/ServoClockBase.h | 1 + src/inet/clock/model/SettableClock.cc | 102 ------------------------- src/inet/clock/model/SettableClock.h | 18 ----- src/inet/clock/model/SettableClock.ned | 2 - 6 files changed, 2 insertions(+), 133 deletions(-) diff --git a/src/inet/clock/model/PiClock.h b/src/inet/clock/model/PiClock.h index 5ddbc803031..3b75a6511b2 100644 --- a/src/inet/clock/model/PiClock.h +++ b/src/inet/clock/model/PiClock.h @@ -40,16 +40,6 @@ class INET_API PiClock : public ServoClockBase virtual void initialize(int stage) override; public: - /** - * Sets the clock time immediately to the given value. Greater than 1 oscillator - * compensation factor means the clock measures time faster. - * - * @param newClockTime the new clock time which should be achieved - * @return the clockTime which was set, which is: - * - newClockTime, if the servo is in the initial phase - * - the old clock time, if the servo is in the PI phase (only the oscillator compensation is updated) - */ -// clocktime_t setClockTime(clocktime_t newClockTime) override; void adjustClockTime(clocktime_t newClockTime) override; }; diff --git a/src/inet/clock/model/ServoClockBase.cc b/src/inet/clock/model/ServoClockBase.cc index 459a387aa02..55f115d8c16 100644 --- a/src/inet/clock/model/ServoClockBase.cc +++ b/src/inet/clock/model/ServoClockBase.cc @@ -9,7 +9,7 @@ namespace inet { - Define_Module(ServoClockBase); + Register_Abstract_Class(ServoClockBase); void ServoClockBase::initialize(int stage) { diff --git a/src/inet/clock/model/ServoClockBase.h b/src/inet/clock/model/ServoClockBase.h index 1f966a88d8b..0640a0b13d8 100644 --- a/src/inet/clock/model/ServoClockBase.h +++ b/src/inet/clock/model/ServoClockBase.h @@ -27,6 +27,7 @@ class ServoClockBase : public OscillatorBasedClock, public IScriptable { virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); virtual void initialize(int stage) override; virtual void processCommand(const cXMLElement& node) override; + public: virtual void adjustClockTime(clocktime_t newClockTime) = 0; virtual void setOscillatorCompensation(ppm oscillatorCompensationValue); diff --git a/src/inet/clock/model/SettableClock.cc b/src/inet/clock/model/SettableClock.cc index b68c475b8ae..86a84911857 100644 --- a/src/inet/clock/model/SettableClock.cc +++ b/src/inet/clock/model/SettableClock.cc @@ -7,54 +7,10 @@ #include "inet/clock/model/SettableClock.h" -//#include "inet/clock/oscillator/ConstantDriftOscillator.h" -//#include "inet/common/XMLUtils.h" - namespace inet { Define_Module(SettableClock); -//void SettableClock::initialize(int stage) -//{ -// OscillatorBasedClock::initialize(stage); -// if (stage == INITSTAGE_LOCAL) { -// const char *text = par("defaultOverdueClockEventHandlingMode"); -// if (!strcmp(text, "execute")) -// defaultOverdueClockEventHandlingMode = EXECUTE; -// else if (!strcmp(text, "skip")) -// defaultOverdueClockEventHandlingMode = SKIP; -// else if (!strcmp(text, "error")) -// defaultOverdueClockEventHandlingMode = ERROR; -// else -// throw cRuntimeError("Unknown defaultOverdueClockEventHandlingMode parameter value"); -// } -//} - -//OverdueClockEventHandlingMode SettableClock::getOverdueClockEventHandlingMode(ClockEvent *event) const -//{ -// auto mode = event->getOverdueClockEventHandlingMode(); -// if (mode == UNSPECIFIED) -// return defaultOverdueClockEventHandlingMode; -// else -// return mode; -//} - -//simtime_t SettableClock::handleOverdueClockEvent(ClockEvent *event, simtime_t t) -//{ -// switch (getOverdueClockEventHandlingMode(event)) { -// case EXECUTE: -// EV_WARN << "Scheduling overdue clock event " << event->getName() << " to current simulation time.\n"; -// return t; -// case SKIP: -// EV_WARN << "Skipping overdue clock event " << event->getName() << ".\n"; -// cancelClockEvent(event); -// return -1; -// case ERROR: -// throw cRuntimeError("Clock event is overdue"); -// default: -// throw cRuntimeError("Unknown overdue clock event handling mode"); -// } -//} void SettableClock::adjustClockTime(clocktime_t newClockTime) { @@ -64,13 +20,11 @@ void SettableClock::adjustClockTime(clocktime_t newClockTime) if (newClockTime != oldClockTime) { emit(timeChangedSignal, oldClockTime.asSimTime()); -// resetOscillator(); simtime_t currentSimTime = simTime(); EV_DEBUG << "Setting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " << currentSimTime << ".\n"; originSimulationTime = simTime(); originClockTime = newClockTime; -// setOscillatorCompensation(oscillatorCompensationValue); ASSERT(newClockTime == getClockTime()); rescheduleClockEvents(oldClockTime, newClockTime); @@ -80,61 +34,5 @@ void SettableClock::adjustClockTime(clocktime_t newClockTime) } - - -//void SettableClock::setClockTime(clocktime_t newClockTime, ppm oscillatorCompensation, bool resetOscillator) -//{ -// Enter_Method("setClockTime"); -// clocktime_t oldClockTime = getClockTime(); -// auto diff = newClockTime - oldClockTime; -// if (newClockTime != oldClockTime) { -// emit(timeChangedSignal, oldClockTime.asSimTime()); -// if (resetOscillator) { -// if (auto constantDriftOscillator = dynamic_cast(oscillator)) -// constantDriftOscillator->setTickOffset(0); -// } -// simtime_t currentSimTime = simTime(); -// EV_DEBUG << "Setting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " << currentSimTime << ".\n"; -// originSimulationTime = simTime(); -// originClockTime = newClockTime; -// this->oscillatorCompensation = oscillatorCompensation; -// -// ASSERT(newClockTime == getClockTime()); -// clocktime_t clockDelta = newClockTime - oldClockTime; -// for (auto event : events) { -// if (event->getRelative()) -// // NOTE: the simulation time of event execution is not affected -// event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); -// else { -// clocktime_t arrivalClockTime = event->getArrivalClockTime(); -// bool isOverdue = arrivalClockTime < newClockTime; -// simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); -// if (isOverdue || arrivalSimTime < currentSimTime) -// arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); -// if (event->isScheduled()) { -// cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); -// cContextSwitcher contextSwitcher(targetModule); -// targetModule->rescheduleAt(arrivalSimTime, event); -// } -// } -// } -// emit(timeChangedSignal, newClockTime.asSimTime()); -// } -//} - -//void SettableClock::processCommand(const cXMLElement& node) -//{ -// Enter_Method("processCommand"); -// if (!strcmp(node.getTagName(), "set-clock")) { -// clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); -// this->oscillatorCompensationValue = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); -// this->isResetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); -// adjustClockTime(time); -//// setClockTime(time, oscillatorCompensation, resetOscillator); -// } -// else -// throw cRuntimeError("Invalid command: %s", node.getTagName()); -//} - } // namespace inet diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/SettableClock.h index aed4383bc46..6f4071957bd 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/SettableClock.h @@ -16,31 +16,13 @@ namespace inet { class INET_API SettableClock : public ServoClockBase { -// protected: -// OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; -// ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time -// // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time - protected: - virtual void initialize(int stage) override; - -// virtual OverdueClockEventHandlingMode getOverdueClockEventHandlingMode(ClockEvent *event) const; -// virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); - - // IScriptable implementation -// virtual void processCommand(const cXMLElement& node) override; - - ppm oscillatorCompensationValue = ppm(0); - public: -// virtual ppm getOscillatorCompensation() const override { return oscillatorCompensation; } - /** * Sets the clock time immediately to the given value. Greater than 1 oscillator * compensation factor means the clock measures time faster. */ -// virtual void setClockTime(clocktime_t time, ppm oscillatorCompensation, bool resetOscillator) override; virtual void adjustClockTime(clocktime_t newClockTime) override; }; diff --git a/src/inet/clock/model/SettableClock.ned b/src/inet/clock/model/SettableClock.ned index 21c58052155..d2f838c5393 100644 --- a/src/inet/clock/model/SettableClock.ned +++ b/src/inet/clock/model/SettableClock.ned @@ -17,7 +17,5 @@ package inet.clock.model; module SettableClock extends ServoClockBase { parameters: - string defaultOverdueClockEventHandlingMode @enum("execute","skip","error") = default("error"); @class(SettableClock); } - From df55cdea62423d7f14ea3c8832763797bd07ca68 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 19 Jun 2024 00:48:40 +0200 Subject: [PATCH 064/138] Fix and Cleanup --- .cproject | 44 ++-- .../tsn/timesynchronization/gptp/omnetpp.ini | 7 +- .../clock/SimpleClockSynchronizer.h | 4 +- src/inet/clock/model/InstantServoClock.cc | 25 +++ .../{SettableClock.h => InstantServoClock.h} | 2 +- ...ettableClock.ned => InstantServoClock.ned} | 4 +- src/inet/clock/model/MultiClock.ned | 2 +- .../model/{PiClock.cc => PiServoClock.cc} | 33 +-- .../clock/model/{PiClock.h => PiServoClock.h} | 2 +- .../model/{PiClock.ned => PiServoClock.ned} | 4 +- src/inet/clock/model/ServoClockBase.cc | 193 +++++++++--------- src/inet/clock/model/ServoClockBase.h | 27 +-- src/inet/clock/model/SettableClock.cc | 38 ---- src/inet/linklayer/ieee8021as/Gptp.cc | 4 +- src/inet/linklayer/ieee8021as/Gptp.h | 2 +- src/inet/node/tsn/TsnDevice.ned | 4 +- src/inet/node/tsn/TsnSwitch.ned | 4 +- 17 files changed, 191 insertions(+), 208 deletions(-) create mode 100644 src/inet/clock/model/InstantServoClock.cc rename src/inet/clock/model/{SettableClock.h => InstantServoClock.h} (90%) rename src/inet/clock/model/{SettableClock.ned => InstantServoClock.ned} (81%) rename src/inet/clock/model/{PiClock.cc => PiServoClock.cc} (73%) rename src/inet/clock/model/{PiClock.h => PiServoClock.h} (94%) rename src/inet/clock/model/{PiClock.ned => PiServoClock.ned} (91%) delete mode 100644 src/inet/clock/model/SettableClock.cc diff --git a/.cproject b/.cproject index 29d37099621..0ed322cc3de 100644 --- a/.cproject +++ b/.cproject @@ -55,19 +55,19 @@ - - - - - + + + + + - - + + - - - - + + + + @@ -128,19 +128,19 @@ - - - - - + + + + + - - + + - - - - + + + + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index de18e1f1e0a..6fe5e393a5d 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -1,5 +1,5 @@ [General] -seed-set = -123 +seed-set = 0 sim-time-limit = 20s description = "abstract" @@ -9,7 +9,7 @@ description = "abstract" # all oscillators have a constant drift rate (specified with a random distribution for each one) # except for the master clocks, which have a random drift rate #**.clock.oscillator.typename = "RandomDriftOscillator" -**.clock.oscillator.typename = "RandomDriftOscillator" +*.tsnClock.clock.oscillator.typename = "RandomDriftOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) **.oscillator.driftRateChange = uniform(-1ppm, 1ppm) @@ -18,7 +18,7 @@ description = "abstract" **.kp=8 **.ki=7 -**.offsetThreshold =1us +**.offsetThreshold = 0us # all Ethernet interfaces have 100 Mbps speed @@ -39,7 +39,6 @@ description = "Basic tree topology with one master clock and multiple devices" #*.*.gptp.slaveClockModule = "^.clock" **.numSwitch=10 -**.clock.typename = "PiClock" # clock type # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] diff --git a/src/inet/applications/clock/SimpleClockSynchronizer.h b/src/inet/applications/clock/SimpleClockSynchronizer.h index 0fea2cf4ba1..c2dd5fff20d 100644 --- a/src/inet/applications/clock/SimpleClockSynchronizer.h +++ b/src/inet/applications/clock/SimpleClockSynchronizer.h @@ -11,7 +11,7 @@ #include #include "inet/applications/base/ApplicationBase.h" -#include "inet/clock/model/SettableClock.h" +#include "inet/clock/model/InstantServoClock.h" #include "inet/common/ModuleRefByPar.h" namespace inet { @@ -21,7 +21,7 @@ class INET_API SimpleClockSynchronizer : public ApplicationBase protected: cMessage *synhronizationTimer = nullptr; ModuleRefByPar masterClock; - ModuleRefByPar slaveClock; + ModuleRefByPar slaveClock; cPar *synchronizationIntervalParameter = nullptr; cPar *synchronizationClockTimeErrorParameter = nullptr; cPar *synchronizationOscillatorCompensationErrorParameter = nullptr; diff --git a/src/inet/clock/model/InstantServoClock.cc b/src/inet/clock/model/InstantServoClock.cc new file mode 100644 index 00000000000..1b895de346c --- /dev/null +++ b/src/inet/clock/model/InstantServoClock.cc @@ -0,0 +1,25 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/clock/model/InstantServoClock.h" + +namespace inet { + +Define_Module(InstantServoClock); + + +void InstantServoClock::adjustClockTime(clocktime_t newClockTime) +{ + Enter_Method("adjustClockTime"); + setClockTime(newClockTime); + + // TODO: Add a mechanism that estimates the drift rate based on the previous and current local and received + // timestamps, similar to case 0 and 1 in PiServoClock +} + +} // namespace inet + diff --git a/src/inet/clock/model/SettableClock.h b/src/inet/clock/model/InstantServoClock.h similarity index 90% rename from src/inet/clock/model/SettableClock.h rename to src/inet/clock/model/InstantServoClock.h index 6f4071957bd..28f68a5d4dd 100644 --- a/src/inet/clock/model/SettableClock.h +++ b/src/inet/clock/model/InstantServoClock.h @@ -14,7 +14,7 @@ namespace inet { -class INET_API SettableClock : public ServoClockBase +class INET_API InstantServoClock : public ServoClockBase { protected: diff --git a/src/inet/clock/model/SettableClock.ned b/src/inet/clock/model/InstantServoClock.ned similarity index 81% rename from src/inet/clock/model/SettableClock.ned rename to src/inet/clock/model/InstantServoClock.ned index d2f838c5393..aec490dcc5b 100644 --- a/src/inet/clock/model/SettableClock.ned +++ b/src/inet/clock/model/InstantServoClock.ned @@ -14,8 +14,8 @@ package inet.clock.model; // // @see ~ScenarioManager // -module SettableClock extends ServoClockBase +module InstantServoClock extends ServoClockBase { parameters: - @class(SettableClock); + @class(InstantServoClock); } diff --git a/src/inet/clock/model/MultiClock.ned b/src/inet/clock/model/MultiClock.ned index 2e1fcef32f7..ab4a1122fb7 100644 --- a/src/inet/clock/model/MultiClock.ned +++ b/src/inet/clock/model/MultiClock.ned @@ -27,7 +27,7 @@ module MultiClock like IClock @signal[timeChanged](type=simtime_t); @statistic[timeChanged](title="Clock time"; source=localSignal(timeChanged); record=vector; interpolationmode=linear); submodules: - clock[numClocks]: like IClock { + clock[numClocks]: like IClock { @display("p=200,200,row,200"); } } diff --git a/src/inet/clock/model/PiClock.cc b/src/inet/clock/model/PiServoClock.cc similarity index 73% rename from src/inet/clock/model/PiClock.cc rename to src/inet/clock/model/PiServoClock.cc index e873df9ef93..ca887e0c08c 100644 --- a/src/inet/clock/model/PiClock.cc +++ b/src/inet/clock/model/PiServoClock.cc @@ -4,7 +4,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // -#include "inet/clock/model/PiClock.h" +#include "inet/clock/model/PiServoClock.h" #include @@ -13,37 +13,26 @@ namespace inet { -Define_Module(PiClock); +Define_Module(PiServoClock); -simsignal_t PiClock::kpSignal = cComponent::registerSignal("kp"); -simsignal_t PiClock::driftSignal = cComponent::registerSignal("drift"); +simsignal_t PiServoClock::kpSignal = cComponent::registerSignal("kp"); +simsignal_t PiServoClock::driftSignal = cComponent::registerSignal("drift"); -void PiClock::initialize(int stage) +void PiServoClock::initialize(int stage) { ServoClockBase::initialize(stage); if (stage == INITSTAGE_LOCAL) { offsetThreshold = &par("offsetThreshold"); kp = par("kp"); ki = par("ki"); - - const char *text = par("defaultOverdueClockEventHandlingMode"); - if (!strcmp(text, "execute")) - defaultOverdueClockEventHandlingMode = EXECUTE; - else if (!strcmp(text, "skip")) - defaultOverdueClockEventHandlingMode = SKIP; - else if (!strcmp(text, "error")) - defaultOverdueClockEventHandlingMode = ERROR; - else - throw cRuntimeError("Unknown defaultOverdueClockEventHandlingMode parameter value"); - } } -void PiClock::adjustClockTime(clocktime_t newClockTime) { +void PiServoClock::adjustClockTime(clocktime_t newClockTime) { Enter_Method("adjustClockTime"); clocktime_t oldClockTime = getClockTime(); - if (newClockTime != oldClockTime){ + if (newClockTime != oldClockTime) { emit(timeChangedSignal, oldClockTime.asSimTime()); clocktime_t offsetNow = newClockTime - oldClockTime; @@ -75,16 +64,11 @@ void PiClock::adjustClockTime(clocktime_t newClockTime) { localNsPrev = local[0].inUnit(SIMTIME_NS); localNs = local[1].inUnit(SIMTIME_NS); -// drift = ppm(0); drift += ppm(1e6 * (offsetNsPrev - offsetNs) / (localNsPrev - localNs)); EV_INFO << "Drift: " << drift << "\n"; - originSimulationTime = simTime(); - originClockTime = newClockTime; + setClockTime(newClockTime); - ASSERT(newClockTime == getClockTime()); - rescheduleClockEvents(oldClockTime, newClockTime); -// this->oscillatorCompensation = drift; setOscillatorCompensation(drift); emit(timeChangedSignal, newClockTime.asSimTime()); @@ -110,7 +94,6 @@ void PiClock::adjustClockTime(clocktime_t newClockTime) { EV_INFO << "kpTerm: " << kpTerm << " kiTerm: " << kiTerm << " offsetUs: " << offsetUs << " drift: " << drift << "\n"; -// this->oscillatorCompensation = kpTerm + kiTerm + drift; setOscillatorCompensation(kpTerm + kiTerm + drift); drift += kiTerm; diff --git a/src/inet/clock/model/PiClock.h b/src/inet/clock/model/PiServoClock.h similarity index 94% rename from src/inet/clock/model/PiClock.h rename to src/inet/clock/model/PiServoClock.h index 3b75a6511b2..9ac59ce947c 100644 --- a/src/inet/clock/model/PiClock.h +++ b/src/inet/clock/model/PiServoClock.h @@ -11,7 +11,7 @@ namespace inet { -class INET_API PiClock : public ServoClockBase +class INET_API PiServoClock : public ServoClockBase { protected: static simsignal_t driftSignal; diff --git a/src/inet/clock/model/PiClock.ned b/src/inet/clock/model/PiServoClock.ned similarity index 91% rename from src/inet/clock/model/PiClock.ned rename to src/inet/clock/model/PiServoClock.ned index 75f59c92a72..c333d7afa82 100644 --- a/src/inet/clock/model/PiClock.ned +++ b/src/inet/clock/model/PiServoClock.ned @@ -14,7 +14,7 @@ package inet.clock.model; // // @see ~ScenarioManager // -module PiClock extends ServoClockBase +module PiServoClock extends ServoClockBase { parameters: double offsetThreshold @unit(s) = default(0s); // Clock will perform a time jump if the offset is greater than this value, 0.0 disables this feature @@ -24,6 +24,6 @@ module PiClock extends ServoClockBase @statistic[kp](title="kp value"; record=vector; interpolationmode=linear); @signal[drift](type=double); @statistic[drift](title="drift value"; record=vector; interpolationmode=linear); - @class(PiClock); + @class(PiServoClock); } diff --git a/src/inet/clock/model/ServoClockBase.cc b/src/inet/clock/model/ServoClockBase.cc index 55f115d8c16..fec3aa14f0c 100644 --- a/src/inet/clock/model/ServoClockBase.cc +++ b/src/inet/clock/model/ServoClockBase.cc @@ -4,106 +4,117 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // - #include "inet/clock/model/ServoClockBase.h" namespace inet { - Register_Abstract_Class(ServoClockBase); - - void ServoClockBase::initialize(int stage) - { - OscillatorBasedClock::initialize(stage); - if (stage == INITSTAGE_LOCAL) { - const char *text = par("defaultOverdueClockEventHandlingMode"); - if (!strcmp(text, "execute")) - defaultOverdueClockEventHandlingMode = EXECUTE; - else if (!strcmp(text, "skip")) - defaultOverdueClockEventHandlingMode = SKIP; - else if (!strcmp(text, "error")) - defaultOverdueClockEventHandlingMode = ERROR; - else - throw cRuntimeError("Unknown defaultOverdueClockEventHandlingMode parameter value"); - } - } - - void ServoClockBase::setOscillatorCompensation(ppm oscillatorCompensationValue) - { - this->oscillatorCompensation = oscillatorCompensationValue; - } - - void ServoClockBase::resetOscillator() const - { - Enter_Method("resetOscillator"); - if (auto constantDriftOscillator = dynamic_cast(oscillator)) - constantDriftOscillator->setTickOffset(0); - +Register_Abstract_Class(ServoClockBase); + +void ServoClockBase::initialize(int stage) +{ + OscillatorBasedClock::initialize(stage); + if (stage == INITSTAGE_LOCAL) { + const char *text = par("defaultOverdueClockEventHandlingMode"); + if (!strcmp(text, "execute")) + defaultOverdueClockEventHandlingMode = EXECUTE; + else if (!strcmp(text, "skip")) + defaultOverdueClockEventHandlingMode = SKIP; + else if (!strcmp(text, "error")) + defaultOverdueClockEventHandlingMode = ERROR; + else + throw cRuntimeError("Unknown defaultOverdueClockEventHandlingMode parameter value"); } - - void ServoClockBase::rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) - { - clocktime_t clockDelta = newClockTime - oldClockTime; - simtime_t currentSimTime = simTime(); - for (auto event : this->events) { - if (event->getRelative()) - // NOTE: the simulation time of event execution is not affected - event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); - else { - clocktime_t arrivalClockTime = event->getArrivalClockTime(); - bool isOverdue = arrivalClockTime < newClockTime; - simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); - if (isOverdue || arrivalSimTime < currentSimTime) - arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); - if (event->isScheduled()) { - cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); - cContextSwitcher contextSwitcher(targetModule); - targetModule->rescheduleAt(arrivalSimTime, event); - } +} + +void ServoClockBase::setOscillatorCompensation(ppm oscillatorCompensationValue) +{ + this->oscillatorCompensation = oscillatorCompensationValue; +} + +void ServoClockBase::resetOscillator() const +{ + Enter_Method("resetOscillator"); + if (auto constantDriftOscillator = dynamic_cast(oscillator)) + constantDriftOscillator->setTickOffset(0); +} + +void ServoClockBase::rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime) +{ + clocktime_t clockDelta = newClockTime - oldClockTime; + simtime_t currentSimTime = simTime(); + for (auto event : this->events) { + if (event->getRelative()) + // NOTE: the simulation time of event execution is not affected + event->setArrivalClockTime(event->getArrivalClockTime() + clockDelta); + else { + clocktime_t arrivalClockTime = event->getArrivalClockTime(); + bool isOverdue = arrivalClockTime < newClockTime; + simtime_t arrivalSimTime = isOverdue ? -1 : computeSimTimeFromClockTime(arrivalClockTime); + if (isOverdue || arrivalSimTime < currentSimTime) + arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); + if (event->isScheduled()) { + cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); + cContextSwitcher contextSwitcher(targetModule); + targetModule->rescheduleAt(arrivalSimTime, event); } } - } - - simtime_t ServoClockBase::handleOverdueClockEvent(ClockEvent *event, simtime_t t) - { - auto mode = event->getOverdueClockEventHandlingMode(); - if (mode == UNSPECIFIED) - mode = defaultOverdueClockEventHandlingMode; - - switch (mode) { - case EXECUTE: - EV_WARN << "Scheduling overdue clock event " << event->getName() << " to current simulation time.\n"; - return t; - case SKIP: - EV_WARN << "Skipping overdue clock event " << event->getName() << ".\n"; - cancelClockEvent(event); - return -1; - case ERROR: - throw cRuntimeError("Clock event is overdue"); - default: - throw cRuntimeError("Unknown overdue clock event handling mode"); - } +} + +simtime_t ServoClockBase::handleOverdueClockEvent(ClockEvent *event, simtime_t t) +{ + auto mode = event->getOverdueClockEventHandlingMode(); + if (mode == UNSPECIFIED) + mode = defaultOverdueClockEventHandlingMode; + + switch (mode) { + case EXECUTE: + EV_WARN << "Scheduling overdue clock event " << event->getName() << " to current simulation time.\n"; + return t; + case SKIP: + EV_WARN << "Skipping overdue clock event " << event->getName() << ".\n"; + cancelClockEvent(event); + return -1; + case ERROR: + throw cRuntimeError("Clock event is overdue"); + default: + throw cRuntimeError("Unknown overdue clock event handling mode"); } - - void ServoClockBase::processCommand(const cXMLElement& node) - { - Enter_Method("processCommand"); - if (!strcmp(node.getTagName(), "adjust-clock")) { - clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); - ppm oscillatorCompensation = ppm(xmlutils::getAttributeDoubleValue(&node, "oscillator-compensation", 0)); - bool isResetOscillator = xmlutils::getAttributeBoolValue(&node, "reset-oscillator", true); - - adjustClockTime(time); - if (isResetOscillator) - resetOscillator(); - setOscillatorCompensation(oscillatorCompensation); - -// setClockTime(time, oscillatorCompensation, resetOscillator); - } - else - throw cRuntimeError("Invalid command: %s", node.getTagName()); +} + +void ServoClockBase::processCommand(const cXMLElement &node) +{ + Enter_Method("processCommand"); + if (!strcmp(node.getTagName(), "adjust-clock")) { + clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); + adjustClockTime(time); } - + if (!strcmp(node.getTagName(), "set-clock")) { + clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); + setClockTime(time); + } + // TODO: Add two more options for the scenario manager here. + // One two update the oscillator compensation + // One to reset the oscillator + else + throw cRuntimeError("Invalid command: %s", node.getTagName()); +} + +void ServoClockBase::setClockTime(clocktime_t newClockTime) { + auto oldClockTime = getClockTime(); + + if (newClockTime != oldClockTime) { + emit(timeChangedSignal, oldClockTime.asSimTime()); + simtime_t currentSimTime = simTime(); + EV_DEBUG << "Setting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " + << currentSimTime << ".\n"; + originSimulationTime = simTime(); + originClockTime = newClockTime; + + ASSERT(newClockTime == getClockTime()); + rescheduleClockEvents(oldClockTime, newClockTime); + emit(timeChangedSignal, newClockTime.asSimTime()); + } +} } // namespace inet - diff --git a/src/inet/clock/model/ServoClockBase.h b/src/inet/clock/model/ServoClockBase.h index 0640a0b13d8..6b41c5bc60a 100644 --- a/src/inet/clock/model/ServoClockBase.h +++ b/src/inet/clock/model/ServoClockBase.h @@ -8,32 +8,35 @@ #define __INET_SERVOCLOCKBASE_H #include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/common/scenario/IScriptable.h" -#include "inet/common/XMLUtils.h" - #include "inet/clock/oscillator/ConstantDriftOscillator.h" +#include "inet/common/XMLUtils.h" +#include "inet/common/scenario/IScriptable.h" namespace inet { -class ServoClockBase : public OscillatorBasedClock, public IScriptable { +class ServoClockBase : public OscillatorBasedClock, public IScriptable +{ -protected: + protected: OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; - ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value means the clock compensates 100 microseconds for every second in clock time - // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) than the actual tick length measured in clock time + ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value + // means the clock compensates 100 microseconds for every second in clock time + // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) + // than the actual tick length measured in clock time -protected: + protected: virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime); virtual simtime_t handleOverdueClockEvent(ClockEvent *event, simtime_t t); virtual void initialize(int stage) override; - virtual void processCommand(const cXMLElement& node) override; + virtual void processCommand(const cXMLElement &node) override; + + public: + virtual ppm getOscillatorCompensation() const override { return oscillatorCompensation; } -public: virtual void adjustClockTime(clocktime_t newClockTime) = 0; + virtual void setClockTime(clocktime_t newClockTime); virtual void setOscillatorCompensation(ppm oscillatorCompensationValue); virtual void resetOscillator() const; - - }; } /* namespace inet */ diff --git a/src/inet/clock/model/SettableClock.cc b/src/inet/clock/model/SettableClock.cc deleted file mode 100644 index 86a84911857..00000000000 --- a/src/inet/clock/model/SettableClock.cc +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (C) 2020 OpenSim Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - - -#include "inet/clock/model/SettableClock.h" - -namespace inet { - -Define_Module(SettableClock); - - -void SettableClock::adjustClockTime(clocktime_t newClockTime) -{ - Enter_Method("adjustClockTime"); - clocktime_t oldClockTime = getClockTime(); - clocktime_t diff = newClockTime - oldClockTime; - - if (newClockTime != oldClockTime) { - emit(timeChangedSignal, oldClockTime.asSimTime()); - - simtime_t currentSimTime = simTime(); - EV_DEBUG << "Setting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " << currentSimTime << ".\n"; - originSimulationTime = simTime(); - originClockTime = newClockTime; - - ASSERT(newClockTime == getClockTime()); - rescheduleClockEvents(oldClockTime, newClockTime); - emit(timeChangedSignal, newClockTime.asSimTime()); - - } - -} - -} // namespace inet - diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index cb14f937908..4b35a894d6d 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -8,7 +8,7 @@ #include "Gptp.h" #include "GptpPacket_m.h" -#include "inet/clock/model/PiClock.h" +#include "inet/clock/model/PiServoClock.h" #include "inet/clock/model/ServoClockBase.h" #include "inet/common/IProtocolRegistrationListener.h" #include "inet/common/clock/ClockUserModuleBase.h" @@ -611,7 +611,7 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU nrrCalculationSetCurrent++; } -// neighborRateRatio = 1; + neighborRateRatio = 1; // See 11.2.19.3.4 computePropTime() and Figure11-1 in IEEE 802.1AS-2020 auto t4 = pDelayRespIngressTimestamp; diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 3cb61bd6614..4cd37a10298 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -12,7 +12,7 @@ #include "inet/clock/common/ClockTime.h" #include "inet/clock/contract/ClockTime.h" -#include "inet/clock/model/SettableClock.h" +#include "inet/clock/model/InstantServoClock.h" #include "inet/common/INETDefs.h" #include "inet/common/ModuleRefByPar.h" #include "inet/common/clock/ClockUserModuleBase.h" diff --git a/src/inet/node/tsn/TsnDevice.ned b/src/inet/node/tsn/TsnDevice.ned index d7f51ae8140..36c842134ba 100644 --- a/src/inet/node/tsn/TsnDevice.ned +++ b/src/inet/node/tsn/TsnDevice.ned @@ -30,12 +30,12 @@ module TsnDevice extends StandardHost bool hasFramePreemption = default(false); // Enable IEEE 802.1 Qbu frame preemption bool hasCutthroughSwitching = default(false); // Enable cut-through switching support bool hasBridging = default(hasIncomingStreams || hasOutgoingStreams || hasStreamRedundancy || hasIngressTrafficFiltering || hasEgressTrafficFiltering); - clock.typename = default(hasTimeSynchronization ? "PiClock" : ""); // Enable explicit local clock model + clock.typename = default(hasTimeSynchronization ? "PiServoClock" : ""); // Enable explicit local clock model ethernet.typename = default("EthernetLayer"); // Use Ethernet protocol layer outside of network interfaces eth[*].typename = default("LayeredEthernetInterface"); // Switch to modular Ethernet interface eth[*].macLayer.typename = default(hasFramePreemption ? "EthernetPreemptingMacLayer" : "EthernetMacLayer"); eth[*].macLayer.queue.typename = default(hasEgressTrafficShaping ? "Ieee8021qTimeAwareShaper" : (hasFramePreemption ? "" : "PacketQueue")); // Use priority queue having multiple subqueues controlled by separate gates - eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamingPhyLayer" : (hasFramePreemption ? "EthernetPreemptingPhyLayer" : "EthernetPhyLayer")); // Use packet streaming when cut-through switching is enabled + eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamThroughPhyLayer" : (hasFramePreemption ? "EthernetPreemptingPhyLayer" : (hasTimeSynchronization ? "EthernetStreamingPhyLayer" : "EthernetPhyLayer"))); // use packet streaming when cut-through switching is enabled bridging.typename = default(hasBridging ? "BridgingLayer" : ""); // Switch to modular bridging bridging.interfaceRelay.typename = default(""); // Disable frame relaying bridging.streamIdentifier.typename = default(hasOutgoingStreams || hasStreamRedundancy ? "StreamIdentifierLayer" : ""); // Enable stream identification when stream redundancy is enabled diff --git a/src/inet/node/tsn/TsnSwitch.ned b/src/inet/node/tsn/TsnSwitch.ned index 68eb0f36591..80cd0df6f4c 100644 --- a/src/inet/node/tsn/TsnSwitch.ned +++ b/src/inet/node/tsn/TsnSwitch.ned @@ -30,12 +30,12 @@ module TsnSwitch extends EthernetSwitch hasGptp = default(hasTimeSynchronization); // Enable gPTP protocol gptp.gptpNodeType = default("BRIDGE_NODE"); // Configure gPTP bridge node type gptp.slavePort = default("eth0"); // Configure default gPTP bridge slave port - clock.typename = default(hasTimeSynchronization ? "PiClock" : ""); // Enable explicit local clock model when time synchronization is enabled + clock.typename = default(hasTimeSynchronization ? "PiServoClock" : ""); // Enable explicit local clock model when time synchronization is enabled ethernet.typename = default("EthernetLayer"); // Use Ethernet protocol layer outside of network interfaces eth[*].typename = default(hasCutthroughSwitching ? "EthernetCutthroughInterface" : "LayeredEthernetInterface"); // Switch to modular Ethernet interface eth[*].macLayer.typename = default(hasFramePreemption ? "EthernetPreemptingMacLayer" : "EthernetMacLayer"); eth[*].macLayer.queue.typename = default(hasEgressTrafficShaping ? "Ieee8021qTimeAwareShaper" : "PacketQueue"); // Use compound priority queue having multiple subqueues controlled by separate gates when egress traffic shaping is enabled - eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamThroughPhyLayer" : (hasFramePreemption ? "EthernetPreemptingPhyLayer" : "EthernetPhyLayer")); // Use packet streaming when cut-through switching is enabled + eth[*].phyLayer.typename = default(hasCutthroughSwitching ? "EthernetStreamThroughPhyLayer" : (hasFramePreemption ? "EthernetPreemptingPhyLayer" : (hasTimeSynchronization ? "EthernetStreamingPhyLayer" : "EthernetPhyLayer"))); // Use packet streaming when cut-through switching is enabled bridging.typename = default("BridgingLayer"); // Switch to modular bridging bridging.directionReverser.cutthroughBarrier.typename = default(hasCutthroughSwitching ? "EthernetCutthroughBarrier" : ""); // Enable cut-through barrier when cut-through switching is enabled bridging.streamIdentifier.typename = default(hasOutgoingStreams || hasStreamRedundancy ? "StreamIdentifierLayer" : ""); // Enable stream identification when stream redundancy is enabled From 604040d9127d4291d0610b6e383b0ce4d65229e1 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Fri, 21 Jun 2024 21:35:59 +0200 Subject: [PATCH 065/138] Added timeJumpSignal to listen to outside time jumps. --- .../timesynchronization/gptp/GptpShowcase.ned | 15 + .../gptp/SteppingClock.anf | 636 ++++++++++++++++++ .../tsn/timesynchronization/gptp/omnetpp.ini | 14 +- .../gptp/scenario-stepping-master.xml | 2 +- .../clock/SimpleClockSynchronizer.cc | 4 +- src/inet/clock/model/InstantServoClock.cc | 6 +- src/inet/clock/model/InstantServoClock.h | 2 +- src/inet/clock/model/PiServoClock.cc | 6 +- src/inet/clock/model/PiServoClock.h | 2 +- src/inet/clock/model/ServoClockBase.cc | 14 +- src/inet/clock/model/ServoClockBase.h | 12 +- src/inet/clock/model/ServoClockBase.ned | 1 + src/inet/linklayer/ieee8021as/Gptp.cc | 85 ++- src/inet/linklayer/ieee8021as/Gptp.h | 52 +- 14 files changed, 771 insertions(+), 80 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.ned b/showcases/tsn/timesynchronization/gptp/GptpShowcase.ned index ae1ffb09719..80c9cbf0837 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.ned +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.ned @@ -70,6 +70,21 @@ network TwoMasterClocksTreeGptpShowcase extends TsnNetworkBase tsnSwitch2.ethg++ <--> EthernetLink <--> tsnDevice4.ethg++; } +network SteppingClockGptpShowcase extends TsnNetworkBase +{ + submodules: + scenarioManager: ScenarioManager; + tsnClock: TsnClock { + @display("p=500,150"); + } + tsnDevice: TsnDevice { + @display("p=500,300"); + } + + connections: + tsnClock.ethg++ <--> EthernetLink <--> tsnDevice.ethg++; +} + network TwoMasterClocksRingGptpShowcase extends TsnNetworkBase { submodules: diff --git a/showcases/tsn/timesynchronization/gptp/SteppingClock.anf b/showcases/tsn/timesynchronization/gptp/SteppingClock.anf index 20f35659643..b58525ac1f9 100644 --- a/showcases/tsn/timesynchronization/gptp/SteppingClock.anf +++ b/showcases/tsn/timesynchronization/gptp/SteppingClock.anf @@ -641,5 +641,641 @@ utils.export_data_if_needed(df, props) + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 6fe5e393a5d..239a9bf1c3f 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -18,7 +18,7 @@ description = "abstract" **.kp=8 **.ki=7 -**.offsetThreshold = 0us +**.offsetThreshold = 0 us # all Ethernet interfaces have 100 Mbps speed @@ -107,14 +107,16 @@ description="Study effect of kp and ki" **.kp = ${kp=0,7.0,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8} **.ki = ${ki=0,2,3,4,5,6,7,8,9,10} + [SteppingClock] -extends=OneMasterClock +extends = OneMasterClock +network = SteppingClockGptpShowcase description="See how the clock behaves when the master clock steps 1us" *.scenarioManager.script = xmldoc("scenario-stepping-master.xml") -**.tsnClock.clock.typename = "SettableClock" -**.tsnClock*.clock.oscillator.typename = "IdealOscillator" -**.tsnSwitch*.clock.oscillator.typename = "ConstantDriftOscillator" -**.tsnDevice*.clock.oscillator.typename = "ConstantDriftOscillator" +*.tsnClock.gptp.masterPorts = ["eth0"] +*.tsnClock.clock.typename = "InstantServoClock" +*.tsnClock.clock.oscillator.typename = "IdealOscillator" +*.tsnDevice.clock.oscillator.typename = "ConstantDriftOscillator" [Config PrimaryAndHotStandbyMasterClocks] diff --git a/showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml b/showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml index c73c2b82e50..9580cb90542 100644 --- a/showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml +++ b/showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml @@ -1,7 +1,7 @@ - + diff --git a/src/inet/applications/clock/SimpleClockSynchronizer.cc b/src/inet/applications/clock/SimpleClockSynchronizer.cc index 5b107321ec3..497b07a4908 100644 --- a/src/inet/applications/clock/SimpleClockSynchronizer.cc +++ b/src/inet/applications/clock/SimpleClockSynchronizer.cc @@ -57,8 +57,8 @@ void SimpleClockSynchronizer::synchronizeSlaveClock() ppm oscillatorCompensation = unit(getCurrentRelativeTickLength(slaveClock.get()) / getCurrentRelativeTickLength(masterClock.get()) * (1 + unit(masterOscillatorBasedClock->getOscillatorCompensation()).get()) * (1 + unit(ppm(synchronizationOscillatorCompensationErrorParameter->doubleValue())).get()) - 1); -// slaveClock->setClockTime(clockTime, oscillatorCompensation, true); - slaveClock->adjustClockTime(clockTime); +// slaveClock->jumpClockTo(clockTime, oscillatorCompensation, true); + slaveClock->adjustClockTo(clockTime); slaveClock->resetOscillator(); slaveClock->setOscillatorCompensation(oscillatorCompensation); diff --git a/src/inet/clock/model/InstantServoClock.cc b/src/inet/clock/model/InstantServoClock.cc index 1b895de346c..983462efac0 100644 --- a/src/inet/clock/model/InstantServoClock.cc +++ b/src/inet/clock/model/InstantServoClock.cc @@ -12,10 +12,10 @@ namespace inet { Define_Module(InstantServoClock); -void InstantServoClock::adjustClockTime(clocktime_t newClockTime) +void InstantServoClock::adjustClockTo(clocktime_t newClockTime) { - Enter_Method("adjustClockTime"); - setClockTime(newClockTime); + Enter_Method("adjustClockTo"); + jumpClockTo(newClockTime); // TODO: Add a mechanism that estimates the drift rate based on the previous and current local and received // timestamps, similar to case 0 and 1 in PiServoClock diff --git a/src/inet/clock/model/InstantServoClock.h b/src/inet/clock/model/InstantServoClock.h index 28f68a5d4dd..74e9ef21fe0 100644 --- a/src/inet/clock/model/InstantServoClock.h +++ b/src/inet/clock/model/InstantServoClock.h @@ -23,7 +23,7 @@ class INET_API InstantServoClock : public ServoClockBase * Sets the clock time immediately to the given value. Greater than 1 oscillator * compensation factor means the clock measures time faster. */ - virtual void adjustClockTime(clocktime_t newClockTime) override; + virtual void adjustClockTo(clocktime_t newClockTime) override; }; diff --git a/src/inet/clock/model/PiServoClock.cc b/src/inet/clock/model/PiServoClock.cc index ca887e0c08c..14ce9341a12 100644 --- a/src/inet/clock/model/PiServoClock.cc +++ b/src/inet/clock/model/PiServoClock.cc @@ -28,8 +28,8 @@ void PiServoClock::initialize(int stage) } } -void PiServoClock::adjustClockTime(clocktime_t newClockTime) { - Enter_Method("adjustClockTime"); +void PiServoClock::adjustClockTo(clocktime_t newClockTime) { + Enter_Method("adjustClockTo"); clocktime_t oldClockTime = getClockTime(); if (newClockTime != oldClockTime) { @@ -67,7 +67,7 @@ void PiServoClock::adjustClockTime(clocktime_t newClockTime) { drift += ppm(1e6 * (offsetNsPrev - offsetNs) / (localNsPrev - localNs)); EV_INFO << "Drift: " << drift << "\n"; - setClockTime(newClockTime); + jumpClockTo(newClockTime); setOscillatorCompensation(drift); diff --git a/src/inet/clock/model/PiServoClock.h b/src/inet/clock/model/PiServoClock.h index 9ac59ce947c..a1c3e0c354f 100644 --- a/src/inet/clock/model/PiServoClock.h +++ b/src/inet/clock/model/PiServoClock.h @@ -40,7 +40,7 @@ class INET_API PiServoClock : public ServoClockBase virtual void initialize(int stage) override; public: - void adjustClockTime(clocktime_t newClockTime) override; + void adjustClockTo(clocktime_t newClockTime) override; }; } // namespace inet diff --git a/src/inet/clock/model/ServoClockBase.cc b/src/inet/clock/model/ServoClockBase.cc index fec3aa14f0c..34866556c9b 100644 --- a/src/inet/clock/model/ServoClockBase.cc +++ b/src/inet/clock/model/ServoClockBase.cc @@ -9,6 +9,7 @@ namespace inet { Register_Abstract_Class(ServoClockBase); +simsignal_t ServoClockBase::clockJumpSignal = cComponent::registerSignal("clockJump"); void ServoClockBase::initialize(int stage) { @@ -87,11 +88,12 @@ void ServoClockBase::processCommand(const cXMLElement &node) Enter_Method("processCommand"); if (!strcmp(node.getTagName(), "adjust-clock")) { clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); - adjustClockTime(time); + adjustClockTo(time); } if (!strcmp(node.getTagName(), "set-clock")) { clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); - setClockTime(time); + bool notifyListeners = xmlutils::getAttributeBoolValue(&node, "notifyListeners", true); + jumpClockTo(time, notifyListeners); } // TODO: Add two more options for the scenario manager here. // One two update the oscillator compensation @@ -100,7 +102,7 @@ void ServoClockBase::processCommand(const cXMLElement &node) throw cRuntimeError("Invalid command: %s", node.getTagName()); } -void ServoClockBase::setClockTime(clocktime_t newClockTime) { +void ServoClockBase::jumpClockTo(clocktime_t newClockTime, bool notifyListeners) { auto oldClockTime = getClockTime(); if (newClockTime != oldClockTime) { @@ -113,6 +115,12 @@ void ServoClockBase::setClockTime(clocktime_t newClockTime) { ASSERT(newClockTime == getClockTime()); rescheduleClockEvents(oldClockTime, newClockTime); + ClockJumpDetails timeJumpDetails = ClockJumpDetails(); + timeJumpDetails.oldClockTime = oldClockTime; + timeJumpDetails.newClockTime = newClockTime; + if (notifyListeners) { + emit(clockJumpSignal, this, &timeJumpDetails); + } emit(timeChangedSignal, newClockTime.asSimTime()); } } diff --git a/src/inet/clock/model/ServoClockBase.h b/src/inet/clock/model/ServoClockBase.h index 6b41c5bc60a..9ed2efc3e53 100644 --- a/src/inet/clock/model/ServoClockBase.h +++ b/src/inet/clock/model/ServoClockBase.h @@ -16,6 +16,14 @@ namespace inet { class ServoClockBase : public OscillatorBasedClock, public IScriptable { + public: + struct ClockJumpDetails : public cObject { + clocktime_t oldClockTime; + clocktime_t newClockTime; + }; + + public: + static simsignal_t clockJumpSignal; protected: OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; @@ -33,8 +41,8 @@ class ServoClockBase : public OscillatorBasedClock, public IScriptable public: virtual ppm getOscillatorCompensation() const override { return oscillatorCompensation; } - virtual void adjustClockTime(clocktime_t newClockTime) = 0; - virtual void setClockTime(clocktime_t newClockTime); + virtual void adjustClockTo(clocktime_t newClockTime) = 0; + virtual void jumpClockTo(clocktime_t newClockTime, bool notifyListeners = true); virtual void setOscillatorCompensation(ppm oscillatorCompensationValue); virtual void resetOscillator() const; }; diff --git a/src/inet/clock/model/ServoClockBase.ned b/src/inet/clock/model/ServoClockBase.ned index d13ce10afd9..52693ef88d9 100644 --- a/src/inet/clock/model/ServoClockBase.ned +++ b/src/inet/clock/model/ServoClockBase.ned @@ -18,6 +18,7 @@ module ServoClockBase extends OscillatorBasedClock { parameters: string defaultOverdueClockEventHandlingMode @enum("execute","skip","error") = default("error"); + @signal[clockJump](); @class(ServoClockBase); } diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 4b35a894d6d..df4afca8b7e 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -137,6 +137,10 @@ void Gptp::initialize(int stage) pdelayInterval = par("pdelayInterval"); scheduleClockEventAfter(par("pdelayInitialOffset"), selfMsgDelayReq); } + + auto servoClock = check_and_cast(clock.get()); + servoClock->subscribe(ServoClockBase::clockJumpSignal, this); + WATCH(meanLinkDelay); } } @@ -442,7 +446,8 @@ void Gptp::synchronize() // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio clocktime_t newTime = preciseOriginTimestamp + correctionField + gmRateRatio * (meanLinkDelay + residenceTime); - auto piControlClock = check_and_cast(clock.get()); + + auto servoClock = check_and_cast(clock.get()); // Only change the oscillator if we have new information about our nrr // TODO: We should change this to a clock servo model in the future anyways! @@ -455,12 +460,11 @@ void Gptp::synchronize() // unit(gmRateRatio * (1 + unit(piControlClock->getOscillatorCompensation()).get()) - 1); // hasNewRateRatioForOscillatorCompensation = false; // } - piControlClock->adjustClockTime(newTime); + servoClock->adjustClockTo(newTime); // EV_INFO << "newOscillatorCompensation " << newOscillatorCompensation << endl; newLocalTimeAtTimeSync = clock->getClockTime(); - timeDiffAtTimeSync = newLocalTimeAtTimeSync - oldLocalTimeAtTimeSync; /************** Rate ratio calculation ************************************* @@ -471,8 +475,8 @@ void Gptp::synchronize() EV_INFO << "LOCAL TIME BEFORE SYNC - " << oldLocalTimeAtTimeSync << endl; EV_INFO << "LOCAL TIME AFTER SYNC - " << newLocalTimeAtTimeSync << endl; EV_INFO << "CALCULATED NEW TIME - " << newTime << endl; - if (piControlClock->referenceClockModule != nullptr) { - auto referenceClockTime = piControlClock->referenceClockModule->getClockTime(); + if (servoClock->referenceClockModule != nullptr) { + auto referenceClockTime = servoClock->referenceClockModule->getClockTime(); auto diffReferenceToOldLocal = oldLocalTimeAtTimeSync - referenceClockTime; auto diffReferenceToNewTime = newTime - referenceClockTime; EV_INFO << "REFERENCE CLOCK TIME - " << referenceClockTime << endl; @@ -492,25 +496,6 @@ void Gptp::synchronize() EV_INFO << "RECIEVED RATE RATIO - " << receivedRateRatio << endl; EV_INFO << "GM RATE RATIO - " << gmRateRatio << endl; - if (newLocalTimeAtTimeSync != oldLocalTimeAtTimeSync) { - adjustLocalTimestamp(syncIngressTimestamp); - adjustLocalTimestamp(pDelayReqEgressTimestamp); - adjustLocalTimestamp(pDelayRespIngressTimestamp); - adjustLocalTimestamp(pDelayRespIngressTimestampSetStart); - EV_INFO << "############## Adjusted times #####################################" << endl; - EV_INFO << "SYNC INGRESS TIME - " << syncIngressTimestamp << endl; - EV_INFO << "PDELAY REQ EGRESS TIME - " << pDelayReqEgressTimestamp << endl; - EV_INFO << "PDELAY RESP INGRESS TIME - " << pDelayRespIngressTimestamp << endl; - EV_INFO << "PDELAY RESP INGRESS TIME SET- " << pDelayRespIngressTimestampSetStart << endl; - // NOTE: Do not pDelayReqIngressTimestamp and pDelayRespEgressTimestamp, because they are based on neighbor clock - } - - - // adjustLocalTimestamp(syncIngressTimestamp); - // adjustLocalTimestamp(syncIngressTimestampLast); - // adjustLocalTimestamp(preciseOriginTimestamp); - // adjustLocalTimestamp(preciseOriginTimestampLast); - syncIngressTimestampLast = syncIngressTimestamp; preciseOriginTimestampLast = preciseOriginTimestamp; @@ -602,7 +587,6 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU if (nrrCalculationSetCurrent == nrrCalculationSetMaximum) { neighborRateRatio = (pDelayRespEgressTimestamp - pDelayRespEgressTimestampSetStart) / (pDelayRespIngressTimestamp - pDelayRespIngressTimestampSetStart); - hasNewRateRatioForOscillatorCompensation = true; pDelayRespEgressTimestampSetStart = -1; pDelayRespIngressTimestampSetStart = -1; nrrCalculationSetCurrent = 0; @@ -656,6 +640,13 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj { Enter_Method("%s", cComponent::getSignalName(simSignal)); + auto servoClock = check_and_cast(clock.get()); + + if (simSignal == ServoClockBase::clockJumpSignal && obj == servoClock) { + auto clockJumpDetails = check_and_cast(details); + handleClockJump(clockJumpDetails); + } + if (simSignal != receptionStartedSignal && simSignal != transmissionStartedSignal && simSignal != receptionEndedSignal) return; @@ -711,4 +702,48 @@ void Gptp::handleTransmissionStartedSignal(const GptpBase *gptp, cComponent *sou } } +void Gptp::handleClockJump(ServoClockBase::ClockJumpDetails *clockJumpDetails) { + EV_INFO << "############## Adjust local timestamps #################################" << endl; + EV_INFO << "BEFORE:" << endl; + EV_INFO << "SYNC INGRESS TIME - " << syncIngressTimestamp << endl; + EV_INFO << "SYNC INGRESS TIME LAST - " << syncIngressTimestampLast << endl; + EV_INFO << "PDELAY REQ EGRESS TIME - " << pDelayReqEgressTimestamp << endl; + EV_INFO << "PDELAY RESP INGRESS TIME - " << pDelayRespIngressTimestamp << endl; + EV_INFO << "PDELAY RESP INGRESS TIME SET- " << pDelayRespIngressTimestampSetStart << endl; + + auto timeDiff = clockJumpDetails->newClockTime - clockJumpDetails->oldClockTime; + adjustLocalTimestamp(syncIngressTimestamp, timeDiff); + adjustLocalTimestamp(syncIngressTimestampLast, timeDiff); + adjustLocalTimestamp(pDelayReqEgressTimestamp, timeDiff); + adjustLocalTimestamp(pDelayRespIngressTimestamp, timeDiff); + adjustLocalTimestamp(pDelayRespIngressTimestampSetStart, timeDiff); + // NOTE: Do not pDelayReqIngressTimestamp and pDelayRespEgressTimestamp, because they are based on neighbor clock + + EV_INFO << "AFTER:" << endl; + EV_INFO << "SYNC INGRESS TIME - " << syncIngressTimestamp << endl; + EV_INFO << "SYNC INGRESS TIME LAST - " << syncIngressTimestampLast << endl; + EV_INFO << "PDELAY REQ EGRESS TIME - " << pDelayReqEgressTimestamp << endl; + EV_INFO << "PDELAY RESP INGRESS TIME - " << pDelayRespIngressTimestamp << endl; + EV_INFO << "PDELAY RESP INGRESS TIME SET- " << pDelayRespIngressTimestampSetStart << endl; + + // This is a very special case, that only occurs when a clock jump occurs between the receptionStarted and + // receptionEnded signal. + // + // You can see this in action in the SteppingClock showcase (At t=9s we do not notify the gPTP module about the clock + // jump, this leads to an incorrect peer delay calculation). + // We want to allow the gPTP module to still work correctly in this case, so we make this adjustment here. + for (auto &entry : ingressTimeMap) { + EV_INFO << " Before Ingress time: " << entry.first << " - " << entry.second << endl; + adjustLocalTimestamp(entry.second, timeDiff); + EV_INFO << " After Ingress time: " << entry.first << " - " << entry.second << endl; + } + // NOTE: There is a special case this does not solve. If the time jump would occur between the reception ended + // and the processing of the packet in the gPTP module the same problem as described above would occur. + // However, this would require a processing delay between the reception ended and the processing of the packet in + // the gPTP module, which to my knowledge is currently not configurable in the INET modules. + // Should this behavior change in the future, this timestamping adjustment mechanism needs to be changed as well to + // somehow also adjust the timestamps already attaches as tags to the gPTP packets. +} + } // namespace inet + diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 4cd37a10298..3fbcf092737 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -12,7 +12,7 @@ #include "inet/clock/common/ClockTime.h" #include "inet/clock/contract/ClockTime.h" -#include "inet/clock/model/InstantServoClock.h" +#include "inet/clock/model/ServoClockBase.h" #include "inet/common/INETDefs.h" #include "inet/common/ModuleRefByPar.h" #include "inet/common/clock/ClockUserModuleBase.h" @@ -42,7 +42,6 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener double gmRateRatio = 1.0; double receivedRateRatio = 1.0; double neighborRateRatio = 1.0; - bool hasNewRateRatioForOscillatorCompensation = false; uint16_t sequenceId = 0; // == Propagation Delay Measurement Procedure == @@ -51,8 +50,8 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener clocktime_t pDelayReqIngressTimestamp = -1; // ingress time of pdelay_req at responder clocktime_t pDelayRespEgressTimestamp = -1; // egress time of pdelay_resp at responder (received in PDelayRespFollowUp) clocktime_t pDelayRespEgressTimestampSetStart = -1; // egress time of previous pdelay_resp at responder (received in PDelayRespFollowUp) - clocktime_t pDelayRespIngressTimestamp = -1; // ingress time of pdelay_resp at initiator (this node) - clocktime_t pDelayRespIngressTimestampSetStart = -1; // ingress time of previous pdelay_resp at initiator (this node) + clocktime_t pDelayRespIngressTimestamp = -1; // ingress time of pdelay_resp at initiator (this node) + clocktime_t pDelayRespIngressTimestampSetStart = -1; // ingress time of previous pdelay_resp at initiator (this node) int nrrCalculationSetMaximum = 1; // TODO: Make this a settable parameter int nrrCalculationSetCurrent = 0; @@ -67,17 +66,16 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener // Unsure why this is also configurable with a parameter (TODO: Check) clocktime_t correctionField = CLOCKTIME_ZERO; - clocktime_t preciseOriginTimestamp = -1; // timestamp when the last sync message was generated at the GM + clocktime_t preciseOriginTimestamp = -1; // timestamp when the last sync message was generated at the GM clocktime_t preciseOriginTimestampLast = -1; // timestamp when the last sync message was generated at the GM - clocktime_t syncIngressTimestamp = -1; // ingress time of Sync at slave (this node) - clocktime_t syncIngressTimestampLast = -1; // ingress time of previous Sync at slave (this node) + clocktime_t syncIngressTimestamp = -1; // ingress time of Sync at slave (this node) + clocktime_t syncIngressTimestampLast = -1; // ingress time of previous Sync at slave (this node) bool rcvdGptpSync = false; uint16_t lastReceivedGptpSyncSequenceId = 0xffff; clocktime_t newLocalTimeAtTimeSync; - clocktime_t timeDiffAtTimeSync; // new local time - old local time // self timers: ClockEvent *selfMsgSync = nullptr; @@ -96,37 +94,28 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener static simsignal_t correctionFieldEgressSignal; // Packet receive signals: - std::map ingressTimeMap; // ingressTimeMap; // Date: Sat, 22 Jun 2024 17:44:18 +0200 Subject: [PATCH 066/138] Cleanup --- .../timesynchronization/gptp/GptpShowcase.anf | 2412 ++++------------- .../timesynchronization/gptp/GptpShowcase.ned | 60 +- .../gptp/Gptp_accumulation_error.ned | 35 - .../gptp/SteppingClock.anf | 1281 --------- .../tsn/timesynchronization/gptp/omnetpp.ini | 8 +- ...-master.xml => scenario-jumping-clock.xml} | 0 src/inet/clock/model/ServoClockBase.cc | 2 +- 7 files changed, 619 insertions(+), 3179 deletions(-) delete mode 100644 showcases/tsn/timesynchronization/gptp/Gptp_accumulation_error.ned delete mode 100644 showcases/tsn/timesynchronization/gptp/SteppingClock.anf rename showcases/tsn/timesynchronization/gptp/{scenario-stepping-master.xml => scenario-jumping-clock.xml} (100%) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 0149daccc4c..b5c33671c37 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -9,6 +9,8 @@ + + @@ -3775,16 +3777,10 @@ utils.export_data_if_needed(df, props) - - + + - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -12695,7 +11411,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -12789,7 +11505,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -12947,7 +11663,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13014,7 +11730,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13125,7 +11841,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13145,7 +11861,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13201,56 +11917,61 @@ utils.export_data_if_needed(df, props) ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -13331,7 +12055,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13425,7 +12149,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13583,7 +12307,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13650,7 +12374,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13761,7 +12485,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13781,7 +12505,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13837,56 +12561,61 @@ utils.export_data_if_needed(df, props) ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -13967,7 +12699,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -14061,7 +12793,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -14219,7 +12951,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -14286,7 +13018,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -14397,7 +13129,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -14417,7 +13149,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -14473,53 +13205,53 @@ utils.export_data_if_needed(df, props) ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 239a9bf1c3f..5fbfda25264 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -108,11 +108,11 @@ description="Study effect of kp and ki" **.ki = ${ki=0,2,3,4,5,6,7,8,9,10} -[SteppingClock] +[JumpingClock] extends = OneMasterClock -network = SteppingClockGptpShowcase -description="See how the clock behaves when the master clock steps 1us" -*.scenarioManager.script = xmldoc("scenario-stepping-master.xml") +network = JumpingClockGptpShowcase +description="See how the clock behaves when the master clock jumps 1us" +*.scenarioManager.script = xmldoc("scenario-jumping-clock.xml") *.tsnClock.gptp.masterPorts = ["eth0"] *.tsnClock.clock.typename = "InstantServoClock" *.tsnClock.clock.oscillator.typename = "IdealOscillator" diff --git a/showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml b/showcases/tsn/timesynchronization/gptp/scenario-jumping-clock.xml similarity index 100% rename from showcases/tsn/timesynchronization/gptp/scenario-stepping-master.xml rename to showcases/tsn/timesynchronization/gptp/scenario-jumping-clock.xml diff --git a/src/inet/clock/model/ServoClockBase.cc b/src/inet/clock/model/ServoClockBase.cc index 34866556c9b..9e9aedda427 100644 --- a/src/inet/clock/model/ServoClockBase.cc +++ b/src/inet/clock/model/ServoClockBase.cc @@ -54,7 +54,7 @@ void ServoClockBase::rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t if (isOverdue || arrivalSimTime < currentSimTime) arrivalSimTime = handleOverdueClockEvent(event, currentSimTime); if (event->isScheduled()) { - cSimpleModule *targetModule = check_and_cast(event->getArrivalModule()); + auto *targetModule = check_and_cast(event->getArrivalModule()); cContextSwitcher contextSwitcher(targetModule); targetModule->rescheduleAt(arrivalSimTime, event); } From f0aec78c436c0270577dd0c6b22932b456baf265 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Sat, 22 Jun 2024 21:16:53 +0200 Subject: [PATCH 067/138] Multihop Setup --- .../timesynchronization/gptp/GptpShowcase.anf | 182 ++++++++++++------ .../timesynchronization/gptp/GptpShowcase.ned | 6 +- .../tsn/timesynchronization/gptp/omnetpp.ini | 39 ++-- 3 files changed, 140 insertions(+), 87 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index b5c33671c37..f749d34f1be 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -11,10 +11,12 @@ + + - - + + - + @@ -13348,7 +13338,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13442,7 +13432,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -13565,13 +13555,6 @@ utils.export_data_if_needed(df, props) - - ]]> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 24618ec4759..c4375818dd5 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -38,6 +38,9 @@ description = "Basic tree topology with one master clock" #*.*.gptp.masterClockModule = "^.^.tsnClock.clock" #*.*.gptp.slaveClockModule = "^.clock" +*.tsnDevice1.clock.typename = "InstantServoClock" +*.tsnSwitch.clock.typename = "InstantServoClock" + # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] @@ -70,8 +73,9 @@ num-rngs = 2 seed-1-mt = 134 **.tsnClock.**.rng-0 = 1 +**.clock.oscillator.typename = "RandomDriftOscillator" -**.numSwitches = ${numHops=1,10,50,100} +*.numSwitches = ${numHops=1,10,50,100} # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] @@ -104,6 +108,45 @@ description="Study effect of kp and ki" **.kp = ${kp=0,7.0,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8} **.ki = ${ki=0,2,3,4,5,6,7,8,9,10} +[Config RateRatioStudies] +network = MultiHopGptpShowcase +description = "Basic tree topology with one master clock and multiple devices" +num-rngs = 2 +seed-1-mt = 134 + +**.tsnClock.**.rng-0 = 1 +**.clock.oscillator.typename = "RandomDriftOscillator" + +*.numSwitches = 50 + +# TSN clock gPTP master ports +*.tsnClock.gptp.masterPorts = ["eth0"] + +# TSN switch gPTP bridge master ports +*.tsnSwitch[*].gptp.masterPorts = ["eth1"] + +# Set all reference clocks to master clock so the time difference can be visualized +**.referenceClock = "tsnClock.clock" + +# data link visualizer displays gPTP time synchronization packets +*.visualizer.dataLinkVisualizer[0].displayLinks = true +*.visualizer.dataLinkVisualizer[0].activityLevel = "protocol" +*.visualizer.dataLinkVisualizer[0].packetFilter = "GptpSync" +*.visualizer.dataLinkVisualizer[0].lineColor = "blue2" + +*.visualizer.numInfoVisualizers = 3 +*.visualizer.infoVisualizer[0].modules = "*.tsnClock.clock" +*.tsnClock.clock.displayStringTextFormat = "time: %T" +*.visualizer.infoVisualizer[1].modules = "*.tsnSwitch*.clock" +*.visualizer.infoVisualizer[1].placementHint = "top" +*.visualizer.infoVisualizer[2].modules = "*.tsnDevice.clock" +*.visualizer.infoVisualizer[2].placementHint = "bottom" +*.tsnDevice.clock.displayStringTextFormat = "diff: %d" +*.tsnSwitch*.clock.displayStringTextFormat = "diff: %d" + +**.useNrr = ${nrr=false,true} +**.gmRateRatioCalculationMethod = ${gmMethod="NONE","NRR","DIRECT"} + [JumpingClock] extends = OneMasterClock diff --git a/src/inet/clock/base/ClockBase.cc b/src/inet/clock/base/ClockBase.cc index 8ba33f5238c..11715f5d7b5 100644 --- a/src/inet/clock/base/ClockBase.cc +++ b/src/inet/clock/base/ClockBase.cc @@ -4,13 +4,14 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // - #include "inet/clock/base/ClockBase.h" + #include "inet/common/ModuleRefByPar.h" namespace inet { simsignal_t ClockBase::timeChangedSignal = cComponent::registerSignal("timeChanged"); +simsignal_t ClockBase::timeDifferenceToReferenceSignal = cComponent::registerSignal("timeDifferenceToReference"); void ClockBase::initialize(int stage) { @@ -24,6 +25,14 @@ void ClockBase::initialize(int stage) } else if (stage == INITSTAGE_LAST) { referenceClockModule.reference(this, "referenceClock", false); + // Subscribe + if (referenceClockModule != nullptr && dynamic_cast(referenceClockModule.get()) != nullptr && + this != referenceClockModule) + { + auto referenceClock = check_and_cast(referenceClockModule.get()); + this->subscribe(ClockBase::timeChangedSignal, this); + referenceClock->subscribe(ClockBase::timeChangedSignal, this); + } updateDisplayString(); emit(timeChangedSignal, getClockTime().asSimTime()); } @@ -39,15 +48,20 @@ void ClockBase::handleMessage(cMessage *msg) throw cRuntimeError("Unknown message"); } -void ClockBase::finish() +void ClockBase::receiveSignal(cComponent *source, int signal, const simtime_t& time, cObject *details) { - emit(timeChangedSignal, getClockTime().asSimTime()); + if (signal == ClockBase::timeChangedSignal) { + auto referenceTime = referenceClockModule->getClockTime(); + auto timeDifference = getClockTime() - referenceTime; + emit(timeDifferenceToReferenceSignal, timeDifference.asSimTime()); + } + else + throw cRuntimeError("Unknown signal"); } -void ClockBase::refreshDisplay() const -{ - updateDisplayString(); -} +void ClockBase::finish() { emit(timeChangedSignal, getClockTime().asSimTime()); } + +void ClockBase::refreshDisplay() const { updateDisplayString(); } void ClockBase::updateDisplayString() const { @@ -83,7 +97,8 @@ void ClockBase::scheduleClockEventAfter(clocktime_t clockTimeDelay, ClockEvent * clocktime_t nowClock = getClockTime(); clocktime_t arrivalClockTime = nowClock + clockTimeDelay; msg->setArrivalClockTime(arrivalClockTime); - simtime_t simTimeDelay = clockTimeDelay.isZero() ? SIMTIME_ZERO : computeSimTimeFromClockTime(arrivalClockTime) - simTime(); + simtime_t simTimeDelay = + clockTimeDelay.isZero() ? SIMTIME_ZERO : computeSimTimeFromClockTime(arrivalClockTime) - simTime(); targetModule->scheduleAfter(simTimeDelay, msg); } @@ -105,16 +120,15 @@ void ClockBase::handleClockEvent(ClockEvent *msg) std::string ClockBase::resolveDirective(char directive) const { switch (directive) { - case 't': - return getClockTime().str() + " s"; - case 'T': - return getClockTime().ustr(); - case 'd': - return (getClockTime() - referenceClockModule->getClockTime()).ustr(); - default: - throw cRuntimeError("Unknown directive: %c", directive); + case 't': + return getClockTime().str() + " s"; + case 'T': + return getClockTime().ustr(); + case 'd': + return (getClockTime() - referenceClockModule->getClockTime()).ustr(); + default: + throw cRuntimeError("Unknown directive: %c", directive); } } } // namespace inet - diff --git a/src/inet/clock/base/ClockBase.h b/src/inet/clock/base/ClockBase.h index 8e063f6da0d..d2856250e4d 100644 --- a/src/inet/clock/base/ClockBase.h +++ b/src/inet/clock/base/ClockBase.h @@ -16,10 +16,11 @@ namespace inet { -class INET_API ClockBase : public cSimpleModule, public IClock, public StringFormat::IDirectiveResolver +class INET_API ClockBase : public cSimpleModule, public IClock, public StringFormat::IDirectiveResolver, public cListener { public: static simsignal_t timeChangedSignal; + static simsignal_t timeDifferenceToReferenceSignal; ModuleRefByPar referenceClockModule; protected: @@ -51,6 +52,8 @@ class INET_API ClockBase : public cSimpleModule, public IClock, public StringFor virtual void scheduleClockEventAfter(clocktime_t time, ClockEvent *event) override; virtual ClockEvent *cancelClockEvent(ClockEvent *event) override; virtual void handleClockEvent(ClockEvent *event) override; + virtual void receiveSignal(cComponent *source, int signal, const simtime_t& time, cObject *details) override; + virtual std::string resolveDirective(char directive) const override; }; diff --git a/src/inet/clock/base/ClockBase.ned b/src/inet/clock/base/ClockBase.ned index cb11052b878..66261130ce3 100644 --- a/src/inet/clock/base/ClockBase.ned +++ b/src/inet/clock/base/ClockBase.ned @@ -19,6 +19,7 @@ module ClockBase @class(ClockBase); @display("i=block/timer"); @signal[timeChanged](type=simtime_t); + @signal[timeDifferenceToReference](type=simtime_t); @statistic[timeChanged](title="Clock time"; record=vector; interpolationmode=linear); + @statistic[timeDifferenceToReference](title="Time difference to reference"; record=vector; interpolationmode=linear); } - diff --git a/src/inet/clock/model/InstantServoClock.cc b/src/inet/clock/model/InstantServoClock.cc index cf17bc6b4fe..b4caeaa0668 100644 --- a/src/inet/clock/model/InstantServoClock.cc +++ b/src/inet/clock/model/InstantServoClock.cc @@ -21,7 +21,6 @@ void InstantServoClock::adjustClockTo(clocktime_t newClockTime) clocktime_t oldClockTime = getClockTime(); if (newClockTime != oldClockTime) { - clocktime_t oldClockTime = getClockTime(); switch (phase) { case 0: offset[0] = newClockTime - oldClockTime; @@ -48,7 +47,7 @@ void InstantServoClock::adjustClockTo(clocktime_t newClockTime) emit(timeChangedSignal, newClockTime.asSimTime()); offset[0] = offset[1]; - local[0] = local[1]; + local[0] = newClockTime; break; } } diff --git a/src/inet/clock/model/OscillatorBasedClock.h b/src/inet/clock/model/OscillatorBasedClock.h index c1c2a42ae94..696398f6bfa 100644 --- a/src/inet/clock/model/OscillatorBasedClock.h +++ b/src/inet/clock/model/OscillatorBasedClock.h @@ -16,7 +16,7 @@ namespace inet { using namespace units::values; -class INET_API OscillatorBasedClock : public ClockBase, public cListener +class INET_API OscillatorBasedClock : public ClockBase { protected: IOscillator *oscillator = nullptr; diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index df4afca8b7e..e8ae2281178 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -58,6 +58,14 @@ void Gptp::initialize(int stage) if (stage == INITSTAGE_LOCAL) { gptpNodeType = static_cast(cEnum::get("GptpNodeType", "inet")->resolve(par("gptpNodeType"))); + useNrr = par("useNrr"); + gmRateRatioCalculationMethod = static_cast( + cEnum::get("GmRateRatioCalculationMethod", "inet")->resolve(par("gmRateRatioCalculationMethod"))); + if (!useNrr && gmRateRatioCalculationMethod == GmRateRatioCalculationMethod::NRR) { + throw cRuntimeError( + "Parameter inconsistency: useNrr=false and gmRateRatioCalculationMethod=NEIGHBOR_RATE_RATIO"); + } + domainNumber = par("domainNumber"); syncInterval = par("syncInterval"); pDelayReqProcessingTime = par("pDelayReqProcessingTime"); @@ -97,7 +105,6 @@ void Gptp::initialize(int stage) nic->subscribe(transmissionStartedSignal, this); nic->subscribe(receptionStartedSignal, this); nic->subscribe(receptionEndedSignal, this); - } if (slavePortId != -1) { @@ -438,35 +445,45 @@ void Gptp::synchronize() ASSERT(gptpNodeType != MASTER_NODE); - gmRateRatio = receivedRateRatio * neighborRateRatio; - gmRateRatio = 1.0; + switch (gmRateRatioCalculationMethod) { + case GmRateRatioCalculationMethod::NONE: + gmRateRatio = 1.0; + break; + case GmRateRatioCalculationMethod::NRR: + gmRateRatio = receivedRateRatio * neighborRateRatio; + break; + case GmRateRatioCalculationMethod::DIRECT: + peerSentTimeSync = preciseOriginTimestamp + correctionField; + if (syncIngressTimestampLast != -1) { + gmRateRatio = (peerSentTimeSync - peerSentTimeSyncLast) / (syncIngressTimestamp - syncIngressTimestampLast); + } + peerSentTimeSyncLast = peerSentTimeSync; + break; + } // preciseOriginTimestamp and correctionField are in the grandmaster's time base // meanLinkDelay and residence time are in the local time base // Thus, we need to multiply the meanLinkDelay and residenceTime with the gmRateRatio clocktime_t newTime = preciseOriginTimestamp + correctionField + gmRateRatio * (meanLinkDelay + residenceTime); - auto servoClock = check_and_cast(clock.get()); // Only change the oscillator if we have new information about our nrr // TODO: We should change this to a clock servo model in the future anyways! -// ppm newOscillatorCompensation; -// if (!hasNewRateRatioForOscillatorCompensation) { -// newOscillatorCompensation = unit(piControlClock->getOscillatorCompensation()); -// } -// else { -// newOscillatorCompensation = -// unit(gmRateRatio * (1 + unit(piControlClock->getOscillatorCompensation()).get()) - 1); -// hasNewRateRatioForOscillatorCompensation = false; -// } + // ppm newOscillatorCompensation; + // if (!hasNewRateRatioForOscillatorCompensation) { + // newOscillatorCompensation = unit(piControlClock->getOscillatorCompensation()); + // } + // else { + // newOscillatorCompensation = + // unit(gmRateRatio * (1 + unit(piControlClock->getOscillatorCompensation()).get()) - 1); + // hasNewRateRatioForOscillatorCompensation = false; + // } servoClock->adjustClockTo(newTime); -// EV_INFO << "newOscillatorCompensation " << newOscillatorCompensation << endl; - + // EV_INFO << "newOscillatorCompensation " << newOscillatorCompensation << endl; newLocalTimeAtTimeSync = clock->getClockTime(); - /************** Rate ratio calculation ************************************* * It is calculated based on interval between two successive Sync messages * ***************************************************************************/ @@ -572,18 +589,6 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU clocktime_t prevRespRegress = -1; clocktime_t prevRespIngress = -1; - /*for (int last = timeInterval - 1; last >= 0; last--) { - if(pDelayRespIngressTimestampIntervals[last] != -1 && pDelayRespEgressTimestampIntervals[last] != -1){ - pDelayRespIngressTimestampLast = pDelayRespIngressTimestampIntervals[last]; - pDelayRespEgressTimestampLast = pDelayRespEgressTimestampIntervals[last]; - break; - } - else{ - pDelayRespIngressTimestampLast = -1; - pDelayRespEgressTimestampLast = -1; - } - }*/ - if (nrrCalculationSetCurrent == nrrCalculationSetMaximum) { neighborRateRatio = (pDelayRespEgressTimestamp - pDelayRespEgressTimestampSetStart) / (pDelayRespIngressTimestamp - pDelayRespIngressTimestampSetStart); @@ -595,7 +600,9 @@ void Gptp::processPdelayRespFollowUp(Packet *packet, const GptpPdelayRespFollowU nrrCalculationSetCurrent++; } - neighborRateRatio = 1; + if (!useNrr) { + neighborRateRatio = 1.0; + } // See 11.2.19.3.4 computePropTime() and Figure11-1 in IEEE 802.1AS-2020 auto t4 = pDelayRespIngressTimestamp; @@ -647,7 +654,8 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj handleClockJump(clockJumpDetails); } - if (simSignal != receptionStartedSignal && simSignal != transmissionStartedSignal && simSignal != receptionEndedSignal) + if (simSignal != receptionStartedSignal && simSignal != transmissionStartedSignal && + simSignal != receptionEndedSignal) return; auto ethernetSignal = check_and_cast(obj); @@ -667,8 +675,10 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj auto ingressTime = clock->getClockTime(); ingressTimeMap[transmissionId] = ingressTime; // Save ingress time in map - } else if (simSignal == receptionEndedSignal) { - packet->addTagIfAbsent()->setArrivalClockTime(ingressTimeMap[ethernetSignal->getTransmissionId()]); + } + else if (simSignal == receptionEndedSignal) { + packet->addTagIfAbsent()->setArrivalClockTime( + ingressTimeMap[ethernetSignal->getTransmissionId()]); ingressTimeMap.erase(ethernetSignal->getTransmissionId()); // Read ingress time from map // Ad tag to packet @@ -702,7 +712,8 @@ void Gptp::handleTransmissionStartedSignal(const GptpBase *gptp, cComponent *sou } } -void Gptp::handleClockJump(ServoClockBase::ClockJumpDetails *clockJumpDetails) { +void Gptp::handleClockJump(ServoClockBase::ClockJumpDetails *clockJumpDetails) +{ EV_INFO << "############## Adjust local timestamps #################################" << endl; EV_INFO << "BEFORE:" << endl; EV_INFO << "SYNC INGRESS TIME - " << syncIngressTimestamp << endl; @@ -729,9 +740,9 @@ void Gptp::handleClockJump(ServoClockBase::ClockJumpDetails *clockJumpDetails) { // This is a very special case, that only occurs when a clock jump occurs between the receptionStarted and // receptionEnded signal. // - // You can see this in action in the SteppingClock showcase (At t=9s we do not notify the gPTP module about the clock - // jump, this leads to an incorrect peer delay calculation). - // We want to allow the gPTP module to still work correctly in this case, so we make this adjustment here. + // You can see this in action in the SteppingClock showcase (At t=9s we do not notify the gPTP module about the + // clock jump, this leads to an incorrect peer delay calculation). We want to allow the gPTP module to still work + // correctly in this case, so we make this adjustment here. for (auto &entry : ingressTimeMap) { EV_INFO << " Before Ingress time: " << entry.first << " - " << entry.second << endl; adjustLocalTimestamp(entry.second, timeDiff); @@ -746,4 +757,3 @@ void Gptp::handleClockJump(ServoClockBase::ClockJumpDetails *clockJumpDetails) { } } // namespace inet - diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 3fbcf092737..6181d893014 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -37,6 +37,8 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener clocktime_t pdelayInterval; clocktime_t pDelayReqProcessingTime; // processing time between arrived // PDelayReq and send of PDelayResp + bool useNrr = false; // use neighbor rate ratio + GmRateRatioCalculationMethod gmRateRatioCalculationMethod = GmRateRatioCalculationMethod::NONE; // Rate Ratios double gmRateRatio = 1.0; @@ -69,6 +71,9 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener clocktime_t preciseOriginTimestamp = -1; // timestamp when the last sync message was generated at the GM clocktime_t preciseOriginTimestampLast = -1; // timestamp when the last sync message was generated at the GM + clocktime_t peerSentTimeSync = -1; // egress time of Sync at master (this node) + clocktime_t peerSentTimeSyncLast = -1; // egress time of previous Sync at master (this node) + clocktime_t syncIngressTimestamp = -1; // ingress time of Sync at slave (this node) clocktime_t syncIngressTimestampLast = -1; // ingress time of previous Sync at slave (this node) diff --git a/src/inet/linklayer/ieee8021as/Gptp.ned b/src/inet/linklayer/ieee8021as/Gptp.ned index a4e6780de51..3fc810ad633 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.ned +++ b/src/inet/linklayer/ieee8021as/Gptp.ned @@ -22,6 +22,8 @@ simple Gptp like IGptp string clockModule = default(""); // Relative path of a module that implements IClock; optional string interfaceTableModule; // Relative path of the interface table module string gptpNodeType; // @enum("GptpNodeType"): MASTER_NODE, BRIDGE_NODE, SLAVE_NODE + bool useNrr = default(false); // Use neighbor rate ratio + string gmRateRatioCalculationMethod = default("NONE"); // @enum("GptpRateRatioCalculationMethod"): NONE, NRR, DIRECT int domainNumber = default(0); // Specifies the time domain number used in gPTP messages string slavePort = default(""); // Port for receiving time (empty for MASTER_NODE) object masterPorts = default([]); // Ports for sending out time (empty for SLAVE_NODE) diff --git a/src/inet/linklayer/ieee8021as/GptpPacket.msg b/src/inet/linklayer/ieee8021as/GptpPacket.msg index 735cc46db30..6d34ebacb8d 100644 --- a/src/inet/linklayer/ieee8021as/GptpPacket.msg +++ b/src/inet/linklayer/ieee8021as/GptpPacket.msg @@ -48,6 +48,12 @@ enum GptpNodeType SLAVE_NODE = 13; } +enum GmRateRatioCalculationMethod { + NONE = 0; + NRR = 1; + DIRECT = 2; +}; + enum GptpPortType { MASTER_PORT = 2; From 615fe65ec5ceeb5e7a942cd74d4ea8fd855970ac Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 25 Jun 2024 12:05:52 +0200 Subject: [PATCH 073/138] Small refactoring --- src/inet/clock/model/InstantServoClock.cc | 46 +++++++++++------------ src/inet/clock/model/PiServoClock.cc | 2 - 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/inet/clock/model/InstantServoClock.cc b/src/inet/clock/model/InstantServoClock.cc index b4caeaa0668..d763093a0fc 100644 --- a/src/inet/clock/model/InstantServoClock.cc +++ b/src/inet/clock/model/InstantServoClock.cc @@ -4,51 +4,48 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // - #include "inet/clock/model/InstantServoClock.h" namespace inet { Define_Module(InstantServoClock); - void InstantServoClock::adjustClockTo(clocktime_t newClockTime) { - //not tested + // not tested Enter_Method("adjustClockTo"); int64_t offsetNsPrev, offsetNs, localNsPrev, localNs; clocktime_t oldClockTime = getClockTime(); - if (newClockTime != oldClockTime) { + if (newClockTime != oldClockTime) { switch (phase) { - case 0: - offset[0] = newClockTime - oldClockTime; - local[0] = oldClockTime; - phase = 1; - break; + case 0: + offset[0] = newClockTime - oldClockTime; + local[0] = oldClockTime; + phase = 1; + break; - case 1: - offset[1] = newClockTime - oldClockTime; - local[1] = oldClockTime; + case 1: + offset[1] = newClockTime - oldClockTime; + local[1] = oldClockTime; - offsetNsPrev = offset[0].inUnit(SIMTIME_NS); - offsetNs = offset[1].inUnit(SIMTIME_NS); + offsetNsPrev = offset[0].inUnit(SIMTIME_NS); + offsetNs = offset[1].inUnit(SIMTIME_NS); - localNsPrev = local[0].inUnit(SIMTIME_NS); - localNs = local[1].inUnit(SIMTIME_NS); + localNsPrev = local[0].inUnit(SIMTIME_NS); + localNs = local[1].inUnit(SIMTIME_NS); - drift += ppm(1e6 * (offsetNsPrev - offsetNs) / (localNsPrev - localNs)); - EV_INFO << "Drift: " << drift << "\n"; + drift += ppm(1e6 * (offsetNsPrev - offsetNs) / (localNsPrev - localNs)); + EV_INFO << "Drift: " << drift << "\n"; - jumpClockTo(newClockTime); + jumpClockTo(newClockTime); - setOscillatorCompensation(drift); + setOscillatorCompensation(drift); - emit(timeChangedSignal, newClockTime.asSimTime()); - offset[0] = offset[1]; - local[0] = newClockTime; - break; + offset[0] = offset[1]; + local[0] = local[1]; + break; } } // TODO: Add a mechanism that estimates the drift rate based on the previous and current local and received @@ -56,4 +53,3 @@ void InstantServoClock::adjustClockTo(clocktime_t newClockTime) } } // namespace inet - diff --git a/src/inet/clock/model/PiServoClock.cc b/src/inet/clock/model/PiServoClock.cc index 14ce9341a12..02bcb121f51 100644 --- a/src/inet/clock/model/PiServoClock.cc +++ b/src/inet/clock/model/PiServoClock.cc @@ -71,7 +71,6 @@ void PiServoClock::adjustClockTo(clocktime_t newClockTime) { setOscillatorCompensation(drift); - emit(timeChangedSignal, newClockTime.asSimTime()); phase = 2; break; case 2: @@ -104,7 +103,6 @@ void PiServoClock::adjustClockTo(clocktime_t newClockTime) { } emit(driftSignal, drift.get()); -// return getClockTime(); } } // namespace inet From cfa08c0027e86283d2bc0c6bca30d1227729def8 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 25 Jun 2024 15:02:45 +0200 Subject: [PATCH 074/138] Small refactoring --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 3 +-- src/inet/clock/model/InstantServoClock.cc | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index c4375818dd5..b38883f7504 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -38,8 +38,7 @@ description = "Basic tree topology with one master clock" #*.*.gptp.masterClockModule = "^.^.tsnClock.clock" #*.*.gptp.slaveClockModule = "^.clock" -*.tsnDevice1.clock.typename = "InstantServoClock" -*.tsnSwitch.clock.typename = "InstantServoClock" +*.tsnDevice2.clock.typename = "InstantServoClock" # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] diff --git a/src/inet/clock/model/InstantServoClock.cc b/src/inet/clock/model/InstantServoClock.cc index d763093a0fc..6742f4cc423 100644 --- a/src/inet/clock/model/InstantServoClock.cc +++ b/src/inet/clock/model/InstantServoClock.cc @@ -23,12 +23,15 @@ void InstantServoClock::adjustClockTo(clocktime_t newClockTime) case 0: offset[0] = newClockTime - oldClockTime; local[0] = oldClockTime; + jumpClockTo(newClockTime); phase = 1; break; case 1: - offset[1] = newClockTime - oldClockTime; - local[1] = oldClockTime; + // TODO: Refactor formula + // It works, but I need to mathematically still understand why + offset[1] = newClockTime - oldClockTime + offset[0]; + local[1] = oldClockTime - offset[0]; offsetNsPrev = offset[0].inUnit(SIMTIME_NS); offsetNs = offset[1].inUnit(SIMTIME_NS); From c112c65b0f2d39de88b053d32c233fc525d51a12 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 25 Jun 2024 18:15:24 +0200 Subject: [PATCH 075/138] Simplify InstantClockServo --- src/inet/clock/model/InstantServoClock.cc | 45 ++++++++--------------- src/inet/clock/model/InstantServoClock.h | 10 +---- 2 files changed, 17 insertions(+), 38 deletions(-) diff --git a/src/inet/clock/model/InstantServoClock.cc b/src/inet/clock/model/InstantServoClock.cc index 6742f4cc423..0c83c62a4c5 100644 --- a/src/inet/clock/model/InstantServoClock.cc +++ b/src/inet/clock/model/InstantServoClock.cc @@ -14,45 +14,30 @@ void InstantServoClock::adjustClockTo(clocktime_t newClockTime) { // not tested Enter_Method("adjustClockTo"); - int64_t offsetNsPrev, offsetNs, localNsPrev, localNs; clocktime_t oldClockTime = getClockTime(); if (newClockTime != oldClockTime) { - switch (phase) { - case 0: - offset[0] = newClockTime - oldClockTime; - local[0] = oldClockTime; - jumpClockTo(newClockTime); - phase = 1; - break; + // At every clock jump we increase the clock time by offset + // For our drift estimation, we need to know to keep track of the local times without + // offsets and the offsets themselves + // This we subtract the offset from the local time to get the local time without the offset + // and accumulate the offsets + auto local = oldClockTime.inUnit(SIMTIME_NS) - offsetPrev; + auto offset = (newClockTime - oldClockTime).inUnit(SIMTIME_NS) + offsetPrev; - case 1: - // TODO: Refactor formula - // It works, but I need to mathematically still understand why - offset[1] = newClockTime - oldClockTime + offset[0]; - local[1] = oldClockTime - offset[0]; + drift += ppm(1e6 * (offsetPrev - offset) / (localPrev - local)); + EV_INFO << "Drift: " << drift << "\n"; - offsetNsPrev = offset[0].inUnit(SIMTIME_NS); - offsetNs = offset[1].inUnit(SIMTIME_NS); + jumpClockTo(newClockTime); - localNsPrev = local[0].inUnit(SIMTIME_NS); - localNs = local[1].inUnit(SIMTIME_NS); + setOscillatorCompensation(drift); - drift += ppm(1e6 * (offsetNsPrev - offsetNs) / (localNsPrev - localNs)); - EV_INFO << "Drift: " << drift << "\n"; - - jumpClockTo(newClockTime); - - setOscillatorCompensation(drift); - - offset[0] = offset[1]; - local[0] = local[1]; - break; - } + offsetPrev = offset; + localPrev = local; } - // TODO: Add a mechanism that estimates the drift rate based on the previous and current local and received - // timestamps, similar to case 0 and 1 in PiServoClock } +// TODO: Add a mechanism that estimates the drift rate based on the previous and current local and received +// timestamps, similar to case 0 and 1 in PiServoClock } // namespace inet diff --git a/src/inet/clock/model/InstantServoClock.h b/src/inet/clock/model/InstantServoClock.h index 06ea66f68bd..65f35e5af6a 100644 --- a/src/inet/clock/model/InstantServoClock.h +++ b/src/inet/clock/model/InstantServoClock.h @@ -17,17 +17,11 @@ namespace inet { class INET_API InstantServoClock : public ServoClockBase { protected: - clocktime_t offset[2]; - clocktime_t local[2]; - int phase = 0; + long offsetPrev = 0; + long localPrev = 0; ppm drift = ppm(0); public: - /** - * Sets the clock time immediately to the given value. Greater than 1 oscillator - * compensation factor means the clock measures time faster. - */ virtual void adjustClockTo(clocktime_t newClockTime) override; - }; } // namespace inet From 7473dab8da4ce4d94daf973ff8b227526f13a7e2 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 25 Jun 2024 18:42:00 +0200 Subject: [PATCH 076/138] Finished InstantServoClock and refactored to have an own directory for ClockServos --- .../tsn/timesynchronization/gptp/omnetpp.ini | 2 ++ .../clock/SimpleClockSynchronizer.h | 4 +-- src/inet/clock/model/InstantServoClock.ned | 21 -------------- .../{model => servo}/InstantServoClock.cc | 28 ++++++++++++++----- .../{model => servo}/InstantServoClock.h | 7 +++-- src/inet/clock/servo/InstantServoClock.ned | 24 ++++++++++++++++ .../clock/{model => servo}/PiServoClock.cc | 2 +- .../clock/{model => servo}/PiServoClock.h | 2 +- .../clock/{model => servo}/PiServoClock.ned | 10 ++----- .../clock/{model => servo}/ServoClockBase.cc | 6 ++-- .../clock/{model => servo}/ServoClockBase.h | 0 .../clock/{model => servo}/ServoClockBase.ned | 12 +++----- src/inet/linklayer/ieee8021as/Gptp.cc | 4 +-- src/inet/linklayer/ieee8021as/Gptp.h | 2 +- 14 files changed, 66 insertions(+), 58 deletions(-) delete mode 100644 src/inet/clock/model/InstantServoClock.ned rename src/inet/clock/{model => servo}/InstantServoClock.cc (60%) rename src/inet/clock/{model => servo}/InstantServoClock.h (76%) create mode 100644 src/inet/clock/servo/InstantServoClock.ned rename src/inet/clock/{model => servo}/PiServoClock.cc (98%) rename src/inet/clock/{model => servo}/PiServoClock.h (94%) rename src/inet/clock/{model => servo}/PiServoClock.ned (72%) rename src/inet/clock/{model => servo}/ServoClockBase.cc (96%) rename src/inet/clock/{model => servo}/ServoClockBase.h (100%) rename src/inet/clock/{model => servo}/ServoClockBase.ned (56%) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index b38883f7504..88c4812fd41 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -38,6 +38,8 @@ description = "Basic tree topology with one master clock" #*.*.gptp.masterClockModule = "^.^.tsnClock.clock" #*.*.gptp.slaveClockModule = "^.clock" +*.tsnSwitch.clock.typename = "InstantServoClock" +*.tsnSwitch.clock.adjustDrift = false *.tsnDevice2.clock.typename = "InstantServoClock" # TSN clock gPTP master ports diff --git a/src/inet/applications/clock/SimpleClockSynchronizer.h b/src/inet/applications/clock/SimpleClockSynchronizer.h index c2dd5fff20d..5d46348811c 100644 --- a/src/inet/applications/clock/SimpleClockSynchronizer.h +++ b/src/inet/applications/clock/SimpleClockSynchronizer.h @@ -11,7 +11,7 @@ #include #include "inet/applications/base/ApplicationBase.h" -#include "inet/clock/model/InstantServoClock.h" +#include "inet/clock/servo/ServoClockBase.h" #include "inet/common/ModuleRefByPar.h" namespace inet { @@ -21,7 +21,7 @@ class INET_API SimpleClockSynchronizer : public ApplicationBase protected: cMessage *synhronizationTimer = nullptr; ModuleRefByPar masterClock; - ModuleRefByPar slaveClock; + ModuleRefByPar slaveClock; cPar *synchronizationIntervalParameter = nullptr; cPar *synchronizationClockTimeErrorParameter = nullptr; cPar *synchronizationOscillatorCompensationErrorParameter = nullptr; diff --git a/src/inet/clock/model/InstantServoClock.ned b/src/inet/clock/model/InstantServoClock.ned deleted file mode 100644 index aec490dcc5b..00000000000 --- a/src/inet/clock/model/InstantServoClock.ned +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (C) 2020 OpenSim Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - - -package inet.clock.model; - -// -// Models a clock which can be set to a different clock time. The clock time -// can be set from C++ or using a command -// in a `ScenarioManager` script. -// -// @see ~ScenarioManager -// -module InstantServoClock extends ServoClockBase -{ - parameters: - @class(InstantServoClock); -} diff --git a/src/inet/clock/model/InstantServoClock.cc b/src/inet/clock/servo/InstantServoClock.cc similarity index 60% rename from src/inet/clock/model/InstantServoClock.cc rename to src/inet/clock/servo/InstantServoClock.cc index 0c83c62a4c5..ae4d7c564ea 100644 --- a/src/inet/clock/model/InstantServoClock.cc +++ b/src/inet/clock/servo/InstantServoClock.cc @@ -4,20 +4,33 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // -#include "inet/clock/model/InstantServoClock.h" +#include "InstantServoClock.h" namespace inet { Define_Module(InstantServoClock); +void InstantServoClock::initialize(int stage) +{ + ServoClockBase::initialize(stage); + if (stage == INITSTAGE_LOCAL) { + adjustClock = par("adjustClock"); + adjustDrift = par("adjustDrift"); + if (!adjustClock && adjustDrift) { + throw cRuntimeError("Cannot adjust drift without adjusting clock"); + } + offsetPrev = 0; + localPrev = 0; + } +} + void InstantServoClock::adjustClockTo(clocktime_t newClockTime) { - // not tested Enter_Method("adjustClockTo"); clocktime_t oldClockTime = getClockTime(); - if (newClockTime != oldClockTime) { + if (newClockTime != oldClockTime && adjustClock) { // At every clock jump we increase the clock time by offset // For our drift estimation, we need to know to keep track of the local times without // offsets and the offsets themselves @@ -26,12 +39,13 @@ void InstantServoClock::adjustClockTo(clocktime_t newClockTime) auto local = oldClockTime.inUnit(SIMTIME_NS) - offsetPrev; auto offset = (newClockTime - oldClockTime).inUnit(SIMTIME_NS) + offsetPrev; - drift += ppm(1e6 * (offsetPrev - offset) / (localPrev - local)); - EV_INFO << "Drift: " << drift << "\n"; - jumpClockTo(newClockTime); - setOscillatorCompensation(drift); + if (adjustDrift) { + drift += ppm(1e6 * (offsetPrev - offset) / (localPrev - local)); + EV_INFO << "Drift: " << drift << "\n"; + setOscillatorCompensation(drift); + } offsetPrev = offset; localPrev = local; diff --git a/src/inet/clock/model/InstantServoClock.h b/src/inet/clock/servo/InstantServoClock.h similarity index 76% rename from src/inet/clock/model/InstantServoClock.h rename to src/inet/clock/servo/InstantServoClock.h index 65f35e5af6a..11a01351c0b 100644 --- a/src/inet/clock/model/InstantServoClock.h +++ b/src/inet/clock/servo/InstantServoClock.h @@ -8,9 +8,7 @@ #ifndef __INET_SETTABLECLOCK_H #define __INET_SETTABLECLOCK_H -#include "inet/clock/model/ServoClockBase.h" - - +#include "ServoClockBase.h" namespace inet { @@ -20,8 +18,11 @@ class INET_API InstantServoClock : public ServoClockBase long offsetPrev = 0; long localPrev = 0; ppm drift = ppm(0); + bool adjustClock = true; + bool adjustDrift = true; public: virtual void adjustClockTo(clocktime_t newClockTime) override; + virtual void initialize(int stage) override; }; } // namespace inet diff --git a/src/inet/clock/servo/InstantServoClock.ned b/src/inet/clock/servo/InstantServoClock.ned new file mode 100644 index 00000000000..ae3276faa4e --- /dev/null +++ b/src/inet/clock/servo/InstantServoClock.ned @@ -0,0 +1,24 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +package inet.clock.servo; + +// As simple clock servo, with three different modes: +// +// 1. No servo: Calls to adjustTime are ignored. (adjustClock = false; adjustDrift = false) +// +// 2. Jump only: Calls to adjustTime are used to jump the clock. (adjustClock = true; adjustDrift = false) +// +// 3. Jump and estimate drift: Calls to adjustTime are used to jump the clock and estimate the drift using the previous +// call to adjustTime. (adjustClock = true; adjustDrift = true) +module InstantServoClock extends ServoClockBase +{ + parameters: + bool adjustClock = default(true); // Whether to adjust the clock. + bool adjustDrift = default(true); // Whether to adjust the drift. + @class(InstantServoClock); +} diff --git a/src/inet/clock/model/PiServoClock.cc b/src/inet/clock/servo/PiServoClock.cc similarity index 98% rename from src/inet/clock/model/PiServoClock.cc rename to src/inet/clock/servo/PiServoClock.cc index 02bcb121f51..79e6c6015b1 100644 --- a/src/inet/clock/model/PiServoClock.cc +++ b/src/inet/clock/servo/PiServoClock.cc @@ -4,7 +4,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // -#include "inet/clock/model/PiServoClock.h" +#include "PiServoClock.h" #include diff --git a/src/inet/clock/model/PiServoClock.h b/src/inet/clock/servo/PiServoClock.h similarity index 94% rename from src/inet/clock/model/PiServoClock.h rename to src/inet/clock/servo/PiServoClock.h index a1c3e0c354f..6300590d86c 100644 --- a/src/inet/clock/model/PiServoClock.h +++ b/src/inet/clock/servo/PiServoClock.h @@ -7,7 +7,7 @@ #ifndef __INET_PICLOCK_H #define __INET_PICLOCK_H -#include "inet/clock/model/ServoClockBase.h" +#include "ServoClockBase.h" namespace inet { diff --git a/src/inet/clock/model/PiServoClock.ned b/src/inet/clock/servo/PiServoClock.ned similarity index 72% rename from src/inet/clock/model/PiServoClock.ned rename to src/inet/clock/servo/PiServoClock.ned index c333d7afa82..b0f047feafe 100644 --- a/src/inet/clock/model/PiServoClock.ned +++ b/src/inet/clock/servo/PiServoClock.ned @@ -5,15 +5,9 @@ // -package inet.clock.model; +package inet.clock.servo; -// -// Models a clock which can be set to a different clock time. The clock time -// can be set from C++ or using a command -// in a ScenarioManager script. -// -// @see ~ScenarioManager -// +// TODO: Write documentation module PiServoClock extends ServoClockBase { parameters: diff --git a/src/inet/clock/model/ServoClockBase.cc b/src/inet/clock/servo/ServoClockBase.cc similarity index 96% rename from src/inet/clock/model/ServoClockBase.cc rename to src/inet/clock/servo/ServoClockBase.cc index 35f345baa95..5a00114b1f1 100644 --- a/src/inet/clock/model/ServoClockBase.cc +++ b/src/inet/clock/servo/ServoClockBase.cc @@ -4,7 +4,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // -#include "inet/clock/model/ServoClockBase.h" +#include "ServoClockBase.h" namespace inet { @@ -96,6 +96,7 @@ void ServoClockBase::processCommand(const cXMLElement &node) jumpClockTo(time, notifyListeners); } if (!strcmp(node.getTagName(), "set-oscillator-compensation")) { + // TODO: Refactor to directly read ppm const char* valueStr = xmlutils::getMandatoryFilledAttribute(node, "value"); double valueDouble = std::atof(valueStr); // Convert string to double ppm oscillatorCompensationValue = ppm(valueDouble); // Create ppm object from double @@ -104,9 +105,6 @@ void ServoClockBase::processCommand(const cXMLElement &node) if (!strcmp(node.getTagName(), "reset-oscillator")) { resetOscillator(); } - // TODO: Add two more options for the scenario manager here. - // One two update the oscillator compensation - // One to reset the oscillator else throw cRuntimeError("Invalid command: %s", node.getTagName()); } diff --git a/src/inet/clock/model/ServoClockBase.h b/src/inet/clock/servo/ServoClockBase.h similarity index 100% rename from src/inet/clock/model/ServoClockBase.h rename to src/inet/clock/servo/ServoClockBase.h diff --git a/src/inet/clock/model/ServoClockBase.ned b/src/inet/clock/servo/ServoClockBase.ned similarity index 56% rename from src/inet/clock/model/ServoClockBase.ned rename to src/inet/clock/servo/ServoClockBase.ned index 52693ef88d9..e0590d637c5 100644 --- a/src/inet/clock/model/ServoClockBase.ned +++ b/src/inet/clock/servo/ServoClockBase.ned @@ -5,15 +5,11 @@ // -package inet.clock.model; +package inet.clock.servo; -// -// Models a clock which can be set to a different clock time. The clock time -// can be set from C++ or using a command -// in a `ScenarioManager` script. -// -// @see ~ScenarioManager -// +import inet.clock.model.OscillatorBasedClock; + +// TODO: Write documentation module ServoClockBase extends OscillatorBasedClock { parameters: diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index e8ae2281178..e2aae03de08 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -8,8 +8,8 @@ #include "Gptp.h" #include "GptpPacket_m.h" -#include "inet/clock/model/PiServoClock.h" -#include "inet/clock/model/ServoClockBase.h" +#include "inet/clock/servo/PiServoClock.h" +#include "inet/clock/servo/ServoClockBase.h" #include "inet/common/IProtocolRegistrationListener.h" #include "inet/common/clock/ClockUserModuleBase.h" #include "inet/linklayer/common/InterfaceTag_m.h" diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 6181d893014..005fb26d686 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -12,7 +12,7 @@ #include "inet/clock/common/ClockTime.h" #include "inet/clock/contract/ClockTime.h" -#include "inet/clock/model/ServoClockBase.h" +#include "inet/clock/servo/ServoClockBase.h" #include "inet/common/INETDefs.h" #include "inet/common/ModuleRefByPar.h" #include "inet/common/clock/ClockUserModuleBase.h" From 75e71e257b2e3d638f4a1d5c728b0a21a0220278 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 25 Sep 2024 14:32:05 +0200 Subject: [PATCH 077/138] Small fix for InstantServoClock --- src/inet/clock/servo/InstantServoClock.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/inet/clock/servo/InstantServoClock.cc b/src/inet/clock/servo/InstantServoClock.cc index ae4d7c564ea..f4982091896 100644 --- a/src/inet/clock/servo/InstantServoClock.cc +++ b/src/inet/clock/servo/InstantServoClock.cc @@ -19,8 +19,8 @@ void InstantServoClock::initialize(int stage) if (!adjustClock && adjustDrift) { throw cRuntimeError("Cannot adjust drift without adjusting clock"); } - offsetPrev = 0; - localPrev = 0; + offsetPrev = -1; + localPrev = -1; } } @@ -41,7 +41,7 @@ void InstantServoClock::adjustClockTo(clocktime_t newClockTime) jumpClockTo(newClockTime); - if (adjustDrift) { + if (adjustDrift && offsetPrev != -1) { drift += ppm(1e6 * (offsetPrev - offset) / (localPrev - local)); EV_INFO << "Drift: " << drift << "\n"; setOscillatorCompensation(drift); From 2c9fee93f63471308e804eeae8b50fa8662e827d Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 2 Oct 2024 16:18:24 +0200 Subject: [PATCH 078/138] Some Refactoring --- .../timesynchronization/gptp/GptpShowcase.anf | 696 +++++++++++++++++- .../tsn/timesynchronization/gptp/omnetpp.ini | 6 +- src/inet/linklayer/ieee8021as/Gptp.cc | 65 +- src/inet/linklayer/ieee8021as/Gptp.h | 25 +- 4 files changed, 720 insertions(+), 72 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 3c6f3a9e2db..6c1ee81ac64 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -17,8 +17,8 @@ - - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp_bmca/GptpShowcase.ned b/showcases/tsn/timesynchronization/gptp_bmca/GptpShowcase.ned index 810b19035ff..4f88f6fdc36 100644 --- a/showcases/tsn/timesynchronization/gptp_bmca/GptpShowcase.ned +++ b/showcases/tsn/timesynchronization/gptp_bmca/GptpShowcase.ned @@ -38,6 +38,9 @@ network BmcaShowcaseSimple extends TsnNetworkBase network BmcaShowcaseDiamond extends TsnNetworkBase { submodules: + scenarioManager: ScenarioManager { + @display("p=100,800;is=s"); + } tsnDevice1: TsnDevice { @display("p=500,150"); } @@ -60,6 +63,9 @@ network BmcaShowcaseDiamond extends TsnNetworkBase network BmcaShowcaseDiamondAsymmetric extends TsnNetworkBase { submodules: + scenarioManager: ScenarioManager { + @display("p=100,800;is=s"); + } tsnDevice1: TsnDevice { @display("p=500,150"); } diff --git a/showcases/tsn/timesynchronization/gptp_bmca/asym-diamond-link-failure.xml b/showcases/tsn/timesynchronization/gptp_bmca/asym-diamond-link-failure.xml new file mode 100644 index 00000000000..a0009ed028d --- /dev/null +++ b/showcases/tsn/timesynchronization/gptp_bmca/asym-diamond-link-failure.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp_bmca/diamond-link-failure.xml b/showcases/tsn/timesynchronization/gptp_bmca/diamond-link-failure.xml new file mode 100644 index 00000000000..d4b6677c4fd --- /dev/null +++ b/showcases/tsn/timesynchronization/gptp_bmca/diamond-link-failure.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp_bmca/omnetpp.ini b/showcases/tsn/timesynchronization/gptp_bmca/omnetpp.ini index 6622a3b4470..e69f1e7f059 100644 --- a/showcases/tsn/timesynchronization/gptp_bmca/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp_bmca/omnetpp.ini @@ -6,10 +6,7 @@ description = "abstract" # enable time synchronization in all network nodes *.*.hasTimeSynchronization = true -# all oscillators have a constant drift rate (specified with a random distribution for each one) -# except for the master clocks, which have a random drift rate -#**.clock.oscillator.typename = "RandomDriftOscillator" -*.tsnClock.clock.oscillator.typename = "IdealOscillator" +**.clock.oscillator.typename = "RandomDriftOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) **.oscillator.driftRateChange = uniform(-1ppm, 1ppm) @@ -18,7 +15,9 @@ description = "abstract" **.kp=8 **.ki=7 -**.offsetThreshold = 0 us +**.offsetThreshold = 1 us + +**.pdelayInitialOffset = 1ms # all Ethernet interfaces have 100 Mbps speed @@ -38,24 +37,12 @@ description = "Showcase BMCA" *.scenarioManager.script = xmldoc("simple-link-failure.xml") -# Uncomment these lines to test out of band time synchronization using the SimpleClockSynchronizer instead of gPTP -#*.tsnClock.gptp.typename = "" -#*.*.gptp.typename = "SimpleClockSynchronizer" -#*.*.gptp.synchronizationInterval = 0.125s -#*.*.gptp.masterClockModule = "^.^.tsnClock.clock" -#*.*.gptp.slaveClockModule = "^.clock" - -*.tsnDevice1.clock.oscillator.typename = "IdealOscillator" -*.tsnSwitch.clock.typename = "InstantServoClock" -**.clock.adjustDrift = true -*.tsnDevice*.clock.typename = "InstantServoClock" - *.tsnDevice*.gptp.bmcaPorts = ["eth0"] *.tsnSwitch.gptp.bmcaPorts = ["eth0", "eth1", "eth2"] -*.tsnDevice1.gptp.grandmasterPriority1 = 0 -*.tsnDevice3.gptp.grandmasterPriority1 = 1 +*.tsnDevice1.gptp.grandmasterPriority1 = 1 +*.tsnDevice3.gptp.grandmasterPriority1 = 2 # Set all reference clocks to master clock so the time difference can be visualized @@ -77,25 +64,13 @@ description = "Showcase BMCA" *.tsnDevice*.clock.displayStringTextFormat = "diff: %d" *.tsnSwitch.clock.displayStringTextFormat = "diff: %d" +[Config BmcaDiamondBase] +abstract=true -[Config BmcaDiamond] -network = BmcaShowcaseDiamond -description = "Showcase BMCA with Diamond topology" - -# Uncomment these lines to test out of band time synchronization using the SimpleClockSynchronizer instead of gPTP -#*.tsnClock.gptp.typename = "" -#*.*.gptp.typename = "SimpleClockSynchronizer" -#*.*.gptp.synchronizationInterval = 0.125s -#*.*.gptp.masterClockModule = "^.^.tsnClock.clock" -#*.*.gptp.slaveClockModule = "^.clock" - -*.tsnDevice1.clock.oscillator.typename = "IdealOscillator" -*.tsnSwitch*.clock.typename = "InstantServoClock" -*.tsnDevice*.clock.typename = "InstantServoClock" *.tsn*.gptp.bmcaPorts = ["eth0", "eth1"] -*.tsnDevice1.gptp.grandmasterPriority1 = 0 +*.tsnDevice1.gptp.grandmasterPriority1 = 1 # Set all reference clocks to master clock so the time difference can be visualized @@ -116,3 +91,18 @@ description = "Showcase BMCA with Diamond topology" *.visualizer.infoVisualizer[2].placementHint = "bottom" *.tsnDevice*.clock.displayStringTextFormat = "diff: %d" *.tsnSwitch*.clock.displayStringTextFormat = "diff: %d" + +[Config BmcaDiamond] +network = BmcaShowcaseDiamond +extends = BmcaDiamondBase +description = "Showcase BMCA with Diamond topology" + +*.scenarioManager.script = xmldoc("diamond-link-failure.xml") + + +[Config BmcaDiamondAsymmetric] +network = BmcaShowcaseDiamondAsymmetric +extends = BmcaDiamondBase +description = "Showcase BMCA with asymmetric diamond topology" + +*.scenarioManager.script = xmldoc("asym-diamond-link-failure.xml") diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 68b21dd5169..9144129e8dc 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -220,7 +220,7 @@ void Gptp::initBmca() { // TODO: harcoded for now localPriorityVector.grandmasterPriority1 = par("grandmasterPriority1"); - localPriorityVector.grandmasterClockQuality.clockClass = 0; + localPriorityVector.grandmasterClockQuality.clockClass = 248; localPriorityVector.grandmasterClockQuality.clockAccuracy = 0; localPriorityVector.grandmasterClockQuality.offsetScaledLogVariance = 0; localPriorityVector.grandmasterPriority2 = 0; @@ -356,42 +356,39 @@ void Gptp::sendPacketToNIC(Packet *packet, int portId) void Gptp::sendAnnounce() { - auto packet = new Packet("GptpAnnounce"); - packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - auto gptp = makeShared(); - gptp->setDomainNumber(domainNumber); - // TODO: IEEE 1588-2019 specifies that it also might be 0, check what this means - gptp->setOriginTimestamp(clock->getClockTime()); - gptp->setSequenceId(sequenceId++); + for (auto port : bmcaPortIds) { + if (port == slavePortId || passivePortIds.find(port) != passivePortIds.end()) + continue; - PortIdentity portId; - portId.clockIdentity = clockIdentity; - portId.portNumber = slavePortId; - gptp->setSourcePortIdentity(portId); + auto packet = new Packet("GptpAnnounce"); + packet->addTag()->setDestAddress(GPTP_MULTICAST_ADDRESS); - if (bestAnnounce == nullptr || bestAnnounce->getPriorityVector() == localPriorityVector) { - gptp->setPriorityVector(localPriorityVector); - } - else { - auto newPriorityVector = bestAnnounce->getPriorityVector(); - newPriorityVector.stepsRemoved++; - gptp->setPriorityVector(newPriorityVector); - } + auto gptp = makeShared(); + gptp->setDomainNumber(domainNumber); + // TODO: IEEE 1588-2019 specifies that it also might be 0, check what this means + gptp->setOriginTimestamp(clock->getClockTime()); + gptp->setSequenceId(sequenceId++); - packet->insertAtFront(gptp); + if (bestAnnounce == nullptr || bestAnnounce->getPriorityVector() == localPriorityVector) { + gptp->setPriorityVector(localPriorityVector); + } + else { + auto newPriorityVector = bestAnnounce->getPriorityVector(); + newPriorityVector.stepsRemoved++; + gptp->setPriorityVector(newPriorityVector); + } - for (auto port : bmcaPortIds) { - if (port == slavePortId) - continue; - sendPacketToNIC(packet->dup(), port); - } + PortIdentity portId; + portId.clockIdentity = clockIdentity; + portId.portNumber = port; - if (!masterPortIds.empty() || slavePortId == -1) { - // When we are the master to at least one port or we are not a slave (init phase) reschedule the Announce - rescheduleClockEventAfter(announceInterval, selfMsgAnnounce); + gptp->setSourcePortIdentity(portId); + packet->insertAtFront(gptp); + + sendPacketToNIC(packet, port); } - delete packet; + rescheduleClockEventAfter(announceInterval, selfMsgAnnounce); } void Gptp::sendSync() @@ -458,6 +455,13 @@ void Gptp::sendFollowUp(int portId, const GptpSync *sync, const clocktime_t &syn void Gptp::processAnnounce(Packet *packet, const GptpAnnounce *announce) { + if (announce->getPriorityVector().stepsRemoved >= 255) { + // IEEE 1588-2019 9.3.2.5 d) + EV_WARN << "Announce message dropped because stepsRemoved is 255" << endl; + delete packet; + return; + } + auto incomingNicId = packet->getTag()->getInterfaceId(); if (receivedAnnounces.find(incomingNicId) != receivedAnnounces.end()) { delete receivedAnnounces[incomingNicId]; @@ -490,130 +494,53 @@ void Gptp::executeBmca() ownAnnounce->setPriorityVector(localPriorityVector); ownAnnounce->setSourcePortIdentity(ownPortIdentity); - auto b = ownAnnounce; - - auto bReceiverIdentity = ownPortIdentity; + auto bestAnnounceCurr = ownAnnounce; + auto bestAnnounceReceiverIdentityCurr = ownPortIdentity; for (const auto &announceEntry : receivedAnnounces) { auto a = announceEntry.second; - auto aVec = a->getPriorityVector(); - auto bVec = b->getPriorityVector(); - auto aReceiverIdentity = PortIdentity(); aReceiverIdentity.clockIdentity = clockIdentity; aReceiverIdentity.portNumber = announceEntry.first; - // IEEE 1588-2019 Figure 34 - if (aVec.grandmasterIdentity == bVec.grandmasterIdentity) { - // Special cases Figure 35 - - // Compare steps removed (diff >=2) - if (aVec.stepsRemoved + 1 < bVec.stepsRemoved) { - b = a; - bReceiverIdentity = aReceiverIdentity; - continue; - } - else if (aVec.stepsRemoved > bVec.stepsRemoved + 1) { - // B is already the best vector -> do nothing - continue; - } - - // Compare steps removed |A-B| <= 1 - if (aVec.stepsRemoved < bVec.stepsRemoved) { - if (bReceiverIdentity == b->getSourcePortIdentity()) { - throw cRuntimeError("Error-1: Indicates that one of the PTP messages was " - "transmitted and received on the same PTP Port.\n" - "(See IEEE 1588-2019 Figure 35 NOTE 2)"); - } - else if (bReceiverIdentity > b->getSourcePortIdentity()) { - b = a; - bReceiverIdentity = aReceiverIdentity; - continue; - } - else { - throw cRuntimeError("Unknown case in BMCA -- should never happen"); - } - } - else if (aVec.stepsRemoved > bVec.stepsRemoved) { - if (aReceiverIdentity == a->getSourcePortIdentity()) { - throw cRuntimeError("Error-1: Indicates that one of the PTP messages was " - "transmitted and received on the same PTP Port.\n" - "(See IEEE 1588-2019 Figure 35 NOTE 2)"); - } - else if (aReceiverIdentity > a->getSourcePortIdentity()) { - // B is already the best vector -> do nothing - continue; - } - else { - throw cRuntimeError("Unknown case in BMCA -- should never happen"); - } - } - - // Compare Identities of senders - if (a->getSourcePortIdentity() < b->getSourcePortIdentity()) { - // A better by topology - b = a; - bReceiverIdentity = aReceiverIdentity; - continue; - } - else if (b->getSourcePortIdentity() < a->getSourcePortIdentity()) { - // B better by topology - // B is already the best vector -> do nothing - continue; - } - - // Compare port number of receivers - if (aReceiverIdentity.portNumber < bReceiverIdentity.portNumber) { - // A better by topology - b = a; - bReceiverIdentity = aReceiverIdentity; - continue; - } - else if (aReceiverIdentity.portNumber > bReceiverIdentity.portNumber) { - // B better by topology - // B is already the best vector -> do nothing - continue; - } - else { - throw cRuntimeError("Error-2: indicates that the PTP messages are duplicates or that they are " - "an earlier and later PTP message from the same Grandmaster PTP Instance.\n" - "(See IEEE 1588-2019 Figure 35 NOTE 2)"); - } - } - else if (aVec < bVec) { - b = a; - bReceiverIdentity = aReceiverIdentity; - } - else if (bVec < aVec) { - // Best vector is already b -> do nothing - } - else { - throw cRuntimeError("Unknown case in BMCA -- should never happen"); + switch (compareAnnounceMessages(a, bestAnnounceCurr, aReceiverIdentity, bestAnnounceReceiverIdentityCurr)) { + case A_BETTER_THAN_B_BY_TOPOLOGY: + case A_BETTER_THAN_B: + // If a is better or better by topology, then a becomes the new best Announce + bestAnnounceCurr = announceEntry.second; + bestAnnounceReceiverIdentityCurr = aReceiverIdentity; + break; + case B_BETTER_THAN_A_BY_TOPOLOGY: + case B_BETTER_THAN_A: + // If bestAnnounceCurr is better or better by topology, then nothing changes + break; } } EV_INFO << "############## BMCA ################################" << endl; EV_INFO << "Choosing from " << receivedAnnounces.size() << " Announces" << endl; - EV_INFO << "Selected Grandmaster Identity - " << b->getPriorityVector().grandmasterIdentity << endl; - EV_INFO << "Selected Grandmaster Priority1 - " << (int)b->getPriorityVector().grandmasterPriority1 << endl; - EV_INFO << "Selected Slave Port Identity - " << bReceiverIdentity.portNumber << endl; + EV_INFO << "Selected Grandmaster Identity - " << bestAnnounceCurr->getPriorityVector().grandmasterIdentity << endl; + EV_INFO << "Selected Grandmaster Priority1 - " << (int)bestAnnounceCurr->getPriorityVector().grandmasterPriority1 + << endl; + EV_INFO << "Selected Slave Port Identity - " << bestAnnounceReceiverIdentityCurr.portNumber << endl; - if (bestAnnounce == b) { + if (bestAnnounce == bestAnnounceCurr) { // Received an Announce message, but best clock is still the same } - else if (bestAnnounce && bestAnnounce->getPriorityVector() == b->getPriorityVector()) { + else if (bestAnnounce && bestAnnounce->getPriorityVector() == bestAnnounceCurr->getPriorityVector() && + slavePortId == bestAnnounceReceiverIdentityCurr.portNumber) { // Same priority vector but newer Announce, no topology change because priority vector is the same // Store the new Announce delete bestAnnounce; - bestAnnounce = b->dup(); + bestAnnounce = bestAnnounceCurr->dup(); } else { // Topology change, update ports and send Announce bool doSendAnnounce = true; - if (b->getPriorityVector().grandmasterIdentity != clockIdentity) { + if (bestAnnounceCurr->getPriorityVector().grandmasterIdentity != clockIdentity) { // We are not the GM - slavePortId = bReceiverIdentity.portNumber; + slavePortId = bestAnnounceReceiverIdentityCurr.portNumber; masterPortIds.clear(); passivePortIds.clear(); for (const auto &portId : bmcaPortIds) { @@ -633,6 +560,8 @@ void Gptp::executeBmca() getContainingNode(this)->bubble("I'm GM"); if (slavePortId == -1) { // We were already GM, no need to send Announce again + // Should only happen in the bootup phase + // (otherwise priority vector would be the same and earlier case would apply) doSendAnnounce = false; } masterPortIds = bmcaPortIds; @@ -641,7 +570,7 @@ void Gptp::executeBmca() } delete bestAnnounce; - bestAnnounce = b->dup(); + bestAnnounce = bestAnnounceCurr->dup(); if (doSendAnnounce) { sendAnnounce(); @@ -649,9 +578,154 @@ void Gptp::executeBmca() scheduleMessageOnTopologyChange(); } + // Calculate ports states (based on IEEE 1588-2019 Figure 33) + masterPortIds.clear(); + passivePortIds.clear(); + for (const auto &portId : bmcaPortIds) { + if (portId != slavePortId) { + masterPortIds.insert(portId); + } + } + if (slavePortId != -1) { + // Don't do this calculation if we're selected as the GM => We assume all gPTP enabled ports are masters + for (const auto &portId : masterPortIds) { + GptpAnnounce *portAnnounce = nullptr; + if (auto it = receivedAnnounces.find(portId); it != receivedAnnounces.end()) { + portAnnounce = it->second; + } + + PortIdentity receiverPortIdentity; + receiverPortIdentity.clockIdentity = clockIdentity; + receiverPortIdentity.portNumber = portId; + + // Standard 1588-2019 defines 1 to 127, but 0 is reserved and unused anyway. + // The standard thus does not define the behavior for 0, so we just use <= 127 + if (localPriorityVector.grandmasterClockQuality.clockClass <= 127) { + if (portAnnounce == nullptr) { + continue; + } + auto compareLocalToPort = + compareAnnounceMessages(ownAnnounce, portAnnounce, ownPortIdentity, receiverPortIdentity); + + if (compareLocalToPort == B_BETTER_THAN_A || compareLocalToPort == B_BETTER_THAN_A_BY_TOPOLOGY) { + passivePortIds.insert(portId); + } + continue; + } + + // Clock class >= 128 + auto compareLocalToBest = compareAnnounceMessages(ownAnnounce, bestAnnounce, ownPortIdentity, + bestAnnounceReceiverIdentityCurr); + + if (compareLocalToBest == A_BETTER_THAN_B || compareLocalToBest == A_BETTER_THAN_B_BY_TOPOLOGY) { + // Local better than best => Master + continue; + } + + if (bestAnnounceReceiverIdentityCurr.portNumber == portId) { + // => Slave (already set above) + continue; + } + + if (portAnnounce == nullptr) { + continue; + } + + auto compareBestToPort = compareAnnounceMessages( + bestAnnounce, portAnnounce, bestAnnounceReceiverIdentityCurr, receiverPortIdentity); + if (compareBestToPort == A_BETTER_THAN_B_BY_TOPOLOGY) { + passivePortIds.insert(portId); + } + } + for (const auto &passivePortId : passivePortIds) { + masterPortIds.erase(passivePortId); + } + } + delete ownAnnounce; } +Gptp::BmcaPriorityVectorComparisonResult Gptp::compareAnnounceMessages(GptpAnnounce *a, GptpAnnounce *b, + PortIdentity aReceiverIdentity, + PortIdentity bReceiverIdentity) +{ + auto aVec = a->getPriorityVector(); + auto bVec = b->getPriorityVector(); + + // IEEE 1588-2019 Figure 34 + if (aVec.grandmasterIdentity == bVec.grandmasterIdentity) { + // Special cases Figure 35 + + // Compare steps removed (|A-B| >=2) + if (aVec.stepsRemoved > bVec.stepsRemoved + 1) { + return B_BETTER_THAN_A; + } + + else if (aVec.stepsRemoved + 1 < bVec.stepsRemoved) { + return A_BETTER_THAN_B; + } + + // Compare steps removed |A-B| <= 1 + if (aVec.stepsRemoved > bVec.stepsRemoved) { + if (aReceiverIdentity == a->getSourcePortIdentity()) { + throw cRuntimeError("Error-1: Indicates that one of the PTP messages was " + "transmitted and received on the same PTP Port.\n" + "(See IEEE 1588-2019 Figure 35 NOTE 2)"); + } + else if (aReceiverIdentity < a->getSourcePortIdentity()) { + return B_BETTER_THAN_A; + } + else { + // aReceiverIdentity > a->getSourcePortIdentity() + return B_BETTER_THAN_A_BY_TOPOLOGY; + } + } + else if (aVec.stepsRemoved < bVec.stepsRemoved) { + if (bReceiverIdentity == b->getSourcePortIdentity()) { + throw cRuntimeError("Error-1: Indicates that one of the PTP messages was " + "transmitted and received on the same PTP Port.\n" + "(See IEEE 1588-2019 Figure 35 NOTE 2)"); + } + else if (bReceiverIdentity < b->getSourcePortIdentity()) { + return A_BETTER_THAN_B; + } + else { + return A_BETTER_THAN_B_BY_TOPOLOGY; + } + } + + // Compare Identities of senders + if (a->getSourcePortIdentity() > b->getSourcePortIdentity()) { + return B_BETTER_THAN_A_BY_TOPOLOGY; + } + else if (a->getSourcePortIdentity() < b->getSourcePortIdentity()) { + return A_BETTER_THAN_B_BY_TOPOLOGY; + } + + // Compare port number of receivers + if (aReceiverIdentity.portNumber > bReceiverIdentity.portNumber) { + return B_BETTER_THAN_A_BY_TOPOLOGY; + } + else if (aReceiverIdentity.portNumber < bReceiverIdentity.portNumber) { + return A_BETTER_THAN_B_BY_TOPOLOGY; + } + else { + throw cRuntimeError("Error-2: indicates that the PTP messages are duplicates or that they are " + "an earlier and later PTP message from the same Grandmaster PTP Instance.\n" + "(See IEEE 1588-2019 Figure 35 NOTE 2)"); + } + } + else if (aVec > bVec) { + return B_BETTER_THAN_A; + } + else if (aVec < bVec) { + return A_BETTER_THAN_B; + } + else { + throw cRuntimeError("Unknown case in BMCA -- should never happen"); + } +} + void Gptp::sendPdelayResp(GptpReqAnswerEvent *req) { reqAnswerEvents.erase(req); diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 1a8562cb462..a333c7753ac 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -24,6 +24,14 @@ namespace inet { class INET_API Gptp : public ClockUserModuleBase, public cListener { + protected: + enum BmcaPriorityVectorComparisonResult { + A_BETTER_THAN_B, + A_BETTER_THAN_B_BY_TOPOLOGY, + B_BETTER_THAN_A, + B_BETTER_THAN_A_BY_TOPOLOGY, + }; + protected: // parameters: ModuleRefByPar interfaceTable; @@ -31,9 +39,9 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener // Configuration GptpNodeType gptpNodeType; int domainNumber = -1; - int slavePortId = -1; // interface ID of slave port - std::set masterPortIds; // interface IDs of master ports - std::set bmcaPortIds; // interface IDs of bmca ports + int slavePortId = -1; // interface ID of slave port + std::set masterPortIds; // interface IDs of master ports + std::set bmcaPortIds; // interface IDs of bmca ports std::set passivePortIds; // interface IDs of passive ports (only relevant for BMCA) uint64_t clockIdentity = 0; clocktime_t syncInterval; @@ -190,6 +198,9 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener void scheduleMessageOnTopologyChange(); void handleAnnounceTimeout(cMessage *pMessage); bool isGM() const { return gptpNodeType == MASTER_NODE || (gptpNodeType == BMCA_NODE && slavePortId == -1); }; + Gptp::BmcaPriorityVectorComparisonResult compareAnnounceMessages(GptpAnnounce *a, GptpAnnounce *b, + PortIdentity aReceiverIdentity, + PortIdentity bReceiverIdentity); }; } // namespace inet From 9d4d6824afe8308942753b6aa43eef0298128165 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Thu, 19 Dec 2024 18:28:09 +0100 Subject: [PATCH 092/138] BMCA result update --- .../timesynchronization/gptp_bmca/BMCA.anf | 734 +++++++++++++++++- 1 file changed, 732 insertions(+), 2 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp_bmca/BMCA.anf b/showcases/tsn/timesynchronization/gptp_bmca/BMCA.anf index 865d2015703..5052e74f9f1 100644 --- a/showcases/tsn/timesynchronization/gptp_bmca/BMCA.anf +++ b/showcases/tsn/timesynchronization/gptp_bmca/BMCA.anf @@ -739,7 +739,7 @@ utils.export_data_if_needed(df, props) - + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f8f9b46dbaa4bc4db9a9d186b6415d63aab6291d Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Thu, 19 Dec 2024 18:36:50 +0100 Subject: [PATCH 093/138] Fixed ToDos --- src/inet/linklayer/ieee8021as/Gptp.cc | 5 +---- src/inet/linklayer/ieee8021as/Gptp.ned | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 9144129e8dc..30fe3a15816 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -38,7 +38,6 @@ simsignal_t Gptp::gptpSyncSuccessfulSignal = cComponent::registerSignal("gptpSyn // MAC address: // 01-80-C2-00-00-0E for Announce and Signaling messages, for Sync, Follow_Up, // Pdelay_Req, Pdelay_Resp, and Pdelay_Resp_Follow_Up messages (ieee 802.1as-2020, 10.5.3 and 11.3.4) -// TODO: This does not seem to be correct const MacAddress Gptp::GPTP_MULTICAST_ADDRESS("01:80:C2:00:00:0E"); // EtherType: @@ -365,7 +364,6 @@ void Gptp::sendAnnounce() auto gptp = makeShared(); gptp->setDomainNumber(domainNumber); - // TODO: IEEE 1588-2019 specifies that it also might be 0, check what this means gptp->setOriginTimestamp(clock->getClockTime()); gptp->setSequenceId(sequenceId++); @@ -468,8 +466,7 @@ void Gptp::processAnnounce(Packet *packet, const GptpAnnounce *announce) } receivedAnnounces[incomingNicId] = announce->dup(); - // TODO: Use parameter - auto gptpAnnounceTimeoutTime = clocktime_t(3000, SIMTIME_MS); + clocktime_t gptpAnnounceTimeoutTime = par("announceTimeout"); if (announceTimeouts.find(incomingNicId) != announceTimeouts.end()) { rescheduleClockEventAfter(gptpAnnounceTimeoutTime, announceTimeouts[incomingNicId]); } diff --git a/src/inet/linklayer/ieee8021as/Gptp.ned b/src/inet/linklayer/ieee8021as/Gptp.ned index e61c6f5315b..73d11ce2b2d 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.ned +++ b/src/inet/linklayer/ieee8021as/Gptp.ned @@ -35,6 +35,8 @@ simple Gptp like IGptp double syncInitialOffset @unit(s) = default(syncInterval); // Time of first SYNC message double pdelayInitialOffset @unit(s) = default(0s); // Time of first link delay measurement double announceInitialOffset @unit(s) = default(0s); // Time of first Announce message (BMCA mode only) + double announceTimeout @unit(s) = default(announceInterval * 3); // Timeout for Announce messages (BMCA mode only) + int grandmasterPriority1 = default(255); // Priority1 value for BMCA // following parameters are used to schedule follow_up and pdelay_resp messages. From 67be183f03c4bd7e4738d3d13b08b1cde73fe098 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Fri, 10 Jan 2025 18:48:28 +0100 Subject: [PATCH 094/138] Finished BMCA implementation --- .cproject | 48 +- .../timesynchronization/gptp/GptpShowcase.anf | 8861 +---------------- .../timesynchronization/gptp/GptpShowcase.ned | 92 - .../tsn/timesynchronization/gptp/omnetpp.ini | 259 - .../timesynchronization/gptp_bmca/BMCA.anf | 732 ++ .../timesynchronization/gptp_bmca/omnetpp.ini | 13 +- .../gptp_bmca/simple-priority-change.xml | 3 + .../gptp_hotstandby/GptpShowcase.anf | 8353 ++++++++++++++++ .../gptp_hotstandby/GptpShowcase.ned | 107 + .../gptp_hotstandby/link_failure.xml | 9 + .../gptp_hotstandby/omnetpp.ini | 290 + src/inet/clock/servo/InstantServoClock.cc | 2 + src/inet/clock/servo/PiServoClock.cc | 2 + src/inet/clock/servo/ServoClockBase.h | 7 + src/inet/linklayer/ieee8021as/Gptp.cc | 56 +- src/inet/linklayer/ieee8021as/Gptp.h | 13 +- src/inet/linklayer/ieee8021as/Gptp.ned | 5 +- src/inet/linklayer/ieee8021as/GptpPacket.msg | 7 +- src/inet/linklayer/ieee8021as/HotStandby.cc | 80 +- src/inet/linklayer/ieee8021as/HotStandby.h | 21 +- src/inet/linklayer/ieee8021as/HotStandy.ned | 2 +- .../linklayer/ieee8021as/MultiDomainGptp.ned | 4 +- 22 files changed, 9663 insertions(+), 9303 deletions(-) create mode 100644 showcases/tsn/timesynchronization/gptp_bmca/simple-priority-change.xml create mode 100644 showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.anf create mode 100644 showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.ned create mode 100644 showcases/tsn/timesynchronization/gptp_hotstandby/link_failure.xml create mode 100644 showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini diff --git a/.cproject b/.cproject index 1d2991422cc..8df079e68cc 100644 --- a/.cproject +++ b/.cproject @@ -55,24 +55,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + @@ -128,24 +128,24 @@ - - - - - + + + + + - - + + - - - - + + + + - + diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 1430dcce897..92a74a61d1b 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -3,10 +3,6 @@ - - - - @@ -18,7 +14,7 @@ - + - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.ned b/showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.ned new file mode 100644 index 00000000000..eb032210e15 --- /dev/null +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.ned @@ -0,0 +1,107 @@ +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +package inet.showcases.tsn.timesynchronization.gptp_hotstandby; + +import inet.common.scenario.ScenarioManager; +import inet.networks.base.TsnNetworkBase; +import inet.node.ethernet.EthernetLink; +import inet.node.tsn.TsnClock; +import inet.node.tsn.TsnDevice; +import inet.node.tsn.TsnSwitch; + +network TwoMasterClocksTreeGptpShowcase extends TsnNetworkBase +{ + submodules: + scenarioManager: ScenarioManager { + @display("p=100,500;is=s"); + } + tsnClock1: TsnClock { + @display("p=500,150"); + } + tsnClock2: TsnClock { + @display("p=700,150"); + } + tsnSwitch1: TsnSwitch { + @display("p=500,300"); + } + tsnSwitch2: TsnSwitch { + @display("p=700,300"); + } + tsnDevice1: TsnDevice { + @display("p=300,450"); + } + tsnDevice2: TsnDevice { + @display("p=500,450"); + } + tsnDevice3: TsnDevice { + @display("p=700,450"); + } + tsnDevice4: TsnDevice { + @display("p=900,450"); + } + connections: + tsnClock1.ethg++ <--> EthernetLink <--> tsnSwitch1.ethg++; + tsnClock2.ethg++ <--> EthernetLink <--> tsnSwitch2.ethg++; + tsnSwitch1.ethg++ <--> EthernetLink <--> tsnSwitch2.ethg++; + tsnSwitch1.ethg++ <--> EthernetLink <--> tsnDevice1.ethg++; + tsnSwitch1.ethg++ <--> EthernetLink <--> tsnDevice2.ethg++; + tsnSwitch2.ethg++ <--> EthernetLink <--> tsnDevice3.ethg++; + tsnSwitch2.ethg++ <--> EthernetLink <--> tsnDevice4.ethg++; +} + + +network TwoMasterClocksRingGptpShowcase extends TsnNetworkBase +{ + submodules: + tsnClock1: TsnClock { + @display("p=500,150"); + } + tsnClock2: TsnClock { + @display("p=900,600"); + } + tsnSwitch1: TsnSwitch { + @display("p=700,150"); + } + tsnSwitch2: TsnSwitch { + @display("p=500,300"); + } + tsnSwitch3: TsnSwitch { + @display("p=500,450"); + } + tsnSwitch4: TsnSwitch { + @display("p=700,600"); + } + tsnSwitch5: TsnSwitch { + @display("p=900,450"); + } + tsnSwitch6: TsnSwitch { + @display("p=900,300"); + } + tsnDevice1: TsnDevice { + @display("p=300,300"); + } + tsnDevice2: TsnDevice { + @display("p=300,450"); + } + tsnDevice3: TsnDevice { + @display("p=1100,450"); + } + tsnDevice4: TsnDevice { + @display("p=1100,300"); + } + connections: + tsnClock1.ethg++ <--> EthernetLink <--> tsnSwitch1.ethg++; + tsnClock2.ethg++ <--> EthernetLink <--> tsnSwitch4.ethg++; + tsnSwitch1.ethg++ <--> EthernetLink <--> tsnSwitch2.ethg++; + tsnSwitch2.ethg++ <--> EthernetLink <--> tsnSwitch3.ethg++; + tsnSwitch3.ethg++ <--> EthernetLink <--> tsnSwitch4.ethg++; + tsnSwitch4.ethg++ <--> EthernetLink <--> tsnSwitch5.ethg++; + tsnSwitch5.ethg++ <--> EthernetLink <--> tsnSwitch6.ethg++; + tsnSwitch6.ethg++ <--> EthernetLink <--> tsnSwitch1.ethg++; + tsnSwitch2.ethg++ <--> EthernetLink <--> tsnDevice1.ethg++; + tsnSwitch3.ethg++ <--> EthernetLink <--> tsnDevice2.ethg++; + tsnSwitch5.ethg++ <--> EthernetLink <--> tsnDevice3.ethg++; + tsnSwitch6.ethg++ <--> EthernetLink <--> tsnDevice4.ethg++; +} diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/link_failure.xml b/showcases/tsn/timesynchronization/gptp_hotstandby/link_failure.xml new file mode 100644 index 00000000000..412db0a0e62 --- /dev/null +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/link_failure.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini b/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini new file mode 100644 index 00000000000..22cd1f8bda1 --- /dev/null +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini @@ -0,0 +1,290 @@ +[General] +seed-set = 0 +sim-time-limit = 20s +description = "abstract" + +# enable time synchronization in all network nodes +*.*.hasTimeSynchronization = true + +# all oscillators have a constant drift rate (specified with a random distribution for each one) +# except for the master clocks, which have a random drift rate +#**.clock.oscillator.typename = "RandomDriftOscillator" +*.tsnClock*.clock.oscillator.typename = "RandomDriftOscillator" +**.oscillator.changeInterval = 12.5ms +**.oscillator.driftRate = uniform(-100ppm, 100ppm) +**.oscillator.driftRateChange = uniform(-1ppm, 1ppm) +**.oscillator.driftRateChangeUpperLimit = 100ppm +**.oscillator.driftRateChangeLowerLimit = -100ppm + +**.kp=8 +**.ki=7 +**.offsetThreshold = 3us + + +# all Ethernet interfaces have 100 Mbps speed +*.*.eth[*].bitrate = 100Mbps + +*.visualizer.typename = "IntegratedMultiCanvasVisualizer" +*.visualizer.infoVisualizer[*].displayInfos = true + +[Config PrimaryAndHotStandbyMasterClocks] +network = TwoMasterClocksTreeGptpShowcase +description = "Extended tree topology with two master clocks" + +*.scenarioManager.script = xmldoc("link_failure.xml") + +# TSN clock2 has a pi clock +*.tsnClock2.clock.typename = "PiServoClock" + +*.tsnClock1.gptp.typename = "Gptp" +*.tsnClock1.gptp.clockModule = "tsnClock1.clock" +*.tsnClock1.gptp.masterPorts = ["eth0"] + +*.tsnClock2.gptp.typename = "MultiDomainGptp" +*.tsnClock2.gptp.numDomains = 2 +*.tsnClock2.gptp.domain[*].clockModule = "tsnClock2.clock" +*.tsnClock2.gptp.domain[0].gptpNodeType = "SLAVE_NODE" +*.tsnClock2.gptp.domain[0].slavePort = "eth0" +*.tsnClock2.gptp.domain[1].gptpNodeType = "MASTER_NODE" +*.tsnClock2.gptp.domain[1].masterPorts = ["eth0"] +*.tsnClock2.gptp.hasHotStandby = false + +# TSN switches have multiple clocks +*.tsnSwitch*.clock.typename = "MultiClock" +*.tsnSwitch*.clock.numClocks = 2 + +# TSN switches have multiple gPTP time synchronization domains +*.tsnSwitch*.gptp.typename = "MultiDomainGptp" +*.tsnSwitch*.gptp.numDomains = 2 +*.tsnSwitch1.gptp.domain[0].slavePort = "eth0" +*.tsnSwitch1.gptp.domain[0].masterPorts = ["eth1", "eth2", "eth3"] +*.tsnSwitch1.gptp.domain[1].slavePort = "eth1" +*.tsnSwitch1.gptp.domain[1].masterPorts = ["eth2", "eth3"] # eth1 is omitted because no sync needed towards primary master +*.tsnSwitch2.gptp.domain[0].slavePort = "eth1" +*.tsnSwitch2.gptp.domain[0].masterPorts = ["eth0", "eth2", "eth3"] +*.tsnSwitch2.gptp.domain[1].slavePort = "eth0" +*.tsnSwitch2.gptp.domain[1].masterPorts = ["eth1", "eth2", "eth3"] + +# TSN devices have multiple clocks +*.tsnDevice*.clock.typename = "MultiClock" +*.tsnDevice*.clock.numClocks = 2 + +# TSN devices have multiple gPTP time synchronization domains +*.tsnDevice*.gptp.typename = "MultiDomainGptp" +*.tsnDevice*.gptp.numDomains = 2 +*.tsnDevice1.gptp.clockModule = "tsnDevice1.clock" +*.tsnDevice2.gptp.clockModule = "tsnDevice2.clock" +*.tsnDevice3.gptp.clockModule = "tsnDevice3.clock" +*.tsnDevice4.gptp.clockModule = "tsnDevice4.clock" +*.tsnDevice*.gptp.domain[*].slavePort = "eth0" + +# different initial gPTP pdelay measurement and time synchronization offsets +**.pdelayInitialOffset = 100us +*.*.gptp.domain[0].syncInitialOffset = syncInterval * 1 / 2 +*.*.gptp.domain[1].syncInitialOffset = syncInterval * 2 / 2 + +# set reference clocks so the time difference can be visualized +**.clock[0].referenceClock = "tsnClock1.clock" +**.clock[1].referenceClock = "tsnClock2.clock" + +# multiple data link visualizers display different gPTP time synchronization domain packets +*.visualizer.numDataLinkVisualizers = 2 +*.visualizer.dataLinkVisualizer[*].displayLinks = true +*.visualizer.dataLinkVisualizer[*].activityLevel = "protocol" +*.visualizer.dataLinkVisualizer[0].packetFilter = expr((has(GptpSync) && GptpSync.domainNumber == 0) || (has(GptpFollowUp) && GptpFollowUp.domainNumber == 0)) +*.visualizer.dataLinkVisualizer[1].packetFilter = expr((has(GptpSync) && GptpSync.domainNumber == 1) || (has(GptpFollowUp) && GptpFollowUp.domainNumber == 1)) +*.visualizer.dataLinkVisualizer[0].tags = "primary GM" +*.visualizer.dataLinkVisualizer[1].tags = "hot-standby GM" +*.visualizer.dataLinkVisualizer[0].lineColor = "blue2" +*.visualizer.dataLinkVisualizer[1].lineColor = "red2" + +*.visualizer.numInfoVisualizers = 8 +*.visualizer.infoVisualizer[0].modules = "*.tsnDevice*.*.clock[0]" +*.visualizer.infoVisualizer[0].textColor = "blue" +*.visualizer.infoVisualizer[1].modules = "*.tsnDevice*.*.clock[1]" +*.visualizer.infoVisualizer[1].textColor = "red" +*.visualizer.infoVisualizer[0..1].placementHint = "bottom" +*.visualizer.infoVisualizer[2].modules = "*.tsnClock1.clock" +*.visualizer.infoVisualizer[3].modules = "*.tsnClock2.clock" +*.visualizer.infoVisualizer[2].textColor = "blue" +*.visualizer.infoVisualizer[3].textColor = "red" +*.tsnClock*.clock.displayStringTextFormat = "time: %T" +*.visualizer.infoVisualizer[4].modules = "*.tsnSwitch1.*.clock[0]" +*.visualizer.infoVisualizer[5].modules = "*.tsnSwitch1.*.clock[1]" +*.visualizer.infoVisualizer[6].modules = "*.tsnSwitch2.*.clock[0]" +*.visualizer.infoVisualizer[7].modules = "*.tsnSwitch2.*.clock[1]" +*.visualizer.infoVisualizer[{4,6}].textColor = "blue" +*.visualizer.infoVisualizer[{5,7}].textColor = "red" +*.tsnDevice*.clock.*.displayStringTextFormat = "diff: %d" +*.tsnSwitch*.clock.*.displayStringTextFormat = "diff: %d" +*.visualizer.infoVisualizer[4..5].placementHint = "topLeft" +*.visualizer.infoVisualizer[6..7].placementHint = "topRight" + +[Config TwoMasterClocksExploitingNetworkRedundancy] +network = TwoMasterClocksRingGptpShowcase +description = "Ring topology with redundant time synchronization domains" +# clock visualization note: bridge and slave nodes display difference from corresponding master clock + +# TSN clock2 has multiple clocks +*.tsnClock2.clock.typename = "MultiClock" +*.tsnClock2.clock.numClocks = 2 + +# TSN clocks have multiple gPTP time synchronization domains +*.tsnClock*.gptp.typename = "MultiDomainGptp" +*.tsnClock1.gptp.numDomains = 2 +*.tsnClock1.gptp.domain[0..1].clockModule = "tsnClock1.clock" +*.tsnClock1.gptp.domain[0].masterPorts = ["eth0"] +*.tsnClock1.gptp.domain[1].masterPorts = ["eth0"] +*.tsnClock2.gptp.numDomains = 4 +*.tsnClock2.gptp.domain[2..3].clockModule = "tsnClock2.clock" +*.tsnClock2.gptp.domain[0].gptpNodeType = "SLAVE_NODE" +*.tsnClock2.gptp.domain[0].slavePort = "eth0" +*.tsnClock2.gptp.domain[1].gptpNodeType = "SLAVE_NODE" +*.tsnClock2.gptp.domain[1].slavePort = "eth0" +*.tsnClock2.gptp.domain[2].gptpNodeType = "MASTER_NODE" +*.tsnClock2.gptp.domain[2].masterPorts = ["eth0"] +*.tsnClock2.gptp.domain[3].gptpNodeType = "MASTER_NODE" +*.tsnClock2.gptp.domain[3].masterPorts = ["eth0"] + +# TSN switches have multiple clocks +*.tsnSwitch*.clock.typename = "MultiClock" +*.tsnSwitch*.clock.numClocks = 4 + +# TSN switches have multiple gPTP time synchronization domains +*.tsnSwitch*.gptp.typename = "MultiDomainGptp" +*.tsnSwitch*.gptp.numDomains = 4 + +# TSN switch 1 +*.tsnSwitch1.gptp.domain[0].masterPorts = ["eth1"] +*.tsnSwitch1.gptp.domain[0].slavePort = "eth0" +*.tsnSwitch1.gptp.domain[1].masterPorts = ["eth2"] +*.tsnSwitch1.gptp.domain[1].slavePort = "eth0" +*.tsnSwitch1.gptp.domain[2].masterPorts = ["eth1"] +*.tsnSwitch1.gptp.domain[2].slavePort = "eth2" +*.tsnSwitch1.gptp.domain[3].masterPorts = ["eth2"] +*.tsnSwitch1.gptp.domain[3].slavePort = "eth1" + +# TSN switch 2 +*.tsnSwitch2.gptp.domain[0].masterPorts = ["eth1", "eth2"] +*.tsnSwitch2.gptp.domain[0].slavePort = "eth0" +*.tsnSwitch2.gptp.domain[1].masterPorts = ["eth2"] +*.tsnSwitch2.gptp.domain[1].slavePort = "eth1" +*.tsnSwitch2.gptp.domain[2].masterPorts = ["eth1", "eth2"] +*.tsnSwitch2.gptp.domain[2].slavePort = "eth0" +*.tsnSwitch2.gptp.domain[3].masterPorts = ["eth0", "eth2"] +*.tsnSwitch2.gptp.domain[3].slavePort = "eth1" + +# TSN switch 3 +*.tsnSwitch3.gptp.domain[0].masterPorts = ["eth1", "eth2"] +*.tsnSwitch3.gptp.domain[0].slavePort = "eth0" +*.tsnSwitch3.gptp.domain[1].masterPorts = ["eth0", "eth2"] +*.tsnSwitch3.gptp.domain[1].slavePort = "eth1" +*.tsnSwitch3.gptp.domain[2].masterPorts = ["eth2"] +*.tsnSwitch3.gptp.domain[2].slavePort = "eth0" +*.tsnSwitch3.gptp.domain[3].masterPorts = ["eth0", "eth2"] +*.tsnSwitch3.gptp.domain[3].slavePort = "eth1" + +# TSN switch 4 +*.tsnSwitch4.gptp.domain[0].masterPorts = ["eth0", "eth2"] +*.tsnSwitch4.gptp.domain[0].slavePort = "eth1" +*.tsnSwitch4.gptp.domain[1].masterPorts = ["eth0", "eth1"] +*.tsnSwitch4.gptp.domain[1].slavePort = "eth2" +*.tsnSwitch4.gptp.domain[2].masterPorts = ["eth2"] +*.tsnSwitch4.gptp.domain[2].slavePort = "eth0" +*.tsnSwitch4.gptp.domain[3].masterPorts = ["eth1"] +*.tsnSwitch4.gptp.domain[3].slavePort = "eth0" + +# TSN switch 5 +*.tsnSwitch5.gptp.domain[0].masterPorts = ["eth1", "eth2"] +*.tsnSwitch5.gptp.domain[0].slavePort = "eth0" +*.tsnSwitch5.gptp.domain[1].masterPorts = ["eth0", "eth2"] +*.tsnSwitch5.gptp.domain[1].slavePort = "eth1" +*.tsnSwitch5.gptp.domain[2].masterPorts = ["eth1", "eth2"] +*.tsnSwitch5.gptp.domain[2].slavePort = "eth0" +*.tsnSwitch5.gptp.domain[3].masterPorts = ["eth2"] +*.tsnSwitch5.gptp.domain[3].slavePort = "eth1" + +# TSN switch 6 +*.tsnSwitch6.gptp.domain[0].masterPorts = ["eth2"] +*.tsnSwitch6.gptp.domain[0].slavePort = "eth0" +*.tsnSwitch6.gptp.domain[1].masterPorts = ["eth0", "eth2"] +*.tsnSwitch6.gptp.domain[1].slavePort = "eth1" +*.tsnSwitch6.gptp.domain[2].masterPorts = ["eth1", "eth2"] +*.tsnSwitch6.gptp.domain[2].slavePort = "eth0" +*.tsnSwitch6.gptp.domain[3].masterPorts = ["eth0", "eth2"] +*.tsnSwitch6.gptp.domain[3].slavePort = "eth1" + +# TSN devices have multiple clocks +*.tsnDevice*.clock.typename = "MultiClock" +*.tsnDevice*.clock.numClocks = 4 + +# TSN devices have multiple gPTP time synchronization domains +*.tsnDevice*.gptp.typename = "MultiDomainGptp" +*.tsnDevice*.gptp.numDomains = 4 +*.tsnDevice1.gptp.clockModule = "tsnDevice1.clock" +*.tsnDevice2.gptp.clockModule = "tsnDevice2.clock" +*.tsnDevice3.gptp.clockModule = "tsnDevice3.clock" +*.tsnDevice4.gptp.clockModule = "tsnDevice4.clock" +*.tsnDevice*.gptp.domain[*].slavePort = "eth0" + +# different initial gPTP pdelay measurement and time synchronization offsets +**.pdelayInitialOffset = 0.1ms +*.*.gptp.domain[0].syncInitialOffset = syncInterval * 1 / 4 +*.*.gptp.domain[1].syncInitialOffset = syncInterval * 2 / 4 +*.*.gptp.domain[2].syncInitialOffset = syncInterval * 3 / 4 +*.*.gptp.domain[3].syncInitialOffset = syncInterval * 4 / 4 + +# multiple data link visualizers display different gPTP time synchronization domain packets +*.visualizer.typename = "IntegratedMultiCanvasVisualizer" +*.visualizer.numDataLinkVisualizers = 4 +*.visualizer.dataLinkVisualizer[*].displayLinks = true +*.visualizer.dataLinkVisualizer[*].activityLevel = "protocol" +*.visualizer.dataLinkVisualizer[0].packetFilter = expr(has(GptpSync) && GptpSync.domainNumber == 0) +*.visualizer.dataLinkVisualizer[1].packetFilter = expr(has(GptpSync) && GptpSync.domainNumber == 1) +*.visualizer.dataLinkVisualizer[2].packetFilter = expr(has(GptpSync) && GptpSync.domainNumber == 2) +*.visualizer.dataLinkVisualizer[3].packetFilter = expr(has(GptpSync) && GptpSync.domainNumber == 3) +*.visualizer.dataLinkVisualizer[0].tags = "primary GM domain0" +*.visualizer.dataLinkVisualizer[1].tags = "primary GM domain1" +*.visualizer.dataLinkVisualizer[2].tags = "hot-standby GM domain0" +*.visualizer.dataLinkVisualizer[3].tags = "hot-standby GM domain1" +*.visualizer.dataLinkVisualizer[0].lineColor = "blue4" +*.visualizer.dataLinkVisualizer[1].lineColor = "blue1" +*.visualizer.dataLinkVisualizer[2].lineColor = "red4" +*.visualizer.dataLinkVisualizer[3].lineColor = "red1" + +*.tsnClock1.clock.referenceClock = "tsnClock1.clock" # so how does it sync to domain1? +*.tsnClock2.clock.clock[*].referenceClock = "tsnClock1.clock" +*.tsnSwitch*.clock.clock[0..1].referenceClock = "tsnClock1.clock" +*.tsnDevice*.clock.clock[0..1].referenceClock = "tsnClock1.clock" +*.tsnSwitch*.clock.clock[2..3].referenceClock = "tsnClock2.clock.clock[1]" +*.tsnDevice*.clock.clock[2..3].referenceClock = "tsnClock2.clock.clock[1]" +*.tsnSwitch*.clock.clock[*].displayStringTextFormat = "%d" +*.tsnDevice*.clock.clock[*].displayStringTextFormat = "%d" +*.tsnClock1.clock.displayStringTextFormat = "time: %T" +*.tsnClock2.clock.clock*.displayStringTextFormat = "time: %T\ndiff: %d" + +*.visualizer.numInfoVisualizers = 11 +*.visualizer.infoVisualizer[0].modules = "*.tsnClock1.clock" +*.visualizer.infoVisualizer[0].textColor = "blue4" +*.visualizer.infoVisualizer[1].modules = "*.tsnSwitch1.*.clock[0] or *.tsnSwitch2.*.clock[0] or *.tsnSwitch6.*.clock[0] or *.tsnDevice1.*.clock[0] or *.tsnDevice4.*.clock[0]" +*.visualizer.infoVisualizer[1].textColor = "blue4" +*.visualizer.infoVisualizer[2].modules = "*.tsnSwitch1.*.clock[1] or *.tsnSwitch2.*.clock[1] or *.tsnSwitch6.*.clock[1] or *.tsnDevice1.*.clock[1] or *.tsnDevice4.*.clock[1]" +*.visualizer.infoVisualizer[2].textColor = "blue1" +*.visualizer.infoVisualizer[3].modules = "*.tsnSwitch1.*.clock[2] or *.tsnSwitch2.*.clock[2] or *.tsnSwitch6.*.clock[2] or *.tsnDevice1.*.clock[2] or *.tsnDevice4.*.clock[2]" +*.visualizer.infoVisualizer[3].textColor = "red4" +*.visualizer.infoVisualizer[4].modules = "*.tsnSwitch1.*.clock[3] or *.tsnSwitch2.*.clock[3] or *.tsnSwitch6.*.clock[3] or *.tsnDevice1.*.clock[3] or *.tsnDevice4.*.clock[3]" +*.visualizer.infoVisualizer[4].textColor = "red1" +*.visualizer.infoVisualizer[5].modules = "*.tsnDevice2.*.clock[0] or *.tsnDevice3.*.clock[0] or *.tsnSwitch3.*.clock[0] or *.tsnSwitch4.*.clock[0] or *.tsnSwitch5.*.clock[0]" +*.visualizer.infoVisualizer[5].textColor = "blue4" +*.visualizer.infoVisualizer[6].modules = "*.tsnDevice2.*.clock[1] or *.tsnDevice3.*.clock[1] or *.tsnSwitch3.*.clock[1] or *.tsnSwitch4.*.clock[1] or *.tsnSwitch5.*.clock[1]" +*.visualizer.infoVisualizer[6].textColor = "blue1" +*.visualizer.infoVisualizer[7].modules = "*.tsnDevice2.*.clock[2] or *.tsnDevice3.*.clock[2] or *.tsnSwitch3.*.clock[2] or *.tsnSwitch4.*.clock[2] or *.tsnSwitch5.*.clock[2]" +*.visualizer.infoVisualizer[7].textColor = "red4" +*.visualizer.infoVisualizer[8].modules = "*.tsnDevice2.*.clock[3] or *.tsnDevice3.*.clock[3] or *.tsnSwitch3.*.clock[3] or *.tsnSwitch4.*.clock[3] or *.tsnSwitch5.*.clock[3]" +*.visualizer.infoVisualizer[8].textColor = "red1" +*.visualizer.infoVisualizer[9].modules = "*.tsnClock2.*.clock[0]" +*.visualizer.infoVisualizer[9].textColor = "red4" +*.visualizer.infoVisualizer[10].modules = "*.tsnClock2.*.clock[1]" +*.visualizer.infoVisualizer[10].textColor = "red1" +*.visualizer.infoVisualizer[5..10].placementHint = "bottom" + diff --git a/src/inet/clock/servo/InstantServoClock.cc b/src/inet/clock/servo/InstantServoClock.cc index f4982091896..8f99bc8e236 100644 --- a/src/inet/clock/servo/InstantServoClock.cc +++ b/src/inet/clock/servo/InstantServoClock.cc @@ -21,6 +21,7 @@ void InstantServoClock::initialize(int stage) } offsetPrev = -1; localPrev = -1; + clockState = INIT; } } @@ -45,6 +46,7 @@ void InstantServoClock::adjustClockTo(clocktime_t newClockTime) drift += ppm(1e6 * (offsetPrev - offset) / (localPrev - local)); EV_INFO << "Drift: " << drift << "\n"; setOscillatorCompensation(drift); + clockState = SYNCED; } offsetPrev = offset; diff --git a/src/inet/clock/servo/PiServoClock.cc b/src/inet/clock/servo/PiServoClock.cc index 79e6c6015b1..829f65bbf53 100644 --- a/src/inet/clock/servo/PiServoClock.cc +++ b/src/inet/clock/servo/PiServoClock.cc @@ -44,6 +44,7 @@ void PiServoClock::adjustClockTo(clocktime_t newClockTime) { EV_INFO << "Offset is too large, resetting phase\n"; EV_INFO << "Offset: " << offsetUs << " maxOffset: " << offsetThresholdUs << "\n"; phase = 0; + clockState = INIT; } switch (phase) { @@ -71,6 +72,7 @@ void PiServoClock::adjustClockTo(clocktime_t newClockTime) { setOscillatorCompensation(drift); + clockState = SYNCED; phase = 2; break; case 2: diff --git a/src/inet/clock/servo/ServoClockBase.h b/src/inet/clock/servo/ServoClockBase.h index 9ed2efc3e53..dd1cb9abe85 100644 --- a/src/inet/clock/servo/ServoClockBase.h +++ b/src/inet/clock/servo/ServoClockBase.h @@ -17,6 +17,11 @@ namespace inet { class ServoClockBase : public OscillatorBasedClock, public IScriptable { public: + enum ClockState { + INIT, + SYNCED + }; + struct ClockJumpDetails : public cObject { clocktime_t oldClockTime; clocktime_t newClockTime; @@ -31,6 +36,7 @@ class ServoClockBase : public OscillatorBasedClock, public IScriptable // means the clock compensates 100 microseconds for every second in clock time // 100 ppm value means the oscillator tick length is compensated to be smaller by a factor of (1 / (1 + 100 / 1E+6)) // than the actual tick length measured in clock time + ClockState clockState = INIT; protected: virtual void rescheduleClockEvents(clocktime_t oldClockTime, clocktime_t newClockTime); @@ -45,6 +51,7 @@ class ServoClockBase : public OscillatorBasedClock, public IScriptable virtual void jumpClockTo(clocktime_t newClockTime, bool notifyListeners = true); virtual void setOscillatorCompensation(ppm oscillatorCompensationValue); virtual void resetOscillator() const; + virtual ClockState getClockState() const { return clockState; } }; } /* namespace inet */ diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 30fe3a15816..e9b26d8d402 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -33,7 +33,7 @@ simsignal_t Gptp::peerDelaySignal = cComponent::registerSignal("peerDelay"); simsignal_t Gptp::residenceTimeSignal = cComponent::registerSignal("residenceTime"); simsignal_t Gptp::correctionFieldIngressSignal = cComponent::registerSignal("correctionFieldIngress"); simsignal_t Gptp::correctionFieldEgressSignal = cComponent::registerSignal("correctionFieldEgress"); -simsignal_t Gptp::gptpSyncSuccessfulSignal = cComponent::registerSignal("gptpSyncSuccessfulSignal"); +simsignal_t Gptp::gptpSyncStateChanged = cComponent::registerSignal("gptpSyncStateChanged"); // MAC address: // 01-80-C2-00-00-0E for Announce and Signaling messages, for Sync, Follow_Up, @@ -54,7 +54,7 @@ Gptp::~Gptp() if (selfMsgAnnounce) cancelAndDeleteClockEvent(selfMsgAnnounce); for (auto &reqAnswerEvent : reqAnswerEvents) - delete reqAnswerEvent; + cancelAndDeleteClockEvent(reqAnswerEvent); for (auto &announce : receivedAnnounces) delete announce.second; for (auto &announceTimeout : announceTimeouts) { @@ -193,6 +193,10 @@ void Gptp::scheduleMessageOnTopologyChange() cancelAndDeleteClockEvent(selfMsgDelayReq); selfMsgDelayReq = nullptr; } + if (selfMsgSyncTimeout) { + cancelAndDeleteClockEvent(selfMsgSyncTimeout); + selfMsgSyncTimeout = nullptr; + } } /* Only grandmaster in the domain can initialize the synchronization message @@ -205,6 +209,10 @@ void Gptp::scheduleMessageOnTopologyChange() clocktime_t scheduleSync = par("syncInitialOffset"); preciseOriginTimestamp = clock->getClockTime() + scheduleSync; scheduleClockEventAfter(scheduleSync, selfMsgSync); + changeSyncState(SYNCED); + } else { + selfMsgSyncTimeout = new ClockEvent("selfMsgSyncTimeout", GPTP_SELF_MSG_SYNC_TIMEOUT); + scheduleClockEventAfter(par("syncTimeout"), selfMsgSyncTimeout); } if (slavePortId != -1) { // Schedule Pdelay_Req message is sent by slave port @@ -257,6 +265,9 @@ void Gptp::handleSelfMessage(cMessage *msg) case GPTP_SELF_MSG_ANNOUNCE_TIMEOUT: handleAnnounceTimeout(msg); break; + case GPTP_SELF_MSG_SYNC_TIMEOUT: + handleSyncTimeout(msg); + break; default: throw cRuntimeError("Unknown self message (%s)%s, kind=%d", msg->getClassName(), msg->getName(), msg->getKind()); @@ -878,6 +889,15 @@ void Gptp::synchronize() newLocalTimeAtTimeSync = clock->getClockTime(); + switch(servoClock->getClockState()) { + case ServoClockBase::ClockState::INIT: + changeSyncState(SyncState::UNSYNCED); + break; + case ServoClockBase::ClockState::SYNCED: + changeSyncState(SyncState::SYNCED); + break; + } + /************** Rate ratio calculation ************************************* * It is calculated based on interval between two successive Sync messages * ***************************************************************************/ @@ -910,12 +930,25 @@ void Gptp::synchronize() syncIngressTimestampLast = syncIngressTimestamp; preciseOriginTimestampLast = preciseOriginTimestamp; + if (selfMsgSyncTimeout->isScheduled()) { + rescheduleClockEventAfter(par("syncTimeout"), selfMsgSyncTimeout); + } + else if (!isGM()) { + scheduleClockEventAfter(par("syncTimeout"), selfMsgSyncTimeout); + } + emit(receivedRateRatioSignal, receivedRateRatio); emit(gmRateRatioSignal, gmRateRatio); emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); - emit(gptpSyncSuccessfulSignal, - CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); // TODO: check if the time stamp is correct +} + +void Gptp::changeSyncState(SyncState state) { + auto prevSyncState = syncState; + syncState = state; + if (prevSyncState != syncState) { + emit(gptpSyncStateChanged, syncState); + } } void Gptp::calculateGmRatio() @@ -1117,6 +1150,11 @@ void Gptp::handleAnnounceTimeout(cMessage *pMessage) executeBmca(); } +void Gptp::handleSyncTimeout(cMessage *pMessage) { + changeSyncState(SyncState::UNSYNCED); + simTime(); +} + void Gptp::handleTransmissionStartedSignal(const GptpBase *gptp, cComponent *source) { int portId = getContainingNicModule(check_and_cast(source))->getInterfaceId(); @@ -1192,5 +1230,15 @@ void Gptp::handleClockJump(ServoClockBase::ClockJumpDetails *clockJumpDetails) // Should this behavior change in the future, this timestamping adjustment mechanism needs to be changed as well to // somehow also adjust the timestamps already attaches as tags to the gPTP packets. } +void Gptp::handleParameterChange(const char *name) { + // TODO: Maybe one could make more paremeters mutable (e.g. syncInterval might be interesting for some people) + + // Compare if parameter is grandmasterPriority1 + if (!strcmp(name, "grandmasterPriority1")) { + // Update local priority vector + localPriorityVector.grandmasterPriority1 = par("grandmasterPriority1"); + executeBmca(); + } +} } // namespace inet \ No newline at end of file diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index cf0f097d5be..0b661a2f95b 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -57,6 +57,8 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener double receivedRateRatio = 1.0; double neighborRateRatio = 1.0; + SyncState syncState = SyncState::UNSYNCED; + uint16_t sequenceId = 0; // == Propagation Delay Measurement Procedure == // Timestamps corresponding to the PDelayRequest and PDelayResponse mechanism @@ -107,6 +109,7 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener ClockEvent *selfMsgSync = nullptr; ClockEvent *selfMsgDelayReq = nullptr; ClockEvent *selfMsgAnnounce = nullptr; + ClockEvent *selfMsgSyncTimeout = nullptr; std::set reqAnswerEvents; @@ -126,7 +129,7 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener public: static const MacAddress GPTP_MULTICAST_ADDRESS; - static simsignal_t gptpSyncSuccessfulSignal; + static simsignal_t gptpSyncStateChanged; uint8_t getDomainNumber() const {return this->domainNumber;}; clocktime_t getSyncInterval() const {return syncInterval;}; @@ -142,6 +145,8 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener virtual void handleSelfMessage(cMessage *msg); + virtual void handleParameterChange(const char *name) override; + virtual void handleClockJump(ServoClockBase::ClockJumpDetails *clockJumpDetails); void handleTransmissionStartedSignal(const GptpBase *gptp, omnetpp::cComponent *source); @@ -194,12 +199,14 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener void calculateGmRatio(); void executeBmca(); void initPorts(); - void scheduleMessageOnTopologyChange(); + virtual void scheduleMessageOnTopologyChange(); void handleAnnounceTimeout(cMessage *pMessage); bool isGM() const { return gptpNodeType == MASTER_NODE || (gptpNodeType == BMCA_NODE && slavePortId == -1); }; - Gptp::BmcaPriorityVectorComparisonResult compareAnnounceMessages(GptpAnnounce *a, GptpAnnounce *b, + static Gptp::BmcaPriorityVectorComparisonResult compareAnnounceMessages(GptpAnnounce *a, GptpAnnounce *b, PortIdentity aReceiverIdentity, PortIdentity bReceiverIdentity); + void changeSyncState(SyncState state); + void handleSyncTimeout(cMessage *pMessage); }; } // namespace inet diff --git a/src/inet/linklayer/ieee8021as/Gptp.ned b/src/inet/linklayer/ieee8021as/Gptp.ned index 73d11ce2b2d..ae06ffbca0b 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.ned +++ b/src/inet/linklayer/ieee8021as/Gptp.ned @@ -36,8 +36,9 @@ simple Gptp like IGptp double pdelayInitialOffset @unit(s) = default(0s); // Time of first link delay measurement double announceInitialOffset @unit(s) = default(0s); // Time of first Announce message (BMCA mode only) double announceTimeout @unit(s) = default(announceInterval * 3); // Timeout for Announce messages (BMCA mode only) + double syncTimeout @unit(s) = default(syncInterval * 3); // When to assume we are out of sync - int grandmasterPriority1 = default(255); // Priority1 value for BMCA + int grandmasterPriority1 @mutable = default(255); // Priority1 value for BMCA // following parameters are used to schedule follow_up and pdelay_resp messages. // These numbers must be large enough to prevent creating a queue in the MAC layer. @@ -56,7 +57,7 @@ simple Gptp like IGptp @signal[residenceTime](type=simtime_t); @signal[correctionFieldIngress](type=simtime_t); @signal[correctionFieldEgress](type=simtime_t); - @signal[gptpSyncSuccessfulSignal](type=simtime_t); + @signal[gptpSyncStateChanged](type=int); @signal[packetDropped](type=inet::Packet); @statistic[localTime](record=vector; interpolationmode=linear); @statistic[timeDifference](record=vector; interpolationmode=linear); diff --git a/src/inet/linklayer/ieee8021as/GptpPacket.msg b/src/inet/linklayer/ieee8021as/GptpPacket.msg index 4e0e52a1b26..42a258ec711 100644 --- a/src/inet/linklayer/ieee8021as/GptpPacket.msg +++ b/src/inet/linklayer/ieee8021as/GptpPacket.msg @@ -79,7 +79,12 @@ enum GptpSelfMsgKind { GPTP_SELF_MSG_PDELAY_REQ = 103; GPTP_SELF_MSG_ANNOUNCE = 104; GPTP_SELF_MSG_ANNOUNCE_TIMEOUT = 105; - GPTP_CLOCK_DOWN_MSG = 106; + GPTP_SELF_MSG_SYNC_TIMEOUT = 106; +} + +enum SyncState { + UNSYNCED = 0; + SYNCED = 1; } // ieee802.1AS-2020 10.6.2.2.8: flags (Octet2) diff --git a/src/inet/linklayer/ieee8021as/HotStandby.cc b/src/inet/linklayer/ieee8021as/HotStandby.cc index ce0975ec8d9..4f65f29ec87 100644 --- a/src/inet/linklayer/ieee8021as/HotStandby.cc +++ b/src/inet/linklayer/ieee8021as/HotStandby.cc @@ -6,83 +6,65 @@ #include "HotStandby.h" - +#include "Gptp.h" namespace inet { Define_Module(HotStandby); -HotStandby::~HotStandby() -{ - if(standByMsg) - cancelAndDeleteClockEvent(standByMsg); -} - void HotStandby::initialize(int stage) { ClockUserModuleBase::initialize(stage); if (stage == INITSTAGE_LINK_LAYER) { auto parent = getParentModule(); - for (int i = 0; i < parent->getSubmoduleVectorSize("domain"); i++) { + auto numDomains = parent->par("numDomains").intValue(); + for (int i = 0; i < numDomains; i++) { auto gptp = parent->getSubmodule("domain", i); - gptp->subscribe(Gptp::gptpSyncSuccessfulSignal, this); + gptp->subscribe(Gptp::gptpSyncStateChanged, this); } - gptpModule = check_and_cast(parent->getSubmodule("domain", 0)); - standByMsg = new ClockEvent("standByMsg", GPTP_CLOCK_DOWN_MSG); - delta = par("delta"); - scheduleClockEventAfter(1, standByMsg); // set clocktime_t as delta, timer or 0 won't work. Why?? + multiClock = check_and_cast(clock.get()); } } -void HotStandby::receiveSignal(cComponent *source, simsignal_t simSignal, const SimTime& t, cObject *details) +void HotStandby::receiveSignal(cComponent *source, simsignal_t simSignal, const intval_t t, cObject *details) { Enter_Method("%s", cComponent::getSignalName(simSignal)); - if (simSignal == Gptp::gptpSyncSuccessfulSignal) { + if (simSignal == Gptp::gptpSyncStateChanged) { auto gptp = check_and_cast(source); - handleGptpSyncSuccessfulSignal(gptp, t); + auto syncState = static_cast(t); + handleSyncStateChanged(gptp, syncState); } } -void HotStandby::handleGptpSyncSuccessfulSignal(const Gptp *gptp, const SimTime& t) +void HotStandby::handleSyncStateChanged(const Gptp *gptp, const SyncState syncState) { - // store time into array + // store sync state into array auto domainNumber = gptp->getDomainNumber(); - auto clockTime = SIMTIME_AS_CLOCKTIME(t); - gptpSyncTime[domainNumber] = clockTime; - - auto syncInterval = gptp->getSyncInterval(); - timer = 2 * syncInterval + delta; - rescheduleClockEventAfter(timer, standByMsg); - EV_INFO << "timer : " << timer << endl; -} - -void HotStandby::handleMessage(cMessage *msg) -{ - if(msg->isSelfMessage() && msg->getKind() == GPTP_CLOCK_DOWN_MSG){ - ASSERT(standByMsg == msg); + syncStates[domainNumber] = syncState; + auto currentActiveIndex = multiClock->par("activeClockIndex").intValue(); + // TODO: We assume lower domain is always preferable + // In the future, domain preference should be configurable + if (domainNumber > currentActiveIndex || (domainNumber == currentActiveIndex && syncState == SYNCED)) { + // if the domain is higher than the current active domain, ignore it + return; + } -// // check the time difference between the domains -- TODO: Fix the logic of judgement for timeDiff -// auto domainNumber = gptpModule->getDomainNumber(); -// auto domainClockTime = gptpSyncTime[domainNumber]; -// auto curr = getClockTime(); -// auto timeDiff = curr - domainClockTime; -// EV_INFO << "domain number : " << domainNumber << std::endl; -// EV_INFO << "domain clock time : " << domainClockTime << std::endl; -// EV_INFO << "current time : " << curr << std::endl; -// EV_INFO << "timeDiff : " << timeDiff << std::endl; -// -// if(timeDiff >= timer){ -// EV_INFO << "timeDiff is greater than timer" << std::endl; -// // reschedule the timer for the next clock down event -// EV_INFO << "TIMER : " << timer << std::endl; -// rescheduleClockEventAfter(timer, standByMsg); -// } + int bestDomainIndex = std::numeric_limits::max(); + for (auto &entry : syncStates) { + if (entry.second == SYNCED && entry.first < bestDomainIndex) { + bestDomainIndex = entry.first; + } } - else { - throw cRuntimeError("Unknown self message (%s)%s, kind=%d", msg->getClassName(), msg->getName(), msg->getKind()); + + if (bestDomainIndex == std::numeric_limits::max()) { + EV_WARN << "All domains out of sync, cannot switch to backup domain" << endl; + return; } + + EV_INFO << "Switching to domain " << bestDomainIndex << endl; + multiClock->par("activeClockIndex").setIntValue(bestDomainIndex); } } // namespace inet diff --git a/src/inet/linklayer/ieee8021as/HotStandby.h b/src/inet/linklayer/ieee8021as/HotStandby.h index d8e1a36ec3a..c4083cdcceb 100644 --- a/src/inet/linklayer/ieee8021as/HotStandby.h +++ b/src/inet/linklayer/ieee8021as/HotStandby.h @@ -12,30 +12,23 @@ #include "inet/common/clock/ClockUserModuleBase.h" #include "inet/common/packet/Packet.h" #include "inet/linklayer/ieee8021as/Gptp.h" +#include "inet/clock/model/MultiClock.h" namespace inet { class INET_API HotStandby : public ClockUserModuleBase, public cListener { protected: - std::map gptpSyncTime; // store each gptp sync time - ClockEvent *standByMsg = nullptr; - const Gptp *gptpModule = nullptr; - clocktime_t delta; - clocktime_t timer = 0; - - public: - virtual ~HotStandby(); + std::map syncStates; // store each gptp sync time + MultiClock *multiClock; + + protected: virtual void initialize(int stage) override; - virtual void receiveSignal(cComponent *source, simsignal_t simSignal, const SimTime& t, cObject *details) override; - void handleGptpSyncSuccessfulSignal(const Gptp *gptp, const SimTime& t); + virtual void receiveSignal(cComponent *source, simsignal_t simSignal, intval_t t, cObject *details) override; + void handleSyncStateChanged(const Gptp *gptp, SyncState syncState); virtual int numInitStages() const override { return NUM_INIT_STAGES; }; - virtual void handleMessage(cMessage *msg) override; - void dropDomain(uint8_t domainNumber); }; } // namespace inet #endif - -; diff --git a/src/inet/linklayer/ieee8021as/HotStandy.ned b/src/inet/linklayer/ieee8021as/HotStandy.ned index 740ace8ebff..72f59994096 100644 --- a/src/inet/linklayer/ieee8021as/HotStandy.ned +++ b/src/inet/linklayer/ieee8021as/HotStandy.ned @@ -21,5 +21,5 @@ simple HotStandby @display("i=block/blackboard"); @class(HotStandby); @selfMessageKinds(inet::GptpSelfMsgKind); - double delta @unit(s) = default(100us); + string clockModule = default(""); } diff --git a/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned b/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned index d241970ba72..c477ddb9e62 100644 --- a/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned +++ b/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned @@ -27,6 +27,7 @@ module MultiDomainGptp like IGptp string interfaceTableModule; // Relative module path of the interface table int numDomains; // Number of time synchronization domains string gptpNodeType; // @enum("GptpNodeType"): MASTER_NODE, BRIDGE_NODE, SLAVE_NODE + bool hasHotStandby = default(true); @display("i=block/app"); gates: input socketIn; @@ -46,7 +47,8 @@ module MultiDomainGptp like IGptp classifierClass = default("inet::GptpDomainNumberClassifier"); @display("p=300,350"); } - hotstandby: HotStandby { + hotstandby: HotStandby if hasHotStandby { + clockModule = default(absPath(parent.clockModule)); @display("p=450,350"); } From 10fc470b5cb9b27ce71b1afce6697d3b0bc34621 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Fri, 10 Jan 2025 19:14:38 +0100 Subject: [PATCH 095/138] Refactor --- src/inet/linklayer/ieee8021as/Gptp.cc | 30 +++++++++++-------- src/inet/linklayer/ieee8021as/Gptp.h | 1 + .../linklayer/ieee8021as/MultiDomainGptp.ned | 1 - 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index e9b26d8d402..39adeff4805 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -889,14 +889,7 @@ void Gptp::synchronize() newLocalTimeAtTimeSync = clock->getClockTime(); - switch(servoClock->getClockState()) { - case ServoClockBase::ClockState::INIT: - changeSyncState(SyncState::UNSYNCED); - break; - case ServoClockBase::ClockState::SYNCED: - changeSyncState(SyncState::SYNCED); - break; - } + updateSyncStateAndRescheduleSyncTimeout(servoClock); /************** Rate ratio calculation ************************************* * It is calculated based on interval between two successive Sync messages * @@ -930,17 +923,28 @@ void Gptp::synchronize() syncIngressTimestampLast = syncIngressTimestamp; preciseOriginTimestampLast = preciseOriginTimestamp; + emit(receivedRateRatioSignal, receivedRateRatio); + emit(gmRateRatioSignal, gmRateRatio); + emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); + emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); +} +void Gptp::updateSyncStateAndRescheduleSyncTimeout(const ServoClockBase *servoClock) +{ + switch(servoClock->getClockState()) { + case ServoClockBase::INIT: + changeSyncState(UNSYNCED); + break; + case ServoClockBase::SYNCED: + changeSyncState(SYNCED); + break; + } + if (selfMsgSyncTimeout->isScheduled()) { rescheduleClockEventAfter(par("syncTimeout"), selfMsgSyncTimeout); } else if (!isGM()) { scheduleClockEventAfter(par("syncTimeout"), selfMsgSyncTimeout); } - - emit(receivedRateRatioSignal, receivedRateRatio); - emit(gmRateRatioSignal, gmRateRatio); - emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); - emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); } void Gptp::changeSyncState(SyncState state) { diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index 0b661a2f95b..330d1c0212c 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -207,6 +207,7 @@ class INET_API Gptp : public ClockUserModuleBase, public cListener PortIdentity bReceiverIdentity); void changeSyncState(SyncState state); void handleSyncTimeout(cMessage *pMessage); + void updateSyncStateAndRescheduleSyncTimeout(const ServoClockBase *servoClock); }; } // namespace inet diff --git a/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned b/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned index c477ddb9e62..a46976106b4 100644 --- a/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned +++ b/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned @@ -52,7 +52,6 @@ module MultiDomainGptp like IGptp @display("p=450,350"); } - connections: for i=0..numDomains-1 { domain[i].socketOut --> multiplexer.in++; From fc73d86cb1438ede58ed8fc90f41b2fcf6e8f4f8 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Fri, 10 Jan 2025 20:51:12 +0100 Subject: [PATCH 096/138] Refactor --- src/inet/linklayer/ieee8021as/Gptp.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 39adeff4805..88f667fd892 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -928,6 +928,7 @@ void Gptp::synchronize() emit(localTimeSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync)); emit(timeDifferenceSignal, CLOCKTIME_AS_SIMTIME(newLocalTimeAtTimeSync) - now); } + void Gptp::updateSyncStateAndRescheduleSyncTimeout(const ServoClockBase *servoClock) { switch(servoClock->getClockState()) { From 8e0e822bdb530bc60e9ca026350446a3c7035fdc Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 14 Jan 2025 16:18:11 +0100 Subject: [PATCH 097/138] Refactoring --- src/inet/clock/base/ClockBase.cc | 1 + src/inet/clock/base/ClockBase.h | 11 +++++-- src/inet/clock/base/ClockBase.ned | 1 + src/inet/clock/model/MultiClock.cc | 4 +++ src/inet/clock/model/MultiClock.ned | 1 + src/inet/clock/servo/ServoClockBase.cc | 3 +- src/inet/clock/servo/ServoClockBase.h | 8 ----- src/inet/clock/servo/ServoClockBase.ned | 1 - src/inet/linklayer/ieee8021as/Gptp.cc | 41 +++++++++++++++---------- 9 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/inet/clock/base/ClockBase.cc b/src/inet/clock/base/ClockBase.cc index 11715f5d7b5..c94217cb31a 100644 --- a/src/inet/clock/base/ClockBase.cc +++ b/src/inet/clock/base/ClockBase.cc @@ -12,6 +12,7 @@ namespace inet { simsignal_t ClockBase::timeChangedSignal = cComponent::registerSignal("timeChanged"); simsignal_t ClockBase::timeDifferenceToReferenceSignal = cComponent::registerSignal("timeDifferenceToReference"); +simsignal_t ClockBase::timeJumpedSignal = cComponent::registerSignal("timeJumped"); void ClockBase::initialize(int stage) { diff --git a/src/inet/clock/base/ClockBase.h b/src/inet/clock/base/ClockBase.h index d2856250e4d..26f72194541 100644 --- a/src/inet/clock/base/ClockBase.h +++ b/src/inet/clock/base/ClockBase.h @@ -19,8 +19,15 @@ namespace inet { class INET_API ClockBase : public cSimpleModule, public IClock, public StringFormat::IDirectiveResolver, public cListener { public: - static simsignal_t timeChangedSignal; - static simsignal_t timeDifferenceToReferenceSignal; + struct ClockJumpDetails : public cObject { + clocktime_t oldClockTime; + clocktime_t newClockTime; + }; + + public: + static simsignal_t timeChangedSignal; // Called every time there is a change in the speed of the clock oscillator + static simsignal_t timeDifferenceToReferenceSignal; // Called every time there is a change in the speed of the clock oscillator + static simsignal_t timeJumpedSignal; // Only called when the clock performs an immediate jump to a new time ModuleRefByPar referenceClockModule; protected: diff --git a/src/inet/clock/base/ClockBase.ned b/src/inet/clock/base/ClockBase.ned index 66261130ce3..4b5e829c567 100644 --- a/src/inet/clock/base/ClockBase.ned +++ b/src/inet/clock/base/ClockBase.ned @@ -20,6 +20,7 @@ module ClockBase @display("i=block/timer"); @signal[timeChanged](type=simtime_t); @signal[timeDifferenceToReference](type=simtime_t); + @signal[timeJumped](); @statistic[timeChanged](title="Clock time"; record=vector; interpolationmode=linear); @statistic[timeDifferenceToReference](title="Time difference to reference"; record=vector; interpolationmode=linear); } diff --git a/src/inet/clock/model/MultiClock.cc b/src/inet/clock/model/MultiClock.cc index 2ae0703dfb9..a9ecc324a58 100644 --- a/src/inet/clock/model/MultiClock.cc +++ b/src/inet/clock/model/MultiClock.cc @@ -18,6 +18,7 @@ void MultiClock::initialize(int stage) if (stage == INITSTAGE_LOCAL) { activeClock = check_and_cast(getSubmodule("clock", par("activeClockIndex"))); subscribe(ClockBase::timeChangedSignal, this); + subscribe(ClockBase::timeJumpedSignal, this); } } @@ -35,6 +36,9 @@ void MultiClock::receiveSignal(cComponent *source, int signal, const simtime_t& if (signal == ClockBase::timeChangedSignal) { if (check_and_cast(source) == activeClock) emit(ClockBase::timeChangedSignal, time, details); + } else if (signal == ClockBase::timeJumpedSignal) { + if (check_and_cast(source) == activeClock) + emit(ClockBase::timeJumpedSignal, details); } else throw cRuntimeError("Unknown signal"); diff --git a/src/inet/clock/model/MultiClock.ned b/src/inet/clock/model/MultiClock.ned index ab4a1122fb7..9ad5e74bb13 100644 --- a/src/inet/clock/model/MultiClock.ned +++ b/src/inet/clock/model/MultiClock.ned @@ -25,6 +25,7 @@ module MultiClock like IClock @display("i=block/timer"); @class(MultiClock); @signal[timeChanged](type=simtime_t); + @signal[clockJumped](); @statistic[timeChanged](title="Clock time"; source=localSignal(timeChanged); record=vector; interpolationmode=linear); submodules: clock[numClocks]: like IClock { diff --git a/src/inet/clock/servo/ServoClockBase.cc b/src/inet/clock/servo/ServoClockBase.cc index 5a00114b1f1..31e3ca7d830 100644 --- a/src/inet/clock/servo/ServoClockBase.cc +++ b/src/inet/clock/servo/ServoClockBase.cc @@ -9,7 +9,6 @@ namespace inet { Register_Abstract_Class(ServoClockBase); -simsignal_t ServoClockBase::clockJumpSignal = cComponent::registerSignal("clockJump"); void ServoClockBase::initialize(int stage) { @@ -126,7 +125,7 @@ void ServoClockBase::jumpClockTo(clocktime_t newClockTime, bool notifyListeners) timeJumpDetails.oldClockTime = oldClockTime; timeJumpDetails.newClockTime = newClockTime; if (notifyListeners) { - emit(clockJumpSignal, this, &timeJumpDetails); + emit(timeJumpedSignal, this, &timeJumpDetails); } emit(timeChangedSignal, newClockTime.asSimTime()); } diff --git a/src/inet/clock/servo/ServoClockBase.h b/src/inet/clock/servo/ServoClockBase.h index dd1cb9abe85..fedaceb4dcf 100644 --- a/src/inet/clock/servo/ServoClockBase.h +++ b/src/inet/clock/servo/ServoClockBase.h @@ -22,14 +22,6 @@ class ServoClockBase : public OscillatorBasedClock, public IScriptable SYNCED }; - struct ClockJumpDetails : public cObject { - clocktime_t oldClockTime; - clocktime_t newClockTime; - }; - - public: - static simsignal_t clockJumpSignal; - protected: OverdueClockEventHandlingMode defaultOverdueClockEventHandlingMode = UNSPECIFIED; ppm oscillatorCompensation = ppm(0); // 0 means no compensation, higher value means faster clock, e.g. 100 ppm value diff --git a/src/inet/clock/servo/ServoClockBase.ned b/src/inet/clock/servo/ServoClockBase.ned index e0590d637c5..e91c3db3f3b 100644 --- a/src/inet/clock/servo/ServoClockBase.ned +++ b/src/inet/clock/servo/ServoClockBase.ned @@ -14,7 +14,6 @@ module ServoClockBase extends OscillatorBasedClock { parameters: string defaultOverdueClockEventHandlingMode @enum("execute","skip","error") = default("error"); - @signal[clockJump](); @class(ServoClockBase); } diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 88f667fd892..bd6428ba1fa 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -57,9 +57,11 @@ Gptp::~Gptp() cancelAndDeleteClockEvent(reqAnswerEvent); for (auto &announce : receivedAnnounces) delete announce.second; - for (auto &announceTimeout : announceTimeouts) { + for (auto &announceTimeout : announceTimeouts) cancelAndDeleteClockEvent(announceTimeout.second); - } + if (selfMsgSyncTimeout) + cancelAndDeleteClockEvent(selfMsgSyncTimeout); + delete bestAnnounce; } @@ -106,8 +108,10 @@ void Gptp::initialize(int stage) scheduleClockEventAfter(par("announceInitialOffset"), selfMsgAnnounce); } - auto servoClock = check_and_cast(clock.get()); - servoClock->subscribe(ServoClockBase::clockJumpSignal, this); + // We cast to cComponent, because clock can also be a MultiClock which does not extend from ClockBase, + // but also implements the clockJumpSignal + auto servoClock = check_and_cast(clock.get()); + servoClock->subscribe(ClockBase::timeJumpedSignal, this); WATCH(meanLinkDelay); } @@ -210,7 +214,8 @@ void Gptp::scheduleMessageOnTopologyChange() preciseOriginTimestamp = clock->getClockTime() + scheduleSync; scheduleClockEventAfter(scheduleSync, selfMsgSync); changeSyncState(SYNCED); - } else { + } + else { selfMsgSyncTimeout = new ClockEvent("selfMsgSyncTimeout", GPTP_SELF_MSG_SYNC_TIMEOUT); scheduleClockEventAfter(par("syncTimeout"), selfMsgSyncTimeout); } @@ -536,7 +541,8 @@ void Gptp::executeBmca() // Received an Announce message, but best clock is still the same } else if (bestAnnounce && bestAnnounce->getPriorityVector() == bestAnnounceCurr->getPriorityVector() && - slavePortId == bestAnnounceReceiverIdentityCurr.portNumber) { + slavePortId == bestAnnounceReceiverIdentityCurr.portNumber) + { // Same priority vector but newer Announce, no topology change because priority vector is the same // Store the new Announce delete bestAnnounce; @@ -622,8 +628,8 @@ void Gptp::executeBmca() } // Clock class >= 128 - auto compareLocalToBest = compareAnnounceMessages(ownAnnounce, bestAnnounce, ownPortIdentity, - bestAnnounceReceiverIdentityCurr); + auto compareLocalToBest = + compareAnnounceMessages(ownAnnounce, bestAnnounce, ownPortIdentity, bestAnnounceReceiverIdentityCurr); if (compareLocalToBest == A_BETTER_THAN_B || compareLocalToBest == A_BETTER_THAN_B_BY_TOPOLOGY) { // Local better than best => Master @@ -639,8 +645,8 @@ void Gptp::executeBmca() continue; } - auto compareBestToPort = compareAnnounceMessages( - bestAnnounce, portAnnounce, bestAnnounceReceiverIdentityCurr, receiverPortIdentity); + auto compareBestToPort = compareAnnounceMessages(bestAnnounce, portAnnounce, + bestAnnounceReceiverIdentityCurr, receiverPortIdentity); if (compareBestToPort == A_BETTER_THAN_B_BY_TOPOLOGY) { passivePortIds.insert(portId); } @@ -931,7 +937,7 @@ void Gptp::synchronize() void Gptp::updateSyncStateAndRescheduleSyncTimeout(const ServoClockBase *servoClock) { - switch(servoClock->getClockState()) { + switch (servoClock->getClockState()) { case ServoClockBase::INIT: changeSyncState(UNSYNCED); break; @@ -948,7 +954,8 @@ void Gptp::updateSyncStateAndRescheduleSyncTimeout(const ServoClockBase *servoCl } } -void Gptp::changeSyncState(SyncState state) { +void Gptp::changeSyncState(SyncState state) +{ auto prevSyncState = syncState; syncState = state; if (prevSyncState != syncState) { @@ -1102,9 +1109,9 @@ void Gptp::receiveSignal(cComponent *source, simsignal_t simSignal, cObject *obj { Enter_Method("%s", cComponent::getSignalName(simSignal)); - auto servoClock = check_and_cast(clock.get()); + auto clockBase = check_and_cast(clock.get()); - if (simSignal == ServoClockBase::clockJumpSignal && obj == servoClock) { + if (simSignal == ClockBase::timeJumpedSignal && obj == clockBase) { auto clockJumpDetails = check_and_cast(details); handleClockJump(clockJumpDetails); } @@ -1155,7 +1162,8 @@ void Gptp::handleAnnounceTimeout(cMessage *pMessage) executeBmca(); } -void Gptp::handleSyncTimeout(cMessage *pMessage) { +void Gptp::handleSyncTimeout(cMessage *pMessage) +{ changeSyncState(SyncState::UNSYNCED); simTime(); } @@ -1235,7 +1243,8 @@ void Gptp::handleClockJump(ServoClockBase::ClockJumpDetails *clockJumpDetails) // Should this behavior change in the future, this timestamping adjustment mechanism needs to be changed as well to // somehow also adjust the timestamps already attaches as tags to the gPTP packets. } -void Gptp::handleParameterChange(const char *name) { +void Gptp::handleParameterChange(const char *name) +{ // TODO: Maybe one could make more paremeters mutable (e.g. syncInterval might be interesting for some people) // Compare if parameter is grandmasterPriority1 From 8bc1b45bf553694026d63ca75d0d38fbac84b5fd Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 15 Jan 2025 11:31:25 +0100 Subject: [PATCH 098/138] Refactoring --- src/inet/clock/model/MultiClock.cc | 12 +++++++----- src/inet/clock/model/MultiClock.h | 1 + src/inet/clock/model/MultiClock.ned | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/inet/clock/model/MultiClock.cc b/src/inet/clock/model/MultiClock.cc index a9ecc324a58..65b8fe01f0b 100644 --- a/src/inet/clock/model/MultiClock.cc +++ b/src/inet/clock/model/MultiClock.cc @@ -4,7 +4,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // - #include "inet/clock/model/MultiClock.h" #include "inet/clock/base/ClockBase.h" @@ -31,18 +30,21 @@ void MultiClock::handleParameterChange(const char *name) } } -void MultiClock::receiveSignal(cComponent *source, int signal, const simtime_t& time, cObject *details) +void MultiClock::receiveSignal(cComponent *source, int signal, const simtime_t &time, cObject *details) { if (signal == ClockBase::timeChangedSignal) { if (check_and_cast(source) == activeClock) emit(ClockBase::timeChangedSignal, time, details); - } else if (signal == ClockBase::timeJumpedSignal) { + } +} +void MultiClock::receiveSignal(cComponent *source, int signal, cObject *obj, cObject *details) +{ + if (signal == ClockBase::timeJumpedSignal) { if (check_and_cast(source) == activeClock) - emit(ClockBase::timeJumpedSignal, details); + emit(ClockBase::timeJumpedSignal, this, details); } else throw cRuntimeError("Unknown signal"); } } // namespace inet - diff --git a/src/inet/clock/model/MultiClock.h b/src/inet/clock/model/MultiClock.h index 151b3a2b81f..0af2bb3c760 100644 --- a/src/inet/clock/model/MultiClock.h +++ b/src/inet/clock/model/MultiClock.h @@ -33,6 +33,7 @@ class INET_API MultiClock : public cModule, public virtual IClock, public cListe virtual void handleClockEvent(ClockEvent *event) override { activeClock->handleClockEvent(event); } virtual void receiveSignal(cComponent *source, int signal, const simtime_t& time, cObject *details) override; + virtual void receiveSignal(cComponent *source, int signal, cObject *obj, cObject *details) override; }; } // namespace inet diff --git a/src/inet/clock/model/MultiClock.ned b/src/inet/clock/model/MultiClock.ned index 9ad5e74bb13..459b4cd2fcc 100644 --- a/src/inet/clock/model/MultiClock.ned +++ b/src/inet/clock/model/MultiClock.ned @@ -25,7 +25,7 @@ module MultiClock like IClock @display("i=block/timer"); @class(MultiClock); @signal[timeChanged](type=simtime_t); - @signal[clockJumped](); + @signal[timeJumped](); @statistic[timeChanged](title="Clock time"; source=localSignal(timeChanged); record=vector; interpolationmode=linear); submodules: clock[numClocks]: like IClock { From 6120672bc147be0f9183ec5c977d5c31df8e1d52 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 15 Jan 2025 15:58:04 +0100 Subject: [PATCH 099/138] Added OffsetLimitedRandomDriftOscillator --- .../tsn/timesynchronization/gptp/omnetpp.ini | 9 +-- .../OffsetLimitedRandomDriftOscillator.cc | 55 +++++++++++++++++++ .../OffsetLimitedRandomDriftOscillator.h | 34 ++++++++++++ .../OffsetLimitedRandomDriftOscillator.ned | 20 +++++++ 4 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc create mode 100644 src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h create mode 100644 src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.ned diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 061d3c95829..dc0e962b59e 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -6,10 +6,11 @@ description = "abstract" # enable time synchronization in all network nodes *.*.hasTimeSynchronization = true -# all oscillators have a constant drift rate (specified with a random distribution for each one) -# except for the master clocks, which have a random drift rate -#**.clock.oscillator.typename = "RandomDriftOscillator" -*.tsnClock.clock.oscillator.typename = "IdealOscillator" +*.tsnClock.clock.oscillator.typename = "OffsetLimitedRandomDriftOscillator" +*.tsnClock.clock.oscillator.maxOffset = 10us +*.tsnClock.clock.oscillator.maxDriftRateAdjustment = 1ppm + +**.clock.oscillator.typename = "RandomDriftOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) **.oscillator.driftRateChange = uniform(-1ppm, 1ppm) diff --git a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc new file mode 100644 index 00000000000..a00e7263e42 --- /dev/null +++ b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc @@ -0,0 +1,55 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h" + +namespace inet { + +Define_Module(OffsetLimitedRandomDriftOscillator); + +void OffsetLimitedRandomDriftOscillator::initialize(int stage) { + RandomDriftOscillator::initialize(stage); + if (stage == INITSTAGE_LOCAL) { + maxOffset = &par("maxOffset"); + maxDriftRateAdjustment = ppm(par("maxDriftRateAdjustment")); + // Get my clock which is my parent + clock = dynamic_cast(getParentModule()); + } +} + +void OffsetLimitedRandomDriftOscillator::handleMessage(cMessage *message) +{ + if (message == changeTimer) { + + driftRateChangeTotal += ppm(driftRateChangeParameter->doubleValue()); + driftRateChangeTotal = std::max(driftRateChangeTotal, driftRateChangeLowerLimit); + driftRateChangeTotal = std::min(driftRateChangeTotal, driftRateChangeUpperLimit); + + auto currSimTime = simTime(); + auto currSimTimeAsClockTime = SIMTIME_AS_CLOCKTIME(simTime()); + auto currClockTime = clock->computeClockTimeFromSimTime(currSimTime); + auto currentOffset = currSimTimeAsClockTime - currClockTime; + if (std::abs(currentOffset.dbl()) > maxOffset->doubleValue()) { + // Adjust the drift rate to bring the offset back within limits + if (currentOffset > 0) { + driftRateChangeTotal += maxDriftRateAdjustment; + } else { + driftRateChangeTotal -= maxDriftRateAdjustment; + } + } + + setDriftRate(initialDriftRate + driftRateChangeTotal); + + scheduleAfter(changeIntervalParameter->doubleValue(), changeTimer); + } + else + throw cRuntimeError("Unknown message"); +} + + +} // namespace inet + diff --git a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h new file mode 100644 index 00000000000..0b9a8920b62 --- /dev/null +++ b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h @@ -0,0 +1,34 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_OFFSETLIMITEDRANDOMDRIFTOSCILLATOR_H +#define __INET_OFFSETLIMITEDRANDOMDRIFTOSCILLATOR_H + +#include "inet/clock/oscillator/RandomDriftOscillator.h" +#include "inet/clock/model/OscillatorBasedClock.h" +#include "inet/common/Units.h" + +namespace inet { + +using namespace units::values; + +class INET_API OffsetLimitedRandomDriftOscillator : public RandomDriftOscillator +{ + protected: + OscillatorBasedClock *clock = nullptr; + cPar *maxOffset = nullptr; + ppm maxDriftRateAdjustment = ppm(NaN); + + protected: + void handleMessage(cMessage *message) override; + void initialize(int stage) override; +}; + +} // namespace inet + +#endif + diff --git a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.ned b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.ned new file mode 100644 index 00000000000..d95aa12eadc --- /dev/null +++ b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.ned @@ -0,0 +1,20 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +package inet.clock.oscillator; + +// +// This module behaves like the RandomDriftOscillator but ensures that the drift does not exceed a certain limit +// from the simulation time. +// +simple OffsetLimitedRandomDriftOscillator extends RandomDriftOscillator +{ + parameters: + @class(OffsetLimitedRandomDriftOscillator); + double maxOffset @unit(s) = default(0s); // Max offset to simulation time until the drift is adjusted + double maxDriftRateAdjustment @unit(ppm) = default(0ppm); // Max drift rate adjustment per changeInterval +} From 21e9433dedde0125d1aa8de9162ee2fdcb9e7317 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Thu, 16 Jan 2025 17:16:52 +0100 Subject: [PATCH 100/138] Small refactoring --- showcases/tsn/timesynchronization/gptp/omnetpp.ini | 14 +++++++++----- src/inet/linklayer/ieee8021as/Gptp.cc | 1 - 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index dc0e962b59e..c196ec90939 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -6,16 +6,20 @@ description = "abstract" # enable time synchronization in all network nodes *.*.hasTimeSynchronization = true +# Limit the tsnClock to an absolute error of 10us (more things visible in graph *.tsnClock.clock.oscillator.typename = "OffsetLimitedRandomDriftOscillator" +*.tsnClock.clock.oscillator.initialDriftRate = 0ppm *.tsnClock.clock.oscillator.maxOffset = 10us -*.tsnClock.clock.oscillator.maxDriftRateAdjustment = 1ppm +*.tsnClock.clock.oscillator.maxDriftRateAdjustment = 0.5ppm **.clock.oscillator.typename = "RandomDriftOscillator" +**.oscillator.driftRate = uniform(-100ppm, 100ppm) # According to 802.1AS B.1.1 +**.oscillator.initialDriftRate = uniform(-100ppm, 100ppm) # According to 802.1AS B.1.1 **.oscillator.changeInterval = 12.5ms -**.oscillator.driftRate = uniform(-100ppm, 100ppm) -**.oscillator.driftRateChange = uniform(-1ppm, 1ppm) -**.oscillator.driftRateChangeUpperLimit = 100ppm -**.oscillator.driftRateChangeLowerLimit = -100ppm +**.oscillator.driftRateChange = uniform(-0.5 ppm, 0.5 ppm) # Arbitrary large value => Leads to greater errors, espacially when using the nrr calculation, +#**.oscillator.driftRateChange = uniform(-0.1235e-3 ppm, 0.1235e-3 ppm) # Based on 802.1AS B.1.3.2 (simplified to max error being 5ns/s, needs to be adapted if changeInterval is also adapted) +**.oscillator.driftRateChangeUpperLimit = 100ppm # According to 802.1AS B.1.1 +**.oscillator.driftRateChangeLowerLimit = -100ppm # According to 802.1AS B.1.1 **.kp=8 **.ki=7 diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index b8ab07cf059..1efbb27050d 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -1163,7 +1163,6 @@ void Gptp::handleAnnounceTimeout(cMessage *pMessage) void Gptp::handleSyncTimeout(cMessage *pMessage) { changeSyncState(SyncState::UNSYNCED); - simTime(); } void Gptp::handleTransmissionStartedSignal(const GptpBase *gptp, cComponent *source) From c8ffffe3ce1640470824604b4da0b1ad0d23bf9e Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Fri, 17 Jan 2025 02:26:27 +0100 Subject: [PATCH 101/138] Small refactoring --- src/inet/clock/base/ClockBase.cc | 14 +++++++++++--- src/inet/clock/base/ClockBase.h | 1 + .../OffsetLimitedRandomDriftOscillator.cc | 9 ++++----- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/inet/clock/base/ClockBase.cc b/src/inet/clock/base/ClockBase.cc index c94217cb31a..11512826efd 100644 --- a/src/inet/clock/base/ClockBase.cc +++ b/src/inet/clock/base/ClockBase.cc @@ -36,6 +36,7 @@ void ClockBase::initialize(int stage) } updateDisplayString(); emit(timeChangedSignal, getClockTime().asSimTime()); + emitTimeDifferenceToReference(); } } @@ -43,18 +44,25 @@ void ClockBase::handleMessage(cMessage *msg) { if (msg == timer) { emit(timeChangedSignal, getClockTime().asSimTime()); + emitTimeDifferenceToReference(); scheduleAfter(emitClockTimeInterval, timer); } else throw cRuntimeError("Unknown message"); } +void ClockBase::emitTimeDifferenceToReference() +{ + if (referenceClockModule == nullptr) + return; + auto referenceTime = referenceClockModule->getClockTime(); + auto timeDifference = getClockTime() - referenceTime; + emit(timeDifferenceToReferenceSignal, timeDifference.asSimTime()); +} void ClockBase::receiveSignal(cComponent *source, int signal, const simtime_t& time, cObject *details) { if (signal == ClockBase::timeChangedSignal) { - auto referenceTime = referenceClockModule->getClockTime(); - auto timeDifference = getClockTime() - referenceTime; - emit(timeDifferenceToReferenceSignal, timeDifference.asSimTime()); + emitTimeDifferenceToReference(); } else throw cRuntimeError("Unknown signal"); diff --git a/src/inet/clock/base/ClockBase.h b/src/inet/clock/base/ClockBase.h index 26f72194541..f18e13fad4f 100644 --- a/src/inet/clock/base/ClockBase.h +++ b/src/inet/clock/base/ClockBase.h @@ -41,6 +41,7 @@ class INET_API ClockBase : public cSimpleModule, public IClock, public StringFor virtual int numInitStages() const override { return NUM_INIT_STAGES; } virtual void initialize(int stage) override; virtual void handleMessage(cMessage *msg) override; + void emitTimeDifferenceToReference(); virtual void finish() override; virtual void refreshDisplay() const override; virtual void updateDisplayString() const; diff --git a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc index a00e7263e42..140a28c4073 100644 --- a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc +++ b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc @@ -4,14 +4,14 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // - #include "inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h" namespace inet { Define_Module(OffsetLimitedRandomDriftOscillator); -void OffsetLimitedRandomDriftOscillator::initialize(int stage) { +void OffsetLimitedRandomDriftOscillator::initialize(int stage) +{ RandomDriftOscillator::initialize(stage); if (stage == INITSTAGE_LOCAL) { maxOffset = &par("maxOffset"); @@ -37,7 +37,8 @@ void OffsetLimitedRandomDriftOscillator::handleMessage(cMessage *message) // Adjust the drift rate to bring the offset back within limits if (currentOffset > 0) { driftRateChangeTotal += maxDriftRateAdjustment; - } else { + } + else { driftRateChangeTotal -= maxDriftRateAdjustment; } } @@ -50,6 +51,4 @@ void OffsetLimitedRandomDriftOscillator::handleMessage(cMessage *message) throw cRuntimeError("Unknown message"); } - } // namespace inet - From fd21cb89f4a647b53e14705b26fdf37a62d2f832 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Sat, 18 Jan 2025 01:52:01 +0100 Subject: [PATCH 102/138] Added new classifier for gPTP domains --- .../ieee8021as/GptpDomainClassifier.cc | 48 +++++++++++++++++++ .../ieee8021as/GptpDomainClassifier.h | 28 +++++++++++ .../ieee8021as/GptpDomainClassifier.ned | 20 ++++++++ .../GptpPacketClassifierFunction.cc | 22 --------- src/inet/linklayer/ieee8021as/HotStandby.cc | 31 ++++++++---- src/inet/linklayer/ieee8021as/HotStandby.h | 9 ++-- .../linklayer/ieee8021as/MultiDomainGptp.ned | 7 ++- 7 files changed, 127 insertions(+), 38 deletions(-) create mode 100644 src/inet/linklayer/ieee8021as/GptpDomainClassifier.cc create mode 100644 src/inet/linklayer/ieee8021as/GptpDomainClassifier.h create mode 100644 src/inet/linklayer/ieee8021as/GptpDomainClassifier.ned delete mode 100644 src/inet/linklayer/ieee8021as/GptpPacketClassifierFunction.cc diff --git a/src/inet/linklayer/ieee8021as/GptpDomainClassifier.cc b/src/inet/linklayer/ieee8021as/GptpDomainClassifier.cc new file mode 100644 index 00000000000..286ceed1d3d --- /dev/null +++ b/src/inet/linklayer/ieee8021as/GptpDomainClassifier.cc @@ -0,0 +1,48 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/linklayer/ieee8021as/GptpDomainClassifier.h" + +#include "Gptp.h" + +namespace inet { + +Define_Module(GptpDomainClassifier); + +void GptpDomainClassifier::initialize(int stage) +{ + PacketClassifierBase::initialize(stage); + if (stage == INITSTAGE_LINK_LAYER) { + // Iterate over all gates and find the ones that are connected to Gptp modules + for (int i = 0; i < gateSize("out"); i++) { + auto outGate = gate("out", i); + auto gptp = dynamic_cast(outGate->getPathEndGate()->getOwnerModule()); + if (gptp != nullptr) { + auto domainNumber = gptp->getDomainNumber(); + gptpDomainToGateIndex[domainNumber] = i; + } + } + } +} + +int GptpDomainClassifier::classifyPacket(Packet *packet) +{ + const auto& header = packet->peekAtFront(); + + if (gptpDomainToGateIndex.find(header->getDomainNumber()) == gptpDomainToGateIndex.end()) { + EV_ERROR << "Message " << packet->getFullName() << " arrived with foreign domainNumber " + << header->getDomainNumber() << ", dropped\n"; + // Frame should be dropped in the gPTP with a not addressed to us error + // There might be better ways to handle this error, but dropping this packet here is not possible + return 0; + } + + return gptpDomainToGateIndex[header->getDomainNumber()]; +} + +} // namespace inet + diff --git a/src/inet/linklayer/ieee8021as/GptpDomainClassifier.h b/src/inet/linklayer/ieee8021as/GptpDomainClassifier.h new file mode 100644 index 00000000000..17b13fdb49f --- /dev/null +++ b/src/inet/linklayer/ieee8021as/GptpDomainClassifier.h @@ -0,0 +1,28 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_GPTPDOMAINCLASSIFIER_H +#define __INET_GPTPDOMAINCLASSIFIER_H + +#include "inet/queueing/base/PacketClassifierBase.h" + +namespace inet { + +class INET_API GptpDomainClassifier : public queueing::PacketClassifierBase +{ + protected: + std::map gptpDomainToGateIndex; + + protected: + virtual void initialize(int stage) override; + virtual int classifyPacket(Packet *packet) override; +}; + +} // namespace inet + +#endif + diff --git a/src/inet/linklayer/ieee8021as/GptpDomainClassifier.ned b/src/inet/linklayer/ieee8021as/GptpDomainClassifier.ned new file mode 100644 index 00000000000..4e663887176 --- /dev/null +++ b/src/inet/linklayer/ieee8021as/GptpDomainClassifier.ned @@ -0,0 +1,20 @@ +// +// Copyright (C) 2020 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +package inet.linklayer.ieee8021as; + +import inet.queueing.base.PacketClassifierBase; +import inet.queueing.contract.IPacketClassifier; + +// +// This module classifies packets based on the domain number in the gPTP header +// +simple GptpDomainClassifier extends PacketClassifierBase like IPacketClassifier +{ + parameters: + @class(GptpDomainClassifier); +} diff --git a/src/inet/linklayer/ieee8021as/GptpPacketClassifierFunction.cc b/src/inet/linklayer/ieee8021as/GptpPacketClassifierFunction.cc deleted file mode 100644 index 6fd1f9a1de3..00000000000 --- a/src/inet/linklayer/ieee8021as/GptpPacketClassifierFunction.cc +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (C) 2020 OpenSim Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - - -#include "inet/linklayer/ieee8021as/GptpPacket_m.h" -#include "inet/queueing/function/PacketClassifierFunction.h" - -namespace inet { - -static int classifyPacketByGptpDomainNumber(Packet *packet) -{ - const auto& header = packet->peekAtFront(); - return header->getDomainNumber(); -} - -Register_Packet_Classifier_Function(GptpDomainNumberClassifier, classifyPacketByGptpDomainNumber); - -} // namespace inet - diff --git a/src/inet/linklayer/ieee8021as/HotStandby.cc b/src/inet/linklayer/ieee8021as/HotStandby.cc index 4f65f29ec87..01fb86da781 100644 --- a/src/inet/linklayer/ieee8021as/HotStandby.cc +++ b/src/inet/linklayer/ieee8021as/HotStandby.cc @@ -12,18 +12,31 @@ namespace inet { Define_Module(HotStandby); +// TODO: Fix: Domain mumber isn't neccessarily the same as the clock index void HotStandby::initialize(int stage) { ClockUserModuleBase::initialize(stage); if (stage == INITSTAGE_LINK_LAYER) { auto parent = getParentModule(); auto numDomains = parent->par("numDomains").intValue(); + + multiClock = check_and_cast(clock.get()); + for (int i = 0; i < numDomains; i++) { auto gptp = parent->getSubmodule("domain", i); gptp->subscribe(Gptp::gptpSyncStateChanged, this); + ModuleRefByPar currentClock; + currentClock.reference(gptp, "clockModule", true); + auto clockParent = currentClock->getParentModule(); + if (clockParent != multiClock) { + EV_WARN + << "Clock module is not a child of MultiClock, cannot determine clock index and ignoring this clock" + << endl; + } + else { + domainNumberToClockIndex[i] = currentClock->getIndex(); + } } - - multiClock = check_and_cast(clock.get()); } } @@ -51,20 +64,22 @@ void HotStandby::handleSyncStateChanged(const Gptp *gptp, const SyncState syncSt return; } - int bestDomainIndex = std::numeric_limits::max(); + int bestDomainNumber = std::numeric_limits::max(); for (auto &entry : syncStates) { - if (entry.second == SYNCED && entry.first < bestDomainIndex) { - bestDomainIndex = entry.first; + if (entry.second == SYNCED && entry.first < bestDomainNumber) { + bestDomainNumber = entry.first; } } - if (bestDomainIndex == std::numeric_limits::max()) { + if (bestDomainNumber == std::numeric_limits::max()) { EV_WARN << "All domains out of sync, cannot switch to backup domain" << endl; return; } - EV_INFO << "Switching to domain " << bestDomainIndex << endl; - multiClock->par("activeClockIndex").setIntValue(bestDomainIndex); + auto clockIndex = domainNumberToClockIndex[bestDomainNumber]; + + EV_INFO << "Switching to domain " << bestDomainNumber << " - clock index " << clockIndex << endl; + multiClock->par("activeClockIndex").setIntValue(clockIndex); } } // namespace inet diff --git a/src/inet/linklayer/ieee8021as/HotStandby.h b/src/inet/linklayer/ieee8021as/HotStandby.h index c4083cdcceb..a576282b8d0 100644 --- a/src/inet/linklayer/ieee8021as/HotStandby.h +++ b/src/inet/linklayer/ieee8021as/HotStandby.h @@ -4,23 +4,24 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // - #ifndef __INET_HOTSTANDBY_H #define __INET_HOTSTANDBY_H #include + +#include "inet/clock/model/MultiClock.h" #include "inet/common/clock/ClockUserModuleBase.h" #include "inet/common/packet/Packet.h" #include "inet/linklayer/ieee8021as/Gptp.h" -#include "inet/clock/model/MultiClock.h" namespace inet { class INET_API HotStandby : public ClockUserModuleBase, public cListener { protected: - std::map syncStates; // store each gptp sync time - MultiClock *multiClock; + std::map syncStates; + std::map domainNumberToClockIndex; + MultiClock *multiClock = nullptr; protected: virtual void initialize(int stage) override; diff --git a/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned b/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned index a46976106b4..975c66854d0 100644 --- a/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned +++ b/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned @@ -8,9 +8,9 @@ package inet.linklayer.ieee8021as; import inet.linklayer.contract.IGptp; -import inet.queueing.classifier.PacketClassifier; import inet.queueing.common.PacketMultiplexer; -import inet.linklayer.ieee8021as.HotStandby; +import inet.queueing.contract.IPacketClassifier; + // // This module combines multiple ~Gptp modules, one per time domain into a multi @@ -43,8 +43,7 @@ module MultiDomainGptp like IGptp multiplexer: PacketMultiplexer { @display("p=150,350"); } - classifier: PacketClassifier { - classifierClass = default("inet::GptpDomainNumberClassifier"); + classifier: like IPacketClassifier { @display("p=300,350"); } hotstandby: HotStandby if hasHotStandby { From 1a89c0f4f791257085837be7c920133ef6c3efe6 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Sat, 18 Jan 2025 18:31:19 +0100 Subject: [PATCH 103/138] Small Bugfixes --- src/inet/clock/base/ClockBase.h | 1 - src/inet/clock/model/OscillatorBasedClock.cc | 18 +++++-- src/inet/clock/model/OscillatorBasedClock.h | 3 +- .../OffsetLimitedRandomDriftOscillator.cc | 54 ------------------- .../OffsetLimitedRandomDriftOscillator.h | 34 ------------ .../OffsetLimitedRandomDriftOscillator.ned | 20 ------- src/inet/clock/servo/PiServoClock.cc | 3 +- src/inet/clock/servo/ServoClockBase.cc | 3 +- 8 files changed, 17 insertions(+), 119 deletions(-) delete mode 100644 src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc delete mode 100644 src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h delete mode 100644 src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.ned diff --git a/src/inet/clock/base/ClockBase.h b/src/inet/clock/base/ClockBase.h index f18e13fad4f..7edcb091f1d 100644 --- a/src/inet/clock/base/ClockBase.h +++ b/src/inet/clock/base/ClockBase.h @@ -62,7 +62,6 @@ class INET_API ClockBase : public cSimpleModule, public IClock, public StringFor virtual void handleClockEvent(ClockEvent *event) override; virtual void receiveSignal(cComponent *source, int signal, const simtime_t& time, cObject *details) override; - virtual std::string resolveDirective(char directive) const override; }; diff --git a/src/inet/clock/model/OscillatorBasedClock.cc b/src/inet/clock/model/OscillatorBasedClock.cc index ac9d8cc8fb0..a4ad753036b 100644 --- a/src/inet/clock/model/OscillatorBasedClock.cc +++ b/src/inet/clock/model/OscillatorBasedClock.cc @@ -65,13 +65,24 @@ void OscillatorBasedClock::initialize(int stage) WATCH_PTRVECTOR(events); } else if (stage == INITSTAGE_CLOCK) { - originSimulationTime = simTime(); - originClockTime = par("initialClockTime"); + setNewOriginTime(simTime(), par("initialClockTime")); if (originClockTime.raw() % oscillator->getNominalTickLength().raw() != 0) throw cRuntimeError("Initial clock time must be a multiple of the oscillator nominal tick length"); } } +void OscillatorBasedClock::setNewOriginTime(const simtime_t &newOriginSimulationTime, + const clocktime_t &newOriginClockTime) +{ + if (clockEventTime != -1) { + auto currentClockTime = getClockTime(); + auto diff = newOriginClockTime - currentClockTime; + clockEventTime += diff; + } + originSimulationTime = newOriginSimulationTime; + originClockTime = newOriginClockTime; +} + clocktime_t OscillatorBasedClock::computeClockTimeFromSimTime(simtime_t t) const { ASSERT(t >= simTime()); @@ -133,8 +144,7 @@ void OscillatorBasedClock::receiveSignal(cComponent *source, int signal, cObject if (signal == IOscillator::preOscillatorStateChangedSignal) { // NOTE: the origin clock must be set first - originClockTime = getClockTime(); - originSimulationTime = simTime(); + setNewOriginTime(simTime(), getClockTime()); } else if (signal == IOscillator::postOscillatorStateChangedSignal) { simtime_t currentSimTime = simTime(); diff --git a/src/inet/clock/model/OscillatorBasedClock.h b/src/inet/clock/model/OscillatorBasedClock.h index 696398f6bfa..5d1eeee8446 100644 --- a/src/inet/clock/model/OscillatorBasedClock.h +++ b/src/inet/clock/model/OscillatorBasedClock.h @@ -4,7 +4,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // - #ifndef __INET_OSCILLATORBASEDCLOCK_H #define __INET_OSCILLATORBASEDCLOCK_H @@ -29,6 +28,7 @@ class INET_API OscillatorBasedClock : public ClockBase protected: virtual void initialize(int stage) override; + void setNewOriginTime(const simtime_t &newOriginSimulationTime, const clocktime_t &newOriginClockTime); public: virtual ~OscillatorBasedClock(); @@ -52,4 +52,3 @@ class INET_API OscillatorBasedClock : public ClockBase } // namespace inet #endif - diff --git a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc deleted file mode 100644 index 140a28c4073..00000000000 --- a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.cc +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (C) 2020 OpenSim Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - -#include "inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h" - -namespace inet { - -Define_Module(OffsetLimitedRandomDriftOscillator); - -void OffsetLimitedRandomDriftOscillator::initialize(int stage) -{ - RandomDriftOscillator::initialize(stage); - if (stage == INITSTAGE_LOCAL) { - maxOffset = &par("maxOffset"); - maxDriftRateAdjustment = ppm(par("maxDriftRateAdjustment")); - // Get my clock which is my parent - clock = dynamic_cast(getParentModule()); - } -} - -void OffsetLimitedRandomDriftOscillator::handleMessage(cMessage *message) -{ - if (message == changeTimer) { - - driftRateChangeTotal += ppm(driftRateChangeParameter->doubleValue()); - driftRateChangeTotal = std::max(driftRateChangeTotal, driftRateChangeLowerLimit); - driftRateChangeTotal = std::min(driftRateChangeTotal, driftRateChangeUpperLimit); - - auto currSimTime = simTime(); - auto currSimTimeAsClockTime = SIMTIME_AS_CLOCKTIME(simTime()); - auto currClockTime = clock->computeClockTimeFromSimTime(currSimTime); - auto currentOffset = currSimTimeAsClockTime - currClockTime; - if (std::abs(currentOffset.dbl()) > maxOffset->doubleValue()) { - // Adjust the drift rate to bring the offset back within limits - if (currentOffset > 0) { - driftRateChangeTotal += maxDriftRateAdjustment; - } - else { - driftRateChangeTotal -= maxDriftRateAdjustment; - } - } - - setDriftRate(initialDriftRate + driftRateChangeTotal); - - scheduleAfter(changeIntervalParameter->doubleValue(), changeTimer); - } - else - throw cRuntimeError("Unknown message"); -} - -} // namespace inet diff --git a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h deleted file mode 100644 index 0b9a8920b62..00000000000 --- a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright (C) 2020 OpenSim Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - - -#ifndef __INET_OFFSETLIMITEDRANDOMDRIFTOSCILLATOR_H -#define __INET_OFFSETLIMITEDRANDOMDRIFTOSCILLATOR_H - -#include "inet/clock/oscillator/RandomDriftOscillator.h" -#include "inet/clock/model/OscillatorBasedClock.h" -#include "inet/common/Units.h" - -namespace inet { - -using namespace units::values; - -class INET_API OffsetLimitedRandomDriftOscillator : public RandomDriftOscillator -{ - protected: - OscillatorBasedClock *clock = nullptr; - cPar *maxOffset = nullptr; - ppm maxDriftRateAdjustment = ppm(NaN); - - protected: - void handleMessage(cMessage *message) override; - void initialize(int stage) override; -}; - -} // namespace inet - -#endif - diff --git a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.ned b/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.ned deleted file mode 100644 index d95aa12eadc..00000000000 --- a/src/inet/clock/oscillator/OffsetLimitedRandomDriftOscillator.ned +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright (C) 2020 OpenSim Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - - -package inet.clock.oscillator; - -// -// This module behaves like the RandomDriftOscillator but ensures that the drift does not exceed a certain limit -// from the simulation time. -// -simple OffsetLimitedRandomDriftOscillator extends RandomDriftOscillator -{ - parameters: - @class(OffsetLimitedRandomDriftOscillator); - double maxOffset @unit(s) = default(0s); // Max offset to simulation time until the drift is adjusted - double maxDriftRateAdjustment @unit(ppm) = default(0ppm); // Max drift rate adjustment per changeInterval -} diff --git a/src/inet/clock/servo/PiServoClock.cc b/src/inet/clock/servo/PiServoClock.cc index 829f65bbf53..43ea25db1d9 100644 --- a/src/inet/clock/servo/PiServoClock.cc +++ b/src/inet/clock/servo/PiServoClock.cc @@ -80,8 +80,7 @@ void PiServoClock::adjustClockTo(clocktime_t newClockTime) { // differenceOffsetNanosecond = offsetNanosecond - offsetNanosecond_prev; - originSimulationTime = simTime(); - originClockTime = oldClockTime; + setNewOriginTime(simTime(), oldClockTime); // As our timestamps is in nanoseconds, to get ppm we need to multiply by 1e-3 kpTerm = ppm(kp * offsetUs); diff --git a/src/inet/clock/servo/ServoClockBase.cc b/src/inet/clock/servo/ServoClockBase.cc index 31e3ca7d830..daa9301b14d 100644 --- a/src/inet/clock/servo/ServoClockBase.cc +++ b/src/inet/clock/servo/ServoClockBase.cc @@ -116,8 +116,7 @@ void ServoClockBase::jumpClockTo(clocktime_t newClockTime, bool notifyListeners) simtime_t currentSimTime = simTime(); EV_DEBUG << "Setting clock time from " << oldClockTime << " to " << newClockTime << " at simtime " << currentSimTime << ".\n"; - originSimulationTime = simTime(); - originClockTime = newClockTime; + setNewOriginTime(simTime(), newClockTime); ASSERT(newClockTime == getClockTime()); rescheduleClockEvents(oldClockTime, newClockTime); From 730fdb0c742cd6c17be5d6ae875f13dad876a6ef Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 21 Jan 2025 20:08:25 +0100 Subject: [PATCH 104/138] Added resetClockState --- .../timesynchronization/gptp/GptpShowcase.anf | 738 +++++++++++++++++- .../tsn/timesynchronization/gptp/omnetpp.ini | 10 +- src/inet/clock/servo/InstantServoClock.cc | 6 + src/inet/clock/servo/InstantServoClock.h | 1 + src/inet/clock/servo/PiServoClock.cc | 13 +- src/inet/clock/servo/PiServoClock.h | 5 +- src/inet/clock/servo/ServoClockBase.cc | 4 + src/inet/clock/servo/ServoClockBase.h | 6 +- src/inet/linklayer/ieee8021as/Gptp.cc | 17 +- src/inet/linklayer/ieee8021as/Gptp.ned | 1 + src/inet/linklayer/ieee8021as/HotStandby.cc | 4 +- 11 files changed, 771 insertions(+), 34 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 92a74a61d1b..c45f7c278fc 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -556,7 +556,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -580,11 +580,11 @@ utils.export_data_if_needed(df, props) - + - + @@ -593,9 +593,739 @@ utils.export_data_if_needed(df, props) - + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 462cdfbe1c2..1417af22e08 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -2,6 +2,7 @@ seed-set = 1 sim-time-limit = 20s description = "abstract" +abstract = true # enable time synchronization in all network nodes *.*.hasTimeSynchronization = true @@ -12,7 +13,18 @@ description = "abstract" **.oscillator.driftRate = uniform(-100ppm, 100ppm) # According to 802.1AS B.1.1 **.oscillator.initialDriftRate = uniform(-100ppm, 100ppm) # According to 802.1AS B.1.1 **.oscillator.changeInterval = 12.5ms -**.oscillator.driftRateChange = uniform(-1ppm, 1ppm) # Arbitrary large value => Leads to greater errors, espacially when using the nrr calculation, + +# Arbitrary large value +# According to the standard this value should be much smaller +**.oscillator.driftRateChange = uniform(-1ppm, 1ppm) +# => This setting leads to greater errors, espacially when using the nrr calculation (drift changes faster than nrr can adapt). +# A more realistic setting would be this: +# Based on 802.1AS B.1.3.2 (simplified to max error being 5ns/s, needs to be adapted if changeInterval is also adapted) +# **.oscillator.driftRateChange = uniform(-0.1235e-3 ppm, 0.1235e-3 ppm) +# However, then the clock drift is not really visible anymore. +# See RateRatioStudies for more. + + #**.oscillator.driftRateChange = uniform(-0.1235e-3 ppm, 0.1235e-3 ppm) # Based on 802.1AS B.1.3.2 (simplified to max error being 5ns/s, needs to be adapted if changeInterval is also adapted) **.oscillator.driftRateChangeUpperLimit = 100ppm # According to 802.1AS B.1.1 **.oscillator.driftRateChangeLowerLimit = -100ppm # According to 802.1AS B.1.1 @@ -33,14 +45,6 @@ description = "abstract" network = OneMasterClockGptpShowcase description = "Basic tree topology with one master clock" -# Uncomment these lines to test out of band time synchronization using the SimpleClockSynchronizer instead of gPTP -#*.tsnClock.gptp.typename = ""# pragma warning(disable:4065) - -#*.*.gptp.typename = "SimpleClockSynchronizer" -#*.*.gptp.synchronizationInterval = 0.125s -#*.*.gptp.masterClockModule = "^.^.tsnClock.clock" -#*.*.gptp.slaveClockModule = "^.clock" - *.tsnSwitch.clock.typename = "InstantServoClock" **.clock.adjustDrift = true *.tsnDevice2.clock.typename = "InstantServoClock" @@ -151,6 +155,7 @@ seed-1-mt = 134 **.useNrr = ${nrr=false,true} **.gmRateRatioCalculationMethod = ${gmMethod="NONE","NRR","DIRECT"} +**.oscillator.driftRateChange = uniform(${driftRateChange=0.1235e-3, 1} * -1ppm, ${driftRateChange} * 1ppm) [JumpingClock] diff --git a/showcases/tsn/timesynchronization/gptp/scenario-jumping-clock.xml b/showcases/tsn/timesynchronization/gptp/scenario-jumping-clock.xml index 83992d71d5e..9580cb90542 100644 --- a/showcases/tsn/timesynchronization/gptp/scenario-jumping-clock.xml +++ b/showcases/tsn/timesynchronization/gptp/scenario-jumping-clock.xml @@ -5,6 +5,4 @@ - - diff --git a/src/inet/clock/servo/ServoClockBase.cc b/src/inet/clock/servo/ServoClockBase.cc index 748d482df9e..4b56b20fdf3 100644 --- a/src/inet/clock/servo/ServoClockBase.cc +++ b/src/inet/clock/servo/ServoClockBase.cc @@ -100,19 +100,19 @@ void ServoClockBase::processCommand(const cXMLElement &node) clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); adjustClockTo(time); } - if (!strcmp(node.getTagName(), "set-clock")) { + else if (!strcmp(node.getTagName(), "set-clock")) { clocktime_t time = ClockTime::parse(xmlutils::getMandatoryFilledAttribute(node, "time")); bool notifyListeners = xmlutils::getAttributeBoolValue(&node, "notifyListeners", true); jumpClockTo(time, notifyListeners); } - if (!strcmp(node.getTagName(), "set-oscillator-compensation")) { + else if (!strcmp(node.getTagName(), "set-oscillator-compensation")) { // TODO: Refactor to directly read ppm const char *valueStr = xmlutils::getMandatoryFilledAttribute(node, "value"); double valueDouble = std::atof(valueStr); // Convert string to double ppm oscillatorCompensationValue = ppm(valueDouble); // Create ppm object from double setOscillatorCompensation(oscillatorCompensationValue); } - if (!strcmp(node.getTagName(), "reset-oscillator")) { + else if (!strcmp(node.getTagName(), "reset-oscillator")) { resetOscillator(); } else From 9e073bac2e9a7af8304e88050288eff5ec6228a0 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 1 Apr 2025 23:40:38 +0200 Subject: [PATCH 127/138] Updated Hot Standby Showcase --- .../tsn/timesynchronization/gptp_hotstandby/GptpShowcase.anf | 2 +- .../tsn/timesynchronization/gptp_hotstandby/omnetpp.ini | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.anf index 8cf0f734c8d..6d2dc8117b6 100644 --- a/showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/GptpShowcase.anf @@ -1306,7 +1306,7 @@ utils.export_data_if_needed(df, props) - + diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini b/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini index 22cd1f8bda1..8f973529b1a 100644 --- a/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini @@ -125,9 +125,14 @@ network = TwoMasterClocksRingGptpShowcase description = "Ring topology with redundant time synchronization domains" # clock visualization note: bridge and slave nodes display difference from corresponding master clock +**.clock.clock[*].typename = "InstantServoClock" + +*.tsnClock1.gptp.hasHotStandby = false + # TSN clock2 has multiple clocks *.tsnClock2.clock.typename = "MultiClock" *.tsnClock2.clock.numClocks = 2 +*.tsnClock2.gptp.hasHotStandby = false # TSN clocks have multiple gPTP time synchronization domains *.tsnClock*.gptp.typename = "MultiDomainGptp" From 9f2a4c55fcae1fab8046754ef9d220ce4e8b53c3 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 1 Apr 2025 23:52:28 +0200 Subject: [PATCH 128/138] Added missing parameters for BTCA --- src/inet/linklayer/ieee8021as/Gptp.cc | 27 ++++++++++++++++++++++---- src/inet/linklayer/ieee8021as/Gptp.ned | 4 ++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index c571e69a3b7..930bf3f1d61 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -235,10 +235,10 @@ void Gptp::initBmca() { // TODO: harcoded for now localPriorityVector.grandmasterPriority1 = par("grandmasterPriority1"); - localPriorityVector.grandmasterClockQuality.clockClass = 248; - localPriorityVector.grandmasterClockQuality.clockAccuracy = 0; - localPriorityVector.grandmasterClockQuality.offsetScaledLogVariance = 0; - localPriorityVector.grandmasterPriority2 = 0; + localPriorityVector.grandmasterClockQuality.clockClass = par("clockClass"); + localPriorityVector.grandmasterClockQuality.clockAccuracy = par("clockAccuracy"); + localPriorityVector.grandmasterClockQuality.offsetScaledLogVariance = par("offsetScaledLogVariance"); + localPriorityVector.grandmasterPriority2 = par("grandmasterPriority2"); localPriorityVector.grandmasterIdentity = clockIdentity; localPriorityVector.stepsRemoved = 0; } @@ -1290,6 +1290,25 @@ void Gptp::handleParameterChange(const char *name) localPriorityVector.grandmasterPriority1 = par("grandmasterPriority1"); executeBmca(); } + else if (!strcmp(name, "clockClass")) { + localPriorityVector.grandmasterClockQuality.clockClass = par("clockClass"); + executeBmca(); + } + else if (!strcmp(name, "clockAccuracy")) { + localPriorityVector.grandmasterClockQuality.clockAccuracy = par("clockAccuracy"); + executeBmca(); + } + else if (!strcmp(name, "offsetScaledLogVariance")) { + localPriorityVector.grandmasterClockQuality.offsetScaledLogVariance = par("offsetScaledLogVariance"); + executeBmca(); + } + else if (!strcmp(name, "priority2")) { + localPriorityVector.grandmasterPriority2 = par("priority2"); + executeBmca(); + } + else { + throw cRuntimeError("Parameter %s is not mutable", name); + } } } // namespace inet \ No newline at end of file diff --git a/src/inet/linklayer/ieee8021as/Gptp.ned b/src/inet/linklayer/ieee8021as/Gptp.ned index aa196c73359..9841bb1d298 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.ned +++ b/src/inet/linklayer/ieee8021as/Gptp.ned @@ -41,6 +41,10 @@ simple Gptp like IGptp bool sendAnnounceImmediately = default(false); // Send Announce message immediately after entering executing BMCA (otherwise wait until next announceInterval) int grandmasterPriority1 @mutable = default(255); // Priority1 value for BMCA + int clockClass @mutable = default(248); // ClockClass value for BMCA + int clockAccuracy @mutable = default(254); // ClockAccuracy value for BMCA + int offsetScaledLogVariance @mutable = default(17258); // OffsetScaledLogVariance value for BMCA + int grandmasterPriority2 @mutable = default(248); // Priority2 value for BMCA // following parameters are used to schedule follow_up and pdelay_resp messages. // These numbers must be large enough to prevent creating a queue in the MAC layer. From 5a8bf76b9bcbf88975a874bb465b5ab1033e9971 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 2 Apr 2025 00:51:59 +0200 Subject: [PATCH 129/138] Update documentation --- .gitignore | 3 +- .../gptp_hotstandby/omnetpp.ini | 1 - src/inet/clock/base/ClockBase.ned | 2 +- src/inet/clock/model/MultiClock.ned | 2 +- src/inet/clock/servo/InstantServoClock.ned | 2 +- src/inet/clock/servo/PiServoClock.ned | 6 ++-- src/inet/linklayer/ieee8021as/Gptp.cc | 4 +++ src/inet/linklayer/ieee8021as/Gptp.h | 4 +++ src/inet/linklayer/ieee8021as/Gptp.ned | 25 +++++++++++++--- src/inet/linklayer/ieee8021as/GptpPacket.msg | 4 +++ src/inet/linklayer/ieee8021as/HotStandby.cc | 16 ++++++++-- src/inet/linklayer/ieee8021as/HotStandy.ned | 29 ++++++++++--------- .../linklayer/ieee8021as/MultiDomainGptp.ned | 5 +++- 13 files changed, 73 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 0fb9aa2cea9..7db6603de4f 100644 --- a/.gitignore +++ b/.gitignore @@ -99,5 +99,4 @@ xxx /tmp/ /.computed/ *.DS_Store -.idea/ -.cproject \ No newline at end of file +.idea/ \ No newline at end of file diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini b/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini index 8f973529b1a..cdc730e0127 100644 --- a/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/omnetpp.ini @@ -132,7 +132,6 @@ description = "Ring topology with redundant time synchronization domains" # TSN clock2 has multiple clocks *.tsnClock2.clock.typename = "MultiClock" *.tsnClock2.clock.numClocks = 2 -*.tsnClock2.gptp.hasHotStandby = false # TSN clocks have multiple gPTP time synchronization domains *.tsnClock*.gptp.typename = "MultiDomainGptp" diff --git a/src/inet/clock/base/ClockBase.ned b/src/inet/clock/base/ClockBase.ned index 459ec3b7908..59b1f8f4a41 100644 --- a/src/inet/clock/base/ClockBase.ned +++ b/src/inet/clock/base/ClockBase.ned @@ -20,7 +20,7 @@ module ClockBase @display("i=block/timer"); @signal[timeChanged](type=simtime_t); @signal[timeDifferenceToReference](type=simtime_t); - @signal[timeJumped](); + @signal[timeJumped](); // Signal is emitted, when the clock performs a time jump. This is for example required for the gPTP module to work correctly. @statistic[timeChanged](title="Clock time"; record=vector; interpolationmode=linear); @statistic[timeDifferenceToReference](title="Time difference to reference"; record=vector; interpolationmode=linear); } diff --git a/src/inet/clock/model/MultiClock.ned b/src/inet/clock/model/MultiClock.ned index 77d6f40355f..e7277bf166f 100644 --- a/src/inet/clock/model/MultiClock.ned +++ b/src/inet/clock/model/MultiClock.ned @@ -25,7 +25,7 @@ module MultiClock like IClock @display("i=block/timer"); @class(MultiClock); @signal[timeChanged](type=simtime_t); - @signal[timeJumped](); + @signal[timeJumped](); // Signal is emitted when the activeClock performs a timeJump or the activeClockIndex updates @signal[timeDifferenceToReference](type=simtime_t); @signal[activeClockIndexChanged](type=int); @statistic[timeChanged](title="Clock time"; source=localSignal(timeChanged); record=vector; interpolationmode=linear); diff --git a/src/inet/clock/servo/InstantServoClock.ned b/src/inet/clock/servo/InstantServoClock.ned index ae3276faa4e..1282f2e2831 100644 --- a/src/inet/clock/servo/InstantServoClock.ned +++ b/src/inet/clock/servo/InstantServoClock.ned @@ -7,7 +7,7 @@ package inet.clock.servo; -// As simple clock servo, with three different modes: +// As simple clock servo (previously known as SettableClock in INET), with three different modes: // // 1. No servo: Calls to adjustTime are ignored. (adjustClock = false; adjustDrift = false) // diff --git a/src/inet/clock/servo/PiServoClock.ned b/src/inet/clock/servo/PiServoClock.ned index 32314d6c400..63fa3ef96f1 100644 --- a/src/inet/clock/servo/PiServoClock.ned +++ b/src/inet/clock/servo/PiServoClock.ned @@ -16,12 +16,14 @@ package inet.clock.servo; // 2. Estimate Drift: Computes drift based on the difference between consecutive offsets. // // 3. Synchronize: Adjusts the clock using proportional and integral terms to minimize offset. +// +// Note: The kp and ki parameters need to be adjusted, if the the syncInterval is adjusted from the default value. module PiServoClock extends ServoClockBase { parameters: double offsetThreshold @unit(s) = default(0s); // Clock will perform a time jump if the offset is greater than this value, 0.0 disables this feature - double kp = default(8); - double ki = default(4); + double kp = default(8); // The proportional parameter of the PI controller + double ki = default(4); // The integral paramater of the PI Controller @signal[kp](type=double); @statistic[kp](title="kp value"; record=vector; interpolationmode=linear); @signal[drift](type=double); diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index 930bf3f1d61..d873cd2763b 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -4,6 +4,10 @@ // Peter Danielis // University of Rostock, Germany // +// Lucas Haug +// Lun-Yu Yuan +// Chunzhi Guo +// University of Stuttgart, Deterministic6G #include "Gptp.h" diff --git a/src/inet/linklayer/ieee8021as/Gptp.h b/src/inet/linklayer/ieee8021as/Gptp.h index bba598e9bd3..b685aec0915 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.h +++ b/src/inet/linklayer/ieee8021as/Gptp.h @@ -4,6 +4,10 @@ // Peter Danielis // University of Rostock, Germany // +// Lucas Haug +// Lun-Yu Yuan +// Chunzhi Guo +// University of Stuttgart, Deterministic6G #ifndef __INET_GPTP_H #define __INET_GPTP_H diff --git a/src/inet/linklayer/ieee8021as/Gptp.ned b/src/inet/linklayer/ieee8021as/Gptp.ned index 9841bb1d298..190fd7008c4 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.ned +++ b/src/inet/linklayer/ieee8021as/Gptp.ned @@ -4,26 +4,44 @@ // Peter Danielis // University of Rostock, Germany // +// Lucas Haug +// Lun-Yu Yuan +// Chunzhi Guo +// University of Stuttgart, Deterministic6G package inet.linklayer.ieee8021as; import inet.linklayer.contract.IGptp; // -// Implements the IEEE 802.1as protocol also known as gPTP. It +// Implements the IEEE 802.1AS protocol also known as gPTP. It // measures link delays to neighboring gPTP network nodes periodically. The // slave and master ports specify where the connected gPTP network nodes // and their roles in the time synchronization domain. The time synchronization // is done periodically and the clock module is set. // +// It can either be statically configured, by setting the slavePort and masterPorts parameter. +// In case of a static configuration the gPTP node type is MASTER_NODE, BRIDGE_NODE or SLAVE_NODE. +// +// Alternatively the Best Master Clock Algorithm (BMCA) can be used to dynamically select a GM based on +// the BMCA parameters. +// In this case the gPTP node type is set to BMCA_NODE. +// All ports that should be used for BMCA must be set in the bmcaPorts parameter. +// +// The implementation also supports the HotStandby ammendment IEEE 802.1ASdm, see MultiDomainGptp. simple Gptp like IGptp { parameters: string clockModule = default(""); // Relative path of a module that implements IClock; optional string interfaceTableModule; // Relative path of the interface table module - string gptpNodeType; // @enum("GptpNodeType"): MASTER_NODE, BRIDGE_NODE, SLAVE_NODE, BMCA_NODE + string gptpNodeType @enum("MASTER_NODE","BRIDGE_NODE","SLAVE_NODE","BMCA_NODE"); // @enum("GptpNodeType"): MASTER_NODE, BRIDGE_NODE, SLAVE_NODE, BMCA_NODE bool useNrr = default(false); // Use neighbor rate ratio - string gmRateRatioCalculationMethod = default("NONE"); // @enum("GptpRateRatioCalculationMethod"): NONE, NRR, DIRECT + + // How to calculate the gmRateRatio. NRR is the method used by the gPTP standard. + // DIRECT is another method previously used by the INET implementation. + // It calculates the gmRateRatio directly by using the preciseOriginTimestamp and correctionField + string gmRateRatioCalculationMethod @enum("NONE","NRR","DIRECT") = default("NONE"); + int domainNumber = default(0); // Specifies the time domain number used in gPTP messages string slavePort = default(""); // Port for receiving time (empty for MASTER_NODE and BMCA_NODE) object masterPorts = default([]); // Ports for sending out time (empty for SLAVE_NODE and BMCA_NODE) @@ -50,7 +68,6 @@ simple Gptp like IGptp // These numbers must be large enough to prevent creating a queue in the MAC layer. // It means they should be larger than the transmission time of the message sent before double pDelayReqProcessingTime @unit(s) = default(8us); // Processing time between the arrival of PDelayReq and the sending of PDelayResp - double followUpInterval @unit(s) = default(7us); @display("i=block/timer"); @signal[localTime](type=simtime_t); // As clocktime_t diff --git a/src/inet/linklayer/ieee8021as/GptpPacket.msg b/src/inet/linklayer/ieee8021as/GptpPacket.msg index 42a258ec711..e58021e5c52 100644 --- a/src/inet/linklayer/ieee8021as/GptpPacket.msg +++ b/src/inet/linklayer/ieee8021as/GptpPacket.msg @@ -10,6 +10,10 @@ // Peter Danielis // University of Rostock, Germany // +// Lucas Haug +// Lun-Yu Yuan +// Chunzhi Guo +// University of Stuttgart, Deterministic6G import inet.clock.common.ClockEvent; import inet.clock.contract.ClockTime; diff --git a/src/inet/linklayer/ieee8021as/HotStandby.cc b/src/inet/linklayer/ieee8021as/HotStandby.cc index 268d18da897..29beb0367ed 100644 --- a/src/inet/linklayer/ieee8021as/HotStandby.cc +++ b/src/inet/linklayer/ieee8021as/HotStandby.cc @@ -24,17 +24,27 @@ void HotStandby::initialize(int stage) for (int i = 0; i < numDomains; i++) { auto gptp = check_and_cast(parent->getSubmodule("domain", i)); + auto domainNumber = gptp->getDomainNumber(); gptp->subscribe(Gptp::gptpSyncStateChanged, this); - ModuleRefByPar currentClock; + ModuleRefByPar currentClock; currentClock.reference(gptp, "clockModule", true); - auto clockParent = currentClock->getParentModule(); + // Check if the currentClock is a ClockBase + // (otherwise it could be a link to the multiClock or an external) + // In that case we just ignore this clock in the HotStandby context + if (dynamic_cast(currentClock.get()) == nullptr) { + EV_WARN << "Clock module " << currentClock->getFullPath() << " for domain " << domainNumber + << "is not a ClockBase, ignoring" << endl; + continue; + } + auto clockBase = check_and_cast(currentClock.get()); + auto clockParent = clockBase->getParentModule(); if (clockParent != multiClock) { EV_WARN << "Clock module is not a child of MultiClock, cannot determine clock index and ignoring this clock" << endl; } else { - domainNumberToClockIndex[gptp->getDomainNumber()] = currentClock->getIndex(); + domainNumberToClockIndex[domainNumber] = clockBase->getIndex(); } } } diff --git a/src/inet/linklayer/ieee8021as/HotStandy.ned b/src/inet/linklayer/ieee8021as/HotStandy.ned index 72f59994096..204b2788da5 100644 --- a/src/inet/linklayer/ieee8021as/HotStandy.ned +++ b/src/inet/linklayer/ieee8021as/HotStandy.ned @@ -1,25 +1,26 @@ // -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// +// @authors: Lucas Haug +// Lun-Yu Yuan +// Chunzhi Guo +// University of Stuttgart, Deterministic6G package inet.linklayer.ieee8021as; +// This module is a basic implementation of the IEEE 802.1ASdm HotStandby amendment. +// It is part of the MultiDomainGptp module and keeps track of the synchronization state of contained +// gPTP modules. +// +// In case one gPTP module loses synchronization, it will switch the activeClockIndex of the multiClock +// to the clock of a gPTP instance still in sync. +// +// Note: This module is not a complete implementation of the HotStandby amendment and only provides +// the basic functionality of switching the active clock if one gPTP instance loses sync. +// For example, a smooth transition when switching between synchronization instances is not implemented. simple HotStandby { parameters: @display("i=block/blackboard"); @class(HotStandby); @selfMessageKinds(inet::GptpSelfMsgKind); - string clockModule = default(""); + string clockModule = default(""); // Path to a multiClock } diff --git a/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned b/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned index 24bcff5a91c..894e870aa6c 100644 --- a/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned +++ b/src/inet/linklayer/ieee8021as/MultiDomainGptp.ned @@ -18,7 +18,10 @@ import inet.queueing.contract.IPacketClassifier; // configured to use the corresponding subclock of the clock passed into this // module. // +// The module also provides a basic implementation of the IEEE 802.1ASdm ~HotStandby amendment +// // @see ~MultiClock +// @see ~HotStandby // module MultiDomainGptp like IGptp { @@ -27,7 +30,7 @@ module MultiDomainGptp like IGptp string interfaceTableModule; // Relative module path of the interface table int numDomains; // Number of time synchronization domains string gptpNodeType; // @enum("GptpNodeType"): MASTER_NODE, BRIDGE_NODE, SLAVE_NODE - bool hasHotStandby = default(true); + bool hasHotStandby = default(true); // Defines if the HotStandby should be used. @display("i=block/app"); gates: input socketIn; From 76ce5a72054680c99083dc60b509ac519727361c Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 2 Apr 2025 02:01:31 +0200 Subject: [PATCH 130/138] Update documentation of gPTP --- doc/src/nedtags.xml | 48 ++++++++++++++--- doc/src/users-guide/ch-clock.rst | 31 ++++++----- doc/src/users-guide/ch-tsn.rst | 51 +++++++++---------- .../timesynchronization/gptp/doc/index.rst | 4 +- .../gptp_bmca/doc/index.rst | 4 +- .../gptp_hotstandby/doc/index.rst | 4 +- 6 files changed, 90 insertions(+), 52 deletions(-) diff --git a/doc/src/nedtags.xml b/doc/src/nedtags.xml index b64975ef2ab..493f5522d64 100644 --- a/doc/src/nedtags.xml +++ b/doc/src/nedtags.xml @@ -348,6 +348,18 @@ inet.linklayer.bmac.BMacInterface inet.linklayer.bmac.BMacInterface.html + + inet.showcases.tsn.timesynchronization.gptp_bmca.BmcaShowcaseDiamond + inet.showcases.tsn.timesynchronization.gptp_bmca.BmcaShowcaseDiamond.html + + + inet.showcases.tsn.timesynchronization.gptp_bmca.BmcaShowcaseDiamondAsymmetric + inet.showcases.tsn.timesynchronization.gptp_bmca.BmcaShowcaseDiamondAsymmetric.html + + + inet.showcases.tsn.timesynchronization.gptp_bmca.BmcaShowcaseSimple + inet.showcases.tsn.timesynchronization.gptp_bmca.BmcaShowcaseSimple.html + inet.mobility.single.BonnMotionMobility inet.mobility.single.BonnMotionMobility.html @@ -1868,6 +1880,10 @@ inet.linklayer.ieee8021as.GptpBridge inet.linklayer.ieee8021as.GptpBridge.html + + inet.linklayer.ieee8021as.GptpDomainClassifier + inet.linklayer.ieee8021as.GptpDomainClassifier.html + inet.linklayer.ieee8021as.GptpEndstation inet.linklayer.ieee8021as.GptpEndstation.html @@ -1956,6 +1972,10 @@ inet.tests.ethernet.HostsWithSwitch inet.tests.ethernet.HostsWithSwitch.html + + inet.linklayer.ieee8021as.HotStandby + inet.linklayer.ieee8021as.HotStandby.html + inet.examples.ethernet.lans.HubLAN inet.examples.ethernet.lans.HubLAN.html @@ -2932,6 +2952,10 @@ inet.queueing.server.InstantServer inet.queueing.server.InstantServer.html + + inet.clock.servo.InstantServoClock + inet.clock.servo.InstantServoClock.html + inet.showcases.visualizer.canvas.instrumentfigures.InstrumentShowcase inet.showcases.visualizer.canvas.instrumentfigures.InstrumentShowcase.html @@ -3508,6 +3532,10 @@ inet.showcases.measurement.jitter.JitterMeasurementShowcase inet.showcases.measurement.jitter.JitterMeasurementShowcase.html + + inet.showcases.tsn.timesynchronization.gptp.JumpingClockGptpShowcase + inet.showcases.tsn.timesynchronization.gptp.JumpingClockGptpShowcase.html + inet.examples.inet.kidsnw1.KIDSNw1 inet.examples.inet.kidsnw1.KIDSNw1.html @@ -3948,6 +3976,10 @@ inet.examples.sctp.cmttest.multihomed inet.examples.sctp.cmttest.multihomed.html + + inet.showcases.tsn.timesynchronization.gptp.MultiHopGptpShowcase + inet.showcases.tsn.timesynchronization.gptp.MultiHopGptpShowcase.html + inet.protocolelement.measurement.MultiMeasurementLayer inet.protocolelement.measurement.MultiMeasurementLayer.html @@ -4980,6 +5012,10 @@ inet.applications.pingapp.PingApp inet.applications.pingapp.PingApp.html + + inet.clock.servo.PiServoClock + inet.clock.servo.PiServoClock.html + inet.showcases.wireless.power.PowerConsumptionShowcase inet.showcases.wireless.power.PowerConsumptionShowcase.html @@ -5717,8 +5753,8 @@ inet.tutorials.queueing.ServerTutorialStep.html - inet.clock.model.SettableClock - inet.clock.model.SettableClock.html + inet.clock.servo.ServoClockBase + inet.clock.servo.ServoClockBase.html inet.linklayer.shortcut.ShortcutInterface @@ -6509,12 +6545,12 @@ inet.tests.ethernet.TwoHosts.html - inet.showcases.tsn.timesynchronization.gptp.TwoMasterClocksRingGptpShowcase - inet.showcases.tsn.timesynchronization.gptp.TwoMasterClocksRingGptpShowcase.html + inet.showcases.tsn.timesynchronization.gptp_hotstandby.TwoMasterClocksRingGptpShowcase + inet.showcases.tsn.timesynchronization.gptp_hotstandby.TwoMasterClocksRingGptpShowcase.html - inet.showcases.tsn.timesynchronization.gptp.TwoMasterClocksTreeGptpShowcase - inet.showcases.tsn.timesynchronization.gptp.TwoMasterClocksTreeGptpShowcase.html + inet.showcases.tsn.timesynchronization.gptp_hotstandby.TwoMasterClocksTreeGptpShowcase + inet.showcases.tsn.timesynchronization.gptp_hotstandby.TwoMasterClocksTreeGptpShowcase.html inet.examples.ospfv2.areas.TwoNetsArea diff --git a/doc/src/users-guide/ch-clock.rst b/doc/src/users-guide/ch-clock.rst index 8531875294f..a008c0e5dd7 100644 --- a/doc/src/users-guide/ch-clock.rst +++ b/doc/src/users-guide/ch-clock.rst @@ -54,19 +54,25 @@ return values. The interface contains functions such as :fun:`getClockTime()`, :fun:`cancelClockEvent()`. INET contains optional clock modules (not used by default) at the network node -and the network interface levels. The following clock models are available: +and the network interface levels. The following simple clock models are available: - :ned:`IdealClock`: clock time is identical to the simulation time. - :ned:`OscillatorBasedClock`: clock time is the number of oscillator ticks multiplied by the nominal tick length. -- :ned:`ServoClockBase`: A based servo clock module provides core functionalities - for clock adjustment and overdue event handling. -- :ned:`PiServoClock`: A PI-based servo clock using proportional (kp) and - integral (ki) gains to synchronize with an external time source and compensate for drift. -- :ned:`InstantServoClock`: A simple clock servo module that supports three modes - of operation to adjust the clock and drift. -- :ned:`MultiClock`: Manages multiple subclocks with a programmatically switchable - active clock, useful for multi-domain gPTP synchronization. + +Above clock models are not adjustable during runtime. +To this end, INET provides several clocks with an adjustable time and drift rate. +These clocks are based on the :ned:`ServoClockBase` extending the :ned:`OscillatorBasedClock`: + +- :ned:`InstantServoClock`: A simple clock servo module supports jumping its clock to another time and optionally + adjusting the drift rate. This allows for a simple synchronization with another clock. +- :ned:`PiServoClock`: A PI-based servo clock using proportional (kp) and integral (ki) gains + to in sync with another time source source while smoothly adjusting the drift rate. + + +If multiple clocks are needed INET provides a :ned:`MultiClock`. +This clock manages holds multiple subclocks with a programmatically switchable active clock. +This is for example useful for multi-domain gPTP synchronization. Clock Time ---------- @@ -141,10 +147,9 @@ provides clocks and oscillators that implement the interface required by the oscillator states from the :ned:`ScenarioManager` XML script and also to mix these operations with many other supported operations. -For example, the :ned:`ServoClockBase` model supports setting the clock time and -optionally resetting the oscillator at a specific moment of simulation time. -The :ned:`InstantServoClock` model is the extended version of the :ned:`ServoClockBase`, -which supports adjusting the clock time, as well as the oscillator drift rate as follows: +For example, the :ned:`InstantServoClock` model supports setting the clock time and +optionally resetting the oscillator at a specific moment of simulation time +as follows: .. code-block:: xml diff --git a/doc/src/users-guide/ch-tsn.rst b/doc/src/users-guide/ch-tsn.rst index 20a76fdadfd..533be8d3673 100644 --- a/doc/src/users-guide/ch-tsn.rst +++ b/doc/src/users-guide/ch-tsn.rst @@ -62,11 +62,11 @@ can still be used to keep track of time in the network nodes. - :ned:`OscillatorBasedClock` models a clock that has a potentially drifting oscillator - :ned:`ServoClockBase` extends the previous model with the approach for clock - adjustments and overdue events + adjustments - :ned:`InstantServoClock` extends :ned:`ServoClockBase` with the capability of - adjusting clock and drift + adjusting clock by jumping to another time and adjusting the drift rate - :ned:`PiServoClock` extends :ned:`ServoClockBase` for synchronizing the clock - with an external time source + with an external time source smoothly by implementing a PI controller Similarly to the above, the following gPTP time synchronization related protocol modules and network nodes can also be used to build time synchronization in a @@ -78,18 +78,6 @@ network: - :ned:`GptpMaster` models a gPTP time synchronization master network node - :ned:`GptpSlave` models a gPTP time synchronization slave network node -In order to implement node failure (e.g. master clock) and link failure (e.g. -between gPTP bridges) protection, multiple time synchronization domains are -required. These time domains operate independently of each other, and it's up to -the clock user modules of each network node to decide which clock they are using. -Typically, they use the active clock of the :ned:`MultiClock`, and there has to -be some means of changing the active clocks when failover happens. The following -modules can be used to implement multiple time domains: - -- :ned:`MultiClock` contains several subclocks for the different time domains -- :ned:`MultiDomainGptp` contains several gPTP submodules for the different - time domains - The following parameters can be used to enable the gPTP time synchronization in various predefined network nodes: @@ -98,20 +86,29 @@ in various predefined network nodes: - :par:`hasGptp` parameter enables the gPTP time synchronization protocol in gPTP specific network nodes -Moreover, gPTP relies on specific clock election and failover mechanisms to -maintain synchronization accuracy, ensuring precise timing across different -devices. The following modules introduce two key mechanisms: Best Master Clock -Algorithm (BMCA) and Hot-Standby redundancy, both of which enhance the reliability -of high-precision time synchronization. - -To implement the Best Master Clock Algorithm (BMCA),three networks BmcaShowcaseSimple, -BmcaShowcaseDiamond, and BmcaShowcaseDiamondAsymmetric are implemented, which extend -TsnNetworkBase. - -- :ned:`HotStandby` ensures seamless time synchronization by automatically switching - to a standby clock if the primary clock fails +There are two possible ways to define the synchronization topology of a network. +The first one is by setting up a static the synchronization topology manually. +The second one is to use the Best Master Clock Algorithm (BMCA) to automatically +determine the synchronization topology. +Please refer to their respective showcases: +:doc:`/showcases/tsn/timesynchronization/gptp/doc/index` and :doc:`/showcases/tsn/timesynchronization/gptp_bmca/doc/index` + +The usage of BMCA is one way to protect the synchronization network +topology against node and link failures, it is not the only one. +Another option is the usage of multiple gPTP synchronization domains. +These time domains operate independently of each other, and it's up to +the clock user modules of each network node to decide which clock they are using. +Typically, they use the active clock of the :ned:`MultiClock`, and there has to +be some means of changing the active clocks when failover happens. +One such a failover mechanism is the :ned:`HotStandby` mechanism, as defined by the IEEE 802.1ASdm amendment. +For a detailed description please refer to the HotStandby showcase: +:doc:`/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index`. +The following modules can be used to implement multiple time domains: +- :ned:`MultiClock` contains several subclocks for the different time domains +- :ned:`MultiDomainGptp` contains several gPTP submodules for the different + time domains .. _ug:sec:tsn:streamfiltering: diff --git a/showcases/tsn/timesynchronization/gptp/doc/index.rst b/showcases/tsn/timesynchronization/gptp/doc/index.rst index 5e5d8a29c59..a979ded83a5 100644 --- a/showcases/tsn/timesynchronization/gptp/doc/index.rst +++ b/showcases/tsn/timesynchronization/gptp/doc/index.rst @@ -1,5 +1,5 @@ -Using gPTP -========== +Using gPTP with a static configuration +====================================== Goals ----- diff --git a/showcases/tsn/timesynchronization/gptp_bmca/doc/index.rst b/showcases/tsn/timesynchronization/gptp_bmca/doc/index.rst index 522d8198225..1ae3d0c3242 100644 --- a/showcases/tsn/timesynchronization/gptp_bmca/doc/index.rst +++ b/showcases/tsn/timesynchronization/gptp_bmca/doc/index.rst @@ -1,5 +1,5 @@ -The Best Master Clock Algorithm (BMCA) -====================================== +Using gPTP with the Best Master Clock Algorithm (BMCA) +====================================================== Overview ~~~~~~~~ diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst b/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst index c3a32b146e4..97abfd4e604 100644 --- a/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst @@ -1,5 +1,5 @@ -gPTP Hotstandby -=============== +Using gPTP with HotStandby +========================== Overview ~~~~~~~~ From 504b00af0f75fe63e1fec4d210286591959dea67 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 2 Apr 2025 13:33:19 +0200 Subject: [PATCH 131/138] Update gPTP showcase --- .../timesynchronization/gptp/GptpShowcase.anf | 10 +- .../timesynchronization/gptp/doc/index.rst | 93 +++++++++---------- .../tsn/timesynchronization/gptp/omnetpp.ini | 9 +- .../gptp_bmca/doc/index.rst | 4 +- .../gptp_hotstandby/doc/index.rst | 4 +- 5 files changed, 59 insertions(+), 61 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index d5029291167..82b8fd70d8f 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -1291,17 +1291,17 @@ utils.export_data_if_needed(df, props) - - + + - + - + @@ -1318,7 +1318,7 @@ utils.export_data_if_needed(df, props) - + diff --git a/showcases/tsn/timesynchronization/gptp/doc/index.rst b/showcases/tsn/timesynchronization/gptp/doc/index.rst index a979ded83a5..1c99db46390 100644 --- a/showcases/tsn/timesynchronization/gptp/doc/index.rst +++ b/showcases/tsn/timesynchronization/gptp/doc/index.rst @@ -1,5 +1,5 @@ -Using gPTP with a static configuration -====================================== +gPTP Basics: Static Configuration +================================= Goals ----- @@ -31,18 +31,17 @@ synchronizing the time between the clocks (i.e., before the time difference gets too large). In gPTP, the times kept by a number of slave clocks are synchronized to a master clock's time within a gPTP `time domain`. A network can have multiple gPTP time domains, i.e., nodes can synchronize multiple clocks to -multiple master clocks for redundancy (in case one of the master clocks fails or -goes offline due to link break, for example, nodes can still have synchronized -time). Each time domain contains one master clock, and any number of slave -clocks. The protocol synchronizes the slave clocks to the master clock by +multiple master clocks for redundancy (see the +:doc:`/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index` showcase for more +information). +The protocol synchronizes the slave clocks to the master clock by sending `sync messages` from the master clock nodes to the slave clock nodes. According to the IEEE 802.1 AS standard, the master clock can be automatically -selected by the Best Master Clock algorithm (BCMA). BMCA also determines the -clock spanning tree, i.e., the routes on which sync messages are propagated to -slave clocks in the network. INET currently doesn't support BMCA; the master -clock and the spanning tree needs to be specified manually for each gPTP time -domain. +selected by the Best Master Clock algorithm (BCMA). +In this showcase, however, we focus on the static configuration of gPTP. +The :doc:`/showcases/tsn/timesynchronization/gptp_bmca/doc/index` showcase +demonstrates the automatic selection of the master clock using BCMA. The operation of gPTP is summarized as follows: @@ -61,17 +60,19 @@ The optional :ned:`Gptp` modules can be enabled with the ``*.tsnHost.hasTimeSynchronization = true`` Nodes can be part of multiple gPTP time domains by having multiple :ned:`Gptp` -submodules, one for each domain. +submodules, one for each domain. The following description of this showcase uses a single domain +with static configuration. +For a dynamic configuration and multiple domains, take a look at the +:doc:`/showcases/tsn/timesynchronization/gptp_bmca/doc/index` and +:doc:`/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index` showcases respectively. :ned:`Gptp` modules operate in one of three roles, according to their location in the spanning tree: `master`, `bridge` or `slave`. Nodes containing master gPTP modules contain the master clock for the time domain, create gPTP sync messages, and broadcast them down-tree to bridge and slave nodes. Bridge nodes forward sync messages to bridge and slave nodes as -well. The module type can be selected by the :ned:`Gptp` module's -:par:`gptpNodeType` parameter (either ``MASTER_NODE``, ``BRIDGE_NODE`` or -``SLAVE_NODE``) - -.. note:: A network node might have different gPTP roles in different time domains, if there are several. +well. In case of a static configuration, the module type can be selected by the +:ned:`Gptp` module's :par:`gptpNodeType` parameter +(either ``MASTER_NODE``, ``BRIDGE_NODE`` or ``SLAVE_NODE``) The spanning tree is created by labeling nodes' interfaces (called `ports`) as either a master or a slave, with the :par:`slavePort` and :par:`masterPorts` @@ -93,34 +94,18 @@ offset of sync and peer delay measurement messages can be specified by parameters (:par:`syncInterval`, :par:`pDelayInterval`, :par:`syncInitialOffset`, :par:`pDelayInitialOffset`). -When nodes have multiple gPTP time domains, each time domain has a corresponding -:ned:`Gptp` module. The :ned:`MultiDomainGptp` module makes this convenient, as -it contains multiple :ned:`Gptp` modules. Also, each domain can have a -corresponding clock module. The :ned:`MultiClock` module can be used for this -purpose, as it contains multiple clock submodules. - -One of the sub-clocks in the :ned:`MultiClock` module is designated as the -active clock. Users of the :ned:`MultiClock` module (i.e., other modules that -get the time from the :ned:`MultiClock` module) use the time of the active -clock. The active clock can be changed by a scenario manager script to another -sub-clock in case of failure of time synchronization in one domain, for example. - -.. note:: When using :ned:`MultiClock` and :ned:`MultiDomainGptp`, it is enough to specify the :ned:`MultiClock` module to the :ned:`MultiDomainGptp` as the clock module. The corresponding sub-clock for each domain is selected automatically (i.e., :ned:`Gptp` submodules are paired to clock submodules in :ned:`MultiClock` based on index). - -For more information on the parameters of :ned:`Gptp`, :ned:`MultiDomainGptp`, and :ned:`MultiClock`, check the NED documentation. - The Model --------- -In this showcase, we demonstrate the setup and operation of gPTP in three simulations: +In this showcase, we demonstrate the setup and operation of gPTP in multiple simulations: - **One Master Clock**: Simple setup with one time domain and one master clock. -- **Primary and Hot-Standby Master Clocks**: More complex setup with two time domains for a primary and a hot-standby master clock. If the primary master node goes offline, - the stand-by clock can take over and become the new Master Clock. -- **Two Master Clocks Exploiting Network Redundancy**: A larger network containing a primary and a hot-standby master node, with two time domains each. Time synchronization is protected against the failure of a master node and any link in the network. +- **Multi Hop**: Setup with multiple hops, useful for testing the peer delay measurement mechanism and the effect of link latency on time synchronization. +- **Rate Ratio Studies**: Allows to analyze the effect of the neighbor rate ratio (nRR) and grandmaster rate ratio (gmRR) on the time synchronization process. +- **Parameter Studies**: This usecase can be used to study the effect of the kp and ki parameter of the :ned:`PiServoClock`. For example, when adjusting the sync interval, these parameters need to be adjusted as well. +- **Jumping Clock**: This usecase can be used to study the effect of what happens when the master clock peforms time jumps into the future or the past. -In the ``General`` configuration, we enable :ned:`Gptp` modules in all network nodes, and configure a random clock drift rate for the master clocks, and a constant clock drift rate -for the clocks in slave and bridge nodes (specified with a random distribution for each one): +In the ``General`` configuration, we enable :ned:`Gptp` modules in all network nodes, and configure a random clock drift rate the clocks (specified with a random distribution for each one): .. literalinclude:: ../omnetpp.ini :language: ini @@ -139,15 +124,20 @@ stations (:ned:`TsnDevice`), connected via a :ned:`TsnSwitch`: .. figure:: media/OneMasterClockNetwork.png :align: center -We configure the spanning tree by setting the master ports in ``tsnClock`` and ``tsnSwitch``: +We configure the spanning tree by setting the master ports in ``tsnClock`` and ``tsnSwitch``. +The configuration also sets different clock servos on different clocks for comparison. .. literalinclude:: ../omnetpp.ini :language: ini :start-at: TSN clock gPTP master ports - :end-at: tsnSwitch + :end-at: true .. note:: The slave ports are set to ``eth0`` by default in :ned:`TsnDevice` and :ned:`TsnSwitch`, so they don't have to be set explicitly. +The tsnSwitch uses the :ned:`InstantServoClock` clock, adjusts its time but not its drift rate. +the tsnDevice2 also uses the :ned:`InstantServoClock` clock, but it also adjusts its drift rate. +The tsnDevice1 uses the :ned:`PiServoClock` clock, which only adjusts its drift rate to stay in sync without performing any time jumps. + Here is a video of the synchronization mechanism (the time of the master clock and the difference from this time in the other nodes are displayed): @@ -167,14 +157,21 @@ We examine clock drift of all clocks by plotting the clock time difference again :align: center The master clock drifts according to a random walk process. The times of slave -clocks is periodically synchronized to the master's. At the second time -synchronization event at 0.25s, the drift rates of the slave clocks are -compensated to be more aligned with the master clock's drift rate. +clocks is periodically synchronized to the master's. +From the figure it becomes clear, that all clocks need the first two sync intervals as a startup phase +to stabilize their drift rate. +The figure also shows, that the tsnSwitch deviates much further from the master, as it is not able +to adjust its drift rate. -All such charts have these two large sawtooth patterns at the beginning, before -the drift rate is compensated. From now on, we will generally omit these to -concentrate on the details of the clock drift after it has been stabilized by -time synchronization. +In the following, we omit the startup phase and only concentrate on the synchronization process afterwards. + +.. figure:: media/OneMasterClock_zoomed.png + :align: center + +The figure above provides a zoomed in view and shows how tsnDevice1 and tsnDevice2 synchronize to the master. +In this figure, the difference between the two clock servos become evident. +While tsnDevice1 (using the :ned:`PiServoClock`) adjusts its clock smoothly to the master's time, +tsnDevice2 (using the :ned:`InstantServoClock`) instead performs jumps to synchronize to the master's time. .. note:: A `clock time difference to simulation time` chart can be easily produced by plotting the ``timeChanged:vector`` statistic, and applying a linear trend operation with -1 as argument. diff --git a/showcases/tsn/timesynchronization/gptp/omnetpp.ini b/showcases/tsn/timesynchronization/gptp/omnetpp.ini index 1417af22e08..7a56c0256ff 100644 --- a/showcases/tsn/timesynchronization/gptp/omnetpp.ini +++ b/showcases/tsn/timesynchronization/gptp/omnetpp.ini @@ -45,16 +45,17 @@ abstract = true network = OneMasterClockGptpShowcase description = "Basic tree topology with one master clock" -*.tsnSwitch.clock.typename = "InstantServoClock" -**.clock.adjustDrift = true -*.tsnDevice2.clock.typename = "InstantServoClock" - # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] # TSN switch gPTP bridge master ports *.tsnSwitch.gptp.masterPorts = ["eth1", "eth2"] +*.tsnSwitch.clock.typename = "InstantServoClock" +*.tsnSwitch.clock.adjustDrift = false +*.tsnDevice2.clock.typename = "InstantServoClock" +*.tsnDevice2.clock.adjustDrift = true + # Set all reference clocks to master clock so the time difference can be visualized **.referenceClock = "tsnClock.clock" diff --git a/showcases/tsn/timesynchronization/gptp_bmca/doc/index.rst b/showcases/tsn/timesynchronization/gptp_bmca/doc/index.rst index 1ae3d0c3242..c6568178bc9 100644 --- a/showcases/tsn/timesynchronization/gptp_bmca/doc/index.rst +++ b/showcases/tsn/timesynchronization/gptp_bmca/doc/index.rst @@ -1,5 +1,5 @@ -Using gPTP with the Best Master Clock Algorithm (BMCA) -====================================================== +gPTP: Best Master Clock Algorithm +================================= Overview ~~~~~~~~ diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst b/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst index 97abfd4e604..26f1e8901fc 100644 --- a/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst @@ -1,5 +1,5 @@ -Using gPTP with HotStandby -========================== +MultiDomain gPTP and Hot-Standby +================================ Overview ~~~~~~~~ From 8934d9016a72e7f075e260594a3ae42b8affad3a Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 2 Apr 2025 14:32:14 +0200 Subject: [PATCH 132/138] Update gPTP showcase: Adding Jumping Clock description. --- .../timesynchronization/gptp/GptpShowcase.anf | 870 +++++++++++++----- .../timesynchronization/gptp/doc/index.rst | 36 +- .../tsn/timesynchronization/gptp/omnetpp.ini | 4 +- .../gptp/scenario-jumping-clock.xml | 4 +- 4 files changed, 692 insertions(+), 222 deletions(-) diff --git a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf index 82b8fd70d8f..763193b1cb8 100644 --- a/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf +++ b/showcases/tsn/timesynchronization/gptp/GptpShowcase.anf @@ -596,9 +596,597 @@ utils.export_data_if_needed(df, props) - + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -647,43 +1238,33 @@ utils.export_data_if_needed(df, props) - - - - - - - - - + + + + + + + + + + - - - - - - - + @@ -692,7 +1273,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -705,7 +1286,7 @@ utils.export_data_if_needed(df, props) - + @@ -779,36 +1360,14 @@ utils.export_data_if_needed(df, props) - - - - - - - - - - - - - ]]> - + @@ -931,6 +1490,13 @@ utils.export_data_if_needed(df, props) + + ]]> - - - - - - - - - - - - - - - - - - - - - - - - -]]> - + @@ -1199,7 +1643,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -1219,7 +1663,7 @@ utils.export_data_if_needed(df, props) ]]> - + @@ -1275,56 +1719,46 @@ utils.export_data_if_needed(df, props) ]]> - - + + + - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + - + + - - + - + + + + + + - + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1471,7 +3661,7 @@ utils.export_data_if_needed(df, props) - + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -3487,13 +2027,13 @@ utils.export_data_if_needed(df, props) - - + + - + @@ -4207,7 +2747,7 @@ utils.export_data_if_needed(df, props) - + @@ -4217,13 +2757,13 @@ utils.export_data_if_needed(df, props) - - + + - + @@ -5362,7 +3902,7 @@ utils.export_data_if_needed(df, props) - + diff --git a/src/inet/clock/servo/InstantServoClock.h b/src/inet/clock/servo/InstantServoClock.h index de4a62eb3dd..a643dcb36c4 100644 --- a/src/inet/clock/servo/InstantServoClock.h +++ b/src/inet/clock/servo/InstantServoClock.h @@ -4,9 +4,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // - -#ifndef __INET_SETTABLECLOCK_H -#define __INET_SETTABLECLOCK_H +#ifndef __INET_INSTANTSERVOCLOCK_H +#define __INET_INSTANTSERVOCLOCK_H #include "ServoClockBase.h" @@ -20,10 +19,11 @@ class INET_API InstantServoClock : public ServoClockBase ppm drift = ppm(0); bool adjustClock = true; bool adjustDrift = true; + public: virtual void adjustClockTo(clocktime_t newClockTime) override; virtual void initialize(int stage) override; - virtual void resetClockState() override; + virtual void resetClockState() override; }; } // namespace inet diff --git a/src/inet/linklayer/ieee8021as/GptpEndstation.ned b/src/inet/linklayer/ieee8021as/GptpEndstation.ned index 896864f242c..698ff06b334 100644 --- a/src/inet/linklayer/ieee8021as/GptpEndstation.ned +++ b/src/inet/linklayer/ieee8021as/GptpEndstation.ned @@ -17,7 +17,7 @@ import inet.node.inet.StandardHost; module GptpEndstation extends StandardHost { parameters: - clock.typename = default("SettableClock"); + clock.typename = default("PiServoClock"); submodules: gptp: like IApp if typename != "" { @display("p=700.11755,75.166"); diff --git a/src/inet/node/ethernet/EthernetSwitch.ned b/src/inet/node/ethernet/EthernetSwitch.ned index b357269a1f9..c0a3fb6d754 100644 --- a/src/inet/node/ethernet/EthernetSwitch.ned +++ b/src/inet/node/ethernet/EthernetSwitch.ned @@ -91,7 +91,7 @@ module EthernetSwitch like IEthernetNetworkNode status: NodeStatus if hasStatus { @display("p=100,400;is=s"); } - clock: like IClock if typename != "" { + clock: like IClock if typename != "" { @display("p=100,500;is=s"); } pcapRecorder[numPcapRecorders]: PcapRecorder { From 08ef6e6aa0f69d56533d45e928db39440c4f5d80 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 2 Apr 2025 22:56:07 +0200 Subject: [PATCH 136/138] Cleanup --- .../gptp_bmca/BMCA Asymmetric Diamond.png | Bin 61366 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 showcases/tsn/timesynchronization/gptp_bmca/BMCA Asymmetric Diamond.png diff --git a/showcases/tsn/timesynchronization/gptp_bmca/BMCA Asymmetric Diamond.png b/showcases/tsn/timesynchronization/gptp_bmca/BMCA Asymmetric Diamond.png deleted file mode 100644 index 860cf0210ebce59e724edd0ca3c3095b12710617..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61366 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 AQvd(} From ab360b8a58018eb485b5e07682faa6fef9ee80f3 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Tue, 27 May 2025 00:10:46 +0200 Subject: [PATCH 137/138] Fixed problems after merge --- src/inet/clock/base/ClockBase.cc | 2 -- src/inet/clock/base/ClockBase.h | 3 +-- src/inet/common/clock/ClockUserModuleBase.h | 1 + src/inet/linklayer/ieee8021as/Gptp.cc | 3 --- src/inet/linklayer/ieee8021as/HotStandy.ned | 5 ++++- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/inet/clock/base/ClockBase.cc b/src/inet/clock/base/ClockBase.cc index 845cd7b7df4..ed2d9b670f7 100644 --- a/src/inet/clock/base/ClockBase.cc +++ b/src/inet/clock/base/ClockBase.cc @@ -61,8 +61,6 @@ void ClockBase::emitTimeDifferenceToReference() void ClockBase::receiveSignal(cComponent *source, int signal, const simtime_t& time, cObject *details) { - auto text = StringFormat::formatString(displayStringTextFormat, this); - getDisplayString().setTagArg("t", 0, text.c_str()); if (signal == ClockBase::timeChangedSignal) { emitTimeDifferenceToReference(); } diff --git a/src/inet/clock/base/ClockBase.h b/src/inet/clock/base/ClockBase.h index f070a678355..a6f32a71b90 100644 --- a/src/inet/clock/base/ClockBase.h +++ b/src/inet/clock/base/ClockBase.h @@ -16,7 +16,7 @@ namespace inet { -class INET_API ClockBase : public SimpleModule, public IClock +class INET_API ClockBase : public SimpleModule, public IClock, public cListener { public: struct ClockJumpDetails : public cObject { @@ -32,7 +32,6 @@ class INET_API ClockBase : public SimpleModule, public IClock protected: clocktime_t clockEventTime = -1; - ModuleRefByPar referenceClockModule; simtime_t emitClockTimeInterval; cMessage *timer = nullptr; diff --git a/src/inet/common/clock/ClockUserModuleBase.h b/src/inet/common/clock/ClockUserModuleBase.h index dae11db377f..8459ffbd2f5 100644 --- a/src/inet/common/clock/ClockUserModuleBase.h +++ b/src/inet/common/clock/ClockUserModuleBase.h @@ -9,6 +9,7 @@ #define __INET_CLOCKUSERMODULEBASE_H #include "inet/common/clock/ClockUserModuleMixin.h" +#include "inet/common/SimpleModule.h" namespace inet { diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index d873cd2763b..e1d93d51a6d 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -1310,9 +1310,6 @@ void Gptp::handleParameterChange(const char *name) localPriorityVector.grandmasterPriority2 = par("priority2"); executeBmca(); } - else { - throw cRuntimeError("Parameter %s is not mutable", name); - } } } // namespace inet \ No newline at end of file diff --git a/src/inet/linklayer/ieee8021as/HotStandy.ned b/src/inet/linklayer/ieee8021as/HotStandy.ned index 204b2788da5..9d83f9cb27f 100644 --- a/src/inet/linklayer/ieee8021as/HotStandy.ned +++ b/src/inet/linklayer/ieee8021as/HotStandy.ned @@ -6,6 +6,9 @@ package inet.linklayer.ieee8021as; +import inet.common.SimpleModule; + + // This module is a basic implementation of the IEEE 802.1ASdm HotStandby amendment. // It is part of the MultiDomainGptp module and keeps track of the synchronization state of contained // gPTP modules. @@ -16,7 +19,7 @@ package inet.linklayer.ieee8021as; // Note: This module is not a complete implementation of the HotStandby amendment and only provides // the basic functionality of switching the active clock if one gPTP instance loses sync. // For example, a smooth transition when switching between synchronization instances is not implemented. -simple HotStandby +simple HotStandby extends SimpleModule { parameters: @display("i=block/blackboard"); From 389db4ae319b0b066c501e91c236e96bcad87e53 Mon Sep 17 00:00:00 2001 From: Lucas Haug Date: Wed, 28 May 2025 13:08:00 +0200 Subject: [PATCH 138/138] Added video to showcase --- showcases/tsn/timesynchronization/clockdrift/omnetpp.ini | 2 +- showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst | 1 + src/inet/linklayer/ieee8021as/Gptp.cc | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/showcases/tsn/timesynchronization/clockdrift/omnetpp.ini b/showcases/tsn/timesynchronization/clockdrift/omnetpp.ini index 6176df80903..82373350cfa 100644 --- a/showcases/tsn/timesynchronization/clockdrift/omnetpp.ini +++ b/showcases/tsn/timesynchronization/clockdrift/omnetpp.ini @@ -1,7 +1,7 @@ [General] network = ClockDriftShowcase sim-time-limit = 0.1s -#abstract-config = true (requires omnet 7) +abstract = true # disable legacy Ethernet *.*.ethernet.typename = "EthernetLayer" diff --git a/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst b/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst index 060595b3f6b..8ff9ab140ff 100644 --- a/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst +++ b/showcases/tsn/timesynchronization/gptp_hotstandby/doc/index.rst @@ -119,6 +119,7 @@ are visualized as arrows. The visualization is color-coded according to domain: .. video_noloop:: media/PrimaryAndHotStandbyMasterClocks.mp4 :align: center + :width: 100% First, the bridge and slave nodes measure link delay by exchanging pDelay messages. Then, the master clocks send gPTP sync messages. Note that there is a diff --git a/src/inet/linklayer/ieee8021as/Gptp.cc b/src/inet/linklayer/ieee8021as/Gptp.cc index e1d93d51a6d..7e65afe8951 100644 --- a/src/inet/linklayer/ieee8021as/Gptp.cc +++ b/src/inet/linklayer/ieee8021as/Gptp.cc @@ -237,7 +237,6 @@ void Gptp::scheduleMessageOnTopologyChange() void Gptp::initBmca() { - // TODO: harcoded for now localPriorityVector.grandmasterPriority1 = par("grandmasterPriority1"); localPriorityVector.grandmasterClockQuality.clockClass = par("clockClass"); localPriorityVector.grandmasterClockQuality.clockAccuracy = par("clockAccuracy");