A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
end-device-lorawan-mac.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2017 University of Padova
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Davide Magrin <magrinda@dei.unipd.it>
7 * Martina Capuzzo <capuzzom@dei.unipd.it>
8 *
9 * Modified by: Peggy Anderson <peggy.anderson@usask.ca>
10 */
11
13
15#include "end-device-lora-phy.h"
16
17#include "ns3/energy-source-container.h"
18#include "ns3/log.h"
19#include "ns3/simulator.h"
20
21#include <bitset>
22
23namespace ns3
24{
25namespace lorawan
26{
27
28NS_LOG_COMPONENT_DEFINE("EndDeviceLorawanMac");
29
31
32TypeId
34{
35 static TypeId tid =
36 TypeId("ns3::EndDeviceLorawanMac")
38 .SetGroupName("lorawan")
39 .AddTraceSource("RequiredTransmissions",
40 "Total number of transmissions required to deliver this packet",
42 "ns3::TracedValueCallback::uint8_t")
43 .AddAttribute("DataRate",
44 "Data rate currently employed by this end device",
48 .AddTraceSource("DataRate",
49 "Data rate currently employed by this end device",
51 "ns3::TracedValueCallback::uint8_t")
52 .AddAttribute(
53 "ADR",
54 "Ensure to the network server that this device will accept data rate, transmission "
55 "power and number of retransmissions configurations received via LinkADRReq. This "
56 "also allows the device's local ADR backoff procedure to reset configurations in "
57 "case of connectivity loss.",
58 BooleanValue(true),
61 .AddTraceSource("TxPower",
62 "Transmission ERP [dBm] currently employed by this end device",
64 "ns3::TracedValueCallback::Double")
65 .AddTraceSource("LastKnownLinkMargin",
66 "Last known demodulation margin in "
67 "communications between this end device "
68 "and a gateway",
70 "ns3::TracedValueCallback::uint8_t")
71 .AddTraceSource("LastKnownGatewayCount",
72 "Last known number of gateways able to "
73 "listen to this end device",
75 "ns3::TracedValueCallback::uint8_t")
76 .AddTraceSource("AggregatedDutyCycle",
77 "Aggregate duty cycle, in fraction form, "
78 "this end device must respect",
80 "ns3::TracedValueCallback::Double")
81 .AddAttribute("MaxTransmissions",
82 "Maximum number of transmissions for a packet (NbTrans)",
83 IntegerValue(1),
86 .AddAttribute("MType",
87 "Specify type of message will be sent by this end device.",
91 "Unconfirmed",
93 "Confirmed"));
94 return tid;
95}
96
98 : m_nbTrans(1),
99 m_dataRate(0),
100 m_txPowerDbm(14),
101 m_codingRate(1),
102 // LoraWAN default
103 m_headerDisabled(false),
104 // LoraWAN default
106 // LoraWAN default
108 // Max initial value
109 m_lastRxSnr(32),
110 m_adrAckCnt(0),
111 m_adr(true),
115 m_mType(LorawanMacHeader::CONFIRMED_DATA_UP),
116 m_currentFCnt(0),
117 m_adrAckReq(false)
118{
119 NS_LOG_FUNCTION(this);
120
121 // Initialize the random variable we'll use to decide which channel to
122 // transmit on.
124
125 // Void the transmission event
126 m_nextTx = EventId();
127 m_nextTx.Cancel();
128
129 // Initialize structure for retransmission parameters
131 m_retxParams.retxLeft = m_nbTrans;
132}
133
138
139////////////////////////
140// Sending methods //
141////////////////////////
142
143void
145{
146 NS_LOG_FUNCTION(this << packet);
147
148 // Retx are scheduled by Receive, FailedReception, CloseSecondReceiveWindow only if retxLeft > 0
149 NS_ASSERT_MSG(packet != m_retxParams.packet || m_retxParams.retxLeft > 0,
150 "Max number of transmissions already achieved for this packet");
151
152 if (packet == m_retxParams.packet)
153 {
154 NS_LOG_DEBUG("Retransmitting an old packet.");
155 // Fail if it is a retransmission already ACKed
156 NS_ASSERT_MSG(m_retxParams.waitingAck, "Trying to retransmit a packet already ACKed.");
157 // Remove the headers
158 LorawanMacHeader macHdr;
159 packet->RemoveHeader(macHdr);
160 LoraFrameHeader frameHdr;
161 packet->RemoveHeader(frameHdr);
162 }
163 else // this is a new packet
164 {
165 NS_LOG_DEBUG("New FRMPayload from application: " << packet);
166 // If needed, trace failed ACKnowledgement of previous packet
167 if (m_retxParams.waitingAck)
168 {
169 uint8_t txs = m_nbTrans - m_retxParams.retxLeft;
170 NS_LOG_WARN("Stopping retransmission procedure of previous packet. Used "
171 << unsigned(txs) << " transmissions out of " << unsigned(m_nbTrans));
172 m_requiredTxCallback(txs, false, m_retxParams.firstAttempt, m_retxParams.packet);
173 }
174 }
175
176 // Evaluate ADR backoff as in LoRaWAN specification, V1.0.4 (2020)
177 // Adapted from: github.com/Lora-net/SWL2001.git v4.8.0
178 m_adrAckReq = (m_adrAckCnt >= ADR_ACK_LIMIT); // Set the ADRACKReq bit in frame header
180 {
181 // Unreachable by retx: they do not increase ADRACKCnt
184 }
185 NS_ASSERT(m_adrAckCnt < 2400);
186
187 // This check is influenced by ADR backoff. This is OK because (by LoRaWAN design) you either
188 // use ADR and constrain your max app payload according to the default initial DR0, or you
189 // disable ADR for a fixed data rate, with the possibility of using bigger payloads.
190 if (!IsPayloadSizeValid(packet->GetSize(), m_dataRate))
191 {
192 NS_LOG_ERROR("Application payload exceeding maximum size. Transmission aborted.");
193 return;
194 }
195
196 // Check if there is a channel suitable for TX (checks data rate & tx power etc.)
197 if (GetCompatibleTxChannels().empty())
198 {
199 NS_LOG_ERROR("No tx channel compatible with current DR/power. Transmission aborted.");
200 return;
201 }
202
203 // If it is not possible to transmit now because of the duty cycle
204 // or because we are currently in the process of receiving, schedule a tx/retx later
205 if (auto netxTxDelay = GetNextTransmissionDelay(); netxTxDelay.IsStrictlyPositive())
206 {
207 PostponeTransmission(netxTxDelay, packet);
209 return;
210 }
211
212 ///////////////////////////////////////////////////////
213 // From here on out, the pkt transmission is assured //
214 ///////////////////////////////////////////////////////
215
216 DoSend(packet);
217}
218
219void
221{
222 NS_LOG_FUNCTION(this);
223 // Delete previously scheduled transmissions if any.
225 m_nextTx = Simulator::Schedule(netxTxDelay, &EndDeviceLorawanMac::DoSend, this, packet);
226 NS_LOG_WARN("Attempting to send, but the aggregate duty cycle won't allow it. Scheduling a tx "
227 "at a delay "
228 << netxTxDelay.As(Time::S) << ".");
229}
230
231void
233{
234 NS_LOG_FUNCTION(this);
235
236 // Add the Lora Frame Header to the packet
237 LoraFrameHeader frameHdr;
238 ApplyNecessaryOptions(frameHdr);
239 packet->AddHeader(frameHdr);
240 NS_LOG_INFO("Added frame header of size " << frameHdr.GetSerializedSize() << " bytes.");
241 // Add the Lora Mac header to the packet
242 LorawanMacHeader macHdr;
243 ApplyNecessaryOptions(macHdr);
244 packet->AddHeader(macHdr);
245 NS_LOG_INFO("Added MAC header of size " << macHdr.GetSerializedSize() << " bytes.");
246
247 if (packet != m_retxParams.packet)
248 {
249 NS_LOG_DEBUG("Resetting retransmission parameters.");
250 // Reset MAC command list
251 /// TODO: Some commands should only be removed on ACK
252 m_macCommandList.clear();
253 // Reset retransmission parameters
255 // Save parameters for the (possible) next retransmissions.
256 m_retxParams.packet = packet->Copy();
257 m_retxParams.firstAttempt = Now();
259 NS_LOG_DEBUG("Message type is " << m_mType);
260 }
261
262 // Send packet
263 SendToPhy(packet);
264 // Decrease the number of transmissions left
265 m_retxParams.retxLeft--;
266 if (packet != m_retxParams.packet)
267 {
268 m_sentNewPacket(packet); // Fire trace source
269 // Bump-up frame counters
271 m_adrAckCnt++;
272 }
273}
274
275void
279
280void
282{
283 NS_LOG_FUNCTION(this);
284
285 // Adapted from: github.com/Lora-net/SWL2001.git v4.8.0
286 // For the time being, this implementation is valid for the EU868 region
287
288 if (!m_adr)
289 {
290 return;
291 }
292
293 if (m_txPowerDbm < 14)
294 {
295 m_txPowerDbm = 14; // Reset transmission power to default
296 return;
297 }
298
299 if (m_dataRate != 0)
300 {
301 m_dataRate--;
302 return;
303 }
304
305 // Set nbTrans to 1 and re-enable default channels
306 m_nbTrans = 1;
307 auto channels = m_channelHelper->GetRawChannelArray();
308 channels.at(0)->EnableForUplink();
309 channels.at(1)->EnableForUplink();
310 channels.at(2)->EnableForUplink();
311}
312
313bool
314EndDeviceLorawanMac::IsPayloadSizeValid(uint32_t appPayloadSize, uint8_t dataRate)
315{
316 uint32_t fOptsLen = 0;
317 for (const auto& c : m_macCommandList)
318 {
319 fOptsLen += c->GetSerializedSize();
320 }
321 /// TODO: FPort could be absent
322 NS_LOG_LOGIC("FHDR(7+FOpts(" << fOptsLen << "))+FPort(1)+FRMPayload(" << appPayloadSize
323 << ")=" << 7 + fOptsLen + 1 + appPayloadSize
324 << "B, max MACPayload=" << m_maxMacPayloadForDataRate.at(dataRate)
325 << "B on DR" << unsigned(dataRate));
326 return 7 + fOptsLen + 1 + appPayloadSize <= m_maxMacPayloadForDataRate.at(dataRate);
327}
328
329//////////////////////////
330// Receiving methods //
331//////////////////////////
332
333void
337
338void
342
343void
345{
346 NS_LOG_FUNCTION(this << frameHeader);
347
348 if (m_retxParams.waitingAck)
349 {
350 if (frameHeader.GetAck())
351 {
352 NS_LOG_INFO("The message is an ACK, not waiting for it anymore.");
353
354 NS_LOG_DEBUG("Reset retransmission variables to default values and cancel "
355 "retransmission if already scheduled.");
356
357 uint8_t txs = m_nbTrans - (m_retxParams.retxLeft);
358 m_requiredTxCallback(txs, true, m_retxParams.firstAttempt, m_retxParams.packet);
359 NS_LOG_DEBUG("Received ACK packet after "
360 << unsigned(txs) << " transmissions: stopping retransmission procedure. ");
361
362 // Reset retransmission parameters
364 }
365 else
366 {
368 "Received downlink message not containing an ACK while we were waiting for it!");
369 }
370 }
371
372 for (const auto& c : frameHeader.GetCommands())
373 {
374 NS_LOG_DEBUG("Iterating over the MAC commands...");
375 enum MacCommandType type = (c)->GetCommandType();
376 switch (type)
377 {
378 case (LINK_CHECK_ANS): {
379 NS_LOG_DEBUG("Detected a LinkCheckAns command.");
380 auto linkCheckAns = DynamicCast<LinkCheckAns>(c);
381 OnLinkCheckAns(linkCheckAns->GetMargin(), linkCheckAns->GetGwCnt());
382 break;
383 }
384 case (LINK_ADR_REQ): {
385 NS_LOG_DEBUG("Detected a LinkAdrReq command.");
386 auto linkAdrReq = DynamicCast<LinkAdrReq>(c);
387 OnLinkAdrReq(linkAdrReq->GetDataRate(),
388 linkAdrReq->GetTxPower(),
389 linkAdrReq->GetChMask(),
390 linkAdrReq->GetChMaskCntl(),
391 linkAdrReq->GetNbTrans());
392 break;
393 }
394 case (DUTY_CYCLE_REQ): {
395 NS_LOG_DEBUG("Detected a DutyCycleReq command.");
396 auto dutyCycleReq = DynamicCast<DutyCycleReq>(c);
397 OnDutyCycleReq(dutyCycleReq->GetMaxDutyCycle());
398 break;
399 }
400 case (RX_PARAM_SETUP_REQ): {
401 NS_LOG_DEBUG("Detected a RxParamSetupReq command.");
402 auto rxParamSetupReq = DynamicCast<RxParamSetupReq>(c);
403 OnRxParamSetupReq(rxParamSetupReq->GetRx1DrOffset(),
404 rxParamSetupReq->GetRx2DataRate(),
405 rxParamSetupReq->GetFrequency());
406 break;
407 }
408 case (DEV_STATUS_REQ): {
409 NS_LOG_DEBUG("Detected a DevStatusReq command.");
410 auto devStatusReq = DynamicCast<DevStatusReq>(c);
412 break;
413 }
414 case (NEW_CHANNEL_REQ): {
415 NS_LOG_DEBUG("Detected a NewChannelReq command.");
416 auto newChannelReq = DynamicCast<NewChannelReq>(c);
417 OnNewChannelReq(newChannelReq->GetChannelIndex(),
418 newChannelReq->GetFrequency(),
419 newChannelReq->GetMinDataRate(),
420 newChannelReq->GetMaxDataRate());
421 break;
422 }
423 case (RX_TIMING_SETUP_REQ):
424 case (TX_PARAM_SETUP_REQ):
425 case (DL_CHANNEL_REQ):
426 default: {
427 NS_LOG_ERROR("CID not recognized or supported");
428 break;
429 }
430 }
431 }
432}
433
434void
436{
438
439 frameHeader.SetAsUplink();
440 frameHeader.SetFPort(1); // TODO Use an appropriate frame port based on the application
441 frameHeader.SetAddress(m_address);
442 frameHeader.SetAdr(m_adr);
443 frameHeader.SetAdrAckReq(m_adrAckReq);
444
445 // FPending does not exist in uplink messages
446 frameHeader.SetFCnt(m_currentFCnt);
447
448 // Add listed MAC commands
449 for (const auto& command : m_macCommandList)
450 {
451 NS_LOG_INFO("Applying a MAC Command of CID "
452 << unsigned(MacCommand::GetCIDFromMacCommand(command->GetCommandType())));
453
454 frameHeader.AddCommand(command);
455 }
456}
457
458void
460{
462
463 macHeader.SetMType(m_mType);
464 macHeader.SetMajor(1);
465}
466
467void
469{
470 m_mType = mType;
471 NS_LOG_DEBUG("Message type is set to " << mType);
472}
473
479
480void
484
485Time
491
492std::vector<Ptr<LogicalLoraChannel>>
494{
495 NS_LOG_FUNCTION(this);
496 /// @todo possibly move to LogicalChannelHelper
497 std::vector<Ptr<LogicalLoraChannel>> candidates;
498 for (const auto& channel : m_channelHelper->GetRawChannelArray())
499 {
500 if (channel && channel->IsEnabledForUplink()) // Skip empty frequency channel slots
501 {
502 uint8_t minDr = channel->GetMinimumDataRate();
503 uint8_t maxDr = channel->GetMaximumDataRate();
504 double maxTxPower = m_channelHelper->GetTxPowerForChannel(channel);
505 NS_LOG_DEBUG("Enabled channel: frequency=" << channel->GetFrequency()
506 << "Hz, minDr=" << unsigned(minDr)
507 << ", maxDr=" << unsigned(maxDr)
508 << ", maxTxPower=" << maxTxPower << "dBm");
509 if (m_dataRate >= minDr && m_dataRate <= maxDr && m_txPowerDbm <= maxTxPower)
510 {
511 candidates.emplace_back(channel);
512 }
513 }
514 }
515 return candidates;
516}
517
518Time
520{
521 NS_LOG_FUNCTION(this);
522 // Check duty cycle of compatible channels
523 auto waitTime = Time::Max();
524 for (const auto& channel : GetCompatibleTxChannels())
525 {
526 auto curr = m_channelHelper->GetWaitTime(channel);
527 NS_LOG_DEBUG("frequency=" << channel->GetFrequency() << "Hz,"
528 << " waitTime=" << curr.As(Time::S));
529 if (curr < waitTime)
530 {
531 waitTime = curr;
532 }
533 }
534 return GetNextClassTransmissionDelay(waitTime);
535}
536
539{
540 NS_LOG_FUNCTION(this);
541 /// @todo possibly move to LogicalChannelHelper
542 std::vector<Ptr<LogicalLoraChannel>> candidates;
543 for (const auto& channel : GetCompatibleTxChannels())
544 {
545 if (m_channelHelper->GetWaitTime(channel).IsZero())
546 {
547 candidates.emplace_back(channel);
548 }
549 }
550 if (candidates.empty())
551 {
552 NS_LOG_DEBUG("No suitable TX channel found");
553 return nullptr;
554 }
555 uint8_t i = m_uniformRV->GetInteger(0, candidates.size() - 1);
556 auto channel = candidates.at(i);
557 NS_LOG_DEBUG("Selected channel with frequency=" << channel->GetFrequency() << "Hz");
558 return channel;
559}
560
561/////////////////////////
562// Setters and Getters //
563/////////////////////////
564
565void
567{
568 m_retxParams.waitingAck = false;
569 m_retxParams.retxLeft = m_nbTrans;
570 m_retxParams.packet = nullptr;
571 m_retxParams.firstAttempt = Time(0);
572
573 // Cancel next retransmissions, if any
575}
576
577void
579{
580 NS_LOG_FUNCTION(this << adr);
581 m_adr = adr;
582}
583
584bool
586{
587 NS_LOG_FUNCTION(this);
588 return m_adr;
589}
590
591void
593{
594 NS_LOG_FUNCTION(this << unsigned(nbTrans));
595 m_nbTrans = nbTrans;
596 m_retxParams.retxLeft = nbTrans;
597}
598
599uint8_t
605
606void
608{
609 NS_LOG_FUNCTION(this << unsigned(dataRate));
610
611 m_dataRate = dataRate;
612}
613
614uint8_t
616{
617 NS_LOG_FUNCTION(this);
618
619 return m_dataRate;
620}
621
622void
624{
625 NS_LOG_FUNCTION(this << txPowerDbm);
626 m_txPowerDbm = txPowerDbm;
627}
628
629double
635
636void
638{
639 NS_LOG_FUNCTION(this << address);
640
641 m_address = address;
642}
643
651
652void
653EndDeviceLorawanMac::OnLinkCheckAns(uint8_t margin, uint8_t gwCnt)
654{
655 NS_LOG_FUNCTION(this << unsigned(margin) << unsigned(gwCnt));
656
659}
660
661void
663 uint8_t txPower,
664 uint16_t chMask,
665 uint8_t chMaskCntl,
666 uint8_t nbTrans)
667{
668 NS_LOG_FUNCTION(this << unsigned(dataRate) << unsigned(txPower) << std::bitset<16>(chMask)
669 << unsigned(chMaskCntl) << unsigned(nbTrans));
670
671 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
672 // For the time being, this implementation is valid for the EU868 region
673
674 NS_ASSERT_MSG(!(dataRate & 0xF0), "dataRate field > 4 bits");
675 NS_ASSERT_MSG(!(txPower & 0xF0), "txPower field > 4 bits");
676 NS_ASSERT_MSG(!(chMaskCntl & 0xF8), "chMaskCntl field > 3 bits");
677 NS_ASSERT_MSG(!(nbTrans & 0xF0), "nbTrans field > 4 bits");
678
679 auto channels = m_channelHelper->GetRawChannelArray();
680
681 bool channelMaskAck = true;
682 bool dataRateAck = true;
683 bool powerAck = true;
684
685 NS_LOG_DEBUG("Channel mask = " << std::bitset<16>(chMask)
686 << ", ChMaskCtrl = " << unsigned(chMaskCntl));
687
688 // Check channel mask
689 switch (chMaskCntl)
690 {
691 // Channels 0 to 15
692 case 0:
693 // Check if all enabled channels have a valid frequency
694 for (size_t i = 0; i < channels.size(); ++i)
695 {
696 if ((chMask & 0b1 << i) && !channels.at(i))
697 {
698 NS_LOG_WARN("Invalid channel mask");
699 channelMaskAck = false;
700 break; // break for loop
701 }
702 }
703 break;
704 // All channels ON independently of the ChMask field value
705 case 6:
706 chMask = 0b0;
707 for (size_t i = 0; i < channels.size(); ++i)
708 {
709 if (channels.at(i))
710 {
711 chMask |= 0b1 << i;
712 }
713 }
714 break;
715 default:
716 NS_LOG_WARN("Invalid channel mask ctrl field");
717 channelMaskAck = false;
718 break;
719 }
720
721 // check if all channels are disabled
722 if (chMask == 0)
723 {
724 NS_LOG_WARN("Invalid channel mask");
725 channelMaskAck = false;
726 }
727
728 // Temporary channel mask is built and validated
729 if (!m_adr) // ADR disabled, only consider channel mask conf.
730 {
731 /// @remark Original code considers this to be mobile-mode
732 if (channelMaskAck) // valid channel mask
733 {
734 bool compatible = false;
735 // Look for enabled channel that supports current data rate.
736 for (size_t i = 0; i < channels.size(); ++i)
737 {
738 if ((chMask & 0b1 << i) && m_dataRate >= channels.at(i)->GetMinimumDataRate() &&
739 m_dataRate <= channels.at(i)->GetMaximumDataRate())
740 { // Found compatible channel, break loop
741 compatible = true;
742 break;
743 }
744 }
745 if (!compatible)
746 {
747 NS_LOG_WARN("Invalid channel mask for current device data rate (ADR off)");
748 channelMaskAck = dataRateAck = powerAck = false; // reject all configurations
749 }
750 else // apply channel mask configuration
751 {
752 for (size_t i = 0; i < channels.size(); ++i)
753 {
754 if (auto c = channels.at(i); c)
755 {
756 (chMask & 0b1 << i) ? c->EnableForUplink() : c->DisableForUplink();
757 }
758 }
759 dataRateAck = powerAck = false; // only ack channel mask
760 }
761 }
762 else // reject
763 {
764 NS_LOG_WARN("Invalid channel mask");
765 dataRateAck = powerAck = false; // reject all configurations
766 }
767 }
768 else // Server-side ADR is enabled
769 {
770 if (dataRate != 0xF) // If value is 0xF, ignore config.
771 {
772 bool compatible = false;
773 // Look for enabled channel that supports config. data rate.
774 for (size_t i = 0; i < channels.size(); ++i)
775 {
776 if (chMask & 0b1 << i) // all enabled by chMask, even if it was invalid
777 {
778 if (const auto& c = channels.at(i); c) // exists
779 {
780 if (dataRate >= c->GetMinimumDataRate() &&
781 dataRate <= c->GetMaximumDataRate())
782 { // Found compatible channel, break loop
783 compatible = true;
784 break;
785 }
786 }
787 else // manages invalid case, checks with defaults
788 {
789 if (GetSfFromDataRate(dataRate) && GetBandwidthFromDataRate(dataRate))
790 { // Found compatible (invalid) channel, break loop
791 compatible = true;
792 break;
793 }
794 }
795 }
796 }
797 // Check if it is acceptable
798 if (!compatible)
799 {
800 NS_LOG_WARN("Invalid data rate");
801 dataRateAck = false;
802 }
803 }
804
805 if (txPower != 0xF) // If value is 0xF, ignore config.
806 {
807 // Check if it is acceptable
808 if (GetDbmForTxPower(txPower) < 0)
809 {
810 NS_LOG_WARN("Invalid tx power");
811 powerAck = false;
812 }
813 }
814
815 // If no error, apply configurations
816 if (channelMaskAck && dataRateAck && powerAck)
817 {
818 for (size_t i = 0; i < channels.size(); ++i)
819 {
820 if (auto c = channels.at(i); c)
821 {
822 (chMask & 0b1 << i) ? c->EnableForUplink() : c->DisableForUplink();
823 }
824 }
825 if (txPower != 0xF) // If value is 0xF, ignore config.
826 {
828 }
829 m_nbTrans = (nbTrans == 0) ? 1 : nbTrans;
830 if (dataRate != 0xF) // If value is 0xF, ignore config.
831 {
832 m_dataRate = dataRate;
833 }
834 NS_LOG_DEBUG("MacTxDataRateAdr = " << unsigned(m_dataRate));
835 NS_LOG_DEBUG("MacTxPower = " << unsigned(m_txPowerDbm) << "dBm");
836 NS_LOG_DEBUG("MacNbTrans = " << unsigned(m_nbTrans));
837 }
838 }
839
840 NS_LOG_INFO("Adding LinkAdrAns reply");
841 m_macCommandList.emplace_back(Create<LinkAdrAns>(powerAck, dataRateAck, channelMaskAck));
842}
843
844void
846{
847 NS_LOG_FUNCTION(this << unsigned(maxDutyCycle));
848 NS_ASSERT_MSG(!(maxDutyCycle & 0xF0), "maxDutyCycle > 4 bits");
849 m_aggregatedDutyCycle = 1 / std::pow(2, maxDutyCycle);
850 NS_LOG_INFO("Adding DutyCycleAns reply");
852}
853
854void
856{
857 NS_LOG_FUNCTION(this);
858
859 uint8_t battery = 255; // could not measure
860 if (m_device && m_device->GetNode())
861 {
862 if (auto sc = m_device->GetNode()->GetObject<energy::EnergySourceContainer>();
863 sc && sc->GetN() == 1)
864 {
865 battery = sc->Get(0)->GetEnergyFraction() * 253 + 1.5; // range 1-254
866 }
867 }
868 else
869 {
870 battery = 0; // external power source
871 }
872
873 // approximate to nearest integer
874 double snr = round(m_lastRxSnr);
875 // clamp value to boundaries
876 snr = snr < -32 ? -32 : snr > 31 ? 31 : snr;
877 // cast to 6-bit signed int and store in uint8_t
878 uint8_t margin = std::bitset<6>(snr).to_ulong();
879
880 NS_LOG_INFO("Adding DevStatusAns reply");
881 m_macCommandList.emplace_back(Create<DevStatusAns>(battery, margin));
882}
883
884void
886 uint32_t frequencyHz,
887 uint8_t minDataRate,
888 uint8_t maxDataRate)
889{
890 NS_LOG_FUNCTION(this << unsigned(chIndex) << frequencyHz << unsigned(minDataRate)
891 << unsigned(maxDataRate));
892
893 NS_ASSERT_MSG(!(minDataRate & 0xF0), "minDataRate field > 4 bits");
894 NS_ASSERT_MSG(!(maxDataRate & 0xF0), "maxDataRate field > 4 bits");
895
896 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
897 // For the time being, this implementation is valid for the EU868 region
898
899 bool dataRateRangeOk = true;
900 bool channelFrequencyOk = true;
901
902 // Valid Channel Index
903 if (chIndex < 3 || chIndex > m_channelHelper->GetRawChannelArray().size() - 1)
904 {
905 NS_LOG_WARN("[WARNING] Invalid channel index");
906 dataRateRangeOk = channelFrequencyOk = false;
907 }
908
909 // Valid Frequency
910 if (frequencyHz != 0 && !m_channelHelper->IsFrequencyValid(frequencyHz))
911 {
912 NS_LOG_WARN("[WARNING] Invalid frequency");
913 channelFrequencyOk = false;
914 }
915
916 // Valid DRMIN/MAX
917 if (!GetSfFromDataRate(minDataRate) || !GetBandwidthFromDataRate(minDataRate))
918 {
919 NS_LOG_WARN("[WARNING] Invalid DR min");
920 dataRateRangeOk = false;
921 }
922
923 if (!GetSfFromDataRate(maxDataRate) || !GetBandwidthFromDataRate(maxDataRate))
924 {
925 NS_LOG_WARN("[WARNING] Invalid DR max");
926 dataRateRangeOk = false;
927 }
928
929 if (maxDataRate < minDataRate)
930 {
931 NS_LOG_WARN("[WARNING] Invalid DR max < DR min");
932 dataRateRangeOk = false;
933 }
934
935 if (dataRateRangeOk && channelFrequencyOk)
936 {
937 auto channel = Create<LogicalLoraChannel>(frequencyHz, minDataRate, maxDataRate);
938 (frequencyHz == 0) ? channel->DisableForUplink() : channel->EnableForUplink();
939 m_channelHelper->SetChannel(chIndex, channel);
940 NS_LOG_DEBUG("MacTxFrequency[" << unsigned(chIndex) << "]=" << frequencyHz
941 << ", DrMin=" << unsigned(minDataRate)
942 << ", DrMax=" << unsigned(maxDataRate));
943 }
944
945 NS_LOG_INFO("Adding NewChannelAns reply");
946 m_macCommandList.emplace_back(Create<NewChannelAns>(dataRateRangeOk, channelFrequencyOk));
947}
948
949uint8_t
954
955uint8_t
960
961double
968
969void
971{
972 NS_LOG_FUNCTION(this << macCommand);
973
974 m_macCommandList.push_back(macCommand);
975}
976
977} // namespace lorawan
978} // namespace ns3
Hold variables of type enum.
Definition enum.h:52
An identifier for simulation events.
Definition event-id.h:44
Hold a signed integer type.
Definition integer.h:34
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:67
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:561
static void Cancel(const EventId &id)
Set the cancel bit on this event: the event's associated function will not be invoked when it expires...
Definition simulator.cc:274
Simulation virtual time values and global simulation resolution.
Definition nstime.h:96
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:409
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:342
@ S
second
Definition nstime.h:107
static Time Max()
Maximum representable Time Not to be confused with Max(Time,Time).
Definition nstime.h:288
a unique identifier for an interface.
Definition type-id.h:49
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
Holds a vector of ns3::EnergySource pointers.
uint32_t GetN() const
Get the number of Ptr<EnergySource> stored in this container.
Class representing the MAC layer of a LoRaWAN device.
LorawanMacHeader::MType m_mType
The message type to apply to packets sent with the Send method.
Ptr< LogicalLoraChannel > GetRandomChannelForTx()
Find a suitable channel for transmission.
virtual void ResetRetransmissionParameters()
Reset retransmission parameters contained in the structure LoraRetxParams.
void FailedReception(Ptr< const Packet > packet) override
Function called by lower layers to inform this layer that reception of a packet we were locked on fai...
bool m_headerDisabled
Whether or not the LoRa PHY header is disabled for communications by this device.
void OnLinkAdrReq(uint8_t dataRate, uint8_t txPower, uint16_t chMask, uint8_t chMaskCntl, uint8_t nbTrans)
Perform the actions that need to be taken when receiving a LinkAdrReq command.
void OnDutyCycleReq(uint8_t maxDutyCycle)
Perform the actions that need to be taken when receiving a DutyCycleReq command.
TracedValue< double > m_txPowerDbm
The transmission ERP [dBm] this device is currently using.
virtual void PostponeTransmission(Time nextTxDelay, Ptr< Packet > packet)
Postpone transmission to the specified time and delete previously scheduled transmissions if present.
virtual void SendToPhy(Ptr< Packet > packet)
Add headers and send a packet with the sending function of the physical layer.
double GetTransmissionPowerDbm()
Get the transmission power this end device is set to use.
uint8_t m_codingRate
The coding rate used by this device.
bool m_adr
Uplink ADR bit contained in the FCtrl field of the LoRaWAN FHDR.
void TxFinished(Ptr< const Packet > packet) override
Perform actions after sending a packet.
void SetUplinkAdrBit(bool adr)
Signals to the network server that this device will or may not comply with LinkADRReq settings (data ...
bool m_adrAckReq
ADRACKReq bit, set to 1 after ADR_ACK_LIMIT consecutive uplinks without downlink messages received fr...
Ptr< UniformRandomVariable > m_uniformRV
An uniform random variable, used to randomly pick from the channel list.
static constexpr uint16_t ADR_ACK_DELAY
ADRACKCnt threshold for ADR backoff action.
static TypeId GetTypeId()
Register this type.
uint8_t GetLastKnownLinkMarginDb() const
Get the last known link margin from the demodulation floor.
TracedValue< double > m_aggregatedDutyCycle
The aggregated duty cycle this device needs to respect across all sub-bands.
double GetAggregatedDutyCycle()
Get the aggregated duty cycle.
void Receive(Ptr< const Packet > packet) override
Receive a packet from the lower layer.
void SetMType(LorawanMacHeader::MType mType)
Set the message type to send when the Send method is called.
uint16_t m_adrAckCnt
ADRACKCnt counter of the number of consecutive uplinks without downlink reply from the server.
std::vector< Ptr< LogicalLoraChannel > > GetCompatibleTxChannels()
Get the set of active transmission channels compatible with the current device data rate and transmis...
void ParseCommands(LoraFrameHeader frameHeader)
Parse and take action on the commands contained on this FrameHeader.
bool IsPayloadSizeValid(uint32_t appPayloadSize, uint8_t dataRate)
Check whether the size of the application payload is under the maximum allowed.
virtual void OnRxParamSetupReq(uint8_t rx1DrOffset, uint8_t rx2DataRate, double frequencyHz)=0
Perform the actions that need to be taken when receiving a RxParamSetupReq command based on the Devic...
void OnNewChannelReq(uint8_t chIndex, uint32_t frequencyHz, uint8_t minDataRate, uint8_t maxDataRate)
Perform the actions that need to be taken when receiving a NewChannelReq command.
void ApplyNecessaryOptions(LoraFrameHeader &frameHeader)
Add the necessary options and MAC commands to the LoraFrameHeader.
TracedCallback< uint8_t, bool, Time, Ptr< Packet > > m_requiredTxCallback
The trace source fired when the transmission procedure is finished.
uint8_t GetMaxNumberOfTransmissions()
Get the max number of unacknowledged redundant transmissions of each packet.
void OnLinkCheckAns(uint8_t margin, uint8_t gwCnt)
Perform the actions that need to be taken when receiving a LinkCheckAns command.
EventId m_nextTx
The event of retransmitting a packet in a consecutive moment if an ACK is not received.
void SetMaxNumberOfTransmissions(uint8_t nbTrans)
Set the max number of unacknowledged redundant transmissions of each packet.
std::list< Ptr< MacCommand > > m_macCommandList
List of the MAC commands that need to be applied to the next UL packet.
TracedValue< uint8_t > m_dataRate
The data rate this device is using to transmit.
LoraDeviceAddress m_address
The address of this device.
bool GetUplinkAdrBit() const
Get the current value of the device's uplink ADR bit of the LoRaWAN FHDR.
void Send(Ptr< Packet > packet) override
Send a packet.
uint16_t m_currentFCnt
current value of the device frame counter.
uint8_t GetLastKnownGatewayCount() const
Get the last known number of gateways concurrently receiving transmissions from the device.
void AddMacCommand(Ptr< MacCommand > macCommand)
Add a MAC command to the list of those that will be sent out in the next packet.
static constexpr uint16_t ADR_ACK_LIMIT
ADRACKCnt threshold for setting ADRACKReq.
Time GetNextTransmissionDelay()
Find the base minimum wait time before the next possible transmission.
LorawanMacHeader::MType GetMType()
Get the message type to send when the Send method is called.
virtual void DoSend(Ptr< Packet > packet)
Checking if we are performing the transmission of a new packet or a retransmission,...
void OnDevStatusReq()
Perform the actions that need to be taken when receiving a DevStatusReq command.
LoraDeviceAddress GetDeviceAddress()
Get the network address of this device.
double m_lastRxSnr
Used to record the last reception SNR measurement to be included in the DevStatusAns.
TracedValue< uint8_t > m_lastKnownGatewayCount
The last known gateway count (i.e., gateways that are in communication range with this end device).
uint8_t m_nbTrans
Default number of unacknowledged redundant transmissions of each packet.
void SetDataRate(uint8_t dataRate)
Set the data rate this end device will use when transmitting.
uint8_t m_receiveWindowDurationInSymbols
The duration of a receive window in number of symbols.
TracedValue< uint8_t > m_lastKnownLinkMarginDb
The last known link margin in dB from the demodulation floor.
void SetTransmissionPowerDbm(double txPowerDbm)
Set the transmission power of this end device.
uint8_t GetDataRate()
Get the data rate this end device is set to use.
virtual Time GetNextClassTransmissionDelay(Time waitTime)
Find the minimum wait time before the next possible transmission based on end device's Class Type.
void ExecuteADRBackoff()
Execute ADR backoff as in LoRaWAN specification, V1.0.4 (2020).
void SetDeviceAddress(LoraDeviceAddress address)
Set the network address of this device.
struct LoraRetxParameters m_retxParams
Structure containing the retransmission parameters for this device.
This class represents the device address of a LoraWAN end device.
This class represents the Frame header (FHDR) used in a LoraWAN network.
std::vector< Ptr< MacCommand > > GetCommands()
Return a vector of pointers to all the MAC commands saved in this header.
bool GetAck() const
Get the value of the ACK bit field.
void AddCommand(Ptr< MacCommand > macCommand)
Add a predefined command to the vector in this frame header.
void SetFCnt(uint16_t fCnt)
Set the FCnt value.
void SetAdr(bool adr)
Set the value of the ADR bit field.
uint32_t GetSerializedSize() const override
Return the size required for serialization of this header.
void SetAddress(LoraDeviceAddress address)
Set the address.
void SetAsUplink()
State that this is an uplink message.
void SetAdrAckReq(bool adrAckReq)
Set the value of the ADRACKReq bit field.
void SetFPort(uint8_t fPort)
Set the FPort value.
This class represents the Mac header of a LoRaWAN packet.
void SetMajor(uint8_t major)
Set the major version of this header.
void SetMType(enum MType mtype)
Set the message type.
uint32_t GetSerializedSize() const override
TracedCallback< Ptr< const Packet > > m_cannotSendBecauseDutyCycle
The trace source that is fired when a packet cannot be sent because of duty cycle limitations.
LorawanMac()
Default constructor.
double GetDbmForTxPower(uint8_t txPower)
Get the transmission power in dBm that corresponds, in this region, to the encoded 8-bit txPower.
uint8_t GetSfFromDataRate(uint8_t dataRate)
Get the spreading factor corresponding to a data rate, based on this MAC's region.
double GetBandwidthFromDataRate(uint8_t dataRate)
Get the bandwidth corresponding to a data rate, based on this MAC's region.
std::vector< uint32_t > m_maxMacPayloadForDataRate
A vector holding the maximum MACPayload size that corresponds to a certain data rate.
Ptr< LogicalLoraChannelHelper > m_channelHelper
The LogicalLoraChannelHelper instance that is assigned to this MAC.
TracedCallback< Ptr< const Packet > > m_sentNewPacket
Trace source that is fired when a new APP layer packet arrives at the MAC layer.
Ptr< NetDevice > m_device
The device this MAC layer is installed on.
static uint8_t GetCIDFromMacCommand(enum MacCommandType commandType)
Get the CID that corresponds to a type of MAC command.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition log.h:243
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:271
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition log.h:250
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:439
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition simulator.cc:294
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
MacCommandType
Enum for every possible command type.
Definition mac-command.h:25
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:114
Ptr< const AttributeChecker > MakeIntegerChecker()
Definition integer.h:99
Ptr< const AttributeAccessor > MakeIntegerAccessor(T1 a1)
Definition integer.h:35
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition uinteger.h:35
Ptr< const AttributeChecker > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition enum.h:179
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:585
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition boolean.h:70
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Definition enum.h:221
Structure representing the parameters that will be used in the retransmission procedure.