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
14#include "lora-phy.h"
15
16#include "ns3/energy-source-container.h"
17#include "ns3/simulator.h"
18
19#include <bitset>
20
21namespace ns3
22{
23namespace lorawan
24{
25
26NS_LOG_COMPONENT_DEFINE("EndDeviceLorawanMac");
27
29
30TypeId
32{
33 static TypeId tid =
34 TypeId("ns3::EndDeviceLorawanMac")
36 .SetGroupName("lorawan")
37 .AddTraceSource(
38 "ConfirmedTransmissionOutcome",
39 "Trace number of retransmissions for acknowledgement of confirmed packets",
41 "ns3::EndDeviceLorawanMac::ConfirmedTxOutcomeCallback")
42 .AddAttribute("DataRate",
43 "Data rate currently employed by this end device",
47 .AddTraceSource("DataRate",
48 "Data rate currently employed by this end device",
50 "ns3::TracedValueCallback::uint8_t")
51 .AddAttribute(
52 "ADR",
53 "Ensure to the network server that this device will accept data rate, transmission "
54 "power and number of retransmissions configurations received via LinkADRReq. This "
55 "also allows the device's local ADR backoff procedure to reset configurations in "
56 "case of connectivity loss.",
57 BooleanValue(true),
60 .AddTraceSource("TxPower",
61 "Transmission ERP [dBm] currently employed by this end device",
63 "ns3::TracedValueCallback::Double")
64 .AddTraceSource("LastKnownLinkMargin",
65 "Last known demodulation margin in "
66 "communications between this end device "
67 "and a gateway",
69 "ns3::TracedValueCallback::uint8_t")
70 .AddTraceSource("LastKnownGatewayCount",
71 "Last known number of gateways able to "
72 "listen to this end device",
74 "ns3::TracedValueCallback::uint8_t")
75 .AddTraceSource("AggregatedDutyCycle",
76 "Aggregate duty cycle, in fraction form, "
77 "this end device must respect",
79 "ns3::TracedValueCallback::Double")
80 .AddAttribute("MaxTransmissions",
81 "Maximum number of transmissions for a packet (NbTrans)",
82 IntegerValue(1),
85 .AddAttribute("FType",
86 "Specify type of message will be sent by this end device.",
90 "Unconfirmed",
92 "Confirmed"));
93 return tid;
94}
95
97 : m_address(LoraDeviceAddress(0)), // LoraWAN default
98 m_fCnt(0),
99 m_adrAckCnt(0),
100 m_dataRate(0),
101 m_txPowerDbm(14),
102 m_nbTrans(1),
104 m_headerDisabled(false),
106 m_lastRxSnr(32), // Max initial value
107 m_fType(LorawanMacHeader::FType::UNCONFIRMED_DATA_UP),
108 m_adr(true),
112 m_adrAckReq(false)
113{
114 NS_LOG_FUNCTION(this);
115 // Initialize random variable for channel selection
117 // Void the next transmission event
118 m_nextTx = EventId();
119 m_nextTx.Cancel();
120}
121
126
127void
129{
130 NS_LOG_FUNCTION(this << packet);
131
132 // Check ability to send and compute delay without touching the internal device state
133 Time nextTxDelay;
134 if (!ValidatePacketForSend(packet, nextTxDelay))
135 {
136 NS_LOG_ERROR("Packet cannot be sent in the current device state, transmission aborted.");
137 return;
138 }
139
140 // We are sending this packet: overwrite any previously queued transmissions if any
141 m_nextTx.Cancel();
142
143 // If it is not possible to transmit now because of the duty cycle or because we are currently
144 // in the process of sending/receiving another packet, schedule a tx/retx later
145 if (nextTxDelay.IsStrictlyPositive())
146 {
147 NS_LOG_WARN("Attempting to send, but device is busy or duty cycle won't allow it. "
148 "Rescheduling a tx in "
149 << nextTxDelay.As(Time::S) << ".");
150 PostponeTransmission(nextTxDelay, packet);
152 return;
153 }
154
155 /////////////////////////////////////////////////////////////
156 // From here on out, immediate pkt transmission is assured //
157 /////////////////////////////////////////////////////////////
158
159 DoSend(packet);
160}
161
162bool
164{
165 // Initialize output delay to max
166 nextTxDelay = Time::Max();
167
168 // Copy current tx parameters and simulate an update on them
169 auto tmpDataRate = m_dataRate.Get();
170 auto tmpTxPower = m_txPowerDbm.Get();
171 auto tmpNbTrans = m_nbTrans;
172 auto tmpTxChannels = m_channelHelper->GetRawChannelArray(); // shallow copy to get size
173 for (auto& c : tmpTxChannels)
174 {
175 c = c ? Copy(c) : c; // deep copy
176 }
177
178 // Evaluate ADR backoff on copied parameters
179 if (m_adr && packet != m_txContext.packet) // Is this a new packet?
180 {
181 // Is there an ongoing retransmission process that would be interrupted?
182 uint16_t tmpAdrAckCnt = m_adrAckCnt + (m_txContext.nbTxLeft > 0);
183 // Simulate ADR Backoff on temporary values
184 if (tmpAdrAckCnt >= ADR_ACK_LIMIT + ADR_ACK_DELAY)
185 {
186 DoExecuteADRBackoff(tmpTxPower, tmpDataRate, tmpNbTrans, tmpTxChannels);
187 }
188 }
189
190 // This check is influenced by ADR backoff. This is OK because (by LoRaWAN design) you
191 // either use ADR and constrain your max app payload according to the default initial DR0,
192 // or you disable ADR for a fixed data rate, with the possibility of using bigger payloads.
193 if (!IsPayloadSizeValid(packet->GetSize(), tmpDataRate))
194 {
195 NS_LOG_WARN("Application payload exceeding maximum size.");
196 return false;
197 }
198
199 // Check if there is a channel suitable for TX (checks data rate & tx power etc.)
200 if (tmpTxChannels = GetCompatibleTxChannels(tmpTxChannels, tmpDataRate, tmpTxPower);
201 tmpTxChannels.empty())
202 {
203 NS_LOG_WARN("No tx channel compatible with current DR/power.");
204 return false;
205 }
206
207 // Evaluate min send delay and return true
208 nextTxDelay = GetNextTransmissionDelay(tmpTxChannels);
209 return true;
210}
211
212void
214{
215 NS_LOG_FUNCTION(this << macCommand);
216
217 m_macCommandList.push_back(macCommand);
218}
219
220void
222{
223 NS_LOG_FUNCTION(this << adr);
224 m_adr = adr;
225}
226
227bool
229{
230 NS_LOG_FUNCTION(this);
231 return m_adr;
232}
233
234void
236{
237 NS_LOG_FUNCTION(this << unsigned(nbTrans));
238 m_nbTrans = nbTrans;
239}
240
241uint8_t
247
248void
250{
251 NS_LOG_FUNCTION(this << unsigned(dataRate));
252
253 m_dataRate = dataRate;
254}
255
256uint8_t
258{
259 NS_LOG_FUNCTION(this);
260
261 return m_dataRate;
262}
263
264void
266{
267 NS_LOG_FUNCTION(this << txPowerDbm);
268 m_txPowerDbm = txPowerDbm;
269}
270
271double
277
278void
280{
281 NS_LOG_FUNCTION(this << address);
282
283 m_address = address;
284}
285
293
294uint8_t
299
300uint8_t
305
306double
313
314void
316{
317 m_fType = fType;
318 NS_LOG_DEBUG("Message type is set to " << fType);
319}
320
326
327uint16_t
333
334void
336{
337 NS_LOG_FUNCTION(this << nextTxDelay << packet);
338 m_nextTx = Simulator::Schedule(nextTxDelay, &EndDeviceLorawanMac::Send, this, packet);
339}
340
343{
344 NS_LOG_FUNCTION(this);
345 /// @todo possibly move to LogicalChannelHelper
346 auto channels = m_channelHelper->GetRawChannelArray();
347 auto compatible = GetCompatibleTxChannels(channels, m_dataRate, m_txPowerDbm);
348 std::vector<Ptr<LogicalLoraChannel>> candidates;
349 for (const auto& c : compatible)
350 {
351 if (m_channelHelper->GetWaitTime(c).IsZero())
352 {
353 candidates.emplace_back(c);
354 }
355 }
356 if (candidates.empty())
357 {
358 NS_LOG_DEBUG("No suitable TX channel found");
359 return nullptr;
360 }
361 uint8_t i = m_uniformRV->GetInteger(0, candidates.size() - 1);
362 auto channel = candidates.at(i);
363 NS_LOG_DEBUG("Selected channel with frequency=" << channel->GetFrequency() << "Hz");
364 return channel;
365}
366
367void
369{
370 NS_LOG_FUNCTION(this << frameHeader);
371 // Parse and apply downlink MAC commands, queue answers
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 uint8_t& dataRate,
437 uint8_t& nbTrans,
438 const std::vector<Ptr<LogicalLoraChannel>>& txChannelArray)
439{
440 // Adapted from: github.com/Lora-net/SWL2001.git v4.8.0
441 // For the time being, this implementation is valid for the EU868 region
442
443 if (txPowerDbm < 14)
444 {
445 txPowerDbm = 14; // Reset transmission power to default
446 return;
447 }
448
449 if (dataRate != 0)
450 {
451 dataRate--;
452 return;
453 }
454
455 // Set nbTrans to 1 and re-enable default channels
456 nbTrans = 1;
457 txChannelArray.at(0)->EnableForUplink();
458 txChannelArray.at(1)->EnableForUplink();
459 txChannelArray.at(2)->EnableForUplink();
460}
461
462bool
463EndDeviceLorawanMac::IsPayloadSizeValid(uint32_t appPayloadSize, uint8_t dataRate) const
464{
465 NS_LOG_FUNCTION(this << appPayloadSize << unsigned(dataRate));
466 uint32_t fOptsLen = 0;
467 for (const auto& c : m_macCommandList)
468 {
469 fOptsLen += c->GetSerializedSize();
470 }
471 /// TODO: FPort could be absent
472 uint32_t macPayloadSize = 7 + fOptsLen + 1 + appPayloadSize;
473 uint32_t maxMacPayloadForDataRate = m_maxMacPayloadForDataRate.at(dataRate);
474 NS_LOG_DEBUG("macPayloadSize=" << macPayloadSize << "B, maxMacPayloadForDataRate="
475 << maxMacPayloadForDataRate << "B");
476 return macPayloadSize <= maxMacPayloadForDataRate;
477}
478
479std::vector<Ptr<LogicalLoraChannel>>
481 const std::vector<Ptr<LogicalLoraChannel>>& txChannelArray,
482 uint8_t dataRate,
483 double txPowerDbm) const
484{
485 NS_LOG_FUNCTION(this);
486 /// @todo possibly move to LogicalChannelHelper
487 std::vector<Ptr<LogicalLoraChannel>> candidates;
488 for (const auto& channel : txChannelArray)
489 {
490 if (channel && channel->IsEnabledForUplink()) // Skip empty frequency channel slots
491 {
492 uint8_t minDr = channel->GetMinimumDataRate();
493 uint8_t maxDr = channel->GetMaximumDataRate();
494 double maxTxPower = m_channelHelper->GetTxPowerForChannel(channel);
495 NS_LOG_DEBUG("Enabled channel: frequency=" << channel->GetFrequency()
496 << "Hz, minDr=" << unsigned(minDr)
497 << ", maxDr=" << unsigned(maxDr)
498 << ", maxTxPower=" << maxTxPower << "dBm");
499 if (dataRate >= minDr && dataRate <= maxDr && txPowerDbm <= maxTxPower)
500 {
501 candidates.emplace_back(channel);
502 }
503 }
504 }
505 return candidates;
506}
507
508Time
510 const std::vector<Ptr<LogicalLoraChannel>>& txChannelArray) const
511{
512 NS_LOG_FUNCTION(this);
513 // Check duty cycle on provided channels
514 auto waitTime = Time::Max();
515 for (const auto& c : txChannelArray)
516 {
517 auto channelWait = m_channelHelper->GetWaitTime(c);
518 NS_LOG_LOGIC("frequency=" << c->GetFrequency() << "Hz, "
519 << "waitTime=" << channelWait.As(Time::S));
520 waitTime = Min(waitTime, channelWait);
521 }
522 NS_LOG_DEBUG("Current minimum duty-cycle wait time is " << waitTime.As(Time::S));
523
524 /// TODO: Check aggregated duty cycle imposed by server
525
526 // Check if we need to postpone more (overridden function!)
527 waitTime = Max(waitTime, GetNextClassTransmissionDelay());
528
529 return waitTime;
530}
531
532void
534{
535 NS_LOG_FUNCTION(this << packet);
536
537 // Store whether this is a new packet (the context may be overwritten)
538 bool packetIsNew = (packet != m_txContext.packet);
539
540 if (packetIsNew) // Transmission of a new packet
541 {
542 NS_LOG_DEBUG("New FRMPayload from application: " << packet->GetSize() << "B");
543 // If re-transmission process of last packet was interrupted, update frame counters
544 if (m_txContext.nbTxLeft > 0)
545 {
546 NS_LOG_DEBUG("Stopping active retransmission process");
547 // Update frame counter and ADRACKCnt (normally updated after exhausting all reTxs)
548 m_fCnt++;
549 m_adrAckCnt++;
550 // If needed, trace failed ACKnowledgement of previous packet
551 if (m_txContext.needsAck)
552 {
553 uint8_t txs = m_nbTrans - m_txContext.nbTxLeft;
554 NS_LOG_WARN("Previous packet not acknowledged, used "
555 << unsigned(txs) << " transmissions out of " << unsigned(m_nbTrans));
557 false,
558 m_txContext.firstAttempt,
559 m_txContext.packet);
560 }
561 }
562 // Reset (re)transmission context
564 .packet = packet,
565 .firstAttempt = Simulator::Now(),
567 .nbTxLeft = int8_t(m_nbTrans),
568 };
569 }
570 else // Retransmission
571 {
572 // Retransmissions must be scheduled by parent classes only if nbTxLeft > 0
573 NS_ASSERT_MSG(m_txContext.nbTxLeft > 0, "No more retransmissions for this packet");
574 NS_LOG_DEBUG("Retransmitting an old packet.");
575 // Remove obsolete headers
576 LorawanMacHeader macHdr;
577 packet->RemoveHeader(macHdr);
578 LoraFrameHeader frameHdr;
579 packet->RemoveHeader(frameHdr);
580 }
581
582 // Evaluate ADR backoff as in LoRaWAN specification, V1.0.4 (2020)
583 // Adapted from: github.com/Lora-net/SWL2001.git v4.8.0
584 m_adrAckReq = (m_adrAckCnt >= ADR_ACK_LIMIT); // Set the ADRACKReq bit in frame header
586 {
587 // Unreachable by retransmissions: they do not increase ADRACKCnt
590 }
591 NS_ASSERT(m_adrAckCnt < 2400);
592
593 // Add the Lora Frame Header to the packet
594 LoraFrameHeader frameHdr;
595 ApplyNecessaryOptions(frameHdr);
596 packet->AddHeader(frameHdr);
597 NS_LOG_INFO("Added frame header of size " << frameHdr.GetSerializedSize() << " bytes.");
598 // Add the Lora Mac header to the packet
599 LorawanMacHeader macHdr;
600 ApplyNecessaryOptions(macHdr);
601 packet->AddHeader(macHdr);
602 NS_LOG_INFO("Added MAC header of size " << macHdr.GetSerializedSize() << " bytes.");
603
604 /// TODO: Add MIC
605
606 // Send packet
607 SendToPhy(packet);
608 // Decrease the number of transmissions left
609 m_txContext.nbTxLeft--;
610 // Fire trace source
611 if (packetIsNew)
612 {
613 m_sentNewPacket(packet);
614 }
615}
616
617void
619{
620 NS_LOG_FUNCTION(this);
621
622 // Adapted from: github.com/Lora-net/SWL2001.git v4.8.0
623 // For the time being, this implementation is valid for the EU868 region
624
625 if (!m_adr)
626 {
627 return;
628 }
629
630 // TracedValue are not easily passed by reference
631 double txPowerDbm = m_txPowerDbm.Get();
632 uint8_t dataRate = m_dataRate.Get();
633 DoExecuteADRBackoff(txPowerDbm, dataRate, m_nbTrans, m_channelHelper->GetRawChannelArray());
634 m_txPowerDbm = txPowerDbm;
635 m_dataRate = dataRate;
636}
637
638void
640{
641 frameHeader.SetAsUplink();
642 frameHeader.SetFPort(1); // TODO Use an appropriate frame port based on the application
643 frameHeader.SetAddress(m_address);
644 frameHeader.SetAdr(m_adr);
645 frameHeader.SetAdrAckReq(m_adrAckReq);
646
647 // FPending does not exist in uplink messages
648 frameHeader.SetFCnt(m_fCnt);
649
650 // Add listed MAC commands
651 for (const auto& command : m_macCommandList)
652 {
653 NS_LOG_INFO("Applying a MAC Command of CID "
654 << unsigned(MacCommand::GetCIDFromMacCommand(command->GetCommandType())));
655
656 frameHeader.AddCommand(command);
657 }
658
659 NS_LOG_DEBUG(frameHeader);
660}
661
662void
664{
665 macHeader.SetFType(m_fType);
666 macHeader.SetMajor(1);
667
668 NS_LOG_DEBUG(macHeader);
669}
670
671void
672EndDeviceLorawanMac::OnLinkCheckAns(uint8_t margin, uint8_t gwCnt)
673{
674 NS_LOG_FUNCTION(this << unsigned(margin) << unsigned(gwCnt));
675
678}
679
680void
682 uint8_t txPower,
683 uint16_t chMask,
684 uint8_t chMaskCntl,
685 uint8_t nbTrans)
686{
687 NS_LOG_FUNCTION(this << unsigned(dataRate) << unsigned(txPower) << std::bitset<16>(chMask)
688 << unsigned(chMaskCntl) << unsigned(nbTrans));
689
690 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
691 // For the time being, this implementation is valid for the EU868 region
692
693 NS_ASSERT_MSG(!(dataRate & 0xF0), "dataRate field > 4 bits");
694 NS_ASSERT_MSG(!(txPower & 0xF0), "txPower field > 4 bits");
695 NS_ASSERT_MSG(!(chMaskCntl & 0xF8), "chMaskCntl field > 3 bits");
696 NS_ASSERT_MSG(!(nbTrans & 0xF0), "nbTrans field > 4 bits");
697
698 auto channels = m_channelHelper->GetRawChannelArray();
699
700 bool channelMaskAck = true;
701 bool dataRateAck = true;
702 bool powerAck = true;
703
704 NS_LOG_DEBUG("Channel mask = " << std::bitset<16>(chMask)
705 << ", ChMaskCtrl = " << unsigned(chMaskCntl));
706
707 // Check channel mask
708 switch (chMaskCntl)
709 {
710 // Channels 0 to 15
711 case 0:
712 // Check if all enabled channels have a valid frequency
713 for (size_t i = 0; i < channels.size(); ++i)
714 {
715 if ((chMask & 0b1 << i) && !channels.at(i))
716 {
717 NS_LOG_WARN("Invalid channel mask");
718 channelMaskAck = false;
719 break; // break for loop
720 }
721 }
722 break;
723 // All channels ON independently of the ChMask field value
724 case 6:
725 chMask = 0b0;
726 for (size_t i = 0; i < channels.size(); ++i)
727 {
728 if (channels.at(i))
729 {
730 chMask |= 0b1 << i;
731 }
732 }
733 break;
734 default:
735 NS_LOG_WARN("Invalid channel mask ctrl field");
736 channelMaskAck = false;
737 break;
738 }
739
740 // check if all channels are disabled
741 if (chMask == 0)
742 {
743 NS_LOG_WARN("Invalid channel mask");
744 channelMaskAck = false;
745 }
746
747 // Temporary channel mask is built and validated
748 if (!m_adr) // ADR disabled, only consider channel mask conf.
749 {
750 /// @remark Original code considers this to be mobile-mode
751 if (channelMaskAck) // valid channel mask
752 {
753 bool compatible = false;
754 // Look for enabled channel that supports current data rate.
755 for (size_t i = 0; i < channels.size(); ++i)
756 {
757 if ((chMask & 0b1 << i) && m_dataRate >= channels.at(i)->GetMinimumDataRate() &&
758 m_dataRate <= channels.at(i)->GetMaximumDataRate())
759 { // Found compatible channel, break loop
760 compatible = true;
761 break;
762 }
763 }
764 if (!compatible)
765 {
766 NS_LOG_WARN("Invalid channel mask for current device data rate (ADR off)");
767 channelMaskAck = dataRateAck = powerAck = false; // reject all configurations
768 }
769 else // apply channel mask configuration
770 {
771 for (size_t i = 0; i < channels.size(); ++i)
772 {
773 if (auto c = channels.at(i); c)
774 {
775 (chMask & 0b1 << i) ? c->EnableForUplink() : c->DisableForUplink();
776 }
777 }
778 dataRateAck = powerAck = false; // only ack channel mask
779 }
780 }
781 else // reject
782 {
783 NS_LOG_WARN("Invalid channel mask");
784 dataRateAck = powerAck = false; // reject all configurations
785 }
786 }
787 else // Server-side ADR is enabled
788 {
789 if (dataRate != 0xF) // If value is 0xF, ignore config.
790 {
791 bool compatible = false;
792 // Look for enabled channel that supports config. data rate.
793 for (size_t i = 0; i < channels.size(); ++i)
794 {
795 if (chMask & 0b1 << i) // all enabled by chMask, even if it was invalid
796 {
797 if (const auto& c = channels.at(i); c) // exists
798 {
799 if (dataRate >= c->GetMinimumDataRate() &&
800 dataRate <= c->GetMaximumDataRate())
801 { // Found compatible channel, break loop
802 compatible = true;
803 break;
804 }
805 }
806 else // manages invalid case, checks with defaults
807 {
808 if (GetSfFromDataRate(dataRate) && GetBandwidthFromDataRate(dataRate))
809 { // Found compatible (invalid) channel, break loop
810 compatible = true;
811 break;
812 }
813 }
814 }
815 }
816 // Check if it is acceptable
817 if (!compatible)
818 {
819 NS_LOG_WARN("Invalid data rate");
820 dataRateAck = false;
821 }
822 }
823
824 if (txPower != 0xF) // If value is 0xF, ignore config.
825 {
826 // Check if it is acceptable
827 if (GetDbmForTxPower(txPower) < 0)
828 {
829 NS_LOG_WARN("Invalid tx power");
830 powerAck = false;
831 }
832 }
833
834 // If no error, apply configurations
835 if (channelMaskAck && dataRateAck && powerAck)
836 {
837 for (size_t i = 0; i < channels.size(); ++i)
838 {
839 if (auto c = channels.at(i); c)
840 {
841 (chMask & 0b1 << i) ? c->EnableForUplink() : c->DisableForUplink();
842 }
843 }
844 if (txPower != 0xF) // If value is 0xF, ignore config.
845 {
847 }
848 m_nbTrans = (nbTrans == 0) ? 1 : nbTrans;
849 if (dataRate != 0xF) // If value is 0xF, ignore config.
850 {
851 m_dataRate = dataRate;
852 }
853 NS_LOG_DEBUG("MacTxDataRateAdr = " << unsigned(m_dataRate));
854 NS_LOG_DEBUG("MacTxPower = " << unsigned(m_txPowerDbm) << "dBm");
855 NS_LOG_DEBUG("MacNbTrans = " << unsigned(m_nbTrans));
856 }
857 }
858
859 NS_LOG_INFO("Adding LinkAdrAns reply");
860 m_macCommandList.emplace_back(Create<LinkAdrAns>(powerAck, dataRateAck, channelMaskAck));
861}
862
863void
865{
866 NS_LOG_FUNCTION(this << unsigned(maxDutyCycle));
867 NS_ASSERT_MSG(!(maxDutyCycle & 0xF0), "maxDutyCycle > 4 bits");
868 m_aggregatedDutyCycle = 1 / std::pow(2, maxDutyCycle);
869 NS_LOG_INFO("Adding DutyCycleAns reply");
871}
872
873void
875{
876 NS_LOG_FUNCTION(this);
877
878 uint8_t battery = 255; // could not measure
879 if (m_device && m_device->GetNode())
880 {
881 if (auto sc = m_device->GetNode()->GetObject<energy::EnergySourceContainer>();
882 sc && sc->GetN() == 1)
883 {
884 battery = sc->Get(0)->GetEnergyFraction() * 253 + 1.5; // range 1-254
885 }
886 }
887 else
888 {
889 battery = 0; // external power source
890 }
891
892 // approximate to nearest integer
893 double snr = round(m_lastRxSnr);
894 // clamp value to boundaries
895 snr = snr < -32 ? -32 : snr > 31 ? 31 : snr;
896 // cast to 6-bit signed int and store in uint8_t
897 uint8_t margin = std::bitset<6>(snr).to_ulong();
898
899 NS_LOG_INFO("Adding DevStatusAns reply");
900 m_macCommandList.emplace_back(Create<DevStatusAns>(battery, margin));
901}
902
903void
905 uint32_t frequencyHz,
906 uint8_t minDataRate,
907 uint8_t maxDataRate)
908{
909 NS_LOG_FUNCTION(this << unsigned(chIndex) << frequencyHz << unsigned(minDataRate)
910 << unsigned(maxDataRate));
911
912 NS_ASSERT_MSG(!(minDataRate & 0xF0), "minDataRate field > 4 bits");
913 NS_ASSERT_MSG(!(maxDataRate & 0xF0), "maxDataRate field > 4 bits");
914
915 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
916 // For the time being, this implementation is valid for the EU868 region
917
918 bool dataRateRangeOk = true;
919 bool channelFrequencyOk = true;
920
921 // Valid Channel Index
922 if (chIndex < 3 || chIndex > m_channelHelper->GetRawChannelArray().size() - 1)
923 {
924 NS_LOG_WARN("[WARNING] Invalid channel index");
925 dataRateRangeOk = channelFrequencyOk = false;
926 }
927
928 // Valid Frequency
929 if (frequencyHz != 0 && !m_channelHelper->IsFrequencyValid(frequencyHz))
930 {
931 NS_LOG_WARN("[WARNING] Invalid frequency");
932 channelFrequencyOk = false;
933 }
934
935 // Valid DRMIN/MAX
936 if (!GetSfFromDataRate(minDataRate) || !GetBandwidthFromDataRate(minDataRate))
937 {
938 NS_LOG_WARN("[WARNING] Invalid DR min");
939 dataRateRangeOk = false;
940 }
941
942 if (!GetSfFromDataRate(maxDataRate) || !GetBandwidthFromDataRate(maxDataRate))
943 {
944 NS_LOG_WARN("[WARNING] Invalid DR max");
945 dataRateRangeOk = false;
946 }
947
948 if (maxDataRate < minDataRate)
949 {
950 NS_LOG_WARN("[WARNING] Invalid DR max < DR min");
951 dataRateRangeOk = false;
952 }
953
954 if (dataRateRangeOk && channelFrequencyOk)
955 {
956 auto channel = Create<LogicalLoraChannel>(frequencyHz, minDataRate, maxDataRate);
957 (frequencyHz == 0) ? channel->DisableForUplink() : channel->EnableForUplink();
958 m_channelHelper->SetChannel(chIndex, channel);
959 NS_LOG_DEBUG("MacTxFrequency[" << unsigned(chIndex) << "]=" << frequencyHz
960 << ", DrMin=" << unsigned(minDataRate)
961 << ", DrMax=" << unsigned(maxDataRate));
962 }
963
964 NS_LOG_INFO("Adding NewChannelAns reply");
965 m_macCommandList.emplace_back(Create<NewChannelAns>(dataRateRangeOk, channelFrequencyOk));
966}
967
968} // namespace lorawan
969} // namespace ns3
#define Max(a, b)
#define Min(a, b)
Hold variables of type enum.
Definition enum.h:52
An identifier for simulation events.
Definition event-id.h:45
Hold a signed integer type.
Definition integer.h:34
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:580
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:191
Simulation virtual time values and global simulation resolution.
Definition nstime.h:95
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:408
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:341
@ S
second
Definition nstime.h:106
static Time Max()
Maximum representable Time Not to be confused with Max(Time,Time).
Definition nstime.h:287
a unique identifier for an interface.
Definition type-id.h:50
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:999
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.
virtual void SendToPhy(Ptr< Packet > packet)=0
Gather PHY transmission parameters and send a packet through the LoRa physical layer.
bool IsPayloadSizeValid(uint32_t appPayloadSize, uint8_t dataRate) const
Check whether the size of the application payload is under the maximum allowed.
Time GetNextTransmissionDelay(const std::vector< Ptr< LogicalLoraChannel > > &txChannelArray) const
Find the base minimum wait time before the next possible transmission based on channels legal duty cy...
Ptr< LogicalLoraChannel > GetRandomChannelForTx()
Find a suitable channel for transmission.
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.
void PostponeTransmission(Time nextTxDelay, Ptr< Packet > packet)
Postpone transmission to the specified time.
double GetTransmissionPowerDbm()
Get the transmission power this end device is set to use.
uint16_t GetUplinkFrameCounter() const
Get the current value of the LoRaWAN uplink frame counter (FCnt) of this device.
bool m_adr
Uplink ADR bit contained in the FCtrl field of the LoRaWAN FHDR.
void SetUplinkAdrBit(bool adr)
Signals to the network server that this device will or may not comply with LinkADRReq settings (data ...
TracedCallback< uint8_t, bool, Time, Ptr< Packet > > m_confirmedTxOutcomeCallback
Traced Callback: confirmed transmission process outcome event.
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 and to sample random retransm...
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.
LorawanMacHeader::FType GetFType()
Get the frame type to send when the Send method is called.
double GetAggregatedDutyCycle()
Get the aggregated duty cycle.
uint16_t m_adrAckCnt
ADRACKCnt counter of the number of consecutive uplinks without downlink reply from the server.
void ApplyMACCommands(LoraFrameHeader frameHeader)
Take action on the commands contained on this FrameHeader.
static void DoExecuteADRBackoff(double &txPowerDbm, uint8_t &dataRate, uint8_t &nbTrans, const std::vector< Ptr< LogicalLoraChannel > > &txChannelArray)
Apply ADR backoff as in LoRaWAN specification, V1.0.4 (2020) on the provided input parameters passed ...
virtual Time GetNextClassTransmissionDelay() const =0
Find the minimum wait time before the next possible transmission based on end device's Class Type sch...
void SetFType(LorawanMacHeader::FType fType)
Set the frame type to send when the Send method is called.
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...
std::vector< Ptr< LogicalLoraChannel > > GetCompatibleTxChannels(const std::vector< Ptr< LogicalLoraChannel > > &txChannelArray, uint8_t dataRate, double txPowerDbm) const
Get the set of active transmission channels among the provided array which are compatible with the a ...
CodingRate m_codingRate
The coding rate used by this device.
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.
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 transmitting a packet at a later moment.
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 LoRaWAN address of this device.
bool GetUplinkAdrBit() const
Get the current value of the device's uplink ADR bit of the LoRaWAN FHDR.
PacketTxContext m_txContext
Structure containing the transmission context for the last packet sent by this device,...
void Send(Ptr< Packet > packet) override
Send a packet.
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.
virtual void DoSend(Ptr< Packet > packet)
Perform operations to update the MAC layer with the new packet context and call SendToPhy function.
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
Record latest reception SNR measurement to provide via DevStatusAns.
TracedValue< uint8_t > m_lastKnownGatewayCount
Last known number of gateways in range of this end device, obtained via LinkCheckReq.
uint8_t m_nbTrans
Default number of repeated 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 reception windows in number of symbols.
TracedValue< uint8_t > m_lastKnownLinkMarginDb
Last known best link margin [dB] from the demodulation floor, obtained via LinkCheckReq.
LorawanMacHeader::FType m_fType
The frame type to apply to packets sent with the Send method.
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.
bool ValidatePacketForSend(Ptr< const Packet > packet, Time &nextTxDelay) const
Evaluate whether this packet can be sent in the current device state.
void ExecuteADRBackoff()
Execute ADR backoff as in LoRaWAN specification, V1.0.4 (2020) on this device.
void SetDeviceAddress(LoraDeviceAddress address)
Set the network address of this device.
uint16_t m_fCnt
Current value of the uplink frame counter.
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.
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 SetFType(enum FType fType)
Set the frame 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.
std::vector< uint32_t > m_maxMacPayloadForDataRate
A vector holding the maximum MACPayload size that corresponds to a certain data rate.
uint32_t GetBandwidthFromDataRate(uint8_t dataRate) const
Get the bandwidth corresponding to a data rate, based on this MAC's region.
uint8_t GetSfFromDataRate(uint8_t dataRate) const
Get the spreading factor corresponding to a data rate, based on this MAC's region.
Ptr< LogicalLoraChannelHelper > m_channelHelper
The LogicalLoraChannelHelper instance that is assigned to this MAC.
double GetDbmForTxPower(uint8_t txPower) const
Get the transmission power in dBm that corresponds, in this region, to the encoded 8-bit txPower.
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:246
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:260
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:274
#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:253
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:267
CodingRate
Enumeration of the LoRa supported coding rates.
Definition lora-phy.h:49
@ CR_4_5
Coding rate 4/5.
Definition lora-phy.h:50
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:627
#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:492
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:24
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
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:181
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:643
Ptr< T > Copy(Ptr< T > object)
Return a deep copy of a Ptr.
Definition ptr.h:667
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition boolean.h:70
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Definition enum.h:223
Current packet transmission context tracking transmissions attempts mandated by the protocol.