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("RequiredTransmissions",
38 "Total number of transmissions required to deliver this packet",
40 "ns3::TracedValueCallback::uint8_t")
41 .AddAttribute("DataRate",
42 "Data rate currently employed by this end device",
46 .AddTraceSource("DataRate",
47 "Data rate currently employed by this end device",
49 "ns3::TracedValueCallback::uint8_t")
50 .AddAttribute(
51 "ADR",
52 "Ensure to the network server that this device will accept data rate, transmission "
53 "power and number of retransmissions configurations received via LinkADRReq. This "
54 "also allows the device's local ADR backoff procedure to reset configurations in "
55 "case of connectivity loss.",
56 BooleanValue(true),
59 .AddTraceSource("TxPower",
60 "Transmission ERP [dBm] currently employed by this end device",
62 "ns3::TracedValueCallback::Double")
63 .AddTraceSource("LastKnownLinkMargin",
64 "Last known demodulation margin in "
65 "communications between this end device "
66 "and a gateway",
68 "ns3::TracedValueCallback::uint8_t")
69 .AddTraceSource("LastKnownGatewayCount",
70 "Last known number of gateways able to "
71 "listen to this end device",
73 "ns3::TracedValueCallback::uint8_t")
74 .AddTraceSource("AggregatedDutyCycle",
75 "Aggregate duty cycle, in fraction form, "
76 "this end device must respect",
78 "ns3::TracedValueCallback::Double")
79 .AddAttribute("MaxTransmissions",
80 "Maximum number of transmissions for a packet (NbTrans)",
81 IntegerValue(1),
84 .AddAttribute("MType",
85 "Specify type of message will be sent by this end device.",
89 "Unconfirmed",
91 "Confirmed"));
92 return tid;
93}
94
96 : m_nbTrans(1),
97 m_dataRate(0),
98 m_txPowerDbm(14),
100 // LoraWAN default
101 m_headerDisabled(false),
102 // LoraWAN default
104 // LoraWAN default
106 // Max initial value
107 m_lastRxSnr(32),
108 m_adrAckCnt(0),
109 m_adr(true),
113 m_mType(LorawanMacHeader::CONFIRMED_DATA_UP),
114 m_currentFCnt(0),
115 m_adrAckReq(false)
116{
117 NS_LOG_FUNCTION(this);
118
119 // Initialize the random variable we'll use to decide which channel to
120 // transmit on.
122
123 // Void the transmission event
124 m_nextTx = EventId();
125 m_nextTx.Cancel();
126
127 // Initialize structure for retransmission parameters
129 m_retxParams.retxLeft = m_nbTrans;
130}
131
136
137////////////////////////
138// Sending methods //
139////////////////////////
140
141void
143{
144 NS_LOG_FUNCTION(this << packet);
145
146 // Retx are scheduled by Receive, FailedReception, CloseSecondReceiveWindow only if retxLeft > 0
147 NS_ASSERT_MSG(packet != m_retxParams.packet || m_retxParams.retxLeft > 0,
148 "Max number of transmissions already achieved for this packet");
149
150 if (packet == m_retxParams.packet)
151 {
152 NS_LOG_DEBUG("Retransmitting an old packet.");
153 // Fail if it is a retransmission already ACKed
154 NS_ASSERT_MSG(m_retxParams.waitingAck, "Trying to retransmit a packet already ACKed.");
155 // Remove the headers
156 LorawanMacHeader macHdr;
157 packet->RemoveHeader(macHdr);
158 LoraFrameHeader frameHdr;
159 packet->RemoveHeader(frameHdr);
160 }
161 else // this is a new packet
162 {
163 NS_LOG_DEBUG("New FRMPayload from application: " << packet);
164 // If needed, trace failed ACKnowledgement of previous packet
165 if (m_retxParams.waitingAck)
166 {
167 uint8_t txs = m_nbTrans - m_retxParams.retxLeft;
168 NS_LOG_WARN("Stopping retransmission procedure of previous packet. Used "
169 << unsigned(txs) << " transmissions out of " << unsigned(m_nbTrans));
170 m_requiredTxCallback(txs, false, m_retxParams.firstAttempt, m_retxParams.packet);
171 }
172 }
173
174 // Evaluate ADR backoff as in LoRaWAN specification, V1.0.4 (2020)
175 // Adapted from: github.com/Lora-net/SWL2001.git v4.8.0
176 m_adrAckReq = (m_adrAckCnt >= ADR_ACK_LIMIT); // Set the ADRACKReq bit in frame header
178 {
179 // Unreachable by retx: they do not increase ADRACKCnt
182 }
183 NS_ASSERT(m_adrAckCnt < 2400);
184
185 // This check is influenced by ADR backoff. This is OK because (by LoRaWAN design) you either
186 // use ADR and constrain your max app payload according to the default initial DR0, or you
187 // disable ADR for a fixed data rate, with the possibility of using bigger payloads.
188 if (!IsPayloadSizeValid(packet->GetSize(), m_dataRate))
189 {
190 NS_LOG_ERROR("Application payload exceeding maximum size. Transmission aborted.");
191 return;
192 }
193
194 // Check if there is a channel suitable for TX (checks data rate & tx power etc.)
195 if (GetCompatibleTxChannels().empty())
196 {
197 NS_LOG_ERROR("No tx channel compatible with current DR/power. Transmission aborted.");
198 return;
199 }
200
201 // If it is not possible to transmit now because of the duty cycle
202 // or because we are currently in the process of receiving, schedule a tx/retx later
203 if (auto netxTxDelay = GetNextTransmissionDelay(); netxTxDelay.IsStrictlyPositive())
204 {
205 PostponeTransmission(netxTxDelay, packet);
207 return;
208 }
209
210 ///////////////////////////////////////////////////////
211 // From here on out, the pkt transmission is assured //
212 ///////////////////////////////////////////////////////
213
214 DoSend(packet);
215}
216
217void
219{
220 NS_LOG_FUNCTION(this);
221 // Delete previously scheduled transmissions if any.
223 m_nextTx = Simulator::Schedule(netxTxDelay, &EndDeviceLorawanMac::DoSend, this, packet);
224 NS_LOG_WARN("Attempting to send, but the aggregate duty cycle won't allow it. Scheduling a tx "
225 "at a delay "
226 << netxTxDelay.As(Time::S) << ".");
227}
228
229void
231{
232 NS_LOG_FUNCTION(this);
233
234 // Add the Lora Frame Header to the packet
235 LoraFrameHeader frameHdr;
236 ApplyNecessaryOptions(frameHdr);
237 packet->AddHeader(frameHdr);
238 NS_LOG_INFO("Added frame header of size " << frameHdr.GetSerializedSize() << " bytes.");
239 // Add the Lora Mac header to the packet
240 LorawanMacHeader macHdr;
241 ApplyNecessaryOptions(macHdr);
242 packet->AddHeader(macHdr);
243 NS_LOG_INFO("Added MAC header of size " << macHdr.GetSerializedSize() << " bytes.");
244
245 if (packet != m_retxParams.packet)
246 {
247 NS_LOG_DEBUG("Resetting retransmission parameters.");
248 // Reset MAC command list
249 /// TODO: Some commands should only be removed on ACK
250 m_macCommandList.clear();
251 // Reset retransmission parameters
253 // Save parameters for the (possible) next retransmissions.
254 m_retxParams.packet = packet->Copy();
255 m_retxParams.firstAttempt = Now();
257 NS_LOG_DEBUG("Message type is " << m_mType);
258 }
259
260 // Send packet
261 SendToPhy(packet);
262 // Decrease the number of transmissions left
263 m_retxParams.retxLeft--;
264 if (packet != m_retxParams.packet)
265 {
266 m_sentNewPacket(packet); // Fire trace source
267 // Bump-up frame counters
269 m_adrAckCnt++;
270 }
271}
272
273void
277
278void
280{
281 NS_LOG_FUNCTION(this);
282
283 // Adapted from: github.com/Lora-net/SWL2001.git v4.8.0
284 // For the time being, this implementation is valid for the EU868 region
285
286 if (!m_adr)
287 {
288 return;
289 }
290
291 if (m_txPowerDbm < 14)
292 {
293 m_txPowerDbm = 14; // Reset transmission power to default
294 return;
295 }
296
297 if (m_dataRate != 0)
298 {
299 m_dataRate--;
300 return;
301 }
302
303 // Set nbTrans to 1 and re-enable default channels
304 m_nbTrans = 1;
305 auto channels = m_channelHelper->GetRawChannelArray();
306 channels.at(0)->EnableForUplink();
307 channels.at(1)->EnableForUplink();
308 channels.at(2)->EnableForUplink();
309}
310
311bool
312EndDeviceLorawanMac::IsPayloadSizeValid(uint32_t appPayloadSize, uint8_t dataRate)
313{
314 uint32_t fOptsLen = 0;
315 for (const auto& c : m_macCommandList)
316 {
317 fOptsLen += c->GetSerializedSize();
318 }
319 /// TODO: FPort could be absent
320 NS_LOG_LOGIC("FHDR(7+FOpts(" << fOptsLen << "))+FPort(1)+FRMPayload(" << appPayloadSize
321 << ")=" << 7 + fOptsLen + 1 + appPayloadSize
322 << "B, max MACPayload=" << m_maxMacPayloadForDataRate.at(dataRate)
323 << "B on DR" << unsigned(dataRate));
324 return 7 + fOptsLen + 1 + appPayloadSize <= m_maxMacPayloadForDataRate.at(dataRate);
325}
326
327//////////////////////////
328// Receiving methods //
329//////////////////////////
330
331void
335
336void
340
341void
343{
344 NS_LOG_FUNCTION(this << frameHeader);
345
346 if (m_retxParams.waitingAck)
347 {
348 if (frameHeader.GetAck())
349 {
350 NS_LOG_INFO("The message is an ACK, not waiting for it anymore.");
351
352 NS_LOG_DEBUG("Reset retransmission variables to default values and cancel "
353 "retransmission if already scheduled.");
354
355 uint8_t txs = m_nbTrans - (m_retxParams.retxLeft);
356 m_requiredTxCallback(txs, true, m_retxParams.firstAttempt, m_retxParams.packet);
357 NS_LOG_DEBUG("Received ACK packet after "
358 << unsigned(txs) << " transmissions: stopping retransmission procedure. ");
359
360 // Reset retransmission parameters
362 }
363 else
364 {
366 "Received downlink message not containing an ACK while we were waiting for it!");
367 }
368 }
369
370 for (const auto& c : frameHeader.GetCommands())
371 {
372 NS_LOG_DEBUG("Iterating over the MAC commands...");
373 enum MacCommandType type = (c)->GetCommandType();
374 switch (type)
375 {
376 case (LINK_CHECK_ANS): {
377 NS_LOG_DEBUG("Detected a LinkCheckAns command.");
378 auto linkCheckAns = DynamicCast<LinkCheckAns>(c);
379 OnLinkCheckAns(linkCheckAns->GetMargin(), linkCheckAns->GetGwCnt());
380 break;
381 }
382 case (LINK_ADR_REQ): {
383 NS_LOG_DEBUG("Detected a LinkAdrReq command.");
384 auto linkAdrReq = DynamicCast<LinkAdrReq>(c);
385 OnLinkAdrReq(linkAdrReq->GetDataRate(),
386 linkAdrReq->GetTxPower(),
387 linkAdrReq->GetChMask(),
388 linkAdrReq->GetChMaskCntl(),
389 linkAdrReq->GetNbTrans());
390 break;
391 }
392 case (DUTY_CYCLE_REQ): {
393 NS_LOG_DEBUG("Detected a DutyCycleReq command.");
394 auto dutyCycleReq = DynamicCast<DutyCycleReq>(c);
395 OnDutyCycleReq(dutyCycleReq->GetMaxDutyCycle());
396 break;
397 }
398 case (RX_PARAM_SETUP_REQ): {
399 NS_LOG_DEBUG("Detected a RxParamSetupReq command.");
400 auto rxParamSetupReq = DynamicCast<RxParamSetupReq>(c);
401 OnRxParamSetupReq(rxParamSetupReq->GetRx1DrOffset(),
402 rxParamSetupReq->GetRx2DataRate(),
403 rxParamSetupReq->GetFrequency());
404 break;
405 }
406 case (DEV_STATUS_REQ): {
407 NS_LOG_DEBUG("Detected a DevStatusReq command.");
408 auto devStatusReq = DynamicCast<DevStatusReq>(c);
410 break;
411 }
412 case (NEW_CHANNEL_REQ): {
413 NS_LOG_DEBUG("Detected a NewChannelReq command.");
414 auto newChannelReq = DynamicCast<NewChannelReq>(c);
415 OnNewChannelReq(newChannelReq->GetChannelIndex(),
416 newChannelReq->GetFrequency(),
417 newChannelReq->GetMinDataRate(),
418 newChannelReq->GetMaxDataRate());
419 break;
420 }
421 case (RX_TIMING_SETUP_REQ):
422 case (TX_PARAM_SETUP_REQ):
423 case (DL_CHANNEL_REQ):
424 default: {
425 NS_LOG_ERROR("CID not recognized or supported");
426 break;
427 }
428 }
429 }
430}
431
432void
434{
436
437 frameHeader.SetAsUplink();
438 frameHeader.SetFPort(1); // TODO Use an appropriate frame port based on the application
439 frameHeader.SetAddress(m_address);
440 frameHeader.SetAdr(m_adr);
441 frameHeader.SetAdrAckReq(m_adrAckReq);
442
443 // FPending does not exist in uplink messages
444 frameHeader.SetFCnt(m_currentFCnt);
445
446 // Add listed MAC commands
447 for (const auto& command : m_macCommandList)
448 {
449 NS_LOG_INFO("Applying a MAC Command of CID "
450 << unsigned(MacCommand::GetCIDFromMacCommand(command->GetCommandType())));
451
452 frameHeader.AddCommand(command);
453 }
454}
455
456void
458{
460
461 macHeader.SetMType(m_mType);
462 macHeader.SetMajor(1);
463}
464
465void
467{
468 m_mType = mType;
469 NS_LOG_DEBUG("Message type is set to " << mType);
470}
471
477
478void
482
483Time
489
490std::vector<Ptr<LogicalLoraChannel>>
492{
493 NS_LOG_FUNCTION(this);
494 /// @todo possibly move to LogicalChannelHelper
495 std::vector<Ptr<LogicalLoraChannel>> candidates;
496 for (const auto& channel : m_channelHelper->GetRawChannelArray())
497 {
498 if (channel && channel->IsEnabledForUplink()) // Skip empty frequency channel slots
499 {
500 uint8_t minDr = channel->GetMinimumDataRate();
501 uint8_t maxDr = channel->GetMaximumDataRate();
502 double maxTxPower = m_channelHelper->GetTxPowerForChannel(channel);
503 NS_LOG_DEBUG("Enabled channel: frequency=" << channel->GetFrequency()
504 << "Hz, minDr=" << unsigned(minDr)
505 << ", maxDr=" << unsigned(maxDr)
506 << ", maxTxPower=" << maxTxPower << "dBm");
507 if (m_dataRate >= minDr && m_dataRate <= maxDr && m_txPowerDbm <= maxTxPower)
508 {
509 candidates.emplace_back(channel);
510 }
511 }
512 }
513 return candidates;
514}
515
516Time
518{
519 NS_LOG_FUNCTION(this);
520 // Check duty cycle of compatible channels
521 auto waitTime = Time::Max();
522 for (const auto& channel : GetCompatibleTxChannels())
523 {
524 auto curr = m_channelHelper->GetWaitTime(channel);
525 NS_LOG_DEBUG("frequency=" << channel->GetFrequency() << "Hz,"
526 << " waitTime=" << curr.As(Time::S));
527 if (curr < waitTime)
528 {
529 waitTime = curr;
530 }
531 }
532 return GetNextClassTransmissionDelay(waitTime);
533}
534
537{
538 NS_LOG_FUNCTION(this);
539 /// @todo possibly move to LogicalChannelHelper
540 std::vector<Ptr<LogicalLoraChannel>> candidates;
541 for (const auto& channel : GetCompatibleTxChannels())
542 {
543 if (m_channelHelper->GetWaitTime(channel).IsZero())
544 {
545 candidates.emplace_back(channel);
546 }
547 }
548 if (candidates.empty())
549 {
550 NS_LOG_DEBUG("No suitable TX channel found");
551 return nullptr;
552 }
553 uint8_t i = m_uniformRV->GetInteger(0, candidates.size() - 1);
554 auto channel = candidates.at(i);
555 NS_LOG_DEBUG("Selected channel with frequency=" << channel->GetFrequency() << "Hz");
556 return channel;
557}
558
559/////////////////////////
560// Setters and Getters //
561/////////////////////////
562
563void
565{
566 m_retxParams.waitingAck = false;
567 m_retxParams.retxLeft = m_nbTrans;
568 m_retxParams.packet = nullptr;
569 m_retxParams.firstAttempt = Time(0);
570
571 // Cancel next retransmissions, if any
573}
574
575void
577{
578 NS_LOG_FUNCTION(this << adr);
579 m_adr = adr;
580}
581
582bool
584{
585 NS_LOG_FUNCTION(this);
586 return m_adr;
587}
588
589void
591{
592 NS_LOG_FUNCTION(this << unsigned(nbTrans));
593 m_nbTrans = nbTrans;
594 m_retxParams.retxLeft = nbTrans;
595}
596
597uint8_t
603
604void
606{
607 NS_LOG_FUNCTION(this << unsigned(dataRate));
608
609 m_dataRate = dataRate;
610}
611
612uint8_t
614{
615 NS_LOG_FUNCTION(this);
616
617 return m_dataRate;
618}
619
620void
622{
623 NS_LOG_FUNCTION(this << txPowerDbm);
624 m_txPowerDbm = txPowerDbm;
625}
626
627double
633
634void
636{
637 NS_LOG_FUNCTION(this << address);
638
639 m_address = address;
640}
641
649
650void
651EndDeviceLorawanMac::OnLinkCheckAns(uint8_t margin, uint8_t gwCnt)
652{
653 NS_LOG_FUNCTION(this << unsigned(margin) << unsigned(gwCnt));
654
657}
658
659void
661 uint8_t txPower,
662 uint16_t chMask,
663 uint8_t chMaskCntl,
664 uint8_t nbTrans)
665{
666 NS_LOG_FUNCTION(this << unsigned(dataRate) << unsigned(txPower) << std::bitset<16>(chMask)
667 << unsigned(chMaskCntl) << unsigned(nbTrans));
668
669 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
670 // For the time being, this implementation is valid for the EU868 region
671
672 NS_ASSERT_MSG(!(dataRate & 0xF0), "dataRate field > 4 bits");
673 NS_ASSERT_MSG(!(txPower & 0xF0), "txPower field > 4 bits");
674 NS_ASSERT_MSG(!(chMaskCntl & 0xF8), "chMaskCntl field > 3 bits");
675 NS_ASSERT_MSG(!(nbTrans & 0xF0), "nbTrans field > 4 bits");
676
677 auto channels = m_channelHelper->GetRawChannelArray();
678
679 bool channelMaskAck = true;
680 bool dataRateAck = true;
681 bool powerAck = true;
682
683 NS_LOG_DEBUG("Channel mask = " << std::bitset<16>(chMask)
684 << ", ChMaskCtrl = " << unsigned(chMaskCntl));
685
686 // Check channel mask
687 switch (chMaskCntl)
688 {
689 // Channels 0 to 15
690 case 0:
691 // Check if all enabled channels have a valid frequency
692 for (size_t i = 0; i < channels.size(); ++i)
693 {
694 if ((chMask & 0b1 << i) && !channels.at(i))
695 {
696 NS_LOG_WARN("Invalid channel mask");
697 channelMaskAck = false;
698 break; // break for loop
699 }
700 }
701 break;
702 // All channels ON independently of the ChMask field value
703 case 6:
704 chMask = 0b0;
705 for (size_t i = 0; i < channels.size(); ++i)
706 {
707 if (channels.at(i))
708 {
709 chMask |= 0b1 << i;
710 }
711 }
712 break;
713 default:
714 NS_LOG_WARN("Invalid channel mask ctrl field");
715 channelMaskAck = false;
716 break;
717 }
718
719 // check if all channels are disabled
720 if (chMask == 0)
721 {
722 NS_LOG_WARN("Invalid channel mask");
723 channelMaskAck = false;
724 }
725
726 // Temporary channel mask is built and validated
727 if (!m_adr) // ADR disabled, only consider channel mask conf.
728 {
729 /// @remark Original code considers this to be mobile-mode
730 if (channelMaskAck) // valid channel mask
731 {
732 bool compatible = false;
733 // Look for enabled channel that supports current data rate.
734 for (size_t i = 0; i < channels.size(); ++i)
735 {
736 if ((chMask & 0b1 << i) && m_dataRate >= channels.at(i)->GetMinimumDataRate() &&
737 m_dataRate <= channels.at(i)->GetMaximumDataRate())
738 { // Found compatible channel, break loop
739 compatible = true;
740 break;
741 }
742 }
743 if (!compatible)
744 {
745 NS_LOG_WARN("Invalid channel mask for current device data rate (ADR off)");
746 channelMaskAck = dataRateAck = powerAck = false; // reject all configurations
747 }
748 else // apply channel mask configuration
749 {
750 for (size_t i = 0; i < channels.size(); ++i)
751 {
752 if (auto c = channels.at(i); c)
753 {
754 (chMask & 0b1 << i) ? c->EnableForUplink() : c->DisableForUplink();
755 }
756 }
757 dataRateAck = powerAck = false; // only ack channel mask
758 }
759 }
760 else // reject
761 {
762 NS_LOG_WARN("Invalid channel mask");
763 dataRateAck = powerAck = false; // reject all configurations
764 }
765 }
766 else // Server-side ADR is enabled
767 {
768 if (dataRate != 0xF) // If value is 0xF, ignore config.
769 {
770 bool compatible = false;
771 // Look for enabled channel that supports config. data rate.
772 for (size_t i = 0; i < channels.size(); ++i)
773 {
774 if (chMask & 0b1 << i) // all enabled by chMask, even if it was invalid
775 {
776 if (const auto& c = channels.at(i); c) // exists
777 {
778 if (dataRate >= c->GetMinimumDataRate() &&
779 dataRate <= c->GetMaximumDataRate())
780 { // Found compatible channel, break loop
781 compatible = true;
782 break;
783 }
784 }
785 else // manages invalid case, checks with defaults
786 {
787 if (GetSfFromDataRate(dataRate) && GetBandwidthFromDataRate(dataRate))
788 { // Found compatible (invalid) channel, break loop
789 compatible = true;
790 break;
791 }
792 }
793 }
794 }
795 // Check if it is acceptable
796 if (!compatible)
797 {
798 NS_LOG_WARN("Invalid data rate");
799 dataRateAck = false;
800 }
801 }
802
803 if (txPower != 0xF) // If value is 0xF, ignore config.
804 {
805 // Check if it is acceptable
806 if (GetDbmForTxPower(txPower) < 0)
807 {
808 NS_LOG_WARN("Invalid tx power");
809 powerAck = false;
810 }
811 }
812
813 // If no error, apply configurations
814 if (channelMaskAck && dataRateAck && powerAck)
815 {
816 for (size_t i = 0; i < channels.size(); ++i)
817 {
818 if (auto c = channels.at(i); c)
819 {
820 (chMask & 0b1 << i) ? c->EnableForUplink() : c->DisableForUplink();
821 }
822 }
823 if (txPower != 0xF) // If value is 0xF, ignore config.
824 {
826 }
827 m_nbTrans = (nbTrans == 0) ? 1 : nbTrans;
828 if (dataRate != 0xF) // If value is 0xF, ignore config.
829 {
830 m_dataRate = dataRate;
831 }
832 NS_LOG_DEBUG("MacTxDataRateAdr = " << unsigned(m_dataRate));
833 NS_LOG_DEBUG("MacTxPower = " << unsigned(m_txPowerDbm) << "dBm");
834 NS_LOG_DEBUG("MacNbTrans = " << unsigned(m_nbTrans));
835 }
836 }
837
838 NS_LOG_INFO("Adding LinkAdrAns reply");
839 m_macCommandList.emplace_back(Create<LinkAdrAns>(powerAck, dataRateAck, channelMaskAck));
840}
841
842void
844{
845 NS_LOG_FUNCTION(this << unsigned(maxDutyCycle));
846 NS_ASSERT_MSG(!(maxDutyCycle & 0xF0), "maxDutyCycle > 4 bits");
847 m_aggregatedDutyCycle = 1 / std::pow(2, maxDutyCycle);
848 NS_LOG_INFO("Adding DutyCycleAns reply");
850}
851
852void
854{
855 NS_LOG_FUNCTION(this);
856
857 uint8_t battery = 255; // could not measure
858 if (m_device && m_device->GetNode())
859 {
860 if (auto sc = m_device->GetNode()->GetObject<energy::EnergySourceContainer>();
861 sc && sc->GetN() == 1)
862 {
863 battery = sc->Get(0)->GetEnergyFraction() * 253 + 1.5; // range 1-254
864 }
865 }
866 else
867 {
868 battery = 0; // external power source
869 }
870
871 // approximate to nearest integer
872 double snr = round(m_lastRxSnr);
873 // clamp value to boundaries
874 snr = snr < -32 ? -32 : snr > 31 ? 31 : snr;
875 // cast to 6-bit signed int and store in uint8_t
876 uint8_t margin = std::bitset<6>(snr).to_ulong();
877
878 NS_LOG_INFO("Adding DevStatusAns reply");
879 m_macCommandList.emplace_back(Create<DevStatusAns>(battery, margin));
880}
881
882void
884 uint32_t frequencyHz,
885 uint8_t minDataRate,
886 uint8_t maxDataRate)
887{
888 NS_LOG_FUNCTION(this << unsigned(chIndex) << frequencyHz << unsigned(minDataRate)
889 << unsigned(maxDataRate));
890
891 NS_ASSERT_MSG(!(minDataRate & 0xF0), "minDataRate field > 4 bits");
892 NS_ASSERT_MSG(!(maxDataRate & 0xF0), "maxDataRate field > 4 bits");
893
894 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
895 // For the time being, this implementation is valid for the EU868 region
896
897 bool dataRateRangeOk = true;
898 bool channelFrequencyOk = true;
899
900 // Valid Channel Index
901 if (chIndex < 3 || chIndex > m_channelHelper->GetRawChannelArray().size() - 1)
902 {
903 NS_LOG_WARN("[WARNING] Invalid channel index");
904 dataRateRangeOk = channelFrequencyOk = false;
905 }
906
907 // Valid Frequency
908 if (frequencyHz != 0 && !m_channelHelper->IsFrequencyValid(frequencyHz))
909 {
910 NS_LOG_WARN("[WARNING] Invalid frequency");
911 channelFrequencyOk = false;
912 }
913
914 // Valid DRMIN/MAX
915 if (!GetSfFromDataRate(minDataRate) || !GetBandwidthFromDataRate(minDataRate))
916 {
917 NS_LOG_WARN("[WARNING] Invalid DR min");
918 dataRateRangeOk = false;
919 }
920
921 if (!GetSfFromDataRate(maxDataRate) || !GetBandwidthFromDataRate(maxDataRate))
922 {
923 NS_LOG_WARN("[WARNING] Invalid DR max");
924 dataRateRangeOk = false;
925 }
926
927 if (maxDataRate < minDataRate)
928 {
929 NS_LOG_WARN("[WARNING] Invalid DR max < DR min");
930 dataRateRangeOk = false;
931 }
932
933 if (dataRateRangeOk && channelFrequencyOk)
934 {
935 auto channel = Create<LogicalLoraChannel>(frequencyHz, minDataRate, maxDataRate);
936 (frequencyHz == 0) ? channel->DisableForUplink() : channel->EnableForUplink();
937 m_channelHelper->SetChannel(chIndex, channel);
938 NS_LOG_DEBUG("MacTxFrequency[" << unsigned(chIndex) << "]=" << frequencyHz
939 << ", DrMin=" << unsigned(minDataRate)
940 << ", DrMax=" << unsigned(maxDataRate));
941 }
942
943 NS_LOG_INFO("Adding NewChannelAns reply");
944 m_macCommandList.emplace_back(Create<NewChannelAns>(dataRateRangeOk, channelFrequencyOk));
945}
946
947uint8_t
952
953uint8_t
958
959double
966
967void
969{
970 NS_LOG_FUNCTION(this << macCommand);
971
972 m_macCommandList.push_back(macCommand);
973}
974
975} // namespace lorawan
976} // namespace ns3
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 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:268
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.
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.
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...
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.
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: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:30
@ CR_4_5
Coding rate 4/5.
Definition lora-phy.h:31
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:454
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition simulator.cc:288
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:605
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition boolean.h:70
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Definition enum.h:223
Structure representing the parameters that will be used in the retransmission procedure.