A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
class-a-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 * qiuyukang <b612n@qq.com>
11 */
12
14
15#include "end-device-lora-phy.h"
17
18#include "ns3/log.h"
19
20#include <algorithm>
21
22namespace ns3
23{
24namespace lorawan
25{
26
27NS_LOG_COMPONENT_DEFINE("ClassAEndDeviceLorawanMac");
28
29NS_OBJECT_ENSURE_REGISTERED(ClassAEndDeviceLorawanMac);
30
31TypeId
33{
34 static TypeId tid = TypeId("ns3::ClassAEndDeviceLorawanMac")
36 .SetGroupName("lorawan")
37 .AddConstructor<ClassAEndDeviceLorawanMac>();
38 return tid;
39}
40
42 : // LoraWAN default
43 m_receiveDelay1(Seconds(1)),
44 // LoraWAN default
45 m_receiveDelay2(Seconds(2)),
46 m_rx1DrOffset(0)
47{
48 NS_LOG_FUNCTION(this);
49
50 // Void the two receiveWindow events
57}
58
63
64/////////////////////
65// Sending methods //
66/////////////////////
67
68void
70{
71 /////////////////////////////////////////////////////////
72 // Add headers, prepare TX parameters and send the packet
73 /////////////////////////////////////////////////////////
74
75 NS_LOG_DEBUG("PacketToSend: " << packetToSend);
76
77 // Data rate adaptation as in LoRaWAN specification, V1.0.2 (2016)
79 (m_retxParams.retxLeft % 2 == 0))
80 {
81 m_txPower = 14; // Reset transmission power
83 }
84
85 // Craft LoraTxParameters object
86 LoraTxParameters params;
87 params.sf = GetSfFromDataRate(m_dataRate);
88 params.headerDisabled = m_headerDisabled;
89 params.codingRate = m_codingRate;
90 params.bandwidthHz = GetBandwidthFromDataRate(m_dataRate);
91 params.nPreamble = m_nPreambleSymbols;
92 params.crcEnabled = true;
93 params.lowDataRateOptimizationEnabled = LoraPhy::GetTSym(params) > MilliSeconds(16);
94
95 // Wake up PHY layer and directly send the packet
96
98
99 NS_LOG_DEBUG("PacketToSend: " << packetToSend);
100 m_phy->Send(packetToSend, params, txChannel->GetFrequency(), m_txPower);
101
102 //////////////////////////////////////////////
103 // Register packet transmission for duty cycle
104 //////////////////////////////////////////////
105
106 // Compute packet duration
107 Time duration = LoraPhy::GetOnAirTime(packetToSend, params);
108
109 // Register the sent packet into the DutyCycleHelper
110 m_channelHelper->AddEvent(duration, txChannel);
111
112 //////////////////////////////
113 // Prepare for the downlink //
114 //////////////////////////////
115
116 // Switch the PHY to the channel so that it will listen here for downlink
117 DynamicCast<EndDeviceLoraPhy>(m_phy)->SetFrequency(txChannel->GetFrequency());
118
119 // Instruct the PHY on the right Spreading Factor to listen for during the window
120 // create a SetReplyDataRate function?
121 uint8_t replyDataRate = GetFirstReceiveWindowDataRate();
122 NS_LOG_DEBUG("m_dataRate: " << unsigned(m_dataRate)
123 << ", m_rx1DrOffset: " << unsigned(m_rx1DrOffset)
124 << ", replyDataRate: " << unsigned(replyDataRate) << ".");
125
126 DynamicCast<EndDeviceLoraPhy>(m_phy)->SetSpreadingFactor(GetSfFromDataRate(replyDataRate));
127}
128
129//////////////////////////
130// Receiving methods //
131//////////////////////////
132void
134{
135 NS_LOG_FUNCTION(this << packet);
136
137 // Work on a copy of the packet
138 Ptr<Packet> packetCopy = packet->Copy();
139
140 // Remove the Mac Header to get some information
141 LorawanMacHeader mHdr;
142 packetCopy->RemoveHeader(mHdr);
143
144 NS_LOG_DEBUG("Mac Header: " << mHdr);
145
146 // Only keep analyzing the packet if it's downlink
147 if (!mHdr.IsUplink())
148 {
149 NS_LOG_INFO("Found a downlink packet.");
150
151 // Remove the Frame Header
152 LoraFrameHeader fHdr;
153 fHdr.SetAsDownlink();
154 packetCopy->RemoveHeader(fHdr);
155
156 NS_LOG_DEBUG("Frame Header: " << fHdr);
157
158 // Determine whether this packet is for us
159 bool messageForUs = (m_address == fHdr.GetAddress());
160
161 if (messageForUs)
162 {
163 NS_LOG_INFO("The message is for us!");
164
165 // If it exists, cancel the second receive window event
166 // THIS WILL BE GetReceiveWindow()
168
169 // Parse the MAC commands
170 ParseCommands(fHdr);
171
172 // TODO Pass the packet up to the NetDevice
173
174 // Call the trace source
175 m_receivedPacket(packet);
176 }
177 else
178 {
179 NS_LOG_DEBUG("The message is intended for another recipient.");
180
181 // In this case, we are either receiving in the first receive window
182 // and finishing reception inside the second one, or receiving a
183 // packet in the second receive window and finding out, after the
184 // fact, that the packet is not for us. In either case, if we no
185 // longer have any retransmissions left, we declare failure.
187 {
188 if (m_retxParams.retxLeft == 0)
189 {
190 uint8_t txs = m_maxNumbTx - (m_retxParams.retxLeft);
192 false,
195 NS_LOG_DEBUG("Failure: no more retransmissions left. Used "
196 << unsigned(txs) << " transmissions.");
197
198 // Reset retransmission parameters
200 }
201 else // Reschedule
202 {
203 this->Send(m_retxParams.packet);
204 NS_LOG_INFO("We have " << unsigned(m_retxParams.retxLeft)
205 << " retransmissions left: rescheduling transmission.");
206 }
207 }
208 }
209 }
211 {
212 NS_LOG_INFO("The packet we are receiving is in uplink.");
213 if (m_retxParams.retxLeft > 0)
214 {
215 this->Send(m_retxParams.packet);
216 NS_LOG_INFO("We have " << unsigned(m_retxParams.retxLeft)
217 << " retransmissions left: rescheduling transmission.");
218 }
219 else
220 {
221 uint8_t txs = m_maxNumbTx - (m_retxParams.retxLeft);
223 NS_LOG_DEBUG("Failure: no more retransmissions left. Used " << unsigned(txs)
224 << " transmissions.");
225
226 // Reset retransmission parameters
228 }
229 }
230
231 DynamicCast<EndDeviceLoraPhy>(m_phy)->SwitchToSleep();
232}
233
234void
236{
237 NS_LOG_FUNCTION(this << packet);
238
239 // Switch to sleep after a failed reception
240 DynamicCast<EndDeviceLoraPhy>(m_phy)->SwitchToSleep();
241
243 {
244 if (m_retxParams.retxLeft > 0)
245 {
246 this->Send(m_retxParams.packet);
247 NS_LOG_INFO("We have " << unsigned(m_retxParams.retxLeft)
248 << " retransmissions left: rescheduling transmission.");
249 }
250 else
251 {
252 uint8_t txs = m_maxNumbTx - (m_retxParams.retxLeft);
254 NS_LOG_DEBUG("Failure: no more retransmissions left. Used " << unsigned(txs)
255 << " transmissions.");
256
257 // Reset retransmission parameters
259 }
260 }
261}
262
263void
265{
267
268 // Schedule the opening of the first receive window
270
271 // Schedule the opening of the second receive window
274 this);
275 // // Schedule the opening of the first receive window
276 // Simulator::Schedule (m_receiveDelay1,
277 // &ClassAEndDeviceLorawanMac::OpenFirstReceiveWindow, this);
278 //
279 // // Schedule the opening of the second receive window
280 // m_secondReceiveWindow = Simulator::Schedule (m_receiveDelay2,
281 // &ClassAEndDeviceLorawanMac::OpenSecondReceiveWindow,
282 // this);
283
284 // Switch the PHY to sleep
285 DynamicCast<EndDeviceLoraPhy>(m_phy)->SwitchToSleep();
286}
287
288void
290{
292
293 // Set Phy in Standby mode
294 DynamicCast<EndDeviceLoraPhy>(m_phy)->SwitchToStandby();
295
296 // Calculate the duration of a single symbol for the first receive window data rate
297 double tSym = pow(2, GetSfFromDataRate(GetFirstReceiveWindowDataRate())) /
299
300 // Schedule return to sleep after "at least the time required by the end
301 // device's radio transceiver to effectively detect a downlink preamble"
302 // (LoraWAN specification)
305 this); // m_receiveWindowDuration
306}
307
308void
310{
312
314
315 // Check the Phy layer's state:
316 // - RX -> We are receiving a preamble.
317 // - STANDBY -> Nothing was received.
318 // - SLEEP -> We have received a packet.
319 // We should never be in TX or SLEEP mode at this point
320 switch (phy->GetState())
321 {
323 NS_ABORT_MSG("PHY was in TX mode when attempting to close a receive window.");
324 break;
326 // PHY is receiving: let it finish. The Receive method will switch it back to SLEEP.
328 // PHY has received, and the MAC's Receive already put the device to sleep
329 break;
331 // Turn PHY layer to SLEEP
332 phy->SwitchToSleep();
333 break;
334 }
335}
336
337void
339{
341
342 // Check for receiver status: if it's locked on a packet, don't open this
343 // window at all.
345 {
346 NS_LOG_INFO("Won't open second receive window since we are in RX mode.");
347
348 return;
349 }
350
351 // Set Phy in Standby mode
352 DynamicCast<EndDeviceLoraPhy>(m_phy)->SwitchToStandby();
353
354 // Switch to appropriate channel and data rate
355 NS_LOG_INFO("Using parameters: " << m_secondReceiveWindowFrequency << "Hz, DR"
356 << unsigned(m_secondReceiveWindowDataRate));
357
359 DynamicCast<EndDeviceLoraPhy>(m_phy)->SetSpreadingFactor(
361
362 // Calculate the duration of a single symbol for the second receive window data rate
363 double tSym = pow(2, GetSfFromDataRate(GetSecondReceiveWindowDataRate())) /
365
366 // Schedule return to sleep after "at least the time required by the end
367 // device's radio transceiver to effectively detect a downlink preamble"
368 // (LoraWAN specification)
371 this);
372}
373
374void
376{
378
380
381 // NS_ASSERT (phy->m_state != EndDeviceLoraPhy::TX &&
382 // phy->m_state != EndDeviceLoraPhy::SLEEP);
383
384 // Check the Phy layer's state:
385 // - RX -> We have received a preamble.
386 // - STANDBY -> Nothing was detected.
387 switch (phy->GetState())
388 {
391 break;
393 // PHY is receiving: let it finish
394 NS_LOG_DEBUG("PHY is receiving: Receive will handle the result.");
395 return;
397 // Turn PHY layer to sleep
398 phy->SwitchToSleep();
399 break;
400 }
401
403 {
404 NS_LOG_DEBUG("No reception initiated by PHY: rescheduling transmission.");
405 if (m_retxParams.retxLeft > 0)
406 {
407 NS_LOG_INFO("We have " << unsigned(m_retxParams.retxLeft)
408 << " retransmissions left: rescheduling transmission.");
409 this->Send(m_retxParams.packet);
410 }
411
412 else if (m_retxParams.retxLeft == 0 &&
414 {
415 uint8_t txs = m_maxNumbTx - (m_retxParams.retxLeft);
417 NS_LOG_DEBUG("Failure: no more retransmissions left. Used " << unsigned(txs)
418 << " transmissions.");
419
420 // Reset retransmission parameters
422 }
423
424 else
425 {
426 NS_ABORT_MSG("The number of retransmissions left is negative ! ");
427 }
428 }
429 else
430 {
431 uint8_t txs = m_maxNumbTx - (m_retxParams.retxLeft);
434 "We have " << unsigned(m_retxParams.retxLeft)
435 << " transmissions left. We were not transmitting confirmed messages.");
436
437 // Reset retransmission parameters
439 }
440}
441
442/////////////////////////
443// Getters and Setters //
444/////////////////////////
445
446Time
448{
450
451 // This is a new packet from APP; it can not be sent until the end of the
452 // second receive window (if the second receive window has not closed yet)
454 {
457 {
459 "Attempting to send when there are receive windows: Transmission postponed.");
460 // Compute the duration of a single symbol for the second receive window data rate
461 double tSym = pow(2, GetSfFromDataRate(GetSecondReceiveWindowDataRate())) /
463 // Compute the closing time of the second receive window
464 Time endSecondRxWindow = Time(m_secondReceiveWindow.GetTs()) +
466
467 NS_LOG_DEBUG("Duration until endSecondRxWindow for new transmission:"
468 << (endSecondRxWindow - Simulator::Now()).GetSeconds());
469 waitingTime = std::max(waitingTime, endSecondRxWindow - Simulator::Now());
470 }
471 }
472 // This is a retransmitted packet, it can not be sent until the end of
473 // ACK_TIMEOUT (this timer starts when the second receive window was open)
474 else
475 {
476 double ack_timeout = m_uniformRV->GetValue(1, 3);
477 // Compute the duration until ACK_TIMEOUT (It may be a negative number, but it doesn't
478 // matter.)
479 Time retransmitWaitingTime =
481
482 NS_LOG_DEBUG("ack_timeout:" << ack_timeout << " retransmitWaitingTime:"
483 << retransmitWaitingTime.GetSeconds());
484 waitingTime = std::max(waitingTime, retransmitWaitingTime);
485 }
486
487 return waitingTime;
488}
489
490uint8_t
495
496void
501
502uint8_t
507
508void
513
514double
519
520/////////////////////////
521// MAC command methods //
522/////////////////////////
523
524void
526{
527 NS_LOG_FUNCTION(this << rxParamSetupReq);
528
529 bool offsetOk = true;
530 bool dataRateOk = true;
531
532 uint8_t rx1DrOffset = rxParamSetupReq->GetRx1DrOffset();
533 uint8_t rx2DataRate = rxParamSetupReq->GetRx2DataRate();
534 double frequency = rxParamSetupReq->GetFrequency();
535
536 NS_LOG_FUNCTION(this << unsigned(rx1DrOffset) << unsigned(rx2DataRate) << frequency);
537
538 // Check that the desired offset is valid
539 if (!(0 <= rx1DrOffset && rx1DrOffset <= 5))
540 {
541 offsetOk = false;
542 }
543
544 // Check that the desired data rate is valid
545 if (GetSfFromDataRate(rx2DataRate) == 0 || GetBandwidthFromDataRate(rx2DataRate) == 0)
546 {
547 dataRateOk = false;
548 }
549
550 // For now, don't check for validity of frequency
551 m_secondReceiveWindowDataRate = rx2DataRate;
552 m_rx1DrOffset = rx1DrOffset;
554
555 // Craft a RxParamSetupAns as response
556 NS_LOG_INFO("Adding RxParamSetupAns reply");
557 m_macCommandList.emplace_back(CreateObject<RxParamSetupAns>(offsetOk, dataRateOk, true));
558}
559
560} /* namespace lorawan */
561} /* namespace ns3 */
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
bool IsExpired() const
This method is syntactic sugar for the ns3::Simulator::IsExpired method.
Definition event-id.cc:58
uint64_t GetTs() const
Definition event-id.cc:85
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
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:392
a unique identifier for an interface.
Definition type-id.h:48
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Class representing the MAC layer of a Class A LoRaWAN device.
uint8_t m_rx1DrOffset
The RX1DROffset parameter value.
void OpenSecondReceiveWindow()
Perform operations needed to open the second receive window.
uint8_t GetSecondReceiveWindowDataRate() const
Get the data rate that will be used in the second receive window.
Time m_receiveDelay2
The interval between when a packet is done sending and when the second receive window is opened.
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...
double GetSecondReceiveWindowFrequency() const
Get the frequency that is used for the second receive window.
double m_secondReceiveWindowFrequency
The frequency to listen on for the second receive window.
EventId m_closeFirstWindow
The event of the closing the first receive window.
uint8_t m_secondReceiveWindowDataRate
The data rate to listen for during the second downlink transmission.
Time m_receiveDelay1
The interval between when a packet is done sending and when the first receive window is opened.
Time GetNextClassTransmissionDelay(Time waitingTime) override
Find the minimum waiting time before the next possible transmission based on end device's Class Type.
void OnRxClassParamSetupReq(Ptr< RxParamSetupReq > rxParamSetupReq) override
Perform the actions that need to be taken when receiving a RxParamSetupReq command based on the Devic...
void CloseFirstReceiveWindow()
Perform operations needed to close the first receive window.
void SendToPhy(Ptr< Packet > packet) override
Add headers and send a packet with the sending function of the physical layer.
void TxFinished(Ptr< const Packet > packet) override
Perform actions after sending a packet.
void CloseSecondReceiveWindow()
Perform operations needed to close the second receive window.
uint8_t GetFirstReceiveWindowDataRate()
Get the data rate that will be used in the first receive window.
EventId m_closeSecondWindow
The event of the closing the second receive window.
EventId m_secondReceiveWindow
The event of the second receive window opening.
void SetSecondReceiveWindowDataRate(uint8_t dataRate)
Set the data rate to be used in the second receive window.
void Receive(Ptr< const Packet > packet) override
Receive a packet.
void SetSecondReceiveWindowFrequency(double frequencyMHz)
Set the frequency that will be used for the second receive window.
void OpenFirstReceiveWindow()
Perform operations needed to open the first receive window.
@ RX
The PHY layer is receiving a packet.
@ TX
The PHY layer is sending a packet.
@ SLEEP
The PHY layer is sleeping.
@ STANDBY
The PHY layer is in STANDBY.
Class representing the MAC layer of a LoRaWAN device.
uint8_t m_maxNumbTx
Default number of unacknowledged redundant transmissions of each packet.
bool m_headerDisabled
Whether or not the LoRa PHY header is disabled for communications by this device.
Ptr< LogicalLoraChannel > GetChannelForTx()
Find a suitable channel for transmission.
TracedValue< double > m_txPower
The transmission power this device is using to transmit.
uint8_t m_codingRate
The coding rate used by this device.
bool m_enableDRAdapt
Enable data rate adaptation (ADR) during the retransmission procedure.
Ptr< UniformRandomVariable > m_uniformRV
An uniform random variable, used by the Shuffle method to randomly reorder the channel list.
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.
TracedCallback< uint8_t, bool, Time, Ptr< Packet > > m_requiredTxCallback
The trace source fired when the transmission procedure is finished.
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.
void Send(Ptr< Packet > packet) override
Send a packet.
uint8_t m_receiveWindowDurationInSymbols
The duration of a receive window in number of symbols.
struct LoraRetxParameters m_retxParams
Structure containing the retransmission parameters for this device.
This class represents the Frame header (FHDR) used in a LoraWAN network.
LoraDeviceAddress GetAddress() const
Get this header's device address value.
void SetAsDownlink()
State that this is a downlink message.
static Time GetOnAirTime(Ptr< Packet > packet, LoraTxParameters txParams)
Compute the time that a packet with certain characteristics will take to be transmitted.
Definition lora-phy.cc:156
static Time GetTSym(LoraTxParameters txParams)
Compute the symbol time from spreading factor and bandwidth.
Definition lora-phy.cc:150
This class represents the Mac header of a LoRaWAN packet.
bool IsUplink() const
Check whether this header is for an uplink message.
TracedCallback< Ptr< const Packet > > m_receivedPacket
Trace source that is fired when a packet reaches the MAC layer.
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.
ReplyDataRateMatrix m_replyDataRateMatrix
The matrix that decides the data rate the gateway will use in a reply based on the end device's sendi...
Ptr< LogicalLoraChannelHelper > m_channelHelper
The LogicalLoraChannelHelper instance that is assigned to this MAC.
Ptr< LoraPhy > m_phy
The PHY instance that sits under this MAC layer.
int m_nPreambleSymbols
The number of symbols to use in the PHY preamble.
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#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
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1308
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1320
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
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.
Structure to collect all parameters that are used to compute the duration of a packet (excluding payl...
Definition lora-phy.h:38