Skip to content

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., your practice guide

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

Python

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

Tier 2 · Python · install required · No account

Version tested: ns-3.48 d2add90b452d600cfb4859baed8e9ea633519447; signetlabdei/lorawan e45b4af428c92f30b2e6c201055809110758125f. Date: 2026-10-08.

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.

README.md

3,199 bytes · Run guide

Download

run.sh

891 bytes · Run guide

Download

analyze.py

2,828 bytes · Run guide

Download

scenario.json

635 bytes · Run guide

Download

expected-summary.csv

324 bytes · Run guide

Download

  1. Put main.py, requirements.txt, and README.md in the same folder.
  2. Create and activate a Python virtual environment, then install requirements.txt.
  3. Run python3 main.py and compare its output with each observed result below.

Steps

Screens captured against ns-3 adr-example in XFCE terminal; Firefox plot view ns-3.48 and signetlabdei/lorawan e45b4af on 2026-10-08; the tool may have moved on — the text steps are the contract.

  1. 1 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.
    Real XFCE terminal on Xvfb showing pinned ns-3 and LoRaWAN commits, fixed seed and the first simulator device-state row.
    Step 1 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab)
  2. 2 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.
    Real XFCE terminal showing the 0 dB ADR and fixed simulator delivery rows and last-window packet counts.
    Step 2 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab)
  3. 3 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.
    Real XFCE terminal showing the 10 dB path-loss comparison from the simulator run files.
    Step 3 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab)
  4. 4 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.
    Real XFCE terminal showing the 20 dB path-loss comparison and final-window packet counts.
    Step 4 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab)
  5. 5 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)`.
    Real XFCE terminal showing the six-row CSV derived from real ns-3 run outputs and the reference-airtime caveat.
    Step 5 · ns-3 with signetlabdei LoRaWAN; numbered callout added to a real capture. Enlarge screenshot (new tab)
  6. 6 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.
    Real Firefox window displaying the Python plot generated from the six-run CSV, with ADR and fixed delivery curves.
    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.

  1. Before enabling ADR for a planned LoRaWAN sensor route, which evidence belongs in the link-budget record?

    Return to the chapter’s knowledge check
  2. A 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

Caution

Synthetic fixed-seed Class A model output is not a radio capture. Reference airtime uses a 10-byte PHY frame at the final observed SF; it is not actual uplink airtime across the run. This result does not establish field coverage, battery life or an appropriate production ADR policy.

Return to LoRaWAN Link Budget and ADR · Browse Labs