diff --git a/README.md b/README.md index 20dcb58..9f9508b 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,43 @@ LoRa Time on Air calculator =========================== A calculator of the time on air of LoRa PHY frame in Python. + This script refers to the section 4.1.1.6. LoRa Packet Structure, [SX1276/77/78/79 Datasheet][http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf]. -The default value is aligned to LoRaWAN AS923. +The default parameters of the equation is based on LoRaWAN AS923 +in the LoRaWAN regional parameters v1.1. + +## Note + +The default value of Explicit Header is enable. +It is guessed from the PHY frame format +though there is no explit text in the LoRaWAN specification. + +The value of LowDataRateOptimization is set automatically +when the symbol duration exceeds 16ms. +Because the datasheet requires that it must be used +when the symbol duration exceeds 16ms. +This is the case below: + +- SF=12 and 11 in 125 kHz. +- SF=12 in 250 kHz. + +You can disable this feature by the --disable-auto-ldro option. +The LDRO is disabled by default if you disable the auto LDRO. +If you want to enable the LDRO, you can specify the --enable-ldro option. + +In the downlink stream, the CRC at the tail of the PHY frame is not used. +To calculate the ToA for the downlink stream, +the --downlink option should be specified. ## Usage - lorawan_toa.py [-h] [--band-width N_BW] [--disable-dro] [--disable-eh] - [--cr N_CR] [--preamble N_PREAMBLE] [-v] [-d] - SF SIZE + lorawan_toa.py [-h] [--band-width N_BW] [--disable-auto-ldro] + [--enable-ldro] [--disable-eh] [--downlink] + [--disable-crc] [--cr N_CR] [--preamble N_PREAMBLE] [-v] + [-d] + SF SIZE positional arguments: SF Spreading Factor. It should be from 7 to 12. @@ -21,10 +48,13 @@ The default value is aligned to LoRaWAN AS923. optional arguments: -h, --help show this help message and exit --band-width N_BW bandwidth in kHz. default is 125 kHz. - --disable-dro disable the low data rate optimization. default is - enable as LoRaWAN does. - --disable-eh disable the explicit header. default is enable as - LoRaWAN does. + --disable-auto-ldro disable the auto LDRO and disable LDRO. + --enable-ldro This option is available when the auto LDRO is + disabled. + --disable-eh disable the explicit header. + --downlink disable the CRC field, which is for the LoRaWAN + downlink stream. + --disable-crc same effect as the --downlink option. --cr N_CR specify the CR value. default is 1 as LoRaWAN does. --preamble N_PREAMBLE specify the preamble. default is 8 for AS923. @@ -34,6 +64,8 @@ The default value is aligned to LoRaWAN AS923. ## Examples with the -v option, it shows the ToA as well as the related information. +below example, it show detail information in SF 12, 64 bytes of PHY payload, +125 kHz bandwidth, preamble 8. % python lorawan_toa.py 12 64 -v PHY payload size : 64 Bytes @@ -43,19 +75,23 @@ with the -v option, it shows the ToA as well as the related information. Low data rate opt. : enable Explicit header : enable CR (coding rate) : 1 (4/5) + Symbol Rate : 30.518 symbol/s + Symbol Time : 32.768 msec/symbol Preamble size : 8 symbols + Packet symbol size : 73 symbols + Preamble ToA : 401.408 msec + Payload ToA : 2392.064 msec Time on Air : 2793.472 msec - MAC frame data rate : 63.998 bps without the -v option, it simply shows the ToA. - % python lorawan_toa.py --band-width=500 7 128 - 71.744 + % python lorawan_toa.py 12 64 + 2793.472 ## graph_as923.py It makes a set of figures about Time on Air and PHYPayload size, especially LoRaWAN AS923 using matlib like below. -![LoRa ToA](lora_toa.png) +![LoRa ToA](image/as923-toa.png) diff --git a/graph_as923.py b/graph_as923.py index db99f7e..979a00f 100644 --- a/graph_as923.py +++ b/graph_as923.py @@ -10,273 +10,274 @@ from lorawan_toa import * def get_line(list_size, n_sf, bw=125): return [ get_toa(i, n_sf, n_bw=bw)["t_packet"] for i in list_size ] -''' -test code -''' - +######### # -# -# -plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') - -plt.title("SF and ToA (BW=125 kHz)") -plt.ylim(0, 5000) +fig = plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') +ax = fig.add_subplot(1,1,1) +ax.set_title("SF and ToA (BW=125 kHz)") x = range(0, 255) -plt.plot(x, get_line(x, 12), "b-", label="SF12", linewidth=3, alpha=1) -plt.plot(x, get_line(x, 11), "g-", label="SF11", linewidth=3, alpha=1) -plt.plot(x, get_line(x, 10), "k-", label="SF10", linewidth=3, alpha=1) -plt.plot(x, get_line(x, 9), "c-", label="SF9", linewidth=3, alpha=1) -plt.plot(x, get_line(x, 8), "m-", label="SF8", linewidth=3, alpha=1) -plt.plot(x, get_line(x, 7), "y-", label="SF7", linewidth=3, alpha=1) +ax.plot(x, get_line(x, 12), "b-", label="SF12", linewidth=3, alpha=1) +ax.plot(x, get_line(x, 11), "g-", label="SF11", linewidth=3, alpha=1) +ax.plot(x, get_line(x, 10), "k-", label="SF10", linewidth=3, alpha=1) +ax.plot(x, get_line(x, 9), "c-", label="SF9", linewidth=3, alpha=1) +ax.plot(x, get_line(x, 8), "m-", label="SF8", linewidth=3, alpha=1) +ax.plot(x, get_line(x, 7), "y-", label="SF7", linewidth=3, alpha=1) -plt.xlabel("PHY Payload Size (Byte)") -plt.ylabel("Time on Air (ms)") - -plt.legend(loc="upper left", fancybox=True, shadow=True) -plt.grid(True) +ax.set_xlim(0, 260) +ax.set_ylim(0, 5000) +ax.set_xlabel("PHY Payload Size (Byte)") +ax.set_ylabel("Time on Air (ms)") +ax.grid(True) +ax.legend(loc="upper left", fancybox=True, shadow=True) +fig.tight_layout() plt.show() +fig.savefig("image/lora-toa-125.png") +######### # -# -# -plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') - -plt.title("AS923 No DwellTime") -plt.ylim(0, 5000) +fig = plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') +ax = fig.add_subplot(1,1,1) +ax.set_title("AS923 No DwellTime") x = range(0, 255) -plt.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) # no dwellTime consideration -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", label="SF12", +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", label="SF12", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 11), "g-", label="SF11", +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 11), "g-", label="SF11", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "k-", label="SF10", +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "k-", label="SF10", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 123), get_line(mpsrange(8, 123), 9), "c-", label="SF9", +ax.plot(mpsrange(8, 123), get_line(mpsrange(8, 123), 9), "c-", label="SF9", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8), "m-", label="SF8", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8), "m-", label="SF8", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", label="SF7", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", label="SF7", linewidth=3, alpha=1) -plt.xlabel("PHY Payload Size (Byte)") -plt.ylabel("Time on Air (ms)") - -plt.legend(loc="upper left", fancybox=True, shadow=True) -plt.grid(True) +ax.set_xlim(0, 260) +ax.set_ylim(0, 5000) +ax.set_xlabel("PHY Payload Size (Byte)") +ax.set_ylabel("Time on Air (ms)") +ax.grid(True) +ax.legend(loc="upper left", fancybox=True, shadow=True) +fig.tight_layout() plt.show() +fig.savefig("image/as923-without-dwelltime.png") +######### # -# -# -plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') - -plt.title("AS923 DwellTime 400ms") -plt.ylim(0, 5000) +fig = plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') +ax = fig.add_subplot(1,1,1) +ax.set_title("AS923 DwellTime 400ms") x = range(0, 255) -plt.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) # required dwellTime consideration -plt.plot([0], [0], "b-", label="SF12", linewidth=3, alpha=1) -plt.plot([0], [0], "c-", label="SF11", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 19), get_line(mpsrange(8, 19), 10), "k-", label="SF10", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 9), "c-", label="SF9", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 133), get_line(mpsrange(8, 133), 8), "m-", label="SF8", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", label="SF7", linewidth=3, alpha=1) +ax.plot([0], [0], "b-", label="SF12", linewidth=3, alpha=1) +ax.plot([0], [0], "c-", label="SF11", linewidth=3, alpha=1) +ax.plot(mpsrange(8, 19), get_line(mpsrange(8, 19), 10), "k-", label="SF10", linewidth=3, alpha=1) +ax.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 9), "c-", label="SF9", linewidth=3, alpha=1) +ax.plot(mpsrange(8, 133), get_line(mpsrange(8, 133), 8), "m-", label="SF8", linewidth=3, alpha=1) +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", label="SF7", linewidth=3, alpha=1) -plt.plot(x, [400 for i in range(0, 255)], "r,", linewidth=1, alpha=0.7) +ax.plot(x, [400 for i in range(0, 255)], "r,", linewidth=1, alpha=0.7) -plt.xlabel("PHY Payload Size (Byte)") -plt.ylabel("Time on Air (ms)") - -plt.legend(loc="upper left", fancybox=True, shadow=True) -plt.grid(True) +ax.set_xlim(0, 260) +ax.set_ylim(0, 5000) +ax.set_xlabel("PHY Payload Size (Byte)") +ax.set_ylabel("Time on Air (ms)") +ax.grid(True) +ax.legend(loc="upper left", fancybox=True, shadow=True) +fig.tight_layout() plt.show() +fig.savefig("image/as923-with-dwelltime.png") +######### # -# -# -plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') - -plt.title("AS923") -plt.ylim(0, 5000) +fig = plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') +ax = fig.add_subplot(1,1,1) +ax.set_title("AS923") x = range(0, 255) -plt.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) # no dwellTime consideration -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 11), "g-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "k-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 123), get_line(mpsrange(8, 123), 9), "c-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8), "m-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 11), "g-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "k-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 123), get_line(mpsrange(8, 123), 9), "c-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8), "m-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", linewidth=1.2, alpha=0.7) # required dwellTime consideration -plt.plot([0], [0], "b-", label="SF12/125kHz", linewidth=3, alpha=1) -plt.plot([0], [0], "g-", label="SF11/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 19), get_line(mpsrange(8, 19), 10), "k-", +ax.plot([0], [0], "b-", label="SF12/125kHz", linewidth=3, alpha=1) +ax.plot([0], [0], "g-", label="SF11/125kHz", linewidth=3, alpha=1) +ax.plot(mpsrange(8, 19), get_line(mpsrange(8, 19), 10), "k-", label="SF10/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 9), "c-", +ax.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 9), "c-", label="SF9/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 133), get_line(mpsrange(8, 133), 8), "m-", +ax.plot(mpsrange(8, 133), get_line(mpsrange(8, 133), 8), "m-", label="SF8/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", label="SF7/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=250), "b--", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=250), "b--", label="SF7/250kHz", linewidth=3, alpha=0.5) -plt.xlabel("PHY Payload Size (Byte)") -plt.ylabel("Time on Air (ms)") - -plt.legend(loc="upper left", fancybox=True, shadow=True) -plt.grid(True) +ax.set_xlim(0, 260) +ax.set_ylim(0, 5000) +ax.set_xlabel("PHY Payload Size (Byte)") +ax.set_ylabel("Time on Air (ms)") +ax.grid(True) +ax.legend(loc="upper left", fancybox=True, shadow=True) +fig.tight_layout() plt.show() +fig.savefig("image/as923-toa.png") +######### # -# -# -plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') - -plt.title("AS923 and ARIB STD-T108") -plt.ylim(0, 5000) +fig = plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') +ax = fig.add_subplot(1,1,1) +ax.set_title("AS923 and ARIB STD-T108") x = range(0, 255) -plt.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) # no dwellTime consideration -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 11), "g-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "k-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 123), get_line(mpsrange(8, 123), 9), "c-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8), "m-", linewidth=1.2, alpha=0.7) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 11), "g-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "k-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 123), get_line(mpsrange(8, 123), 9), "c-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8), "m-", linewidth=1.2, alpha=0.7) +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", linewidth=1.2, alpha=0.7) # required dwellTime consideration -plt.plot([0], [0], "b-", label="SF12/125kHz", linewidth=3, alpha=1) -plt.plot([0], [0], "g-", label="SF11/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 19), get_line(mpsrange(8, 19), 10), "k-", +ax.plot([0], [0], "b-", label="SF12/125kHz", linewidth=3, alpha=1) +ax.plot([0], [0], "g-", label="SF11/125kHz", linewidth=3, alpha=1) +ax.plot(mpsrange(8, 19), get_line(mpsrange(8, 19), 10), "k-", label="SF10/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 9), "c-", +ax.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 9), "c-", label="SF9/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 133), get_line(mpsrange(8, 133), 8), "m-", +ax.plot(mpsrange(8, 133), get_line(mpsrange(8, 133), 8), "m-", label="SF8/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", label="SF7/125kHz", linewidth=3, alpha=1) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=250), "b--", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=250), "b--", label="SF7/250kHz", linewidth=3, alpha=0.5) -plt.plot(x, [400 for i in range(0, 255)], "r--", linewidth=2, alpha=0.7) -plt.plot(x, [200 for i in range(0, 255)], "r--", linewidth=2, alpha=0.7) -plt.plot(x, [4000 for i in range(0, 255)], "r--", linewidth=2, alpha=0.7) +ax.plot(x, [400 for i in range(0, 255)], "r--", linewidth=2, alpha=0.7) +ax.plot(x, [200 for i in range(0, 255)], "r--", linewidth=2, alpha=0.7) +ax.plot(x, [4000 for i in range(0, 255)], "r--", linewidth=2, alpha=0.7) -plt.xlabel("PHY Payload Size (Byte)") -plt.ylabel("Time on Air (ms)") - -plt.legend(loc="upper left", fancybox=True, shadow=True) -plt.grid(True) +ax.set_xlim(0, 260) +ax.set_ylim(0, 5000) +ax.set_xlabel("PHY Payload Size (Byte)") +ax.set_ylabel("Time on Air (ms)") +ax.grid(True) +ax.legend(loc="upper left", fancybox=True, shadow=True) +fig.tight_layout() plt.show() +fig.savefig("image/as923-with-arib180.png") +######### # -# -# -plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') -plt.title("AS923 vs Others (SF12)") - -plt.ylim(0, 5000) +fig = plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') +ax = fig.add_subplot(1,1,1) +ax.set_title("AS923 vs Others (SF12)") x = range(0, 255) -plt.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 12, bw=500), "r-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 12, bw=500), "r-", linewidth=3, alpha=0.05) # no dwellTime consideration -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", label="SF12/125kHz", linewidth=3.0, alpha=1) # LoRa: SF12 / 500 kHz -plt.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 12, bw=500), "r-", +ax.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 12, bw=500), "r-", label="SF12/500kHz", linewidth=3, alpha=1) -plt.xlabel("PHY Payload Size (Byte)") -plt.ylabel("Time on Air (ms)") - -plt.legend(loc="best", fancybox=True, shadow=True) -plt.grid(True) +ax.set_xlim(0, 260) +ax.set_ylim(0, 5000) +ax.set_xlabel("PHY Payload Size (Byte)") +ax.set_ylabel("Time on Air (ms)") +ax.grid(True) +ax.legend(loc="best", fancybox=True, shadow=True) +fig.tight_layout() plt.show() +fig.savefig("image/as923-vs-others-sf12.png") +######### # -# -# -plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') -plt.title("AS923 vs Others (SF10)") - -plt.ylim(0, 5000) +fig = plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') +ax = fig.add_subplot(1,1,1) +ax.set_title("AS923 vs Others (SF10)") x = range(0, 255) -plt.plot(x, get_line(x, 10), "b-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 10, bw=500), "r-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 10), "b-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 10, bw=500), "r-", linewidth=3, alpha=0.05) # no dwellTime consideration -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "b-", +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "b-", label="SF10/125kHz", linewidth=3.0, alpha=1) # LoRa: SF10 / 500 kHz -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 10, bw=500), "r-", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 10, bw=500), "r-", label="SF10/500kHz", linewidth=3, alpha=1) -plt.xlabel("PHY Payload Size (Byte)") -plt.ylabel("Time on Air (ms)") - -plt.legend(loc="best", fancybox=True, shadow=True) -plt.grid(True) +ax.set_xlim(0, 260) +ax.set_ylim(0, 5000) +ax.set_xlabel("PHY Payload Size (Byte)") +ax.set_ylabel("Time on Air (ms)") +ax.grid(True) +ax.legend(loc="best", fancybox=True, shadow=True) +fig.tight_layout() plt.show() +fig.savefig("image/as923-vs-others-sf10.png") +######### # -# -# -plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') - -plt.title("LoRaWAN") -plt.ylim(0, 5000) +fig = plt.figure(num=None, figsize=(16, 8), facecolor='w', edgecolor='k') +ax = fig.add_subplot(1,1,1) +ax.set_title("LoRaWAN") x = range(0, 255) -plt.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) -plt.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 12), "b-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 11), "g-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 10), "k-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 9), "c-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 8), "m-", linewidth=3, alpha=0.05) +ax.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) # SF BW bit rate Max. MACPayload # 12 125 250 59 @@ -285,22 +286,22 @@ plt.plot(x, get_line(x, 7), "y-", linewidth=3, alpha=0.05) # 9 125 1760 123 # 8 125 3125 250 # 7 125 5470 250 -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 12), "b-", label="SF12/125kHz", linewidth=2.0) -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 11), "g-", +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 11), "g-", label="SF11/125kHz", linewidth=2.0) -plt.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "k-", +ax.plot(mpsrange(8, 59), get_line(mpsrange(8, 59), 10), "k-", label="SF10/125kHz", linewidth=2.0) -plt.plot(mpsrange(8, 123), get_line(mpsrange(8, 123), 9), "c-", +ax.plot(mpsrange(8, 123), get_line(mpsrange(8, 123), 9), "c-", label="SF9/125kHz", linewidth=2.0) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8), "m-", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8), "m-", label="SF8/125kHz", linewidth=2.0) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7), "y-", label="SF7/125kHz", linewidth=2.0) # SF BW bit rate Max. MACPayload # 7 250 11000 250 -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=250), "b-.", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=250), "b-.", label="SF7/250kHz", linewidth=2.0) # SF BW bit rate Max. MACPayload @@ -310,24 +311,27 @@ plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=250), "b-.", # 9 500 7000 250 # 8 500 12500 250 # 7 500 21900 250 -plt.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 12, bw=500), "b--", +ax.plot(mpsrange(8, 61), get_line(mpsrange(8, 61), 12, bw=500), "b--", label="SF12/500kHz", linewidth=2.0) -plt.plot(mpsrange(8, 137), get_line(mpsrange(8, 137), 11, bw=500), "g--", +ax.plot(mpsrange(8, 137), get_line(mpsrange(8, 137), 11, bw=500), "g--", label="SF11/500kHz", linewidth=2.0) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 10, bw=500), "k--", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 10, bw=500), "k--", label="SF10/500kHz", linewidth=2.0) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 9, bw=500), "c--", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 9, bw=500), "c--", label="SF9/500kHz", linewidth=2.0) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8, bw=500), "m--", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 8, bw=500), "m--", label="SF8/500kHz", linewidth=2.0) -plt.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=500), "y--", +ax.plot(mpsrange(8, 250), get_line(mpsrange(8, 250), 7, bw=500), "y--", label="SF7/500kHz", linewidth=2.0) -plt.xlabel("PHY Payload Size (Byte)") -plt.ylabel("Time on Air (ms)") - -plt.legend(loc="upper right", fancybox=True, shadow=True) -plt.grid(True) +ax.set_xlim(0, 260) +ax.set_ylim(0, 5000) +ax.set_xlabel("PHY Payload Size (Byte)") +ax.set_ylabel("Time on Air (ms)") +ax.grid(True) +ax.legend(loc="upper right", fancybox=True, shadow=True) +fig.tight_layout() plt.show() +fig.savefig("image/lorawan-toa.png") diff --git a/image/as923-toa.png b/image/as923-toa.png new file mode 100644 index 0000000..2da904a Binary files /dev/null and b/image/as923-toa.png differ diff --git a/image/as923-vs-others-sf10.png b/image/as923-vs-others-sf10.png new file mode 100644 index 0000000..2e85c2c Binary files /dev/null and b/image/as923-vs-others-sf10.png differ diff --git a/image/as923-vs-others-sf12.png b/image/as923-vs-others-sf12.png new file mode 100644 index 0000000..f0622cc Binary files /dev/null and b/image/as923-vs-others-sf12.png differ diff --git a/image/as923-with-arib180.png b/image/as923-with-arib180.png new file mode 100644 index 0000000..12c0193 Binary files /dev/null and b/image/as923-with-arib180.png differ diff --git a/image/as923-with-dwelltime.png b/image/as923-with-dwelltime.png new file mode 100644 index 0000000..23a0ad8 Binary files /dev/null and b/image/as923-with-dwelltime.png differ diff --git a/image/as923-without-dwelltime.png b/image/as923-without-dwelltime.png new file mode 100644 index 0000000..f98e2ac Binary files /dev/null and b/image/as923-without-dwelltime.png differ diff --git a/image/lora-toa-125.png b/image/lora-toa-125.png new file mode 100644 index 0000000..c3a683f Binary files /dev/null and b/image/lora-toa-125.png differ diff --git a/image/lorawan-toa.png b/image/lorawan-toa.png new file mode 100644 index 0000000..4f2ffd2 Binary files /dev/null and b/image/lorawan-toa.png differ diff --git a/lorawan_toa.py b/lorawan_toa.py index b9c4066..eb86341 100644 --- a/lorawan_toa.py +++ b/lorawan_toa.py @@ -15,24 +15,32 @@ def mpsrange(a, b): b += 6 # MHDR + MIC + 1 return range(a, b) -def get_toa(n_size, n_sf, n_bw=125, enable_dro=1, disable_h=0, n_cr=1, - n_preamble=8): +def get_toa(n_size, n_sf, n_bw=125, enable_auto_ldro=True, enable_ldro=False, + enable_eh=True, enable_crc=True, n_cr=1, n_preamble=8): ''' Parameters: n_size: PL in the fomula. PHY Payload size in byte (= MAC Payload + 5) n_sf: SF (12 to 7) n_bw: Bandwidth in kHz. default is 125 kHz for AS923. - enable_dro: - DE in the fomula. - 1 when the low data rate optimization is enabled. - 0 for disabled. - LoRaWAN always use the low data optimization. - disable_eh: - IH in the fomula. - 0 when the explicit header is enabled. - 1 when no header is present. - LoRaWAN always use the header. + enable_auto_ldro + flag whether the auto Low Data Rate Optimization is enabled or not. + default is True. + enable_ldro: + if enable_auto_ldro is disabled, LDRO is disable by default, + which means that DE in the fomula is going to be 0. + When enable_ldro is set to True, DE is going to be 1. + LoRaWAN specification does not specify the usage. + SX1276 datasheet reuiqres to enable LDRO + when the symbol duration exceeds 16ms. + enable_eh: + when enable_eh is set to False, IH in the fomula is going to be 1. + default is True, which means IH is 0. + LoRaWAN always enables the explicit header. + enable_crc: + when enable_crc is set to False, CRC in the fomula is going to be 0. + The downlink stream doesn't use the CRC in the LoRaWAN spec. + default is True to calculate ToA for the uplink stream. n_cr: CR in the fomula, should be from 1 to 4. Coding Rate = (n_cr/(n_cr+1)). @@ -53,8 +61,24 @@ def get_toa(n_size, n_sf, n_bw=125, enable_dro=1, disable_h=0, n_cr=1, r_sym = (n_bw*1000.) / math.pow(2,n_sf) t_sym = 1000. / r_sym t_preamble = (n_preamble + 4.25) * t_sym - a = 8.*n_size - 4.*n_sf + 28 + 16 - 20.*disable_h - b = 4.*(n_sf-2.*enable_dro) + # LDRO + v_DE = 0 + if enable_auto_ldro: + if t_sym >= 16: + v_DE = 1 + elif enable_ldro: + v_DE = 1 + # IH + v_IH = 0 + if not enable_eh: + v_IH = 1 + # CRC + v_CRC = 1 + if enable_crc == False: + v_CRC = 0 + # + a = 8.*n_size - 4.*n_sf + 28 + 16*v_CRC - 20.*v_IH + b = 4.*(n_sf-2.*v_DE) v_ceil = a/b n_payload = 8 + max(math.ceil(a/b)*(n_cr+4), 0) t_payload = n_payload * t_sym @@ -65,6 +89,7 @@ def get_toa(n_size, n_sf, n_bw=125, enable_dro=1, disable_h=0, n_cr=1, ret["t_sym"] = t_sym ret["n_preamble"] = n_preamble ret["t_preamble"] = t_preamble + ret["v_DE"] = v_DE ret["v_ceil"] = v_ceil ret["n_payload"] = n_payload ret["t_payload"] = t_payload @@ -87,12 +112,17 @@ if __name__ == "__main__" : p.add_argument("--band-width", action="store", dest="n_bw", type=int, default=125, help="bandwidth in kHz. default is 125 kHz.") - p.add_argument("--disable-dro", action="store_const", dest="v_de", - const=0, default=1, - help="disable the low data rate optimization. default is enable as LoRaWAN does.") - p.add_argument("--disable-eh", action="store_const", dest="v_h", - const=1, default=0, - help="disable the explicit header. default is enable as LoRaWAN does.") + p.add_argument("--disable-auto-ldro", action="store_false", + dest="enable_auto_ldro", + help="disable the auto LDRO and disable LDRO.") + p.add_argument("--enable-ldro", action="store_true", dest="enable_ldro", + help="This option is available when the auto LDRO is disabled.") + p.add_argument("--disable-eh", action="store_false", dest="enable_eh", + help="disable the explicit header.") + p.add_argument("--downlink", action="store_false", dest="enable_crc", + help="disable the CRC field, which is for the LoRaWAN downlink stream.") + p.add_argument("--disable-crc", action="store_false", dest="enable_crc", + help="same effect as the --downlink option.") p.add_argument("--cr", action="store", dest="n_cr", type=int, default=1, help="specify the CR value. default is 1 as LoRaWAN does.") p.add_argument("--preamble", action="store", dest="n_preamble", @@ -105,6 +135,8 @@ if __name__ == "__main__" : const=1, help="increase debug mode.") args = p.parse_args() + + args.v_de = False args.debug_level = len(args._f_debug) return args @@ -112,15 +144,20 @@ if __name__ == "__main__" : # main # opt = parse_args() - ret = get_toa(opt.n_size, opt.n_sf, n_bw=opt.n_bw, enable_dro=opt.v_de, - disable_h=opt.v_h, n_cr=opt.n_cr, n_preamble=opt.n_preamble) + ret = get_toa(opt.n_size, opt.n_sf, n_bw=opt.n_bw, + enable_auto_ldro=opt.enable_auto_ldro, + enable_ldro=opt.enable_ldro, + enable_eh=opt.enable_eh, enable_crc=opt.enable_crc, + n_cr=opt.n_cr, n_preamble=opt.n_preamble) if opt.f_verbose: print "PHY payload size : %d Bytes" % opt.n_size print "MAC payload size : %d Bytes" % (opt.n_size-5) print "Spreading Factor : %d" % opt.n_sf print "Band width : %d kHz" % opt.n_bw - print "Low data rate opt. : %s" % ("enable" if opt.v_de else "disable") - print "Explicit header : %s" % ("disable" if opt.v_h else "enable") + print "Low data rate opt. : %s" % ("enable" if ret["v_DE"] + else "disable") + print "Explicit header : %s" % ("enable" if opt.enable_eh + else "disable") print "CR (coding rate) : %d (4/%d)" % (opt.n_cr, 4+opt.n_cr) print "Symbol Rate : %.3f symbol/s" % ret["r_sym"] print "Symbol Time : %.3f msec/symbol" % ret["t_sym"] @@ -131,7 +168,9 @@ if __name__ == "__main__" : print "Time on Air : %.3f msec" % ret["t_packet"] if opt.debug_level: ret0 = get_toa(0, opt.n_sf, n_bw=opt.n_bw, - enable_dro=opt.v_de, disable_h=opt.v_h, + enable_auto_ldro=opt.enable_auto_ldro, + enable_ldro=opt.enable_ldro, + enable_eh=opt.enable_eh, enable_crc=opt.enable_crc, n_cr=opt.n_cr, n_preamble=opt.n_preamble) print "PHY PL=0 ToA : %.3f msec" % (ret0["t_packet"]) # preamble=8 cr=1? payload-len=7? crc=16 (payload) payload-crc=16