Project

A LoRaWAN link budget and coverage map calculator

Compute EU868 LoRaWAN uplink sensitivity, time on air, duty-cycle limits and range per spreading factor from Semtech, LoRa Alliance, ITU-R and 3GPP sources, then map coverage around one gateway in NumPy.

IntermediateUpdated 15 Nov 2026
Runs onLaptop CPU (macOS, Linux or Windows), Python and NumPy, under 5 seconds
Timeabout 3 hours
Stackpython, numpy

What you will build

This project is a planning calculator for a LoRaWAN deployment, in one NumPy file. For each spreading factor (SF) it works out the receiver sensitivity, the time on air of a 12-byte uplink, the number of uplinks per hour allowed by the 1 % duty cycle, the maximum path loss and the range under two propagation models. It then lays a 10 km by 10 km grid around one gateway, applies a path-loss model with random shadowing, and reports which SF each point needs.

Every input is sourced or stated as an assumption below. In our run a 12-byte uplink takes 61.7 ms at SF7 and 1482.8 ms at SF12. The 1 % duty cycle then allows 583 and 24 uplinks per hour. With an urban path-loss model the median range runs from 1.96 km at SF7 to 4.22 km at SF12, and 69.2 % of the 5 km disc around the gateway is covered at some SF.

The lessons on decibels and link budgets and noise, SNR and capacity explain the arithmetic. The decibels and link budget cheat sheet and the propagation formulas have the formulas in one place.

What you need

  • Python 3.10 or later and NumPy. We ran it with Python 3.12 and NumPy 2.5.4. It also uses math and statistics from the standard library.
  • No radio and no maps. Terrain and buildings are a statistical model, not geography.
  • About 3 hours.

Sources and assumptions

Input Value Source or reason
Region EU863-870, uplink on 868.1 MHz Default channel in LoRa Alliance RP002-1.0.4 (Regional Parameters, 2022)
Data rates DR0 to DR5 = SF12 to SF7 at 125 kHz RP002-1.0.4, EU863-870 data rate table
Device EIRP +16 dBm RP002-1.0.4 default maximum EIRP for EU863-870
Duty cycle 1 % RP002-1.0.4 lists less than 1 % for the default channels
LoRa settings preamble 8 symbols, explicit header, CRC on, coding rate 4/5 RP002-1.0.4 LoRa physical layer settings for uplinks
Low data rate optimisation on for SF11 and SF12 at 125 kHz RP002-1.0.4, and the SX1261/2 datasheet’s 16.38 ms symbol-time rule
Frame overhead 13 bytes (MHDR 1, FHDR 7, FPort 1, MIC 4) LoRa Alliance TS001-1.0.4 frame format, with no FOpts
Sensitivity, SF7 and SF12 at 125 kHz −124 dBm and −137 dBm SX1261/2 datasheet Rev 2.2 (December 2024), Table 3-8, Rx boosted gain, 1 % packet error rate with 64-byte packets
Sensitivity, SF8 to SF11 linear in SF between the two datasheet values Assumption, cross-checked against the noise floor below
Demodulator SNR per SF −7.5 dB at SF7 to −20 dB at SF12, 2.5 dB steps SX1261/2 datasheet Rev 2.2, Table 6-1 (typical values)
Gateway receiver performs like the SX1262 Assumption. Gateway concentrators have their own datasheets
Gateway antenna and feeder 6 dBi, 2 dB loss Assumption
Design margin 3 dB Assumption, for small-scale fading and implementation loss
Free-space model 32.4 + 20 log f(MHz) + 20 log d(km) ITU-R P.525-5, equation (6)
Empirical model UMa NLOS, gateway 25 m, device 1.5 m, 10 m to 5 km 3GPP TR 38.901 V19.5.0 (Release 19), Table 7.4.1-1, valid from 0.5 GHz
Shadow fading log-normal, 6 dB standard deviation, independent per 50 m cell TR 38.901 UMa NLOS σ, correlation distance 50 m
Location probability for range 90 % Assumption. Margin = 1.28 × 6 dB = 7.7 dB

The SX1262 is Semtech’s sub-GHz LoRa and FSK transceiver. According to the SX1261/2 datasheet it covers 150 MHz to 960 MHz continuously, supports LoRa bandwidths from 7.8 kHz to 500 kHz and transmits up to +22 dBm. It is a half-duplex transceiver, so it either transmits or receives at any moment, and the datasheet does not give an ADC resolution. We read revision 2.2 of the datasheet (December 2024). Semtech’s product page lists the datasheet with a date of 7 April 2025. We could not download that file, so check the sensitivity table against it. See the hardware table for more.

Design

RP002 + TS001SF, BW, preamble, 13 Btime on airSemtech formuladuty cycle 1 %uplinks per hourSX1262 sensitivitySF7 and SF12 anchorsEIRP, gains, losses+16 dBm, 6 dBi, 2 dBmax path lossper SF, minus 3 dBpath-loss modelsP.525 and 38.901 UMarange per SFmedian and 90 %coveragegrid50 m cells+ shadowingmax path loss per SF
Structure of the calculator. Regional parameters and the frame format fix the payload, time on air and duty-cycle budget. Datasheet sensitivity, EIRP, antenna gains and margins fix the maximum path loss per spreading factor. Two path-loss models turn that into range, and the UMa model with random shadowing produces the coverage grid.

Time on air

A LoRa symbol at spreading factor SF and bandwidth BW lasts 2^SF / BW. A LoRaWAN uplink has an 8-symbol preamble, 4.25 symbols of sync word and start-of-frame, 8 symbols that carry the header, and then the payload symbols. The number of payload symbols is ceil((8 PL + 16 CRC − 4 SF + 8 + 20 H) / (4 (SF − 2 DE))) × (CR + 4), where PL is the PHY payload in bytes, CRC is 1 when the CRC is on, H is 1 for an explicit header, DE is 1 when low data rate optimisation is on and CR is 1 for coding rate 4/5. The datasheet revision we read points to the SX126x drivers for this formula, and the code follows the integer arithmetic in Semtech’s sx126x_driver for SF7 to SF12. RP002-1.0.4’s LoRa packet structure table gives the same 8 + 4.25 + 8 symbol layout.

The PHY payload is the 12 application bytes plus 13 bytes of LoRaWAN framing: a 1-byte MAC header, a 7-byte frame header with no options, a 1-byte port and a 4-byte message integrity code.

Duty cycle

At 1 % a device may transmit for 36 s in any hour. The calculator divides 36 s by the time on air and rounds down. RP002-1.0.4 points to ETSI EN 300 220-2 for the detailed European rules, and a network operator may impose tighter limits, so treat these numbers as an upper bound.

Sensitivity

The SX1261/2 datasheet gives LoRa sensitivity at SF7 and SF12 only, for each bandwidth. The calculator draws a straight line in SF between −124 dBm and −137 dBm at 125 kHz. As a check it also computes the noise-floor estimate −174 dBm/Hz + 10 log10(125 kHz) + NF + SNRmin, with the datasheet’s typical demodulator SNR per SF and an assumed 6 dB noise figure. The two agree within 0.5 dB at every SF, which supports the interpolation. The 6 dB noise figure is only for this check. It is not a datasheet value.

Maximum path loss = EIRP + gateway antenna gain − feeder loss − sensitivity − design margin. With +16 dBm EIRP, 6 dBi, 2 dB and 3 dB, that is 141.0 dB at SF7 and 154.0 dB at SF12. Each step of SF adds 2.6 dB. See the glossary entries for EIRP, receiver sensitivity and link margin.

Path-loss models

Free-space path loss from ITU-R P.525-5 is the best case. It assumes a clear line of sight and no ground, so at street level it overstates range by orders of magnitude. It is here to show that.

The empirical model is the urban macro (UMa) non-line-of-sight model from 3GPP TR 38.901 Table 7.4.1-1. The NLOS loss is the larger of the UMa LOS loss and 13.54 + 39.08 log10(d3D) + 20 log10(fc) − 0.6 (hUT − 1.5), with fc in GHz and d3D in metres. The LOS part uses a breakpoint distance 4 h’BS h’UT fc / c with an effective environment height of 1 m, which the report specifies for a device below 13 m. TR 38.901 states the table applies from 0.5 GHz, so 868 MHz is inside its range, and from 10 m to 5 km. It was built for cellular macro sites, which share the geometry of a gateway on a 25 m mast in a town. It has no terrain.

Coverage grid

The grid is 201 by 201 points at 50 m spacing with the gateway in the centre. Only the 31,416 points between 10 m and 5 km from the gateway are inside the model’s range and count. Each point gets the UMa NLOS median loss plus a shadowing sample with a 6 dB standard deviation. TR 38.901 gives 50 m as the shadowing correlation distance for UMa NLOS, equal to the grid step, so we draw independent samples per cell as a simplification. Each point is then assigned the fastest SF whose maximum path loss it meets.

Code

Save this as lorawan_coverage.py. It is one block: parameters, time on air, the two path-loss models, the link budget, the grid and the payload limits.

# lorawan_coverage.py: EU868 LoRaWAN uplink link budget, time on air, duty cycle and a coverage grid.
import math
from statistics import NormalDist

import numpy as np

# Region and radio (sources and assumptions are listed on the page)
FREQ_MHZ = 868.1                 # EU868 default channel
BW_HZ = 125e3                    # DR0 to DR5 use 125 kHz
SFS = [7, 8, 9, 10, 11, 12]      # DR5 ... DR0
EIRP_DBM = 16.0                  # RP002-1.0.4 default max EIRP for EU863-870
DUTY_CYCLE = 0.01                # default channels: less than 1 %
APP_PAYLOAD = 12                 # application bytes per uplink
LORAWAN_OVERHEAD = 13            # MHDR 1 + FHDR 7 + FPort 1 + MIC 4, no FOpts
PREAMBLE_SYMB = 8                # LoRaWAN uplink preamble
CR = 1                           # coding rate 4/5
SENS_DATASHEET = {7: -124.0, 12: -137.0}       # SX1262 at 125 kHz, Rx boosted gain (dBm)
SNR_MIN_DB = {7: -7.5, 8: -10.0, 9: -12.5, 10: -15.0, 11: -17.5, 12: -20.0}
NF_DB = 6.0                      # assumed noise figure for the cross-check only

# Link and site assumptions
GW_ANT_GAIN_DBI = 6.0            # gateway antenna
GW_CABLE_LOSS_DB = 2.0           # feeder and connectors
DESIGN_MARGIN_DB = 3.0           # small-scale fading and implementation allowance
H_BS, H_UT = 25.0, 1.5           # 38.901 UMa defaults: gateway on a mast, device at street level
SIGMA_SF_DB = 6.0                # 38.901 UMa NLOS shadow fading standard deviation
LOCATION_PROB = 0.90             # edge-of-cell location probability for the range table
GRID_STEP_M, GRID_HALF_M = 50.0, 5000.0
rng = np.random.default_rng(868)


def time_on_air_s(payload_bytes: int, sf: int, bw_hz: float = BW_HZ, preamble: int = PREAMBLE_SYMB,
                  cr: int = CR, crc: bool = True, explicit_header: bool = True) -> float:
    """LoRa time on air for SF7 to SF12, following Semtech's SX126x driver."""
    ldro = (2 ** sf) / bw_hz >= 16.38e-3            # on for SF11 and SF12 at 125 kHz
    num = 8 * payload_bytes + 16 * crc - 4 * sf + 8 + 20 * explicit_header
    den = 4 * (sf - 2 * ldro)
    n_payload = math.ceil(max(num, 0) / den) * (cr + 4)
    n_symbols = preamble + 4.25 + 8 + n_payload
    return n_symbols * (2 ** sf) / bw_hz


def fspl_db(d_m: np.ndarray) -> np.ndarray:
    """ITU-R P.525-5 equation (6): 32.4 + 20 log f(MHz) + 20 log d(km)."""
    return 32.4 + 20 * np.log10(FREQ_MHZ) + 20 * np.log10(d_m / 1e3)


def uma_nlos_db(d2d_m: np.ndarray) -> np.ndarray:
    """3GPP TR 38.901 Table 7.4.1-1, UMa NLOS = max(UMa LOS, PL'UMa-NLOS). fc in GHz, d in m."""
    fc = FREQ_MHZ / 1e3
    d3d = np.sqrt(d2d_m ** 2 + (H_BS - H_UT) ** 2)
    d_bp = 4 * (H_BS - 1.0) * (H_UT - 1.0) * fc * 1e9 / 3e8      # hE = 1 m because hUT is below 13 m
    pl1 = 28.0 + 22 * np.log10(d3d) + 20 * np.log10(fc)
    pl2 = 28.0 + 40 * np.log10(d3d) + 20 * np.log10(fc) - 9 * np.log10(d_bp ** 2 + (H_BS - H_UT) ** 2)
    pl_los = np.where(d2d_m <= d_bp, pl1, pl2)
    pl_nlos = 13.54 + 39.08 * np.log10(d3d) + 20 * np.log10(fc) - 0.6 * (H_UT - 1.5)
    return np.maximum(pl_los, pl_nlos)


def range_for(model, max_loss_db: float) -> float:
    """Largest distance (m) at which the model loss stays below max_loss_db, by bisection."""
    lo, hi = 10.0, 1e7
    for _ in range(200):
        mid = math.sqrt(lo * hi)
        lo, hi = (mid, hi) if model(np.array([mid]))[0] <= max_loss_db else (lo, mid)
    return lo


# 1. Receiver sensitivity per SF: datasheet anchors, linear in SF between them, plus a noise-floor check.
sens = {sf: SENS_DATASHEET[7] + (sf - 7) * (SENS_DATASHEET[12] - SENS_DATASHEET[7]) / 5 for sf in SFS}
print("SF  DR  sensitivity (dBm)  check -174+10log(BW)+NF+SNRmin  ToA (ms)  uplinks/hour at 1 %")
phy = APP_PAYLOAD + LORAWAN_OVERHEAD
toa = {sf: time_on_air_s(phy, sf) for sf in SFS}
for sf in SFS:
    check = -174 + 10 * math.log10(BW_HZ) + NF_DB + SNR_MIN_DB[sf]
    per_hour = math.floor(3600 * DUTY_CYCLE / toa[sf])
    print(f"{sf:>2}  {12 - sf:>2}  {sens[sf]:>17.1f}  {check:>31.1f}  {toa[sf] * 1e3:>8.1f}  {per_hour:>19}")
print(f"PHYPayload {phy} bytes, preamble {PREAMBLE_SYMB} symbols, CR 4/5, CRC on, explicit header")

# 2. Link budget and range per SF.
z = NormalDist().inv_cdf(LOCATION_PROB)
shadow_margin = z * SIGMA_SF_DB
print(f"\nEIRP {EIRP_DBM} dBm, gateway gain {GW_ANT_GAIN_DBI} dBi, cable {GW_CABLE_LOSS_DB} dB, "
      f"design margin {DESIGN_MARGIN_DB} dB, shadowing margin {shadow_margin:.1f} dB ({LOCATION_PROB:.0%})")
print("SF  max path loss (dB)  range free space (km)  range UMa NLOS median (km)  at 90 % (km)")
mapl = {}
for sf in SFS:
    mapl[sf] = EIRP_DBM + GW_ANT_GAIN_DBI - GW_CABLE_LOSS_DB - sens[sf] - DESIGN_MARGIN_DB
    r_fs = range_for(fspl_db, mapl[sf]) / 1e3
    r_med = range_for(uma_nlos_db, mapl[sf]) / 1e3
    r_90 = range_for(uma_nlos_db, mapl[sf] - shadow_margin) / 1e3
    print(f"{sf:>2}  {mapl[sf]:>18.1f}  {r_fs:>21.0f}  {r_med:>26.2f}  {r_90:>12.2f}")

# 3. Path loss against distance for the chart.
print("\nd (km)  FSPL (dB)  UMa NLOS (dB)")
for d_km in [0.1, 0.2, 0.3, 0.5, 0.7, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0]:
    d = np.array([d_km * 1e3])
    print(f"{d_km:>6}  {fspl_db(d)[0]:>9.1f}  {uma_nlos_db(d)[0]:>13.1f}")

# 4. Coverage grid: 10 km x 10 km, gateway at the centre, UMa NLOS plus log-normal shadowing.
axis = np.arange(-GRID_HALF_M, GRID_HALF_M + GRID_STEP_M, GRID_STEP_M)
x, y = np.meshgrid(axis, axis)
d2d = np.hypot(x, y)
inside = (d2d >= 10.0) & (d2d <= 5000.0)                       # UMa NLOS validity: 10 m to 5 km
loss = uma_nlos_db(np.maximum(d2d, 10.0)) + rng.normal(0.0, SIGMA_SF_DB, d2d.shape)
best_sf = np.full(d2d.shape, 0)
for sf in reversed(SFS):                                       # overwrite with the fastest SF that closes
    best_sf[(loss <= mapl[sf]) & inside] = sf
n_in = int(inside.sum())
print(f"\ncoverage grid: {len(axis)} x {len(axis)} points at {GRID_STEP_M:.0f} m, {n_in} inside the 5 km disc")
print("SF  share of area, fastest SF that closes (%)  cumulative (%)  by ring 0-1, 1-2, 2-3, 3-4, 4-5 km (%)")
cum = 0.0
rings = [(0, 1000), (1000, 2000), (2000, 3000), (3000, 4000), (4000, 5000)]
for sf in SFS:
    share = 100 * np.sum(best_sf[inside] == sf) / n_in
    cum += share
    ring_cov = []
    for r0, r1 in rings:
        sel = inside & (d2d > r0) & (d2d <= r1)
        ring_cov.append(100 * np.sum((best_sf[sel] > 0) & (best_sf[sel] <= sf)) / sel.sum())
    print(f"{sf:>2}  {share:>41.1f}  {cum:>14.1f}  " + ", ".join(f"{c:.0f}" for c in ring_cov))
print(f"not covered at any SF: {100 - cum:.1f} % of the disc")

# 5. Payload limits: the longest EU868 uplink, and the US915 400 ms dwell time at DR0 (SF10, 125 kHz).
t51 = time_on_air_s(51 + LORAWAN_OVERHEAD, 12)
print(f"\nEU868 DR0, 51-byte application payload: {t51 * 1e3:.1f} ms, {math.floor(36 / t51)} uplinks/hour at 1 %")
for n_app in (11, 12):
    t = time_on_air_s(n_app + LORAWAN_OVERHEAD, 10)
    print(f"US915 DR0 (SF10), {n_app}-byte application payload: {t * 1e3:.1f} ms, "
          f"{'within' if t <= 0.4 else 'over'} the 400 ms dwell time")

range_for solves for distance by bisection on a logarithmic scale, so it works for both models.

Run

python -m pip install numpy
python lorawan_coverage.py

Test

This is the output from our run, with the shadowing seed fixed at 868.

SF  DR  sensitivity (dBm)  check -174+10log(BW)+NF+SNRmin  ToA (ms)  uplinks/hour at 1 %
 7   5             -124.0                           -124.5      61.7                  583
 8   4             -126.6                           -127.0     113.2                  318
 9   3             -129.2                           -129.5     205.8                  174
10   2             -131.8                           -132.0     411.6                   87
11   1             -134.4                           -134.5     823.3                   43
12   0             -137.0                           -137.0    1482.8                   24
PHYPayload 25 bytes, preamble 8 symbols, CR 4/5, CRC on, explicit header

EIRP 16.0 dBm, gateway gain 6.0 dBi, cable 2.0 dB, design margin 3.0 dB, shadowing margin 7.7 dB (90%)
SF  max path loss (dB)  range free space (km)  range UMa NLOS median (km)  at 90 % (km)
 7               141.0                    310                        1.96          1.25
 8               143.6                    418                        2.29          1.45
 9               146.2                    564                        2.67          1.70
10               148.8                    761                        3.11          1.98
11               151.4                   1027                        3.62          2.30
12               154.0                   1385                        4.22          2.68

d (km)  FSPL (dB)  UMa NLOS (dB)
   0.1       71.2           90.9
   0.2       77.2          102.4
   0.3       80.7          109.2
   0.5       85.2          117.8
   0.7       88.1          123.5
   1.0       91.2          129.6
   1.5       94.7          136.4
   2.0       97.2          141.3
   3.0      100.7          148.2
   4.0      103.2          153.1
   5.0      105.2          156.9

coverage grid: 201 x 201 points at 50 m, 31416 inside the 5 km disc
SF  share of area, fastest SF that closes (%)  cumulative (%)  by ring 0-1, 1-2, 2-3, 3-4, 4-5 km (%)
 7                                       19.7            19.7  100, 73, 25, 6, 1
 8                                        6.8            26.5  100, 83, 40, 12, 3
 9                                        8.6            35.1  100, 91, 57, 22, 7
10                                       10.4            45.5  100, 96, 72, 37, 15
11                                       11.7            57.2  100, 99, 84, 53, 27
12                                       11.9            69.2  100, 100, 92, 70, 43
not covered at any SF: 30.8 % of the disc

EU868 DR0, 51-byte application payload: 2793.5 ms, 12 uplinks/hour at 1 %
US915 DR0 (SF10), 11-byte application payload: 370.7 ms, within the 400 ms dwell time
US915 DR0 (SF10), 12-byte application payload: 411.6 ms, over the 400 ms dwell time
801001201400.11free space, ITU-R P.525UMa NLOS median, TR 38.901SF7 maximum path loss, 141.0 dBSF12 maximum path loss, 154.0 dBpath loss (dB)distance (km)
Path loss against distance at 868.1 MHz for free space (ITU-R P.525-5) and the 3GPP TR 38.901 UMa NLOS median with a 25 m gateway and a 1.5 m device, from the printed table above, with the maximum path loss for SF7 and SF12 from the link budget. The UMa curve crosses the SF7 line near 2 km and the SF12 line near 4.2 km. Free space never reaches either line within 5 km.
0204060SF7SF8SF9SF10SF11SF12area covered (%)spreading factor
Share of the 5 km disc around the gateway covered at each spreading factor or faster, from the coverage grid printed above (UMa NLOS with 6 dB log-normal shadowing). SF7 alone covers 19.7 % of the area and adding every slower SF raises it to 69.2 %.
05001,000SF7SF8SF9SF10SF11SF12time on air (ms)spreading factor
Time on air of a LoRaWAN uplink with a 12-byte application payload (25-byte PHY payload) at 125 kHz, from the printed table above. Each SF step roughly doubles the time, and SF11 and SF12 use low data rate optimisation.

How to read it:

  • Time on air and duty cycle. Going from SF7 to SF12 buys 13 dB of link budget and costs 24 times the airtime (61.7 ms against 1482.8 ms). The battery pays for that airtime too. The largest EU868 uplink at DR0, 51 application bytes, takes 2793.5 ms, which allows only 12 uplinks per hour.
  • Range. Free space gives 310 km at SF7. That number is meaningless at street level and is why free space must not be used for ground-level planning. The UMa median range is 1.96 km at SF7 and 4.22 km at SF12. To be covered at 90 % of locations on the edge of the cell, those shrink to 1.25 km and 2.68 km.
  • The grid. Inside 1 km every point closes at SF7. Between 4 km and 5 km only 43 % of points close even at SF12. Overall 30.8 % of the disc is not covered. Serving the full 5 km radius here needs a better site, a higher antenna or a second gateway.
  • US915. At DR0 (SF10, 125 kHz) an 11-byte payload takes 370.7 ms, within the 400 ms dwell time. A 12-byte payload takes 411.6 ms and does not fit. That is why RP002-1.0.4 limits the DR0 application payload in US902-928 to 11 bytes.

If your output differs, check the frequency units first. P.525 takes the frequency in MHz and the distance in km, while TR 38.901 takes the frequency in GHz and the distance in metres. Mixing them shifts the loss by tens of dB.

On a real radio

This section needs radios: an SX1262 development kit or module as the device and a LoRaWAN gateway connected to a network server.

  • Measure, then fit. Drive a test device around the gateway and log RSSI and SNR for each uplink with its GPS position. Fit path loss against log distance and compare with the UMa curve. The measured spread replaces the 6 dB shadowing assumption.
  • Check the gateway. The gateway concentrator, not an SX1262, receives the uplinks. Replace the sensitivity assumption with the figures from its datasheet, and add the actual antenna gain and feeder loss.
  • Indoor devices. Meters in basements and cupboards add building entry loss. TR 38.901 has outdoor-to-indoor penetration models in clause 7.4.3. Add one before you promise indoor coverage.
  • Regulation. Stay within the EIRP and duty-cycle limits of your region. RP002-1.0.4 summarises them per region but states that it is not intended to be an authoritative source of regional regulation. Check the national rules where you deploy.

Things to try

  1. Switch to US915. Set the uplink data rates to SF7 to SF10 at 125 kHz and the frequency to 902.3 MHz, and replace the duty-cycle limit with the US902-928 rules in RP002-1.0.4. Then add the 400 ms dwell-time check to every SF and see which payload sizes survive at each one. RP002-1.0.4 also has the US902-928 power limits.
  2. A rural site. Replace the UMa model with the TR 38.901 rural macro (RMa) NLOS model with a 35 m mast, valid to 5 km for NLOS.
  3. Correlated shadowing. Generate white Gaussian noise on the grid and smooth it with a 2D filter so that the correlation distance is 50 m, then rescale it to 6 dB standard deviation.
  4. A bigger payload. Set APP_PAYLOAD = 51. Check the time on air at each SF against the 1 % budget for an hourly reporting interval with retransmissions.
  5. Second gateway. Put a second gateway 4 km east, compute the loss to both, and take the better one per point. Report the coverage gain.

Summary

  • A LoRaWAN link budget needs four documents: the regional parameters (power, duty cycle, data rates), the MAC frame format (overhead), the radio datasheet (sensitivity) and a propagation model. Name the version of each.
  • Each SF step adds about 2.6 dB of link budget and doubles the airtime. SF12 had 13 dB more budget than SF7 and used 24 times the airtime here.
  • The 1 % EU868 duty cycle allowed 583 uplinks per hour at SF7 and 24 at SF12 for a 12-byte payload.
  • Free-space loss overstates ground-level range by two orders of magnitude. The TR 38.901 UMa NLOS model gave median ranges of 1.96 km at SF7 and 4.22 km at SF12.
  • Shadowing turns a range into a probability. A 90 % location margin cut the SF12 range to 2.68 km, and 30.8 % of the 5 km disc was left uncovered.

Background: the LPWAN and IoT and link budgets and noise topic hubs, and the glossary entry for path loss.