Compare LoRaWAN ADR with a fixed spreading factor
Use the module guide's LoRaWAN link-budget and ADR pathway to compare spreading factor, reference airtime and delivered uplinks under changing path loss.

Read delivered uplinks alongside the final rate; require real link evidence before a deployment decision.
Predict the reading, then compare it with the measurement.
ns-3 with signetlabdei LoRaWAN
PythonUse the module guide's LoRaWAN link-budget and ADR pathway to compare spreading factor, reference airtime and delivered uplinks under changing path loss.
Download the lab files, build the pinned ns-3 LoRaWAN module, and run run.sh followed by analyze.py.
Open the Python run guide (new tab)Get the files
Download all three prepared files into one folder. No account or paid service is required.
- Put main.py, requirements.txt, and README.md in the same folder.
- Create and activate a Python virtual environment, then install requirements.txt.
- Run python3 main.py and compare its output with each observed result below.
Steps
Step 1
- Do
- In the terminal panel, follow README.md to pin and build ns-3 and the LoRaWAN module. Check the two commits, then run all six cases with run.sh. Read the first row in results/adr-loss0/nodeData.txt.
- You will see
- The tested commits are ns-3 d2add90 and LoRaWAN e45b4af. At simulated 0 s, one device row has DR0 and 14 dBm; EU DR0 maps to SF12. The positions and loss draw are synthetic, with RngSeed=7 and RngRun=1.
- Why it matters
- The version, seed and starting data rate make every later comparison traceable to the actual model run.

Step 1 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 2
- Do
- In the terminal panel, compare the 0 dB ADR and fixed rows printed by analyze.py. Read each case's final-window line in stdout.log. Keep full-run delivery separate from that last-window count.
- You will see
- At 0 dB, ADR delivers 445/464 uplinks (95.9%); fixed SF12 delivers 464/464 (100.0%). The final observed ADR spreading factor is SF7 for all 16 devices. The last 20-minute window reports 11/16 for ADR and 16/16 for fixed.
- Why it matters
- ADR is faster at the final observed setting, but the complete-run delivery record already shows a tradeoff.

Step 2 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 3
- Do
- In the terminal panel, read the paired 10 dB loss rows and their stdout.log tails. Check the same RngSeed=7 and RngRun=1 in run.sh. Compare delivered packets before discussing airtime.
- You will see
- At 10 dB, ADR delivers 433/464 (93.3%); fixed SF12 delivers 464/464 (100.0%). The final observed ADR rate remains SF7, while fixed remains SF12. The last window reports 10/16 for ADR and 16/16 for fixed.
- Why it matters
- The paired input isolates the effect of enabling ADR in this synthetic path-loss condition.

Step 3 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 4
- Do
- In the terminal panel, inspect the 20 dB rows and final-window counts. Compare both modes against their 0 dB cases. Record whether the chosen final spreading factors changed.
- You will see
- At 20 dB, ADR delivers 408/464 (87.9%); fixed SF12 delivers 463/464 (99.8%). The final observed settings remain SF7 and SF12 respectively. The last window reports 9/16 for ADR and 16/16 for fixed. In the 20 dB ADR trace, device 26 has 20 received SNRs from -11.755 to +7.21097 dB; the default uses their maximum.
- Why it matters
- The loss sweep shows the delivery cost of this ADR outcome as modeled conditions become harsher. The strongest received packet can outweigh weaker ones when random loss changes between uplinks.

Step 4 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 5
- Do
- In the terminal panel, inspect results/summary.csv made by analyze.py. Compare mean final SF and the 10-byte reference airtime across all six rows. Mark the reference as a formula result, not recorded frame airtime.
- You will see
- Every ADR row ends at mean SF7; every fixed row ends at SF12. The corresponding 10-byte reference airtimes are 41.22 ms and 991.23 ms. The CSV keeps all six full-run delivery ratios beside these final-setting references. For device 26, max SNR 7.21097 minus the SF12 threshold -20.0 gives a 27.21097 dB margin; floor(27.21097/3)=9 steps. Five steps take SF12 to SF7; four more take transmit power from 14 to 6 dBm.
- Why it matters
- A shorter reference airtime describes the rate tradeoff without pretending to measure actual uplink airtime or battery life. Pinned source: adr-component.cc:207 `double margin_SNR = m_SNR - req_SNR;`; :213 `int steps = std::floor(margin_SNR / 3);`. The source uses no separate device-margin offset: adr-component.h:160 comments it out. Pinned adr-component.h:160 `// const int offset = 10; //!< Device specific SNR margin (dB)`.

Step 5 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 6
- Do
- In the browser tab, open the delivery.png plot generated from your run's summary.csv. Trace the ADR and fixed lines from 0 to 20 dB. Write a decision that includes both the airtime reference and lost uplinks.
- You will see
- The ADR line declines from 95.9% to 93.3% to 87.9%. The fixed SF12 line is 100.0%, 100.0% and 99.8%. All points come from the six ns-3 runs, not a hand-drawn result panel. Pinned defaults: adr-component.cc:46 `EnumValue(AdrComponent::MAXIMUM),`; :56 `IntegerValue(20),`. The SF12 and SF7 required SNRs are -20.0 and -7.5 dB (adr-component.h:164,169). Pinned adr-component.h:164 `-20.0,` and :169 `-7.5}; //!< Vector containing the required SNR for the 6 allowed spreading factor`. Pinned adr-component.cc:182 `m_SNR = GetMaxSNR(status->GetReceivedPacketList(), historyRange);`.
- Why it matters
- The plot supports a bounded model comparison; it cannot select a field ADR policy without measured links and deployment requirements. Selecting the best of 20 received packets overlooks weaker receptions and packets that did not arrive; changing synthetic loss can therefore make SF7 lose later uplinks. SF7 has about 24 times less 10-byte reference airtime than SF12, but this run delivers fewer packets under fluctuating loss. A deployment could test the exposed history statistic or range (adr-component.cc:44-58) against measured links before setting its policy.

Step 6 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab)
Chapter checks
These questions refer to the chapter’s examples. Use the return links to review their answers.
Before enabling ADR for a planned LoRaWAN sensor route, which evidence belongs in the link-budget record?
Return to the chapter’s knowledge checkA device has one excellent uplink and several weak uplinks from the same installation. What should the reviewer do before approving a less robust setting?
Return to the chapter’s knowledge check