Decoding downlink ADRReq MAC payload

Hi all, I am trying to debug an issue with a gateway that I setup using basicstation+chirpstack gateway bridge with the basicstation backedn. Something is not going right, probably linked to ADR. So I am trying to decode the ADR Mac info that the gateway is sending back to the sensor.
The downlink message is in the image below. I know from arduino-lmic/LoRaWAN-at-a-glance.pdf at master · mcci-catena/arduino-lmic · GitHub that the mac payload is in the FOpts field. I can decode the base64 encoded field in python and show the individual bits and bytes. So IU basiocally have 3 questions:

  1. am I reading the Mac commands correctly below?
  2. Why are there 2 LinkADRReq mac commands in a single message? Is that normal?
  3. Is there an easier way to see the MAC commands that chirpstack sends back?
>>> from base64 import b64decode
>>> [hex(i) for i in bytearray(b64decode('AwABAHADQP8AAQ=='))]
['0x3', '0x0', '0x1', '0x0', '0x70', '0x3', '0x40', '0xff', '0x0', '0x1']
>>> [f'0b{i:08b}' for i in bytearray(b64decode('AwABAHADQP8AAQ=='))]
['0b00000011', '0b00000000', '0b00000001', '0b00000000', '0b01110000', '0b00000011', '0b01000000', '0b11111111', '0b00000000', '0b00000001']

From what I see here, it looks like there are 10 bytes, and the CID is 0x3, so ADRReq. But I am expecting only 5 bytes for ADRReq? Are there 2 ADR messages in one MAC payload here?
So if that’s the case, the first one seems to have the second byte to 0x0, which contains the DataRate and TXPower. SO is the network server telling the sensor to transmit at DR0 (for US915 SF10 BW125kHz). And TXPower 0 means 30dBm? (from https://lora-alliance.org/wp-content/uploads/2020/11/RP_2-1.0.2.pdf#page=31).
But what about the second ADRReq message? It now says data rate 0b0100, i.e. DR4? And still tx power 0?

And for the channel mask, which should be 2 bytes, the first MAC comand contains ‘0b00000001’, ‘0b00000000’ and the second 0b11111111’, '0b00000000. I am confused here for the bit order. It would make most sense to me if the 1st byte containst channels 0-7 and the second 8-15. Is that correct? In that case for the first ADR it sets channel 0 on only, and for the second ADR it sets channels 0-7 all on? I assume those are the MULTI_SF channels right? Not the Lora_std (DR4/SF8/500kHz channel for us915).

image

EDIT:
If my explanation above is correct, then this python script would allow you to decode it quickly. Currently only with LinkADRReq implemented, but more could be easily added:

from base64 import b64decode
import sys


def decode_link_adr_req(payload):
    dr = payload[1] >> 4
    tx_power = payload[1] & 0xf
    chmask = reversed(f"{payload[3] << 8 | payload[2]:16b}")
    channels = ", ".join([str(i) for i,v in enumerate(chmask) if v=='1'])
    print(f"LinkADRReq: DR: {dr}, TX Power: {tx_power}, channels: {channels}")

CIDMAP = {
    0x3: {
        'length': 5,
        'decode': decode_link_adr_req
    }
}

payload = bytearray(b64decode(sys.argv[1]))
print('raw payload')
print([f'0b{i:08b}' for i in payload])
print([f'0x{i:02x}' for i in payload])
i=0

while i<len(payload):
    cid = payload[i]
    size = CIDMAP[cid]['length']
    CIDMAP[cid]['decode'](payload[i:i+size])
    i+=size
$ python decode_mac_command.py AwABAHADQf8AAQ==
raw payload
['0b00000011', '0b00000000', '0b00000001', '0b00000000', '0b01110000', '0b00000011', '0b01000001', '0b11111111', '0b00000000', '0b00000001']
['0x03', '0x00', '0x01', '0x00', '0x70', '0x03', '0x41', '0xff', '0x00', '0x01']
LinkADRReq: DR: 0, TX Power: 0, channels: 0
LinkADRReq: DR: 4, TX Power: 1, channels: 0, 1, 2, 3, 4, 5, 6, 7

If you open the LoRaWAN frames tab on the device page, the mac-commands are already decoded :slight_smile: (note that they are not on the gateway tab)

Lol, thanks so much @brocaar for pointing that out :smiley: