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
30NS_OBJECT_ENSURE_REGISTERED(EndDeviceLorawanMac);
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.",
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("EnableEDDataRateAdaptation",
85 "Whether the end device should up its data rate "
86 "in case it doesn't get a reply from the network server.",
87 BooleanValue(false),
90 .AddAttribute("MType",
91 "Specify type of message will be sent by this end device.",
95 "Unconfirmed",
97 "Confirmed"));
98 return tid;
99}
100
102 : m_enableDRAdapt(false),
103 m_nbTrans(1),
104 m_dataRate(0),
105 m_txPowerDbm(14),
106 m_codingRate(1),
107 // LoraWAN default
108 m_headerDisabled(false),
109 // LoraWAN default
110 m_address(LoraDeviceAddress(0)),
111 // LoraWAN default
112 m_receiveWindowDurationInSymbols(8),
113 // Max initial value
114 m_lastRxSnr(32),
115 m_adr(true),
116 m_lastKnownLinkMarginDb(0),
117 m_lastKnownGatewayCount(0),
118 m_aggregatedDutyCycle(1),
119 m_mType(LorawanMacHeader::CONFIRMED_DATA_UP),
120 m_currentFCnt(0)
121{
122 NS_LOG_FUNCTION(this);
123
124 // Initialize the random variable we'll use to decide which channel to
125 // transmit on.
127
128 // Void the transmission event
129 m_nextTx = EventId();
131
132 // Initialize structure for retransmission parameters
135}
136
141
142////////////////////////
143// Sending methods //
144////////////////////////
145
146void
148{
149 NS_LOG_FUNCTION(this << packet);
150
151 // If it is not possible to transmit now because of the duty cycle,
152 // or because we are receiving, schedule a tx/retx later
153
154 Time netxTxDelay = GetNextTransmissionDelay();
155 if (netxTxDelay.IsStrictlyPositive())
156 {
157 postponeTransmission(netxTxDelay, packet);
158 return;
159 }
160
161 // Pick a channel on which to transmit the packet
163
164 if (!(txChannel && m_retxParams.retxLeft > 0))
165 {
166 if (!txChannel)
167 {
169 }
170 else
171 {
172 NS_LOG_INFO("Max number of transmission achieved: packet not transmitted.");
173 }
174 }
175 else
176 // the transmitting channel is available and we have not run out the maximum number of
177 // retransmissions
178 {
179 // Make sure we can transmit at the current power on this channel
180 NS_ASSERT_MSG(m_txPowerDbm <= m_channelHelper->GetTxPowerForChannel(txChannel),
181 " The selected power is too high to be supported by this channel.");
182 DoSend(packet);
183 }
184}
185
186void
188{
189 NS_LOG_FUNCTION(this);
190 // Delete previously scheduled transmissions if any.
192 m_nextTx = Simulator::Schedule(netxTxDelay, &EndDeviceLorawanMac::DoSend, this, packet);
193 NS_LOG_WARN("Attempting to send, but the aggregate duty cycle won't allow it. Scheduling a tx "
194 "at a delay "
195 << netxTxDelay.As(Time::S) << ".");
196}
197
198void
200{
201 NS_LOG_FUNCTION(this);
202 // Checking if this is the transmission of a new packet
203 if (packet != m_retxParams.packet)
204 {
206 "Received a new packet from application. Resetting retransmission parameters.");
208 NS_LOG_DEBUG("APP packet: " << packet << ".");
209
210 // Add the Lora Frame Header to the packet
211 LoraFrameHeader frameHdr;
212 ApplyNecessaryOptions(frameHdr);
213 packet->AddHeader(frameHdr);
214
215 auto fhdrSize = frameHdr.GetSerializedSize();
216 NS_LOG_INFO("Added frame header of size " << fhdrSize << " bytes.");
217
218 // Check that MACPayload length is below the allowed maximum
219 if (packet->GetSize() > m_maxAppPayloadForDataRate.at(m_dataRate))
220 {
221 NS_LOG_WARN("Attempting to send a packet larger than the maximum allowed"
222 << " size at this Data Rate (DR" << unsigned(m_dataRate)
223 << "). Transmission canceled.");
224 return;
225 }
226
227 // Add the Lora Mac header to the packet
228 LorawanMacHeader macHdr;
229 ApplyNecessaryOptions(macHdr);
230 packet->AddHeader(macHdr);
231
232 // Reset MAC command list
233 m_macCommandList.clear();
234
236 {
237 // Call the callback to notify about the failure
238 uint8_t txs = m_nbTrans - (m_retxParams.retxLeft);
240 NS_LOG_DEBUG(" Received new packet from the application layer: stopping retransmission "
241 "procedure. Used "
242 << unsigned(txs) << " transmissions out of a maximum of "
243 << unsigned(m_nbTrans) << ".");
244 }
245
246 // Reset retransmission parameters
248
249 // If this is the first transmission of a confirmed packet, save parameters for the
250 // (possible) next retransmissions.
252 {
253 m_retxParams.packet = packet->Copy();
258 m_retxParams.retxLeft - 1; // decreasing the number of retransmissions
259
260 NS_LOG_DEBUG("Message type is " << m_mType);
261 NS_LOG_DEBUG("It is a confirmed packet. Setting retransmission parameters and "
262 "decreasing the number of transmissions left.");
263
264 auto mhdrSize = macHdr.GetSerializedSize();
265 NS_LOG_INFO("Added MAC header of size " << mhdrSize << " bytes.");
266
267 // Sent a new packet
268 NS_LOG_DEBUG("Copied packet: " << m_retxParams.packet);
270
271 // static_cast<ClassAEndDeviceLorawanMac*>(this)->SendToPhy (m_retxParams.packet);
273 }
274 else
275 {
276 m_sentNewPacket(packet);
277 // static_cast<ClassAEndDeviceLorawanMac*>(this)->SendToPhy (packet);
278 SendToPhy(packet);
279 }
280 }
281 // this is a retransmission
282 else
283 {
285 {
286 // Remove the headers
287 LorawanMacHeader macHdr;
288 LoraFrameHeader frameHdr;
289 packet->RemoveHeader(macHdr);
290 packet->RemoveHeader(frameHdr);
291
292 // Add the Lora Frame Header to the packet
293 frameHdr = LoraFrameHeader();
294 ApplyNecessaryOptions(frameHdr);
295 packet->AddHeader(frameHdr);
296
297 auto fhdrSize = frameHdr.GetSerializedSize();
298 NS_LOG_INFO("Added frame header of size " << fhdrSize << " bytes.");
299
300 // Add the Lorawan Mac header to the packet
301 macHdr = LorawanMacHeader();
302 ApplyNecessaryOptions(macHdr);
303 packet->AddHeader(macHdr);
305 m_retxParams.retxLeft - 1; // decreasing the number of retransmissions
306 NS_LOG_DEBUG("Retransmitting an old packet.");
307
308 // static_cast<ClassAEndDeviceLorawanMac*>(this)->SendToPhy (m_retxParams.packet);
310 }
311 }
312}
313
314void
318
319//////////////////////////
320// Receiving methods //
321//////////////////////////
322
323void
327
328void
332
333void
335{
336 NS_LOG_FUNCTION(this << frameHeader);
337
339 {
340 if (frameHeader.GetAck())
341 {
342 NS_LOG_INFO("The message is an ACK, not waiting for it anymore.");
343
344 NS_LOG_DEBUG("Reset retransmission variables to default values and cancel "
345 "retransmission if already scheduled.");
346
347 uint8_t txs = m_nbTrans - (m_retxParams.retxLeft);
349 NS_LOG_DEBUG("Received ACK packet after "
350 << unsigned(txs) << " transmissions: stopping retransmission procedure. ");
351
352 // Reset retransmission parameters
354 }
355 else
356 {
358 "Received downlink message not containing an ACK while we were waiting for it!");
359 }
360 }
361
362 for (const auto& c : frameHeader.GetCommands())
363 {
364 NS_LOG_DEBUG("Iterating over the MAC commands...");
365 enum MacCommandType type = (c)->GetCommandType();
366 switch (type)
367 {
368 case (LINK_CHECK_ANS): {
369 NS_LOG_DEBUG("Detected a LinkCheckAns command.");
370 auto linkCheckAns = DynamicCast<LinkCheckAns>(c);
371 OnLinkCheckAns(linkCheckAns->GetMargin(), linkCheckAns->GetGwCnt());
372 break;
373 }
374 case (LINK_ADR_REQ): {
375 NS_LOG_DEBUG("Detected a LinkAdrReq command.");
376 auto linkAdrReq = DynamicCast<LinkAdrReq>(c);
377 OnLinkAdrReq(linkAdrReq->GetDataRate(),
378 linkAdrReq->GetTxPower(),
379 linkAdrReq->GetChMask(),
380 linkAdrReq->GetChMaskCntl(),
381 linkAdrReq->GetNbTrans());
382 break;
383 }
384 case (DUTY_CYCLE_REQ): {
385 NS_LOG_DEBUG("Detected a DutyCycleReq command.");
386 auto dutyCycleReq = DynamicCast<DutyCycleReq>(c);
387 OnDutyCycleReq(dutyCycleReq->GetMaxDutyCycle());
388 break;
389 }
390 case (RX_PARAM_SETUP_REQ): {
391 NS_LOG_DEBUG("Detected a RxParamSetupReq command.");
392 auto rxParamSetupReq = DynamicCast<RxParamSetupReq>(c);
393 OnRxParamSetupReq(rxParamSetupReq->GetRx1DrOffset(),
394 rxParamSetupReq->GetRx2DataRate(),
395 rxParamSetupReq->GetFrequency());
396 break;
397 }
398 case (DEV_STATUS_REQ): {
399 NS_LOG_DEBUG("Detected a DevStatusReq command.");
400 auto devStatusReq = DynamicCast<DevStatusReq>(c);
402 break;
403 }
404 case (NEW_CHANNEL_REQ): {
405 NS_LOG_DEBUG("Detected a NewChannelReq command.");
406 auto newChannelReq = DynamicCast<NewChannelReq>(c);
407 OnNewChannelReq(newChannelReq->GetChannelIndex(),
408 newChannelReq->GetFrequency(),
409 newChannelReq->GetMinDataRate(),
410 newChannelReq->GetMaxDataRate());
411 break;
412 }
413 case (RX_TIMING_SETUP_REQ):
414 case (TX_PARAM_SETUP_REQ):
415 case (DL_CHANNEL_REQ):
416 default: {
417 NS_LOG_ERROR("CID not recognized or supported");
418 break;
419 }
420 }
421 }
422}
423
424void
426{
428
429 frameHeader.SetAsUplink();
430 frameHeader.SetFPort(1); // TODO Use an appropriate frame port based on the application
431 frameHeader.SetAddress(m_address);
432 frameHeader.SetAdr(m_adr);
433 frameHeader.SetAdrAckReq(false); // TODO Set ADRACKREQ if a member variable is true
434
435 // FPending does not exist in uplink messages
436 frameHeader.SetFCnt(m_currentFCnt);
437
438 // Add listed MAC commands
439 for (const auto& command : m_macCommandList)
440 {
441 NS_LOG_INFO("Applying a MAC Command of CID "
442 << unsigned(MacCommand::GetCIDFromMacCommand(command->GetCommandType())));
443
444 frameHeader.AddCommand(command);
445 }
446}
447
448void
450{
452
453 macHeader.SetMType(m_mType);
454 macHeader.SetMajor(1);
455}
456
457void
459{
460 m_mType = mType;
461 NS_LOG_DEBUG("Message type is set to " << mType);
462}
463
469
470void
474
475Time
481
482Time
484{
485 NS_LOG_FUNCTION(this);
486 // Check duty cycle
487 /// @todo possibly move to LogicalChannelHelper
488 auto waitTime = Time::Max();
489 for (const auto& channel : m_channelHelper->GetRawChannelArray())
490 {
491 if (channel && channel->IsEnabledForUplink()) // Skip empty frequency channel slots
492 {
493 auto curr = m_channelHelper->GetWaitTime(channel);
494 if (curr < waitTime)
495 {
496 waitTime = curr;
497 }
498 NS_LOG_DEBUG("frequency=" << channel->GetFrequency() << "Hz,"
499 << " waitTime=" << waitTime.As(Time::S));
500 }
501 }
502 return GetNextClassTransmissionDelay(waitTime);
503}
504
507{
508 NS_LOG_FUNCTION(this);
509 /// @todo possibly move to LogicalChannelHelper
510 std::vector<Ptr<LogicalLoraChannel>> candidates;
511 for (const auto& channel : m_channelHelper->GetRawChannelArray())
512 {
513 if (channel && channel->IsEnabledForUplink()) // Skip empty frequency channel slots
514 {
515 uint8_t minDr = channel->GetMinimumDataRate();
516 uint8_t maxDr = channel->GetMaximumDataRate();
517 Time waitTime = m_channelHelper->GetWaitTime(channel);
518 NS_LOG_DEBUG("Enabled channel: frequency="
519 << channel->GetFrequency() << " Hz, minDr=" << unsigned(minDr)
520 << ", maxDr=" << unsigned(maxDr) << ", waitTime=" << waitTime.As(Time::S));
521 if (m_dataRate >= minDr && m_dataRate <= maxDr && waitTime.IsZero())
522 {
523 candidates.emplace_back(channel);
524 }
525 }
526 }
527 if (candidates.empty())
528 {
529 NS_LOG_DEBUG("No suitable TX channel found");
530 return nullptr;
531 }
532 uint8_t i = m_uniformRV->GetInteger(0, candidates.size() - 1);
533 auto channel = candidates.at(i);
534 NS_LOG_DEBUG("Selected channel with frequency=" << channel->GetFrequency() << " Hz");
535 return channel;
536}
537
538/////////////////////////
539// Setters and Getters //
540/////////////////////////
541
542void
544{
545 m_retxParams.waitingAck = false;
547 m_retxParams.packet = nullptr;
549
550 // Cancel next retransmissions, if any
552}
553
554void
556{
557 NS_LOG_FUNCTION(this << adr);
558 m_adr = adr;
559}
560
561bool
563{
564 NS_LOG_FUNCTION(this);
565 return m_adr;
566}
567
568void
570{
571 NS_LOG_FUNCTION(this << unsigned(nbTrans));
572 m_nbTrans = nbTrans;
573 m_retxParams.retxLeft = nbTrans;
574}
575
576uint8_t
582
583void
585{
586 NS_LOG_FUNCTION(this << unsigned(dataRate));
587
588 m_dataRate = dataRate;
589}
590
591uint8_t
593{
594 NS_LOG_FUNCTION(this);
595
596 return m_dataRate;
597}
598
599void
601{
602 NS_LOG_FUNCTION(this << txPowerDbm);
603 m_txPowerDbm = txPowerDbm;
604}
605
606double
612
613void
615{
616 NS_LOG_FUNCTION(this << address);
617
618 m_address = address;
619}
620
628
629void
630EndDeviceLorawanMac::OnLinkCheckAns(uint8_t margin, uint8_t gwCnt)
631{
632 NS_LOG_FUNCTION(this << unsigned(margin) << unsigned(gwCnt));
633
636}
637
638void
640 uint8_t txPower,
641 uint16_t chMask,
642 uint8_t chMaskCntl,
643 uint8_t nbTrans)
644{
645 NS_LOG_FUNCTION(this << unsigned(dataRate) << unsigned(txPower) << std::bitset<16>(chMask)
646 << unsigned(chMaskCntl) << unsigned(nbTrans));
647
648 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
649 // For the time being, this implementation is valid for the EU868 region
650
651 NS_ASSERT_MSG(!(dataRate & 0xF0), "dataRate field > 4 bits");
652 NS_ASSERT_MSG(!(txPower & 0xF0), "txPower field > 4 bits");
653 NS_ASSERT_MSG(!(chMaskCntl & 0xF8), "chMaskCntl field > 3 bits");
654 NS_ASSERT_MSG(!(nbTrans & 0xF0), "nbTrans field > 4 bits");
655
656 auto channels = m_channelHelper->GetRawChannelArray();
657
658 bool channelMaskAck = true;
659 bool dataRateAck = true;
660 bool powerAck = true;
661
662 NS_LOG_DEBUG("Channel mask = " << std::bitset<16>(chMask)
663 << ", ChMaskCtrl = " << unsigned(chMaskCntl));
664
665 // Check channel mask
666 switch (chMaskCntl)
667 {
668 // Channels 0 to 15
669 case 0:
670 // Check if all enabled channels have a valid frequency
671 for (size_t i = 0; i < channels.size(); ++i)
672 {
673 if ((chMask & 0b1 << i) && !channels.at(i))
674 {
675 NS_LOG_WARN("Invalid channel mask");
676 channelMaskAck = false;
677 break; // break for loop
678 }
679 }
680 break;
681 // All channels ON independently of the ChMask field value
682 case 6:
683 chMask = 0b0;
684 for (size_t i = 0; i < channels.size(); ++i)
685 {
686 if (channels.at(i))
687 {
688 chMask |= 0b1 << i;
689 }
690 }
691 break;
692 default:
693 NS_LOG_WARN("Invalid channel mask ctrl field");
694 channelMaskAck = false;
695 break;
696 }
697
698 // check if all channels are disabled
699 if (chMask == 0)
700 {
701 NS_LOG_WARN("Invalid channel mask");
702 channelMaskAck = false;
703 }
704
705 // Temporary channel mask is built and validated
706 if (!m_adr) // ADR disabled, only consider channel mask conf.
707 {
708 /// @remark Original code considers this to be mobile-mode
709 if (channelMaskAck) // valid channel mask
710 {
711 bool compatible = false;
712 // Look for enabled channel that supports current data rate.
713 for (size_t i = 0; i < channels.size(); ++i)
714 {
715 if ((chMask & 0b1 << i) && m_dataRate >= channels.at(i)->GetMinimumDataRate() &&
716 m_dataRate <= channels.at(i)->GetMaximumDataRate())
717 { // Found compatible channel, break loop
718 compatible = true;
719 break;
720 }
721 }
722 if (!compatible)
723 {
724 NS_LOG_WARN("Invalid channel mask for current device data rate (ADR off)");
725 channelMaskAck = dataRateAck = powerAck = false; // reject all configurations
726 }
727 else // apply channel mask configuration
728 {
729 for (size_t i = 0; i < channels.size(); ++i)
730 {
731 if (auto c = channels.at(i); c)
732 {
733 (chMask & 0b1 << i) ? c->EnableForUplink() : c->DisableForUplink();
734 }
735 }
736 dataRateAck = powerAck = false; // only ack channel mask
737 }
738 }
739 else // reject
740 {
741 NS_LOG_WARN("Invalid channel mask");
742 dataRateAck = powerAck = false; // reject all configurations
743 }
744 }
745 else // Server-side ADR is enabled
746 {
747 if (dataRate != 0xF) // If value is 0xF, ignore config.
748 {
749 bool compatible = false;
750 // Look for enabled channel that supports config. data rate.
751 for (size_t i = 0; i < channels.size(); ++i)
752 {
753 if (chMask & 0b1 << i) // all enabled by chMask, even if it was invalid
754 {
755 if (const auto& c = channels.at(i); c) // exists
756 {
757 if (dataRate >= c->GetMinimumDataRate() &&
758 dataRate <= c->GetMaximumDataRate())
759 { // Found compatible channel, break loop
760 compatible = true;
761 break;
762 }
763 }
764 else // manages invalid case, checks with defaults
765 {
766 if (GetSfFromDataRate(dataRate) && GetBandwidthFromDataRate(dataRate))
767 { // Found compatible (invalid) channel, break loop
768 compatible = true;
769 break;
770 }
771 }
772 }
773 }
774 // Check if it is acceptable
775 if (!compatible)
776 {
777 NS_LOG_WARN("Invalid data rate");
778 dataRateAck = false;
779 }
780 }
781
782 if (txPower != 0xF) // If value is 0xF, ignore config.
783 {
784 // Check if it is acceptable
785 if (GetDbmForTxPower(txPower) < 0)
786 {
787 NS_LOG_WARN("Invalid tx power");
788 powerAck = false;
789 }
790 }
791
792 // If no error, apply configurations
793 if (channelMaskAck && dataRateAck && powerAck)
794 {
795 for (size_t i = 0; i < channels.size(); ++i)
796 {
797 if (auto c = channels.at(i); c)
798 {
799 (chMask & 0b1 << i) ? c->EnableForUplink() : c->DisableForUplink();
800 }
801 }
802 if (txPower != 0xF) // If value is 0xF, ignore config.
803 {
805 }
806 m_nbTrans = (nbTrans == 0) ? 1 : nbTrans;
807 if (dataRate != 0xF) // If value is 0xF, ignore config.
808 {
809 m_dataRate = dataRate;
810 }
811 NS_LOG_DEBUG("MacTxDataRateAdr = " << unsigned(m_dataRate));
812 NS_LOG_DEBUG("MacTxPower = " << unsigned(m_txPowerDbm) << "dBm");
813 NS_LOG_DEBUG("MacNbTrans = " << unsigned(m_nbTrans));
814 }
815 }
816
817 NS_LOG_INFO("Adding LinkAdrAns reply");
818 m_macCommandList.emplace_back(Create<LinkAdrAns>(powerAck, dataRateAck, channelMaskAck));
819}
820
821void
823{
824 NS_LOG_FUNCTION(this << unsigned(maxDutyCycle));
825 NS_ASSERT_MSG(!(maxDutyCycle & 0xF0), "maxDutyCycle > 4 bits");
826 m_aggregatedDutyCycle = 1 / std::pow(2, maxDutyCycle);
827 NS_LOG_INFO("Adding DutyCycleAns reply");
829}
830
831void
833{
834 NS_LOG_FUNCTION(this);
835
836 uint8_t battery = 255; // could not measure
837 if (m_device && m_device->GetNode())
838 {
839 if (auto sc = m_device->GetNode()->GetObject<energy::EnergySourceContainer>();
840 sc && sc->GetN() == 1)
841 {
842 battery = sc->Get(0)->GetEnergyFraction() * 253 + 1.5; // range 1-254
843 }
844 }
845 else
846 {
847 battery = 0; // external power source
848 }
849
850 // approximate to nearest integer
851 double snr = round(m_lastRxSnr);
852 // clamp value to boundaries
853 snr = snr < -32 ? -32 : snr > 31 ? 31 : snr;
854 // cast to 6-bit signed int and store in uint8_t
855 uint8_t margin = std::bitset<6>(snr).to_ulong();
856
857 NS_LOG_INFO("Adding DevStatusAns reply");
858 m_macCommandList.emplace_back(Create<DevStatusAns>(battery, margin));
859}
860
861void
863 uint32_t frequencyHz,
864 uint8_t minDataRate,
865 uint8_t maxDataRate)
866{
867 NS_LOG_FUNCTION(this << unsigned(chIndex) << frequencyHz << unsigned(minDataRate)
868 << unsigned(maxDataRate));
869
870 NS_ASSERT_MSG(!(minDataRate & 0xF0), "minDataRate field > 4 bits");
871 NS_ASSERT_MSG(!(maxDataRate & 0xF0), "maxDataRate field > 4 bits");
872
873 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
874 // For the time being, this implementation is valid for the EU868 region
875
876 bool dataRateRangeOk = true;
877 bool channelFrequencyOk = true;
878
879 // Valid Channel Index
880 if (chIndex < 3 || chIndex > m_channelHelper->GetRawChannelArray().size() - 1)
881 {
882 NS_LOG_WARN("[WARNING] Invalid channel index");
883 dataRateRangeOk = channelFrequencyOk = false;
884 }
885
886 // Valid Frequency
887 if (frequencyHz != 0 && !m_channelHelper->IsFrequencyValid(frequencyHz))
888 {
889 NS_LOG_WARN("[WARNING] Invalid frequency");
890 channelFrequencyOk = false;
891 }
892
893 // Valid DRMIN/MAX
894 if (!GetSfFromDataRate(minDataRate) || !GetBandwidthFromDataRate(minDataRate))
895 {
896 NS_LOG_WARN("[WARNING] Invalid DR min");
897 dataRateRangeOk = false;
898 }
899
900 if (!GetSfFromDataRate(maxDataRate) || !GetBandwidthFromDataRate(maxDataRate))
901 {
902 NS_LOG_WARN("[WARNING] Invalid DR max");
903 dataRateRangeOk = false;
904 }
905
906 if (maxDataRate < minDataRate)
907 {
908 NS_LOG_WARN("[WARNING] Invalid DR max < DR min");
909 dataRateRangeOk = false;
910 }
911
912 if (dataRateRangeOk && channelFrequencyOk)
913 {
914 auto channel = Create<LogicalLoraChannel>(frequencyHz, minDataRate, maxDataRate);
915 (frequencyHz == 0) ? channel->DisableForUplink() : channel->EnableForUplink();
916 m_channelHelper->SetChannel(chIndex, channel);
917 NS_LOG_DEBUG("MacTxFrequency[" << unsigned(chIndex) << "]=" << frequencyHz
918 << ", DrMin=" << unsigned(minDataRate)
919 << ", DrMax=" << unsigned(maxDataRate));
920 }
921
922 NS_LOG_INFO("Adding NewChannelAns reply");
923 m_macCommandList.emplace_back(Create<NewChannelAns>(dataRateRangeOk, channelFrequencyOk));
924}
925
926uint8_t
931
932uint8_t
937
938double
945
946void
948{
949 NS_LOG_FUNCTION(this << macCommand);
950
951 m_macCommandList.push_back(macCommand);
952}
953
954} // namespace lorawan
955} // namespace ns3
Hold variables of type enum.
Definition enum.h:52
An identifier for simulation events.
Definition event-id.h:45
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
Hold a signed integer type.
Definition integer.h:34
Smart pointer class similar to boost::intrusive_ptr.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
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:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:340
@ S
second
Definition nstime.h:105
static Time Max()
Maximum representable Time Not to be confused with Max(Time,Time).
Definition nstime.h:286
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
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.
LorawanMacHeader::MType m_mType
The message type to apply to packets sent with the Send method.
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...
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.
Ptr< LogicalLoraChannel > GetChannelForTx()
Find a suitable channel for transmission.
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.
bool m_enableDRAdapt
Enable data rate adaptation (ADR) during the retransmission procedure.
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 ...
Ptr< UniformRandomVariable > m_uniformRV
An uniform random variable, used to randomly pick from the channel list.
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.
virtual void resetRetransmissionParameters()
Reset retransmission parameters contained in the structure LoraRetxParams.
void ParseCommands(LoraFrameHeader frameHeader)
Parse and take action on the commands contained on this FrameHeader.
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.
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.
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 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
Class representing the LoRaWAN MAC layer.
Definition lorawan-mac.h:37
TracedCallback< Ptr< const Packet > > m_cannotSendBecauseDutyCycle
The trace source that is fired when a packet cannot be sent because of duty cycle limitations.
std::vector< uint32_t > m_maxAppPayloadForDataRate
A vector holding the maximum app payload size that corresponds to a certain data rate.
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.
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_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_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:436
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: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:179
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
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.
bool waitingAck
Whether the packet requires explicit acknowledgment.
uint8_t retxLeft
Number of retransmission attempts left.
Time firstAttempt
Timestamp of the first transmission of the packet.
Ptr< Packet > packet
A pointer to the packet being retransmitted.