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"
16#include "lora-tag.h"
17
18#include "ns3/simulator.h"
19
20namespace ns3
21{
22namespace lorawan
23{
24
25NS_LOG_COMPONENT_DEFINE("ClassAEndDeviceLorawanMac");
26
28
29TypeId
31{
32 static TypeId tid = TypeId("ns3::ClassAEndDeviceLorawanMac")
34 .SetGroupName("lorawan")
35 .AddConstructor<ClassAEndDeviceLorawanMac>();
36 return tid;
37}
38
40 : // LoraWAN defaults
44 m_busy(false)
45{
46 NS_LOG_FUNCTION(this);
47 // Void the RX2 event
49 m_secondReceiveWindow.Cancel();
50}
51
56
57uint8_t
62
63void
68
69uint8_t
74
75void
80
86
87Time
89{
90 NS_LOG_FUNCTION(this);
91 if (m_busy) // device is in the process of sending and opening RX windows
92 {
93 NS_LOG_WARN("Attempting to send when device is still busy, postponed by 5s.");
94 return Seconds(5);
95 }
96 return Time();
97}
98
99void
101{
102 NS_LOG_DEBUG("PacketToSend: " << packetToSend);
103
104 // Lock the MAC layer during the TX -> RX1 -> RX2 process
105 NS_ASSERT_MSG(!m_busy, "Trying to send while device is already marked busy");
106 m_busy = true;
107
108 /////////////////////////
109 // Prepare TX parameters
110 /////////////////////////
111
112 auto sf = GetSfFromDataRate(m_dataRate);
114 // see SX1272/73 Datasheet, Section 4.1.1.6, Rev. 4, Jan. 2019
115 auto ldro = bool((sf == 11 || sf == 12) && bw == 125'000);
116
117 // Craft LoraTxParameters object
118 LoraTxParameters params;
119 params.spreadingFactor = sf;
120 params.bandwidthHz = bw;
121 params.codingRate = m_codingRate;
122 params.lowDataRateOptimize = ldro;
123 params.preambleLenSymb = m_nPreambleSymbols;
124 params.implicitHeader = m_headerDisabled;
125 params.crcEnabled = true;
126
127 // Select random frequency channel
129
130 ///////////////////////////////////////////////
131 // Register packet transmission for duty cycle
132 ///////////////////////////////////////////////
133
134 // Compute tx duration for duty-cycle management
135 Time duration = LoraPhy::GetTimeOnAir(packetToSend->GetSize(), params);
136 // Register the tx duration into the LogicalLoraChannelHelper
137 m_channelHelper->AddEvent(duration, txChannel);
138
139 /////////////////////////////////////////
140 // Store dynamic RX1 window parameters //
141 /////////////////////////////////////////
142
143 // Switch the PHY to the channel so that it will listen here for downlink
144 m_firstReceiveWindowFrequencyHz = txChannel->GetFrequency();
145
146 /////////////////////
147 // Init transmission
148 /////////////////////
149
150 // Check that the PHY layer is in an expected state
151 auto phyState = DynamicCast<EndDeviceLoraPhy>(m_phy)->GetState();
154 "Busy PHY device (not in SLEEP or STANDBY state): phyState=" << phyState);
155 // Wake up PHY layer and directly send the packet
156 m_phy->Send(packetToSend, txChannel->GetFrequency(), IQPolarity::UP, params, m_txPowerDbm);
157}
158
159void
161{
162 NS_LOG_FUNCTION(this << packet);
163 // We should always be in STANDBY mode at this point, as this function is meant to be
164 // invoked as a callback by the PHY on transmission end
165 auto phyState = DynamicCast<EndDeviceLoraPhy>(m_phy)->GetState();
167 "Unexpected PHY state on TX conclusion: phyState=" << phyState);
168 // Switch the PHY to sleep
170
171 // Schedule the opening of the first receive window
173
174 // Schedule the opening of the second receive window
177 this);
178}
179
180void
182{
183 NS_LOG_FUNCTION(this);
184 // We should always be in SLEEP mode at this point
185 auto phyState = DynamicCast<EndDeviceLoraPhy>(m_phy)->GetState();
187 "Unexpected PHY state on RX1 opening: phyState=" << phyState);
188
189 // Gather the parameters required for the first reception window
190 auto rx1DataRate = GetFirstReceiveWindowDataRate();
191 NS_LOG_DEBUG("m_dataRate=" << unsigned(m_dataRate)
192 << ", m_rx1DrOffset=" << unsigned(m_rx1DrOffset)
193 << ", rx1DataRate=" << unsigned(rx1DataRate));
194 // Request a timed reception from the PHY
195 DynamicCast<EndDeviceLoraPhy>(m_phy)->ReceiveSingle(
198 GetSfFromDataRate(rx1DataRate),
199 GetBandwidthFromDataRate(rx1DataRate),
202}
203
204void
206{
207 NS_LOG_FUNCTION(this);
208 // We should always be in STANDBY mode at this point, as this function is meant to be
209 // invoked as a callback by the PHY on reception timeout
210 auto phyState = DynamicCast<EndDeviceLoraPhy>(m_phy)->GetState();
212 "Unexpected PHY state on RX1 closure: phyState=" << phyState);
214}
215
216void
218{
219 NS_LOG_FUNCTION(this);
220 // We might be in SLEEP or in RX_ACTIVE mode at this point
221 auto phyState = DynamicCast<EndDeviceLoraPhy>(m_phy)->GetState();
224 "Unexpected PHY state on RX2 opening: phyState=" << phyState);
225
226 // Return immediately if a reception started on RX1 has not concluded yet
228 {
229 return;
230 }
231
232 // Gather the parameters required for the second reception window
233 NS_LOG_DEBUG("m_secondReceiveWindowFrequencyHz=" << m_secondReceiveWindowFrequencyHz
234 << ", m_secondReceiveWindowDataRate="
235 << unsigned(m_secondReceiveWindowDataRate));
236 // Request a timed reception from the PHY
237 DynamicCast<EndDeviceLoraPhy>(m_phy)->ReceiveSingle(
244}
245
246void
248{
249 NS_LOG_FUNCTION(this);
250 // We should always be in STANDBY mode at this point, as this function is meant to be
251 // invoked as a callback by the PHY on reception timeout
252 auto phyState = DynamicCast<EndDeviceLoraPhy>(m_phy)->GetState();
254 "Unexpected PHY state on RX2 closure: phyState=" << phyState);
256
257 // We are here if no reception happened
259 // Open the context to new transmissions
260 m_busy = false;
261}
262
263void
265{
266 NS_LOG_FUNCTION(this << outcome);
267
268 bool recv = (outcome == RECV || outcome == ACK); // We received something
269 bool needsAck = m_txContext.needsAck; // We were waiting for acknowledgement
270 bool gotAck = (outcome == ACK); // We got acknowledgement
271 bool canReTx = (m_txContext.nbTxLeft > 0 && !m_nextTx.IsPending()); // We can retransmit
272 NS_LOG_DEBUG("recv=" << recv << ", needsAck=" << needsAck << ", gotAck=" << gotAck
273 << ", canReTx=" << canReTx);
274
275 // Condition to schedule retransmission:
276 // either we did not receive or we weren't acknowledged + we can retransmit
277 if ((!recv || (needsAck && !gotAck)) && canReTx)
278 {
279 if (outcome == RECV)
280 {
281 NS_LOG_DEBUG("Received packet without ACK: rescheduling transmission.");
282 }
283 else if (outcome == FAIL)
284 {
285 NS_LOG_DEBUG("Reception failed: rescheduling transmission.");
286 }
287 else if (outcome == NONE)
288 {
289 NS_LOG_DEBUG("No reception initiated by PHY: rescheduling transmission.");
290 }
291 NS_LOG_INFO("We have " << unsigned(m_txContext.nbTxLeft) << " retransmissions left.");
292 double retransmitTimeout = m_uniformRV->GetValue(1, 3);
293 PostponeTransmission(Seconds(retransmitTimeout), m_txContext.packet);
294 return;
295 }
296
297 // Tracing: end of re-transmission process
298 uint8_t txs = m_nbTrans - m_txContext.nbTxLeft;
299 // Acknowledgement success of confirmed txs
300 if (recv && needsAck && gotAck)
301 {
302 m_confirmedTxOutcomeCallback(txs, true, m_txContext.firstAttempt, m_txContext.packet);
303 NS_LOG_DEBUG("Received ACK packet after "
304 << unsigned(txs) << " transmissions: stopping retransmission process");
305 }
306 // Acknowledgement failure of confirmed txs
307 // (either exhausted all reTxs or new pkt scheduled while busy)
308 else if (needsAck && !gotAck && !canReTx)
309 {
310 m_confirmedTxOutcomeCallback(txs, false, m_txContext.firstAttempt, m_txContext.packet);
311 NS_LOG_DEBUG("Ack failure: no more retransmission opportunities. Used "
312 << unsigned(txs) << " transmissions.");
313 }
314
315 // Exhaust remaining re-transmissions
316 m_txContext.nbTxLeft = 0;
317
318 // Update uplink frame counter
319 m_fCnt++;
320 // Update ADRACKCnt only if nothing was received
321 if (!recv)
322 {
323 m_adrAckCnt++;
324 }
325}
326
327void
329{
330 NS_LOG_FUNCTION(this << packet);
331
332 // We should always be in STANDBY mode at this point, as this function is meant to be invoked as
333 // a callback by the PHY on reception end
334 auto phyState = DynamicCast<EndDeviceLoraPhy>(m_phy)->GetState();
336 "Unexpected PHY state on RX end: phyState=" << phyState);
337
338 // Work on a copy of the packet
339 Ptr<Packet> packetCopy = packet->Copy();
340
341 // Remove the Mac Header to get some information
342 LorawanMacHeader mHdr;
343 packetCopy->RemoveHeader(mHdr);
344 NS_ASSERT_MSG(!mHdr.IsUplink(), "Received uplink package, check PHY polarity");
345 NS_LOG_DEBUG("Downlink Mac Header: " << mHdr);
346 // Remove the Frame Header
347 LoraFrameHeader fHdr;
348 fHdr.SetAsDownlink();
349 packetCopy->RemoveHeader(fHdr);
350 NS_LOG_DEBUG("Downlink Frame Header: " << fHdr);
351
352 /// TODO: early packet filtering at PHY layer
353
354 // Determine whether this packet is for us
355 if (m_address != fHdr.GetAddress())
356 {
357 NS_LOG_DEBUG("The message is intended for another recipient.");
358 FailedReception(packet);
359 return;
360 }
361
362 // Set PHY to sleep
364
365 NS_LOG_INFO("The message is for us!");
366 // If it exists, cancel the second receive window event
367 m_secondReceiveWindow.Cancel();
368 // Open the context to new transmissions
369 m_busy = false;
370 // Reset ADR backoff counter
371 m_adrAckCnt = 0;
372 // Clear commands that are re-sent until downlink (DlChannelAns and RxTimingSetupAns)
373 m_macCommandList.clear();
374
375 // Link quality metadata
376 LoraTag tag;
377 packet->PeekPacketTag(tag);
378 /// @see ns3::lorawan::AdrComponent::RxPowerToSNR
379 m_lastRxSnr = tag.GetReceivePower() + 174 - 10 * log10(125000) - 6;
380
381 // Parse the MAC commands
382 ApplyMACCommands(fHdr);
383 // Manage acknowledgement and retransmission
385
386 // Pass the packet up to the NetDevice
387 if (!m_receiveCallback.IsNull())
388 {
389 m_receiveCallback(packetCopy);
390 }
391 // Call the trace source
392 m_receivedPacket(packet);
393}
394
395void
397{
398 NS_LOG_FUNCTION(this << packet);
399 // We should always be in STANDBY mode at this point, as this function is meant to be
400 // invoked as a callback by the PHY on reception end
401 auto phyState = DynamicCast<EndDeviceLoraPhy>(m_phy)->GetState();
403 "Unexpected PHY state on RX end: phyState=" << phyState);
404 // Switch to sleep after a failed reception
406
407 // Nothing valid was received; if we are past the 2nd RX window, we can reschedule
408 if (m_secondReceiveWindow.IsExpired())
409 {
411 // Open the context to new transmissions
412 m_busy = false;
413 }
414}
415
416void
418 uint8_t rx2DataRate,
419 double frequencyHz)
420{
421 NS_LOG_FUNCTION(this << unsigned(rx1DrOffset) << unsigned(rx2DataRate)
422 << uint32_t(frequencyHz));
423
424 // Adapted from: github.com/Lora-net/SWL2001.git v4.3.1
425 // For the time being, this implementation is valid for the EU868 region
426
427 bool rx1DrOffsetAck = true;
428 bool rx2DataRateAck = true;
429 bool channelAck = true;
430
431 if (rx1DrOffset >= m_replyDataRateMatrix.at(m_dataRate).size())
432 {
433 NS_LOG_WARN("Invalid rx1DrOffset");
434 rx1DrOffsetAck = false;
435 }
436
437 if (!GetSfFromDataRate(rx2DataRate) || !GetBandwidthFromDataRate(rx2DataRate))
438 {
439 NS_LOG_WARN("Invalid rx2DataRate");
440 rx2DataRateAck = false;
441 }
442
443 if (!m_channelHelper->IsFrequencyValid(frequencyHz))
444 {
445 NS_LOG_WARN("Invalid rx2 frequency");
446 channelAck = false;
447 }
448
449 if (rx1DrOffsetAck && rx2DataRateAck && channelAck)
450 {
451 m_rx1DrOffset = rx1DrOffset;
452 m_secondReceiveWindowDataRate = rx2DataRate;
454 }
455
456 NS_LOG_INFO("Adding RxParamSetupAns reply");
457 m_macCommandList.emplace_back(
458 Create<RxParamSetupAns>(rx1DrOffsetAck, rx2DataRateAck, channelAck));
459}
460
461} /* namespace lorawan */
462} /* namespace ns3 */
An identifier for simulation events.
Definition event-id.h:45
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
Simulation virtual time values and global simulation resolution.
Definition nstime.h:95
a unique identifier for an interface.
Definition type-id.h:50
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:999
Class representing the MAC layer of a Class A LoRaWAN device.
uint8_t m_rx1DrOffset
The RX1DROffset parameter value.
uint32_t m_firstReceiveWindowFrequencyHz
The frequency [Hz] to listen on for the first receive window.
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
Inform this layer that reception of a packet we were locked on failed.
uint32_t GetSecondReceiveWindowFrequency() const
Get the frequency that is used for the second receive window.
RxOutcome
Set of possible outcomes of a reception window.
@ ACK
Correctly received a network acknowledgement.
@ RECV
Correctly received a downlink packet (no ACK).
Time GetNextClassTransmissionDelay() const override
Find the minimum wait time before the next possible transmission based on end device's Class Type sch...
void SetSecondReceiveWindowFrequency(uint32_t frequencyHz)
Set the frequency that will be used for the second receive window.
bool m_busy
Whether the MAC layer is currently busy with in the LoRaWAN Class A process of transmitting an uplink...
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.
void CloseFirstReceiveWindow()
Perform operations needed to close the first receive window.
void SendToPhy(Ptr< Packet > packet) override
Gather PHY transmission parameters and send a packet through the LoRa physical layer.
uint32_t m_secondReceiveWindowFrequencyHz
The frequency [Hz] to listen on for the second receive window.
void TxFinished(Ptr< const Packet > packet) override
Perform actions after sending a packet.
void CloseSecondReceiveWindow()
Perform operations needed to close the second receive window.
void OnRxParamSetupReq(uint8_t rx1DrOffset, uint8_t rx2DataRate, double frequencyHz) override
Perform the actions that need to be taken when receiving a RxParamSetupReq command based on the Devic...
uint8_t GetFirstReceiveWindowDataRate()
Get the data rate that will be used in the first receive window.
void ManageRetransmissions(RxOutcome outcome)
Decide whether we can retransmit based on reception outcome.
EventId m_secondReceiveWindow
The event of the second receive window opening, used to cancel the second window in case the first on...
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 from the lower layer.
void OpenFirstReceiveWindow()
Perform operations needed to open the first receive window.
@ RX_ACTIVE
The PHY layer is actively receiving a transmission after locking onto a preamble.
@ SLEEP
The PHY layer is in low-power sleep state.
@ STANDBY
The PHY layer is in standby mode.
Ptr< LogicalLoraChannel > GetRandomChannelForTx()
Find a suitable channel for transmission.
bool m_headerDisabled
Whether or not the LoRa PHY header is disabled for communications by this device.
TracedValue< double > m_txPowerDbm
The transmission ERP [dBm] this device is currently using.
void PostponeTransmission(Time nextTxDelay, Ptr< Packet > packet)
Postpone transmission to the specified time.
TracedCallback< uint8_t, bool, Time, Ptr< Packet > > m_confirmedTxOutcomeCallback
Traced Callback: confirmed transmission process outcome event.
Ptr< UniformRandomVariable > m_uniformRV
An uniform random variable, used to randomly pick from the channel list and to sample random retransm...
uint16_t m_adrAckCnt
ADRACKCnt counter of the number of consecutive uplinks without downlink reply from the server.
void ApplyMACCommands(LoraFrameHeader frameHeader)
Take action on the commands contained on this FrameHeader.
CodingRate m_codingRate
The coding rate used by this device.
EventId m_nextTx
The event of transmitting a packet at a later moment.
std::list< Ptr< MacCommand > > m_macCommandList
List of the MAC commands that need to be applied to the next UL packet.
TracedValue< uint8_t > m_dataRate
The data rate this device is using to transmit.
LoraDeviceAddress m_address
The LoRaWAN address of this device.
PacketTxContext m_txContext
Structure containing the transmission context for the last packet sent by this device,...
double m_lastRxSnr
Record latest reception SNR measurement to provide via DevStatusAns.
uint8_t m_nbTrans
Default number of repeated transmissions of each packet.
uint8_t m_receiveWindowDurationInSymbols
The duration of reception windows in number of symbols.
uint16_t m_fCnt
Current value of the uplink frame counter.
This class represents the Frame header (FHDR) used in a LoraWAN network.
bool GetAck() const
Get the value of the ACK bit field.
LoraDeviceAddress GetAddress() const
Get this header's device address value.
void SetAsDownlink()
State that this is a downlink message.
static Time GetTimeOnAir(uint32_t phyPayloadLen, const LoraTxParameters &txParams)
Compute the total transmission time for a physical packet based on modulation parameters.
Definition lora-phy.cc:110
Tag used to save various data about a packet, like its Spreading Factor and data about interference.
Definition lora-tag.h:26
double GetReceivePower() const
Read the power this packet arrived with.
Definition lora-tag.cc:92
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.
ReplyDataRateMatrix m_replyDataRateMatrix
The matrix that decides the data rate the gateway will use in a reply based on the end device's sendi...
uint32_t GetBandwidthFromDataRate(uint8_t dataRate) const
Get the bandwidth corresponding to a data rate, based on this MAC's region.
uint8_t GetSfFromDataRate(uint8_t dataRate) const
Get the spreading factor corresponding to a data rate, based on this MAC's region.
Ptr< LogicalLoraChannelHelper > m_channelHelper
The LogicalLoraChannelHelper instance that is assigned to this MAC.
ReceiveCallback m_receiveCallback
! Callback to forward to upper layers
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_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
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:690
#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_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
@ DOWN
Downlink / Downchirp / Inverted polarity.
Definition lora-phy.h:35
@ UP
Uplink / Upchirp / Normal polarity.
Definition lora-phy.h:34
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:492
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1273
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:643
Structure to collect all parameters that are used to compute the duration of a packet (excluding payl...
Definition lora-phy.h:73