pamoja.lorawan.relay
A LoRaWAN relay, TS011-1.0.1: what an end device and a relay say to each other.
A relay sleeps, waking every scan period to look for radio activity. An end device out of a
gateway's reach first sends a wake-on-radio (WOR) frame whose preamble spans that sleep and
which says where its LoRaWAN uplink follows. The relay may acknowledge with its own timing, so
the next preamble can be short, then forwards the uplink to the network on port
LA_FPORT_RELAY over its own session.
>>> from pamoja.lorawan import session
>>> from pamoja.lorawan import relay
>>> device = session(0x26011BDA, bytes([0x2B]) * 16, bytes([0x99]) * 16)
>>> keys = device.wor_keys()
>>> wor, uplink = relay.Carrier(865_100_000, 3), relay.Carrier(868_100_000, 5)
>>> frame = relay.wor_uplink(keys, device.dev_addr, 1, uplink, wor)
>>> relay.open_wor(frame, keys, 1, wor) == uplink
True
1"""A LoRaWAN relay, TS011-1.0.1: what an end device and a relay say to each other. 2 3A relay sleeps, waking every scan period to look for radio activity. An end device out of a 4gateway's reach first sends a wake-on-radio (WOR) frame whose preamble spans that sleep and 5which says where its LoRaWAN uplink follows. The relay may acknowledge with its own timing, so 6the next preamble can be short, then forwards the uplink to the network on port 7:data:`LA_FPORT_RELAY` over its own session. 8 9>>> from pamoja.lorawan import session 10>>> from pamoja.lorawan import relay 11>>> device = session(0x26011BDA, bytes([0x2B]) * 16, bytes([0x99]) * 16) 12>>> keys = device.wor_keys() 13>>> wor, uplink = relay.Carrier(865_100_000, 3), relay.Carrier(868_100_000, 5) 14>>> frame = relay.wor_uplink(keys, device.dev_addr, 1, uplink, wor) 15>>> relay.open_wor(frame, keys, 1, wor) == uplink 16True 17""" 18 19from __future__ import annotations 20 21import enum 22 23from pamoja._native import ( 24 LORAWAN_FORWARD_OVERHEAD, 25 LORAWAN_LA_FPORT_RELAY, 26 LORAWAN_MIN_WOR_PREAMBLE_SYMBOLS, 27 LORAWAN_RELAY_FWD_DELAY_US, 28 LORAWAN_RXR_DELAY_US, 29 LORAWAN_TRUSTED_ED_NUMBER, 30 LORAWAN_WOR_ACK_DELAY_US, 31 LORAWAN_WOR_ATTEMPTS_WO_ACK, 32 LORAWAN_WOR_DATA_DELAY_US, 33 LoraRelayChannel, 34 LorawanCarrier, 35 LorawanForwardedUplink, 36 LorawanStateSync, 37 LorawanSynchronization, 38 LorawanUplinkMetadata, 39 LorawanWor, 40 LorawanWorKeys, 41 LorawanWorSlot, 42 lorawan_relay_forward_encode, 43 lorawan_relay_forward_parse, 44 lorawan_relay_next_wor, 45 lorawan_relay_root_wor_s_key, 46 lorawan_relay_second_channel, 47 lorawan_relay_synchronization, 48 lorawan_relay_t_offset_ms, 49 lorawan_relay_unsynchronized_preamble, 50 lorawan_relay_wor_ack, 51 lorawan_relay_wor_ack_open, 52 lorawan_relay_wor_join_request, 53 lorawan_relay_wor_keys, 54 lorawan_relay_wor_open, 55 lorawan_relay_wor_parse, 56 lorawan_relay_wor_uplink, 57) 58 59__all__ = [ 60 "FORWARD_OVERHEAD", 61 "LA_FPORT_RELAY", 62 "MIN_WOR_PREAMBLE_SYMBOLS", 63 "RELAY_FWD_DELAY_US", 64 "RXR_DELAY_US", 65 "TRUSTED_ED_NUMBER", 66 "WOR_ACK_DELAY_US", 67 "WOR_ATTEMPTS_WO_ACK", 68 "WOR_DATA_DELAY_US", 69 "CadPeriodicity", 70 "CadToRx", 71 "Carrier", 72 "ForwardedUplink", 73 "RelayForward", 74 "StateSync", 75 "Synchronization", 76 "UplinkMetadata", 77 "Wor", 78 "WorChannel", 79 "WorKeys", 80 "WorKind", 81 "WorSlot", 82 "XtalAccuracy", 83 "encode_forward", 84 "next_wor", 85 "open_wor", 86 "open_wor_ack", 87 "parse_forward", 88 "parse_wor", 89 "root_wor_s_key", 90 "second_channel", 91 "synchronization", 92 "t_offset_ms", 93 "unsynchronized_preamble", 94 "wor_ack", 95 "wor_join_request", 96 "wor_keys", 97 "wor_uplink", 98] 99 100#: The port every message between a relay and its network uses, TS011-1.0.1 section 9. 101LA_FPORT_RELAY = LORAWAN_LA_FPORT_RELAY 102#: How many end devices a relay verifies wake-on-radio frames for. 103TRUSTED_ED_NUMBER = LORAWAN_TRUSTED_ED_NUMBER 104#: How many WOR frames go without an acknowledgment before the uplink goes anyway. 105WOR_ATTEMPTS_WO_ACK = LORAWAN_WOR_ATTEMPTS_WO_ACK 106#: The gap between a WOR frame, or its acknowledgment, and the LoRaWAN frame after it. 107WOR_DATA_DELAY_US = LORAWAN_WOR_DATA_DELAY_US 108#: The gap between a WOR frame and its acknowledgment. 109WOR_ACK_DELAY_US = LORAWAN_WOR_ACK_DELAY_US 110#: The gap between a relay hearing an uplink and forwarding it. 111RELAY_FWD_DELAY_US = LORAWAN_RELAY_FWD_DELAY_US 112#: How long after an uplink an end device's RXR window opens at the latest. 113RXR_DELAY_US = LORAWAN_RXR_DELAY_US 114#: The bytes a forwarded uplink adds in front of the end device's frame. 115FORWARD_OVERHEAD = LORAWAN_FORWARD_OVERHEAD 116#: The shortest WOR preamble, in symbols. 117MIN_WOR_PREAMBLE_SYMBOLS = LORAWAN_MIN_WOR_PREAMBLE_SYMBOLS 118 119#: Where a frame goes and how fast. 120Carrier = LorawanCarrier 121#: The integrity and encryption keys of one end device's WOR frames. 122WorKeys = LorawanWorKeys 123#: A WOR frame, as a relay reads it. 124Wor = LorawanWor 125#: What a relay tells an end device about itself in a WOR ACK. 126StateSync = LorawanStateSync 127#: What a relay heard of an uplink it forwards. 128UplinkMetadata = LorawanUplinkMetadata 129#: An end device's uplink as a relay forwards it on port 226. 130ForwardedUplink = LorawanForwardedUplink 131#: What an end device knows of a relay's scans once a WOR ACK has arrived. 132Synchronization = LorawanSynchronization 133#: When a synchronized end device's next WOR frame goes out. 134WorSlot = LorawanWorSlot 135 136 137class WorKind(str, enum.Enum): 138 """Which WOR frame a relay heard.""" 139 140 #: Ahead of a join request, which nothing protects. 141 JOIN_REQUEST = "join_request" 142 #: Ahead of a Class A uplink, sealed with the device's WOR keys. 143 UPLINK = "uplink" 144 145 146class CadPeriodicity(str, enum.Enum): 147 """How often a relay scans a channel for a WOR preamble, TS011-1.0.1 table 18.""" 148 149 #: Once a second, the default. 150 MS1000 = "ms1000" 151 #: Every 500 milliseconds. 152 MS500 = "ms500" 153 #: Every 250 milliseconds. 154 MS250 = "ms250" 155 #: Every 100 milliseconds. 156 MS100 = "ms100" 157 #: Every 50 milliseconds. 158 MS50 = "ms50" 159 #: Every 20 milliseconds. 160 MS20 = "ms20" 161 162 163class CadToRx(str, enum.Enum): 164 """How many symbols a relay takes from detecting activity to receiving, table 15.""" 165 166 #: Two symbols. 167 SYMBOLS2 = "symbols2" 168 #: Four symbols. 169 SYMBOLS4 = "symbols4" 170 #: Six symbols. 171 SYMBOLS6 = "symbols6" 172 #: Eight symbols, which a device assumes before it has heard from a relay. 173 SYMBOLS8 = "symbols8" 174 175 176class XtalAccuracy(str, enum.Enum): 177 """How accurate a relay's crystal is, table 17.""" 178 179 #: Better than 10 parts per million. 180 PPM10 = "ppm10" 181 #: Better than 20. 182 PPM20 = "ppm20" 183 #: Better than 30. 184 PPM30 = "ppm30" 185 #: Better than 40, which a device assumes before it has heard from a relay. 186 PPM40 = "ppm40" 187 188 189class RelayForward(str, enum.Enum): 190 """Whether a relay will forward the uplink after a WOR frame, table 16.""" 191 192 #: It has room to. 193 AVAILABLE = "available" 194 #: A forwarding limit is reached; try again in 30 minutes. 195 RETRY_IN_30_MINUTES = "retry_in_30_minutes" 196 #: A forwarding limit is reached; try again in 60 minutes. 197 RETRY_IN_60_MINUTES = "retry_in_60_minutes" 198 #: Forwarding is off. 199 DISABLED = "disabled" 200 201 202class WorChannel(str, enum.Enum): 203 """Which of a relay's channels a WOR frame arrived on, table 28.""" 204 205 #: The default channel. 206 DEFAULT = "default" 207 #: The second channel a network configured. 208 SECOND = "second" 209 210 211def _name(value: str | enum.Enum) -> str: 212 return value.value if isinstance(value, enum.Enum) else str(value) 213 214 215def root_wor_s_key(network_key: bytes) -> bytes: 216 """Derive an end device's root relay session key from its network key, section 4.4. 217 218 :param network_key: The 16-byte network session key. 219 :returns: The 16-byte root key. 220 :raises PamojaError: If the key is not 16 bytes. 221 """ 222 return lorawan_relay_root_wor_s_key(bytes(network_key)) 223 224 225def wor_keys(root_key: bytes, dev_addr: int) -> LorawanWorKeys: 226 """Derive an end device's WOR keys from its root relay session key, section 4.5. 227 228 :param root_key: The 16-byte root key. 229 :param dev_addr: The device's address. 230 :returns: The integrity and encryption keys. 231 :raises PamojaError: If the key is not 16 bytes. 232 """ 233 return lorawan_relay_wor_keys(bytes(root_key), dev_addr) 234 235 236def wor_join_request(uplink: LorawanCarrier) -> bytes: 237 """Build the WOR frame ahead of a join request, section 5.3.1. 238 239 :param uplink: Where and how fast the join request follows. 240 :returns: The five-byte frame. 241 :raises PamojaError: For a data rate past 15 or a frequency the field cannot carry. 242 """ 243 return lorawan_relay_wor_join_request(uplink) 244 245 246def wor_uplink( 247 keys: LorawanWorKeys, dev_addr: int, wfcnt: int, uplink: LorawanCarrier, wor: LorawanCarrier 248) -> bytes: 249 """Build the WOR frame ahead of a Class A uplink, section 5.3.2. 250 251 :param keys: The device's WOR keys. 252 :param dev_addr: Its address. 253 :param wfcnt: The WOR frame counter, raised for every WOR frame. 254 :param uplink: Where and how fast the uplink follows. 255 :param wor: The carrier this WOR frame goes out on. 256 :returns: The fifteen-byte frame. 257 :raises PamojaError: For a carrier the fields cannot carry. 258 """ 259 return lorawan_relay_wor_uplink(keys, dev_addr, wfcnt, uplink, wor) 260 261 262def parse_wor(frame: bytes) -> LorawanWor: 263 """Read a WOR frame, leaving an uplink's carrier sealed. 264 265 :param frame: The bytes a relay received. 266 :returns: The frame's kind, and a join request's carrier or an uplink's address and 267 counter. 268 :raises PamojaError: For a reserved or proprietary type, or a length that is not the 269 type's. 270 """ 271 return lorawan_relay_wor_parse(bytes(frame)) 272 273 274def open_wor(frame: bytes, keys: LorawanWorKeys, wfcnt: int, wor: LorawanCarrier) -> LorawanCarrier: 275 """Check a WOR frame ahead of a Class A uplink and read where the uplink follows. 276 277 :param frame: The frame a relay received. 278 :param keys: The keys of the device it names. 279 :param wfcnt: The full 32-bit counter the relay takes it to carry. 280 :param wor: The carrier it arrived on. 281 :returns: Where and how fast the uplink follows. 282 :raises PamojaError: When the frame is not an uplink WOR, or its integrity code does not 283 verify. 284 """ 285 return lorawan_relay_wor_open(bytes(frame), keys, wfcnt, wor) 286 287 288def wor_ack( 289 keys: LorawanWorKeys, 290 dev_addr: int, 291 wfcnt: int, 292 ack: LorawanCarrier, 293 uplink: LorawanCarrier, 294 state: LorawanStateSync, 295) -> bytes: 296 """Build a relay's WOR ACK, section 6.2. 297 298 :param keys: The end device's WOR keys. 299 :param dev_addr: Its address. 300 :param wfcnt: The counter of the acknowledged WOR frame. 301 :param ack: The carrier the acknowledgment goes out on. 302 :param uplink: The carrier the WOR frame named for the uplink. 303 :param state: What the relay tells the device. 304 :returns: The seven-byte acknowledgment. 305 :raises PamojaError: For a state or carrier the fields cannot carry. 306 """ 307 return lorawan_relay_wor_ack(keys, dev_addr, wfcnt, ack, uplink, state) 308 309 310def open_wor_ack( 311 frame: bytes, 312 keys: LorawanWorKeys, 313 dev_addr: int, 314 wfcnt: int, 315 ack: LorawanCarrier, 316 uplink: LorawanCarrier, 317) -> LorawanStateSync: 318 """Check and read a WOR ACK, section 6.2. 319 320 :param frame: The acknowledgment an end device received. 321 :param keys: The device's WOR keys. 322 :param dev_addr: Its address. 323 :param wfcnt: The counter of the WOR frame it sent. 324 :param ack: The carrier the acknowledgment arrived on. 325 :param uplink: The carrier the WOR frame named. 326 :returns: What the relay said about itself. 327 :raises PamojaError: When the integrity code does not verify, or the periodicity is 328 reserved. 329 """ 330 return lorawan_relay_wor_ack_open(bytes(frame), keys, dev_addr, wfcnt, ack, uplink) 331 332 333def state_sync( 334 cad_to_rx: str | CadToRx, 335 forward: str | RelayForward, 336 relay_data_rate: int, 337 xtal_accuracy: str | XtalAccuracy, 338 cad_periodicity: str | CadPeriodicity, 339 t_offset_ms: int, 340) -> LorawanStateSync: 341 """Describe what a relay tells an end device, taking the enums or their names. 342 343 :param cad_to_rx: How long the relay takes to start receiving. 344 :param forward: Whether it forwards. 345 :param relay_data_rate: The data rate it forwards at. 346 :param xtal_accuracy: How accurate its crystal is. 347 :param cad_periodicity: How often it scans. 348 :param t_offset_ms: Milliseconds from the scan to the end of the WOR preamble. 349 :returns: The state. 350 :raises PamojaError: For a name that is not one of the enum's. 351 """ 352 return LorawanStateSync( 353 _name(cad_to_rx), 354 _name(forward), 355 relay_data_rate, 356 _name(xtal_accuracy), 357 _name(cad_periodicity), 358 t_offset_ms, 359 ) 360 361 362def encode_forward( 363 metadata: LorawanUplinkMetadata, frequency_hz: int, phy_payload: bytes 364) -> bytes: 365 """Write an uplink a relay forwards on port 226, section 9.1. 366 367 A strength or ratio past what its field carries goes out as the closest value. 368 369 :param metadata: What the relay heard of it. 370 :param frequency_hz: The frequency it arrived on. 371 :param phy_payload: The end device's frame. 372 :returns: The relay uplink's payload. 373 :raises PamojaError: For a data rate or frequency the fields cannot carry. 374 """ 375 return lorawan_relay_forward_encode(metadata, frequency_hz, bytes(phy_payload)) 376 377 378def parse_forward(payload: bytes) -> LorawanForwardedUplink: 379 """Read an uplink a relay forwarded, section 9.1. 380 381 :param payload: The relay uplink's payload on port 226. 382 :returns: The metadata, the frequency, and the end device's frame. 383 :raises PamojaError: For a payload too short or a reserved WOR channel. 384 """ 385 return lorawan_relay_forward_parse(bytes(payload)) 386 387 388def unsynchronized_preamble( 389 cad_periodicity: str | CadPeriodicity, symbol_us: int, cad_to_rx: str | CadToRx 390) -> int: 391 """The WOR preamble of an end device that does not know when the relay scans, section 5.2. 392 393 :param cad_periodicity: How often the relay scans. 394 :param symbol_us: The symbol time of the WOR frame's data rate, in microseconds. 395 :param cad_to_rx: The relay's time to start receiving. 396 :returns: The preamble length in symbols. 397 398 >>> unsynchronized_preamble(CadPeriodicity.MS500, 8_192, CadToRx.SYMBOLS4) 399 72 400 """ 401 return lorawan_relay_unsynchronized_preamble(_name(cad_periodicity), symbol_us, _name(cad_to_rx)) 402 403 404def t_offset_ms(scan_start_us: int, preamble_end_us: int) -> int | None: 405 """The offset a relay reports in a WOR ACK, appendix 1. 406 407 :param scan_start_us: When the scan that detected the frame started. 408 :param preamble_end_us: When the frame's preamble ended: when it finished arriving, 409 less the airtime of its sync word and payload. 410 :returns: The offset in milliseconds, or ``None`` when the preamble ended before the 411 scan or more than eleven bits of milliseconds after it. 412 """ 413 return lorawan_relay_t_offset_ms(scan_start_us, preamble_end_us) 414 415 416def synchronization( 417 wor_start_us: int, preamble_symbols: int, symbol_us: int, state: LorawanStateSync 418) -> LorawanSynchronization: 419 """Work out when a relay scanned from the WOR ACK that answered a frame, appendix 1. 420 421 :param wor_start_us: When the acknowledged WOR frame started going out. 422 :param preamble_symbols: Its preamble length. 423 :param symbol_us: Its symbol time. 424 :param state: What the acknowledgment said. 425 :returns: The synchronization. 426 """ 427 return lorawan_relay_synchronization(wor_start_us, preamble_symbols, symbol_us, state) 428 429 430def next_wor( 431 sync: LorawanSynchronization, 432 now_us: int, 433 device_xtal_ppm: int, 434 symbol_us: int, 435 other_channel: bool = False, 436) -> LorawanWorSlot | None: 437 """Pick the relay scan a synchronized end device aims its next WOR frame at, appendix 1. 438 439 :param sync: What the device knows of the relay. 440 :param now_us: The time. 441 :param device_xtal_ppm: The device's crystal accuracy. 442 :param symbol_us: The symbol time of the WOR frame's data rate. 443 :param other_channel: Whether the frame goes out on the relay's other channel. 444 :returns: When to send and how long a preamble, or ``None`` once the drift exceeds a 445 period. 446 """ 447 return lorawan_relay_next_wor(sync, now_us, device_xtal_ppm, symbol_us, other_channel) 448 449 450def second_channel( 451 second_channel_index: int, data_rate: int, ack_offset: int, frequency_hz: int 452) -> LoraRelayChannel | None: 453 """Read the second channel a relay or end device configuration describes. 454 455 :param second_channel_index: The coded index, 1 for a second channel. 456 :param data_rate: Its data rate. 457 :param ack_offset: The coded acknowledgment offset, table 35. 458 :param frequency_hz: Its frequency. 459 :returns: The channel with its acknowledgment frequency, or ``None`` when none is named. 460 """ 461 return lorawan_relay_second_channel(second_channel_index, data_rate, ack_offset, frequency_hz)
147class CadPeriodicity(str, enum.Enum): 148 """How often a relay scans a channel for a WOR preamble, TS011-1.0.1 table 18.""" 149 150 #: Once a second, the default. 151 MS1000 = "ms1000" 152 #: Every 500 milliseconds. 153 MS500 = "ms500" 154 #: Every 250 milliseconds. 155 MS250 = "ms250" 156 #: Every 100 milliseconds. 157 MS100 = "ms100" 158 #: Every 50 milliseconds. 159 MS50 = "ms50" 160 #: Every 20 milliseconds. 161 MS20 = "ms20"
How often a relay scans a channel for a WOR preamble, TS011-1.0.1 table 18.
164class CadToRx(str, enum.Enum): 165 """How many symbols a relay takes from detecting activity to receiving, table 15.""" 166 167 #: Two symbols. 168 SYMBOLS2 = "symbols2" 169 #: Four symbols. 170 SYMBOLS4 = "symbols4" 171 #: Six symbols. 172 SYMBOLS6 = "symbols6" 173 #: Eight symbols, which a device assumes before it has heard from a relay. 174 SYMBOLS8 = "symbols8"
How many symbols a relay takes from detecting activity to receiving, table 15.
190class RelayForward(str, enum.Enum): 191 """Whether a relay will forward the uplink after a WOR frame, table 16.""" 192 193 #: It has room to. 194 AVAILABLE = "available" 195 #: A forwarding limit is reached; try again in 30 minutes. 196 RETRY_IN_30_MINUTES = "retry_in_30_minutes" 197 #: A forwarding limit is reached; try again in 60 minutes. 198 RETRY_IN_60_MINUTES = "retry_in_60_minutes" 199 #: Forwarding is off. 200 DISABLED = "disabled"
Whether a relay will forward the uplink after a WOR frame, table 16.
203class WorChannel(str, enum.Enum): 204 """Which of a relay's channels a WOR frame arrived on, table 28.""" 205 206 #: The default channel. 207 DEFAULT = "default" 208 #: The second channel a network configured. 209 SECOND = "second"
Which of a relay's channels a WOR frame arrived on, table 28.
138class WorKind(str, enum.Enum): 139 """Which WOR frame a relay heard.""" 140 141 #: Ahead of a join request, which nothing protects. 142 JOIN_REQUEST = "join_request" 143 #: Ahead of a Class A uplink, sealed with the device's WOR keys. 144 UPLINK = "uplink"
Which WOR frame a relay heard.
177class XtalAccuracy(str, enum.Enum): 178 """How accurate a relay's crystal is, table 17.""" 179 180 #: Better than 10 parts per million. 181 PPM10 = "ppm10" 182 #: Better than 20. 183 PPM20 = "ppm20" 184 #: Better than 30. 185 PPM30 = "ppm30" 186 #: Better than 40, which a device assumes before it has heard from a relay. 187 PPM40 = "ppm40"
How accurate a relay's crystal is, table 17.
363def encode_forward( 364 metadata: LorawanUplinkMetadata, frequency_hz: int, phy_payload: bytes 365) -> bytes: 366 """Write an uplink a relay forwards on port 226, section 9.1. 367 368 A strength or ratio past what its field carries goes out as the closest value. 369 370 :param metadata: What the relay heard of it. 371 :param frequency_hz: The frequency it arrived on. 372 :param phy_payload: The end device's frame. 373 :returns: The relay uplink's payload. 374 :raises PamojaError: For a data rate or frequency the fields cannot carry. 375 """ 376 return lorawan_relay_forward_encode(metadata, frequency_hz, bytes(phy_payload))
Write an uplink a relay forwards on port 226, section 9.1.
A strength or ratio past what its field carries goes out as the closest value.
Parameters
- metadata: What the relay heard of it.
- frequency_hz: The frequency it arrived on.
- phy_payload: The end device's frame. :returns: The relay uplink's payload.
Raises
- PamojaError: For a data rate or frequency the fields cannot carry.
431def next_wor( 432 sync: LorawanSynchronization, 433 now_us: int, 434 device_xtal_ppm: int, 435 symbol_us: int, 436 other_channel: bool = False, 437) -> LorawanWorSlot | None: 438 """Pick the relay scan a synchronized end device aims its next WOR frame at, appendix 1. 439 440 :param sync: What the device knows of the relay. 441 :param now_us: The time. 442 :param device_xtal_ppm: The device's crystal accuracy. 443 :param symbol_us: The symbol time of the WOR frame's data rate. 444 :param other_channel: Whether the frame goes out on the relay's other channel. 445 :returns: When to send and how long a preamble, or ``None`` once the drift exceeds a 446 period. 447 """ 448 return lorawan_relay_next_wor(sync, now_us, device_xtal_ppm, symbol_us, other_channel)
Pick the relay scan a synchronized end device aims its next WOR frame at, appendix 1.
Parameters
- sync: What the device knows of the relay.
- now_us: The time.
- device_xtal_ppm: The device's crystal accuracy.
- symbol_us: The symbol time of the WOR frame's data rate.
- other_channel: Whether the frame goes out on the relay's other channel.
:returns: When to send and how long a preamble, or
Noneonce the drift exceeds a period.
275def open_wor(frame: bytes, keys: LorawanWorKeys, wfcnt: int, wor: LorawanCarrier) -> LorawanCarrier: 276 """Check a WOR frame ahead of a Class A uplink and read where the uplink follows. 277 278 :param frame: The frame a relay received. 279 :param keys: The keys of the device it names. 280 :param wfcnt: The full 32-bit counter the relay takes it to carry. 281 :param wor: The carrier it arrived on. 282 :returns: Where and how fast the uplink follows. 283 :raises PamojaError: When the frame is not an uplink WOR, or its integrity code does not 284 verify. 285 """ 286 return lorawan_relay_wor_open(bytes(frame), keys, wfcnt, wor)
Check a WOR frame ahead of a Class A uplink and read where the uplink follows.
Parameters
- frame: The frame a relay received.
- keys: The keys of the device it names.
- wfcnt: The full 32-bit counter the relay takes it to carry.
- wor: The carrier it arrived on. :returns: Where and how fast the uplink follows.
Raises
- PamojaError: When the frame is not an uplink WOR, or its integrity code does not verify.
311def open_wor_ack( 312 frame: bytes, 313 keys: LorawanWorKeys, 314 dev_addr: int, 315 wfcnt: int, 316 ack: LorawanCarrier, 317 uplink: LorawanCarrier, 318) -> LorawanStateSync: 319 """Check and read a WOR ACK, section 6.2. 320 321 :param frame: The acknowledgment an end device received. 322 :param keys: The device's WOR keys. 323 :param dev_addr: Its address. 324 :param wfcnt: The counter of the WOR frame it sent. 325 :param ack: The carrier the acknowledgment arrived on. 326 :param uplink: The carrier the WOR frame named. 327 :returns: What the relay said about itself. 328 :raises PamojaError: When the integrity code does not verify, or the periodicity is 329 reserved. 330 """ 331 return lorawan_relay_wor_ack_open(bytes(frame), keys, dev_addr, wfcnt, ack, uplink)
Check and read a WOR ACK, section 6.2.
Parameters
- frame: The acknowledgment an end device received.
- keys: The device's WOR keys.
- dev_addr: Its address.
- wfcnt: The counter of the WOR frame it sent.
- ack: The carrier the acknowledgment arrived on.
- uplink: The carrier the WOR frame named. :returns: What the relay said about itself.
Raises
- PamojaError: When the integrity code does not verify, or the periodicity is reserved.
379def parse_forward(payload: bytes) -> LorawanForwardedUplink: 380 """Read an uplink a relay forwarded, section 9.1. 381 382 :param payload: The relay uplink's payload on port 226. 383 :returns: The metadata, the frequency, and the end device's frame. 384 :raises PamojaError: For a payload too short or a reserved WOR channel. 385 """ 386 return lorawan_relay_forward_parse(bytes(payload))
Read an uplink a relay forwarded, section 9.1.
Parameters
- payload: The relay uplink's payload on port 226. :returns: The metadata, the frequency, and the end device's frame.
Raises
- PamojaError: For a payload too short or a reserved WOR channel.
263def parse_wor(frame: bytes) -> LorawanWor: 264 """Read a WOR frame, leaving an uplink's carrier sealed. 265 266 :param frame: The bytes a relay received. 267 :returns: The frame's kind, and a join request's carrier or an uplink's address and 268 counter. 269 :raises PamojaError: For a reserved or proprietary type, or a length that is not the 270 type's. 271 """ 272 return lorawan_relay_wor_parse(bytes(frame))
Read a WOR frame, leaving an uplink's carrier sealed.
Parameters
- frame: The bytes a relay received. :returns: The frame's kind, and a join request's carrier or an uplink's address and counter.
Raises
- PamojaError: For a reserved or proprietary type, or a length that is not the type's.
216def root_wor_s_key(network_key: bytes) -> bytes: 217 """Derive an end device's root relay session key from its network key, section 4.4. 218 219 :param network_key: The 16-byte network session key. 220 :returns: The 16-byte root key. 221 :raises PamojaError: If the key is not 16 bytes. 222 """ 223 return lorawan_relay_root_wor_s_key(bytes(network_key))
Derive an end device's root relay session key from its network key, section 4.4.
Parameters
- network_key: The 16-byte network session key. :returns: The 16-byte root key.
Raises
- PamojaError: If the key is not 16 bytes.
451def second_channel( 452 second_channel_index: int, data_rate: int, ack_offset: int, frequency_hz: int 453) -> LoraRelayChannel | None: 454 """Read the second channel a relay or end device configuration describes. 455 456 :param second_channel_index: The coded index, 1 for a second channel. 457 :param data_rate: Its data rate. 458 :param ack_offset: The coded acknowledgment offset, table 35. 459 :param frequency_hz: Its frequency. 460 :returns: The channel with its acknowledgment frequency, or ``None`` when none is named. 461 """ 462 return lorawan_relay_second_channel(second_channel_index, data_rate, ack_offset, frequency_hz)
Read the second channel a relay or end device configuration describes.
Parameters
- second_channel_index: The coded index, 1 for a second channel.
- data_rate: Its data rate.
- ack_offset: The coded acknowledgment offset, table 35.
- frequency_hz: Its frequency.
:returns: The channel with its acknowledgment frequency, or
Nonewhen none is named.
417def synchronization( 418 wor_start_us: int, preamble_symbols: int, symbol_us: int, state: LorawanStateSync 419) -> LorawanSynchronization: 420 """Work out when a relay scanned from the WOR ACK that answered a frame, appendix 1. 421 422 :param wor_start_us: When the acknowledged WOR frame started going out. 423 :param preamble_symbols: Its preamble length. 424 :param symbol_us: Its symbol time. 425 :param state: What the acknowledgment said. 426 :returns: The synchronization. 427 """ 428 return lorawan_relay_synchronization(wor_start_us, preamble_symbols, symbol_us, state)
Work out when a relay scanned from the WOR ACK that answered a frame, appendix 1.
Parameters
- wor_start_us: When the acknowledged WOR frame started going out.
- preamble_symbols: Its preamble length.
- symbol_us: Its symbol time.
- state: What the acknowledgment said. :returns: The synchronization.
405def t_offset_ms(scan_start_us: int, preamble_end_us: int) -> int | None: 406 """The offset a relay reports in a WOR ACK, appendix 1. 407 408 :param scan_start_us: When the scan that detected the frame started. 409 :param preamble_end_us: When the frame's preamble ended: when it finished arriving, 410 less the airtime of its sync word and payload. 411 :returns: The offset in milliseconds, or ``None`` when the preamble ended before the 412 scan or more than eleven bits of milliseconds after it. 413 """ 414 return lorawan_relay_t_offset_ms(scan_start_us, preamble_end_us)
The offset a relay reports in a WOR ACK, appendix 1.
Parameters
- scan_start_us: When the scan that detected the frame started.
- preamble_end_us: When the frame's preamble ended: when it finished arriving,
less the airtime of its sync word and payload.
:returns: The offset in milliseconds, or
Nonewhen the preamble ended before the scan or more than eleven bits of milliseconds after it.
389def unsynchronized_preamble( 390 cad_periodicity: str | CadPeriodicity, symbol_us: int, cad_to_rx: str | CadToRx 391) -> int: 392 """The WOR preamble of an end device that does not know when the relay scans, section 5.2. 393 394 :param cad_periodicity: How often the relay scans. 395 :param symbol_us: The symbol time of the WOR frame's data rate, in microseconds. 396 :param cad_to_rx: The relay's time to start receiving. 397 :returns: The preamble length in symbols. 398 399 >>> unsynchronized_preamble(CadPeriodicity.MS500, 8_192, CadToRx.SYMBOLS4) 400 72 401 """ 402 return lorawan_relay_unsynchronized_preamble(_name(cad_periodicity), symbol_us, _name(cad_to_rx))
The WOR preamble of an end device that does not know when the relay scans, section 5.2.
Parameters
- cad_periodicity: How often the relay scans.
- symbol_us: The symbol time of the WOR frame's data rate, in microseconds.
- cad_to_rx: The relay's time to start receiving. :returns: The preamble length in symbols.
>>> unsynchronized_preamble(CadPeriodicity.MS500, 8_192, CadToRx.SYMBOLS4)
72
289def wor_ack( 290 keys: LorawanWorKeys, 291 dev_addr: int, 292 wfcnt: int, 293 ack: LorawanCarrier, 294 uplink: LorawanCarrier, 295 state: LorawanStateSync, 296) -> bytes: 297 """Build a relay's WOR ACK, section 6.2. 298 299 :param keys: The end device's WOR keys. 300 :param dev_addr: Its address. 301 :param wfcnt: The counter of the acknowledged WOR frame. 302 :param ack: The carrier the acknowledgment goes out on. 303 :param uplink: The carrier the WOR frame named for the uplink. 304 :param state: What the relay tells the device. 305 :returns: The seven-byte acknowledgment. 306 :raises PamojaError: For a state or carrier the fields cannot carry. 307 """ 308 return lorawan_relay_wor_ack(keys, dev_addr, wfcnt, ack, uplink, state)
Build a relay's WOR ACK, section 6.2.
Parameters
- keys: The end device's WOR keys.
- dev_addr: Its address.
- wfcnt: The counter of the acknowledged WOR frame.
- ack: The carrier the acknowledgment goes out on.
- uplink: The carrier the WOR frame named for the uplink.
- state: What the relay tells the device. :returns: The seven-byte acknowledgment.
Raises
- PamojaError: For a state or carrier the fields cannot carry.
237def wor_join_request(uplink: LorawanCarrier) -> bytes: 238 """Build the WOR frame ahead of a join request, section 5.3.1. 239 240 :param uplink: Where and how fast the join request follows. 241 :returns: The five-byte frame. 242 :raises PamojaError: For a data rate past 15 or a frequency the field cannot carry. 243 """ 244 return lorawan_relay_wor_join_request(uplink)
Build the WOR frame ahead of a join request, section 5.3.1.
Parameters
- uplink: Where and how fast the join request follows. :returns: The five-byte frame.
Raises
- PamojaError: For a data rate past 15 or a frequency the field cannot carry.
226def wor_keys(root_key: bytes, dev_addr: int) -> LorawanWorKeys: 227 """Derive an end device's WOR keys from its root relay session key, section 4.5. 228 229 :param root_key: The 16-byte root key. 230 :param dev_addr: The device's address. 231 :returns: The integrity and encryption keys. 232 :raises PamojaError: If the key is not 16 bytes. 233 """ 234 return lorawan_relay_wor_keys(bytes(root_key), dev_addr)
Derive an end device's WOR keys from its root relay session key, section 4.5.
Parameters
- root_key: The 16-byte root key.
- dev_addr: The device's address. :returns: The integrity and encryption keys.
Raises
- PamojaError: If the key is not 16 bytes.
247def wor_uplink( 248 keys: LorawanWorKeys, dev_addr: int, wfcnt: int, uplink: LorawanCarrier, wor: LorawanCarrier 249) -> bytes: 250 """Build the WOR frame ahead of a Class A uplink, section 5.3.2. 251 252 :param keys: The device's WOR keys. 253 :param dev_addr: Its address. 254 :param wfcnt: The WOR frame counter, raised for every WOR frame. 255 :param uplink: Where and how fast the uplink follows. 256 :param wor: The carrier this WOR frame goes out on. 257 :returns: The fifteen-byte frame. 258 :raises PamojaError: For a carrier the fields cannot carry. 259 """ 260 return lorawan_relay_wor_uplink(keys, dev_addr, wfcnt, uplink, wor)
Build the WOR frame ahead of a Class A uplink, section 5.3.2.
Parameters
- keys: The device's WOR keys.
- dev_addr: Its address.
- wfcnt: The WOR frame counter, raised for every WOR frame.
- uplink: Where and how fast the uplink follows.
- wor: The carrier this WOR frame goes out on. :returns: The fifteen-byte frame.
Raises
- PamojaError: For a carrier the fields cannot carry.