Loading...
Searching...
No Matches
lora-adr-component.cc
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2018 University of Padova
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Matteo Perin <matteo.perin.2@studenti.unipd.it
19 *
20 * Modified by: Bastien Tauran <bastien.tauran@viveris.fr>
21 */
22
23#include "lora-adr-component.h"
24
25#include "ns3/log.h"
26
27#include <cmath>
28#include <list>
29
30namespace ns3
31{
32
34// LinkAdrRequest commands management //
36
37NS_LOG_COMPONENT_DEFINE("LoraAdrComponent");
38
39NS_OBJECT_ENSURE_REGISTERED(LoraAdrComponent);
40
41TypeId
43{
44 static TypeId tid =
45 TypeId("ns3::LoraAdrComponent")
46 .AddConstructor<LoraAdrComponent>()
47 .SetParent<LoraNetworkControllerComponent>()
48 .AddAttribute(
49 "MultipleGwCombiningMethod",
50 "Whether to average the received power of gateways or to use the maximum",
52 MakeEnumAccessor<LoraAdrComponent::CombiningMethod>(&LoraAdrComponent::tpAveraging),
53 MakeEnumChecker(LoraAdrComponent::AVERAGE,
54 "avg",
56 "max",
58 "min"))
59 .AddAttribute("MultiplePacketsCombiningMethod",
60 "Whether to average SNRs from multiple packets or to use the maximum",
62 MakeEnumAccessor<LoraAdrComponent::CombiningMethod>(
64 MakeEnumChecker(LoraAdrComponent::AVERAGE,
65 "avg",
67 "max",
69 "min"))
70 .AddAttribute("HistoryRange",
71 "Number of packets to use for averaging",
72 IntegerValue(4),
73 MakeIntegerAccessor(&LoraAdrComponent::historyRange),
74 MakeIntegerChecker<int>(0, 100))
75 .AddAttribute("ChangeTransmissionPower",
76 "Whether to toggle the transmission power or not",
77 BooleanValue(true),
78 MakeBooleanAccessor(&LoraAdrComponent::m_toggleTxPower),
79 MakeBooleanChecker());
80 return tid;
81}
82
86
90
91void
92LoraAdrComponent::OnReceivedPacket(Ptr<const Packet> packet,
93 Ptr<LoraEndDeviceStatus> status,
94 Ptr<LoraNetworkStatus> networkStatus)
95{
96 NS_LOG_FUNCTION(this->GetTypeId() << packet << networkStatus);
97
98 // We will only act just before reply, when all Gateways will have received
99 // the packet, since we need their respective received power.
100}
101
102void
103LoraAdrComponent::BeforeSendingReply(Ptr<LoraEndDeviceStatus> status,
104 Ptr<LoraNetworkStatus> networkStatus)
105{
106 NS_LOG_FUNCTION(this << status << networkStatus);
107
108 Ptr<Packet> myPacket = status->GetLastPacketReceivedFromDevice()->Copy();
109 LorawanMacHeader mHdr;
110 LoraFrameHeader fHdr;
111 fHdr.SetAsUplink();
112 myPacket->RemoveHeader(mHdr);
113 myPacket->RemoveHeader(fHdr);
114
115 // Execute the ADR algotithm only if the request bit is set
116 if (fHdr.GetAdr())
117 {
118 if (int(status->GetReceivedPacketList().size()) < historyRange)
119 {
120 NS_LOG_ERROR("Not enough packets received by this device ("
121 << status->GetReceivedPacketList().size()
122 << ") for the algorithm to work (need " << historyRange << ")");
123 }
124 else
125 {
126 NS_LOG_DEBUG("New ADR request");
127
128 // Get the SF used by the device
129 uint8_t spreadingFactor = status->GetFirstReceiveWindowSpreadingFactor();
130
131 // Get the device transmission power (dBm)
132 uint8_t transmissionPower = status->GetMac()->GetTransmissionPower();
133
134 // New parameters for the end-device
135 uint8_t newDataRate;
136 uint8_t newTxPower;
137
138 // ADR Algorithm
139 AdrImplementation(&newDataRate, &newTxPower, status);
140
141 // Change the power back to the default if we don't want to change it
142 if (!m_toggleTxPower)
143 {
144 newTxPower = transmissionPower;
145 }
146
147 if (newDataRate != SfToDr(spreadingFactor) || newTxPower != transmissionPower)
148 {
149 // Create a list with mandatory channel indexes
150 int channels[] = {0, 1, 2};
151 std::list<int> enabledChannels(channels, channels + sizeof(channels) / sizeof(int));
152
153 // Repetitions Setting
154 const int rep = 1;
155
156 NS_LOG_DEBUG("Sending LinkAdrReq with DR = " << (unsigned)newDataRate
157 << " and TP = " << (unsigned)newTxPower
158 << " dBm");
159
160 status->m_reply.frameHeader.AddLinkAdrReq(newDataRate,
161 GetTxPowerIndex(newTxPower),
162 enabledChannels,
163 rep);
164 status->m_reply.frameHeader.SetAsDownlink();
165 status->m_reply.macHeader.SetMType(LorawanMacHeader::UNCONFIRMED_DATA_DOWN);
166
167 status->m_reply.needsReply = true;
168 }
169 else
170 {
171 NS_LOG_DEBUG("Skipped request");
172 }
173 }
174 }
175 else
176 {
177 // Do nothing
178 }
179}
180
181void
182LoraAdrComponent::OnFailedReply(Ptr<LoraEndDeviceStatus> status,
183 Ptr<LoraNetworkStatus> networkStatus)
184{
185 NS_LOG_FUNCTION(this->GetTypeId() << networkStatus);
186}
187
188void
190 uint8_t* newTxPower,
191 Ptr<LoraEndDeviceStatus> status)
192{
193 // Compute the maximum or median SNR, based on the boolean value historyAveraging
194 double m_SNR = 0;
195 switch (historyAveraging)
196 {
198 m_SNR = GetAverageSNR(status->GetReceivedPacketList(), historyRange);
199 break;
201 m_SNR = GetMaxSNR(status->GetReceivedPacketList(), historyRange);
202 break;
204 m_SNR = GetMinSNR(status->GetReceivedPacketList(), historyRange);
205 }
206
207 NS_LOG_DEBUG("m_SNR = " << m_SNR);
208
209 // Get the SF used by the device
210 uint8_t spreadingFactor = status->GetFirstReceiveWindowSpreadingFactor();
211
212 NS_LOG_DEBUG("SF = " << (unsigned)spreadingFactor);
213
214 // Get the device data rate and use it to get the SNR demodulation treshold
215 double req_SNR = treshold[SfToDr(spreadingFactor)];
216
217 NS_LOG_DEBUG("Required SNR = " << req_SNR);
218
219 // Get the device transmission power (dBm)
220 double transmissionPower = status->GetMac()->GetTransmissionPower();
221
222 NS_LOG_DEBUG("Transmission Power = " << transmissionPower);
223
224 // Compute the SNR margin taking into consideration the SNR of
225 // previously received packets
226 double margin_SNR = m_SNR - req_SNR;
227
228 NS_LOG_DEBUG("Margin = " << margin_SNR);
229
230 // Number of steps to decrement the SF (thereby increasing the Data Rate)
231 // and the TP.
232 int steps = std::floor(margin_SNR / 3);
233
234 NS_LOG_DEBUG("steps = " << steps);
235
236 // If the number of steps is positive (margin_SNR is positive, so its
237 // decimal value is high) increment the data rate, if there are some
238 // leftover steps after reaching the maximum possible data rate
239 //(corresponding to the minimum SF) decrement the transmission power as
240 // well for the number of steps left.
241 // If, on the other hand, the number of steps is negative (margin_SNR is
242 // negative, so its decimal value is low) increase the transmission power
243 //(note that the SF is not incremented as this particular algorithm
244 // expects the node itself to raise its SF whenever necessary).
245 while (steps > 0 && spreadingFactor > min_spreadingFactor)
246 {
247 spreadingFactor--;
248 steps--;
249 NS_LOG_DEBUG("Decreased SF by 1");
250 }
251 while (steps > 0 && transmissionPower > min_transmissionPower)
252 {
253 transmissionPower -= 2;
254 steps--;
255 NS_LOG_DEBUG("Decreased Ptx by 2");
256 }
257 while (steps < 0 && transmissionPower < max_transmissionPower)
258 {
259 transmissionPower += 2;
260 steps++;
261 NS_LOG_DEBUG("Increased Ptx by 2");
262 }
263
264 *newDataRate = SfToDr(spreadingFactor);
265 *newTxPower = transmissionPower;
266}
267
268uint8_t
270{
271 switch (sf)
272 {
273 case 12:
274 return 0;
275 break;
276 case 11:
277 return 1;
278 break;
279 case 10:
280 return 2;
281 break;
282 case 9:
283 return 3;
284 break;
285 case 8:
286 return 4;
287 break;
288 default:
289 return 5;
290 break;
291 }
292}
293
294double
295LoraAdrComponent::RxPowerToSNR(double transmissionPower)
296{
297 // The following conversion ignores interfering packets
298 return transmissionPower + 174 - 10 * log10(B) - NF;
299}
300
301// Get the maximum received power (it considers the values in dB!)
302double
304{
305 LoraEndDeviceStatus::GatewayList::iterator it = gwList.begin();
306 double min = it->second.rxPower;
307
308 for (; it != gwList.end(); it++)
309 {
310 if (it->second.rxPower < min)
311 {
312 min = it->second.rxPower;
313 }
314 }
315
316 return min;
317}
318
319// Get the maximum received power (it considers the values in dB!)
320double
322{
323 LoraEndDeviceStatus::GatewayList::iterator it = gwList.begin();
324 double max = it->second.rxPower;
325
326 for (; it != gwList.end(); it++)
327 {
328 if (it->second.rxPower > max)
329 {
330 max = it->second.rxPower;
331 }
332 }
333
334 return max;
335}
336
337// Get the maximum received power
338double
340{
341 double sum = 0;
342
343 for (LoraEndDeviceStatus::GatewayList::iterator it = gwList.begin(); it != gwList.end(); it++)
344 {
345 NS_LOG_DEBUG("Gateway at " << it->first << " has TP " << it->second.rxPower);
346 sum += it->second.rxPower;
347 }
348
349 double average = sum / gwList.size();
350
351 NS_LOG_DEBUG("TP (average) = " << average);
352
353 return average;
354}
355
356double
358{
359 switch (tpAveraging)
360 {
362 return GetAverageTxFromGateways(gwList);
364 return GetMaxTxFromGateways(gwList);
366 return GetMinTxFromGateways(gwList);
367 default:
368 return -1;
369 }
370}
371
372// TODO Make this more elegant
373double
375{
376 double m_SNR;
377
378 // Take elements from the list starting at the end
379 auto it = packetList.rbegin();
380 double min = RxPowerToSNR(GetReceivedPower(it->second.gwList));
381
382 for (int i = 0; i < historyRange; i++, it++)
383 {
384 m_SNR = RxPowerToSNR(GetReceivedPower(it->second.gwList));
385
386 NS_LOG_DEBUG("Received power: " << GetReceivedPower(it->second.gwList));
387 NS_LOG_DEBUG("m_SNR = " << m_SNR);
388
389 if (m_SNR < min)
390 {
391 min = m_SNR;
392 }
393 }
394
395 NS_LOG_DEBUG("SNR (min) = " << min);
396
397 return min;
398}
399
400double
402{
403 double m_SNR;
404
405 // Take elements from the list starting at the end
406 auto it = packetList.rbegin();
407 double max = RxPowerToSNR(GetReceivedPower(it->second.gwList));
408
409 for (int i = 0; i < historyRange; i++, it++)
410 {
411 m_SNR = RxPowerToSNR(GetReceivedPower(it->second.gwList));
412
413 NS_LOG_DEBUG("Received power: " << GetReceivedPower(it->second.gwList));
414 NS_LOG_DEBUG("m_SNR = " << m_SNR);
415
416 if (m_SNR > max)
417 {
418 max = m_SNR;
419 }
420 }
421
422 NS_LOG_DEBUG("SNR (max) = " << max);
423
424 return max;
425}
426
427double
429 int historyRange)
430{
431 double sum = 0;
432 double m_SNR;
433
434 // Take elements from the list starting at the end
435 auto it = packetList.rbegin();
436 for (int i = 0; i < historyRange; i++, it++)
437 {
438 m_SNR = RxPowerToSNR(GetReceivedPower(it->second.gwList));
439
440 NS_LOG_DEBUG("Received power: " << GetReceivedPower(it->second.gwList));
441 NS_LOG_DEBUG("m_SNR = " << m_SNR);
442
443 sum += m_SNR;
444 }
445
446 double average = sum / historyRange;
447
448 NS_LOG_DEBUG("SNR (average) = " << average);
449
450 return average;
451}
452
453int
455{
456 if (txPower >= 16)
457 {
458 return 0;
459 }
460 else if (txPower >= 14)
461 {
462 return 1;
463 }
464 else if (txPower >= 12)
465 {
466 return 2;
467 }
468 else if (txPower >= 10)
469 {
470 return 3;
471 }
472 else if (txPower >= 8)
473 {
474 return 4;
475 }
476 else if (txPower >= 6)
477 {
478 return 5;
479 }
480 else if (txPower >= 4)
481 {
482 return 6;
483 }
484 else
485 {
486 return 7;
487 }
488}
489} // namespace ns3
enum CombiningMethod tpAveraging
uint8_t SfToDr(uint8_t sf)
double GetMinSNR(LoraEndDeviceStatus::ReceivedPacketList packetList, int historyRange)
double GetAverageSNR(LoraEndDeviceStatus::ReceivedPacketList packetList, int historyRange)
enum CombiningMethod historyAveraging
void OnFailedReply(Ptr< LoraEndDeviceStatus > status, Ptr< LoraNetworkStatus > networkStatus)
Method that is called when a packet cannot be sent in the downlink.
double RxPowerToSNR(double transmissionPower)
void OnReceivedPacket(Ptr< const Packet > packet, Ptr< LoraEndDeviceStatus > status, Ptr< LoraNetworkStatus > networkStatus)
Method that is called when a new packet is received by the NetworkServer.
static TypeId GetTypeId(void)
double GetMinTxFromGateways(LoraEndDeviceStatus::GatewayList gwList)
void AdrImplementation(uint8_t *newDataRate, uint8_t *newTxPower, Ptr< LoraEndDeviceStatus > status)
double GetAverageTxFromGateways(LoraEndDeviceStatus::GatewayList gwList)
void BeforeSendingReply(Ptr< LoraEndDeviceStatus > status, Ptr< LoraNetworkStatus > networkStatus)
double GetMaxSNR(LoraEndDeviceStatus::ReceivedPacketList packetList, int historyRange)
int GetTxPowerIndex(int txPower)
double GetMaxTxFromGateways(LoraEndDeviceStatus::GatewayList gwList)
double GetReceivedPower(LoraEndDeviceStatus::GatewayList gwList)
std::list< std::pair< Ptr< const Packet >, ReceivedPacketInfo > > ReceivedPacketList
std::map< Address, PacketInfoPerGw > GatewayList
This class represents the Frame header (FHDR) used in a LoraWAN network.
void SetAsUplink(void)
State that this is an uplink message.
bool GetAdr(void) const
Get the Adr value.
This class represents the Mac header of a LoRaWAN packet.
SatArqSequenceNumber is handling the sequence numbers for the ARQ process.