mcci-catena / arduino-lmic

LoraWAN-MAC-in-C library, adapted to run under the Arduino environment
https://forum.mcci.io/c/device-software/arduino-lmic/
MIT License
650 stars 212 forks source link

AU915 exceeds Time-on-air for ADR and packet confirmation #929

Open DylanGWork opened 1 year ago

DylanGWork commented 1 year ago

Hi, I have been testing my device under different fail conditions such, in this case I was testing what happens with an ACK is not received on a confirmed packet.

What occurred was that the packet of 9 bytes was sent 7 times starting at SF 10 and finishing at SF12.

The SF increase of 11 and 12 which is not allowable on AU915 (or US915 I believe).

On a similar note the 11 byte limit (or sub 400ms time-on-air) on SF10 is not taken into consideration either.

Environment I am using the latest version of this library: https://github.com/manuelbl/ttn-esp32 I am using ESP-IDF framework in Visual Studio Code The region is AU915 for this issue (but I work with 868 and US915 as well) We are using Loriot as an LNS and SX1276

What's the best to to prevent that SF exceeding it's SF10 limit?

This would need to be applied for all reasons that the SF is changed, such as ADR, linkcheck, packet confirmation.

Cheers, Dylan

terrillmoore commented 1 year ago

I don't know much about manuelbl/ttn-esp32. Are you sure you are using the current version of the Arduino LMIC? The problems you describe sound like you're using V3. In our testing, V4 certainly refuses to send longer messages than are allowed.

DylanGWork commented 1 year ago

Looks like it: "Upgraded underlying library mcci-catena/arduino-lmic to v4.2.0-1" - 7 months ago In a nutshell the library uses all of the arduino-lmic lorawan related code but ports it for the ESP-IDF and allows for things such as deepsleep etc.

terrillmoore commented 1 year ago

Well, since we rely on the LMIC rejecting messages that are too large in our applications, I'm very sure that there's not a problem in the base LMIC. The base LMIC will either refuse to send, or will automatically change DRs to a faster rate if you send a packet larger than the current DR supports. You might want to consult with Manuel -- either there's something tricky he's doing, or there's a misunderstanding. In any case, he's your point of contact on this.

DylanGWork commented 1 year ago

Ok, I have opened an issue there to consult with Manuel.

I have re-done the test after editing lmic_au915.c from:

// ================================================================================
//
// BEG: AU915 related stuff
//

CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
        ILLEGAL_RPS,                            // [-1]
        MAKERPS(SF12, BW125, CR_4_5, 0, 0),     // [0]
        MAKERPS(SF11, BW125, CR_4_5, 0, 0),     // [1]
        MAKERPS(SF10, BW125, CR_4_5, 0, 0),     // [2]
        MAKERPS(SF9 , BW125, CR_4_5, 0, 0),     // [3]
        MAKERPS(SF8 , BW125, CR_4_5, 0, 0),     // [4]
        MAKERPS(SF7 , BW125, CR_4_5, 0, 0),     // [5]
        MAKERPS(SF8 , BW500, CR_4_5, 0, 0),     // [6]
        ILLEGAL_RPS ,                           // [7]
        MAKERPS(SF12, BW500, CR_4_5, 0, 0),     // [8]
        MAKERPS(SF11, BW500, CR_4_5, 0, 0),     // [9]
        MAKERPS(SF10, BW500, CR_4_5, 0, 0),     // [10]
        MAKERPS(SF9 , BW500, CR_4_5, 0, 0),     // [11]
        MAKERPS(SF8 , BW500, CR_4_5, 0, 0),     // [12]
        MAKERPS(SF7 , BW500, CR_4_5, 0, 0),     // [13]
        ILLEGAL_RPS
};

To:

// ================================================================================
//
// BEG: AU915 related stuff
//

CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
        ILLEGAL_RPS,                            // [-1]
        ILLEGAL_RPS,     // [0]
        ILLEGAL_RPS,     // [1]
        MAKERPS(SF10, BW125, CR_4_5, 0, 0),     // [2]
        MAKERPS(SF9 , BW125, CR_4_5, 0, 0),     // [3]
        MAKERPS(SF8 , BW125, CR_4_5, 0, 0),     // [4]
        MAKERPS(SF7 , BW125, CR_4_5, 0, 0),     // [5]
        MAKERPS(SF8 , BW500, CR_4_5, 0, 0),     // [6]
        ILLEGAL_RPS ,                           // [7]
        MAKERPS(SF12, BW500, CR_4_5, 0, 0),     // [8]
        MAKERPS(SF11, BW500, CR_4_5, 0, 0),     // [9]
        MAKERPS(SF10, BW500, CR_4_5, 0, 0),     // [10]
        MAKERPS(SF9 , BW500, CR_4_5, 0, 0),     // [11]
        MAKERPS(SF8 , BW500, CR_4_5, 0, 0),     // [12]
        MAKERPS(SF7 , BW500, CR_4_5, 0, 0),     // [13]
        ILLEGAL_RPS
};

And the DR0 and DR1 no longer occur.

This code is the same in bother Manuel and your libraries, does this sound like an appropriate solution? I'll still run this by Manuel in case.

Cheers, Dylan

DylanGWork commented 1 year ago

Looks like there is some potential allowance for different regions using AU915 in the standards, in V1.0.3 it states: Upstream – 64 channels numbered 0 to 63 utilizing LoRa 125 kHz BW varying from DR0 to DR5

While page 22 shows this for dwell time: [0:63] 400ms (regional dependence) [64:71] No

I believe DR0 exceeds 400ms even with an empty payload?

Australia seems to follow US FCC regulations but this is outside of the LoRaWAN advise so I'd need to dig deeper, but the US states: FCC regulation imposes for frequency hopping systems, a maximum dwell time of 400ms on uplinks.

I've done a fair bit of research around this previously and spoken to many people, I'm pretty sure we are limited to 400ms, the TTN documentation also indicates this: https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/ "There is no duty cycle limitation applicable and the dwell time limitation is 400ms."

terrillmoore commented 1 year ago

MCCI uses SF10 (DR0) in the US all the time, and the LMIC correctly limits uplinks to 11 bytes. See the regional spec 1.0.3. It's possible that the AU region changed after the code was written; I think, anyway, you're looking at the AS923 spec, which is also used in Australia, but must be selected as such. The version of the AU915 spec I have says there is NO duty cycle limitation, but there is a "Dwell time" (inter-packet limitation). In AU915, SF12 and 11 are specifically allowed.

image

I think you are confusing dwell time with time-on-channel. Two different things. Or you're looking at A923, which is also used in Australia, but must be configured as AS923.

Best wishes, --Terry

DylanGWork commented 1 year ago

I've always used AU915 so I don't believe I'm confused with A923.

Reading this documentation: https://resources.lora-alliance.org/document/lorawan-specification-v1-0-3

Line 248 states: The end-device respects the maximum transmit duration (or dwell time) relative to the sub-band used and local regulations.

Sounds like this would have to do with the TParamSetupReq on line 858: The TxParamSetupReq command can be used to notify the end-device of the maximum allowed dwell time, i.e. the maximum continuous transmission time of a packet over the air, as well as the maximum allowed end-device Effective Isotropic Radiated Power (EIRP).

Using this calculator: https://avbentem.github.io/airtime-calculator/ttn/au915/0

You can see AU915 exceeding the dwell time limit on DR1 and DR0 with a payload of 0 bytes.

I'm happy to continue discussing the standard as I believe I have a good understanding of V1.0.3 and it appears to be relevant (And I actually enjoy getting to know the standards better, not the most popular hobby haha).

However, I'd like to know if you believe the implementation I did above is the appropriate way to prevent DR1 and DR0 being used on AU915?

Cheers, Dylan

slavendam commented 1 year ago

@terrillmoore @DylanGWork I found issue in library. File lmic_au915.c, line 83, function LMICau915_maxFrameLen https://github.com/mcci-catena/arduino-lmic/blob/4ceb2b049b59bb2390491f2db63e4951b986d277/src/lmic/lmic_au915.c#L83

In line if (LMICau915_getUplinkDwellBit()) there should be if (LMICau915_getUplinkDwellBit() == 0) because if dwel bit is 0, than dwell limit is disabled and in that case it should use maxFrameLensdwell0. If dwell bit is 1, dwell limit is set, and in that case it should use maxFrameLensdwell1

This issue gives result that SF11 and SF12 where enabled by default (and bigger payload size was allowed), and when network sends dwell_bit=0 in downlink message (which should disable dwell), it was enabling dwell limit (disabling SF11 and SF12). So opposite logic was applied.

The same logic is for AS923, but there is logic (if else) set correctly. https://github.com/mcci-catena/arduino-lmic/blob/4ceb2b049b59bb2390491f2db63e4951b986d277/src/lmic/lmic_as923.c#L106

SOLUTION 1: add "== 0" to IF condition

uint8_t LMICau915_maxFrameLen(uint8_t dr) {
        if (LMICau915_getUplinkDwellBit() == 0) { // ADDED "== 0" -> by default LMICau915_getUplinkDwellBit function will return AU915_INITIAL_TxParam_UplinkDwellTime which value is 1, and because of that (not == 0) ELSE will be called (dwell1 disables SF11 and SF12)
                if (dr < LENOF_TABLE(maxFrameLens_dwell0))
                        return TABLE_GET_U1(maxFrameLens_dwell0, dr);
                else
                        return 0;
        } else {
                if (dr < LENOF_TABLE(maxFrameLens_dwell1))
                        return TABLE_GET_U1(maxFrameLens_dwell1, dr);
                else
                        return 0;
        }
}

SOLUTION 2: use approach/function same as in AS923 file (in IF use dwell1 table, and for ELSE use dwell0)

uint8_t LMICau915_maxFrameLen(uint8_t dr) {
        if (dr < LENOF_TABLE(maxFrameLens_dwell0)) {
        if (LMICau915_getUplinkDwellBit()) //if LMICau915_getUplinkDwellBit returns 1, dwell limit is enabled and we use dwell1 table
            return TABLE_GET_U1(maxFrameLens_dwell1, dr);
        else //if LMICau915_getUplinkDwellBit returns 0, dwell limit is enabled and we use dwell0 table
            return TABLE_GET_U1(maxFrameLens_dwell0, dr);
    } else {
                return 0;
    }
}