Using LMIC_setClockError on MCCI LMIC [HowTo]

A wiki-post on its own to make it easier to find/refer to, and a heads up for those who didn’t know about changes.

Many old posts refer to setting LMIC_setClockError to mitigate timing problems in LMIC:

// Make LMIC start its RX windows a bit earlier, to compensate for an
// inaccurate clock.
// Beware that a specific value may work for a slow data rate, but not
// for faster ones, and remember that RX1 may use different data rates
// than RX2. Like for some tests, RX2 in EU868 (using SF9) worked with
// the standard settings, but RX1 for SF8 needed 2%, while RX1 for SF7
// even needed 5% of the maximum error. For corrections exceeding 0.4%
// (0.4 / 100) this also needs LMIC_ENABLE_arbitrary_clock_error; see
LMIC_setClockError(MAX_CLOCK_ERROR * 5 / 100);

Please read MCCI LMIC’s extensive README. First, this may no longer be the preferred first step for debugging, since bugs have been fixed in the MCCI LMIC fork:

Users of older versions of the library were advised to set large clock errors if they were experiencing timing problems. However, close analysis and debugging during the preparation of v3.1.0 of this library revealed that the real errors were in the timing calculations in the library. Once those were corrected, the need for large clock error settings was reduced. It’s still possible to use large clock errors if needed, but this must be enabled via a compile time switch.

If you still want to give it a try, then note:

For a variety of reasons, the LMIC normally ignores clock errors greater than 4000 ppm (0.4%). The compile-time flag LMIC_ENABLE_arbitrary_clock_error can remove this limit. To do this, define it to a non-zero value.

So, for values larger than 0.004 (0.4%), one also needs LMIC_ENABLE_arbitrary_clock_error in the library’s Arduino/libraries/MCCI_LoRaWAN_LMIC_library/project_config/lmic_project_config.h file (when using the Arduino IDE) or the compiler settings (when using, e.g., PlatformIO). Without that flag, MCCI LMIC will silenty limit the correction to 0.4%.

You’ll also want to test using all spreading factors: timing may be more strict for a small SF than for a large SF (as the time for a single LoRa symbol on SF7 is only half of the time on SF8, and so on, and all timing is based on symbols, if I understand correctly). Also, large values for the clock errors may cause overlapping windows or otherwise be troublesome:

The window opens earlier when clock error is made greater; this means that there is more chance for noise to close the receive window.

In short: setting the clock error is no guarantee that timing problems are fixed, and may cause side effects.

Note that an OTAA Join Accept downlink is not much different from a regular downlink (except for RX1 being 5 seconds rather than 1, RX2 being 6 seconds instead of 2, and for RX2 in EU868 using SF9 like automatically configured in the EU868 Join Accept). So, when trying to solve OTAA issues, it may be easier to test regular downlinks first, using ABP (along with manually setting LMIC.dn2Dr = DR_SF9 for EU868, which should not be set for OTAA).

If an uplink is received by multiple gateways then TTN will tell one of them to transmit the downlink. If duty cycle limitations allow for it then TTN will usually select the gateway with the best reception of the uplink to transmit the downlink. If one suspects that some gateway may not transmit downlinks at all (like due to network latency of that very gateway, or a non-compliant gateway) but cannot peek into its logs, then move closer to another gateway to increase the chances that it’s selected by TTN.

And aside: MCCI’s LMIC is the best maintained LMIC fork. Make sure to use that.