Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LoRaWAN: Mitigating reception issues at lower data rates & FCnt increment after retry exhaustion #8822

Merged
merged 7 commits into from
Dec 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions UNITTESTS/features/lorawan/loraphy/unittest.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ set(unittest-test-sources
)


set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMBED_CONF_LORA_UPLINK_PREAMBLE_LENGTH=8 -DMBED_CONF_LORA_DUTY_CYCLE_ON_JOIN=true")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMBED_CONF_LORA_UPLINK_PREAMBLE_LENGTH=8 -DMBED_CONF_LORA_DUTY_CYCLE_ON_JOIN=true")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMBED_CONF_LORA_UPLINK_PREAMBLE_LENGTH=8 -DMBED_CONF_LORA_DUTY_CYCLE_ON_JOIN=true -DMBED_CONF_LORA_WAKEUP_TIME=5")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMBED_CONF_LORA_UPLINK_PREAMBLE_LENGTH=8 -DMBED_CONF_LORA_DUTY_CYCLE_ON_JOIN=true -DMBED_CONF_LORA_WAKEUP_TIME=5")
7 changes: 4 additions & 3 deletions UNITTESTS/stubs/LoRaPHY_stub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,10 @@ uint8_t LoRaPHY::verify_link_ADR_req(verify_adr_params_t *verify_params,
return LoRaPHY_stub::uint8_value;
}

void LoRaPHY::get_rx_window_params(double t_symb, uint8_t min_rx_symb,
uint32_t rx_error, uint32_t wakeup_time,
uint32_t *window_timeout, int32_t *window_offset)
void LoRaPHY::get_rx_window_params(float t_symbol, uint8_t min_rx_symbols,
float rx_error, float wakeup_time,
uint32_t *window_length, int32_t *window_offset,
uint8_t phy_dr)
{
}

Expand Down
2 changes: 2 additions & 0 deletions features/lorawan/LoRaWANStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,8 @@ void LoRaWANStack::post_process_tx_with_reception()
_loramac.get_device_class() == CLASS_A ? "A" : "C");
_ctrl_flags &= ~TX_DONE_FLAG;
_ctrl_flags |= RETRY_EXHAUSTED_FLAG;
_loramac.post_process_mcps_req();
make_tx_metadata_available();
state_controller(DEVICE_STATE_STATUS_CHECK);
}
}
Expand Down
5 changes: 3 additions & 2 deletions features/lorawan/lorastack/mac/LoRaMac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,12 @@ void LoRaMac::post_process_mcps_req()
_params.is_node_ack_requested = false;
_mcps_confirmation.ack_received = false;
_mcps_indication.is_ack_recvd = false;
_params.ul_frame_counter++;
_params.adr_ack_counter++;
} else {
_mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
}

_params.ul_frame_counter++;
_params.adr_ack_counter++;
} else {
//UNCONFIRMED or PROPRIETARY
_params.ul_frame_counter++;
Expand Down
74 changes: 54 additions & 20 deletions features/lorawan/lorastack/phy/LoRaPHY.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ SPDX-License-Identifier: BSD-3-Clause
#define BACKOFF_DC_1_HOUR 100
#define BACKOFF_DC_10_HOURS 1000
#define BACKOFF_DC_24_HOURS 10000

#define CHANNELS_IN_MASK 16
#define MAX_PREAMBLE_LENGTH 8.0f
#define TICK_GRANULARITY_JITTER 1.0f
#define CHANNELS_IN_MASK 16

LoRaPHY::LoRaPHY()
: _radio(NULL),
Expand Down Expand Up @@ -388,23 +389,56 @@ uint8_t LoRaPHY::verify_link_ADR_req(verify_adr_params_t *verify_params,
return status;
}

double LoRaPHY::compute_symb_timeout_lora(uint8_t phy_dr, uint32_t bandwidth)
float LoRaPHY::compute_symb_timeout_lora(uint8_t phy_dr, uint32_t bandwidth)
{
return ((double)(1 << phy_dr) / (double) bandwidth) * 1000;
// in milliseconds
return ((float)(1 << phy_dr) / (float) bandwidth * 1000);
}

double LoRaPHY::compute_symb_timeout_fsk(uint8_t phy_dr)
float LoRaPHY::compute_symb_timeout_fsk(uint8_t phy_dr)
{
return (8.0 / (double) phy_dr); // 1 symbol equals 1 byte
return (8.0f / (float) phy_dr); // 1 symbol equals 1 byte
}

void LoRaPHY::get_rx_window_params(double t_symb, uint8_t min_rx_symb,
uint32_t rx_error, uint32_t wakeup_time,
uint32_t *window_timeout, int32_t *window_offset)

void LoRaPHY::get_rx_window_params(float t_symb, uint8_t min_rx_symb,
float error_fudge, float wakeup_time,
uint32_t *window_length, int32_t *window_offset,
uint8_t phy_dr)
{
// Computed number of symbols
*window_timeout = MAX((uint32_t) ceil(((2 * min_rx_symb - 8) * t_symb + 2 * rx_error) / t_symb), min_rx_symb);
*window_offset = (int32_t) ceil((4.0 * t_symb) - ((*window_timeout * t_symb) / 2.0) - wakeup_time);
float target_rx_window_offset;
float window_len_in_ms;

if (phy_params.fsk_supported && phy_dr == phy_params.max_rx_datarate) {
min_rx_symb = MAX_PREAMBLE_LENGTH;
}

// We wish to be as close as possible to the actual start of data, i.e.,
// we are interested in the preamble symbols which are at the tail of the
// preamble sequence.
target_rx_window_offset = (MAX_PREAMBLE_LENGTH - min_rx_symb) * t_symb; //in ms

// Actual window offset in ms in response to timing error fudge factor and
// radio wakeup/turned around time.
*window_offset = floor(target_rx_window_offset - error_fudge - wakeup_time);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question for @c1728p9 - does/will the sleep code compensate for the 10ms or whatever deep sleep latency, if a long sleep is requested?

Eg if wait(1000) or call_in(1000), do we get a wake from deep sleep at 990 then wake from shallow at 1000, so we hit 1000 precisely, or do we just schedule an interrupt at 1000 so we potentially wake at 1010?

This code in particular is trying to hit a 1 second delay for a receive window after a transmit with millisecond precision. Can that be achieved without holding a deep sleep lock?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the RTX knows the wakeup time in advance (via some OS delay/wait call) then it should wakeup correctly with millisecond precision. If you are waking up using the low power ticker or a GPIO pin directly then deep sleep latency compensation won't help, as the OS doesn't know you are wanting to wake up in advance, and you'll need to hold the lock manually.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, that's what I hoped. I knew you'd been looking at the compensation, but couldn't recall if it was just covering "teeny" sleeps or reached out for long targets.

In this case, it's via an EventQueue::call_in, so that's a timed semaphore wake, so we're good.

This "sleep latency compensation only via the OS" thing feels like it should be documented somewhere, but not sure where to encourage people to use OS mechanisms rather than LowPowerTimers. Some sort of knowledge base article about deep sleep/tickless or something. Or maybe even a note on LowPowerTimer, which is what people might be thinking of using with deep sleep active.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kjbracey-arm can you create a tracking issue for this one?


// possible wait for next symbol start if we start inside the preamble
float possible_wait_for_symb_start = MIN(t_symb,
((2 * error_fudge) + wakeup_time + TICK_GRANULARITY_JITTER));

// how early we might start reception relative to transmit start (so negative if before transmit starts)
float earliest_possible_start_time = *window_offset - error_fudge - TICK_GRANULARITY_JITTER;

// time in (ms) we may have to wait for the other side to start transmission
float possible_wait_for_transmit = -earliest_possible_start_time;

// Minimum reception time plus extra time (in ms) we may have turned on before the
// other side started transmission
window_len_in_ms = (min_rx_symb * t_symb) + MAX(possible_wait_for_transmit, possible_wait_for_symb_start);

// Setting the window_length in terms of 'symbols' for LoRa modulation or
// in terms of 'bytes' for FSK
*window_length = (uint32_t) ceil(window_len_in_ms / t_symb);
}

int8_t LoRaPHY::compute_tx_power(int8_t tx_power_idx, float max_eirp,
Expand Down Expand Up @@ -791,7 +825,7 @@ void LoRaPHY::compute_rx_win_params(int8_t datarate, uint8_t min_rx_symbols,
uint32_t rx_error,
rx_config_params_t *rx_conf_params)
{
double t_symbol = 0.0;
float t_symbol = 0.0;

// Get the datarate, perform a boundary check
rx_conf_params->datarate = MIN(datarate, phy_params.max_rx_datarate);
Expand All @@ -811,9 +845,9 @@ void LoRaPHY::compute_rx_win_params(int8_t datarate, uint8_t min_rx_symbols,
rx_conf_params->frequency = phy_params.channels.channel_list[rx_conf_params->channel].frequency;
}


get_rx_window_params(t_symbol, min_rx_symbols, rx_error, RADIO_WAKEUP_TIME,
&rx_conf_params->window_timeout, &rx_conf_params->window_offset);
get_rx_window_params(t_symbol, min_rx_symbols, (float) rx_error, MBED_CONF_LORA_WAKEUP_TIME,
&rx_conf_params->window_timeout, &rx_conf_params->window_offset,
rx_conf_params->datarate);
}

bool LoRaPHY::rx_config(rx_config_params_t *rx_conf)
Expand Down Expand Up @@ -847,13 +881,13 @@ bool LoRaPHY::rx_config(rx_config_params_t *rx_conf)
// Radio configuration
if (dr == DR_7 && phy_params.fsk_supported) {
modem = MODEM_FSK;
_radio->set_rx_config(modem, 50000, phy_dr * 1000, 0, 83333, 5,
_radio->set_rx_config(modem, 50000, phy_dr * 1000, 0, 83333, MAX_PREAMBLE_LENGTH,
rx_conf->window_timeout, false, 0, true, 0, 0,
false, rx_conf->is_rx_continuous);
} else {
modem = MODEM_LORA;
_radio->set_rx_config(modem, rx_conf->bandwidth, phy_dr, 1, 0,
MBED_CONF_LORA_DOWNLINK_PREAMBLE_LENGTH,
MAX_PREAMBLE_LENGTH,
rx_conf->window_timeout, false, 0, false, 0, 0,
true, rx_conf->is_rx_continuous);
}
Expand Down Expand Up @@ -899,8 +933,8 @@ bool LoRaPHY::tx_config(tx_config_params_t *tx_conf, int8_t *tx_power,
// High Speed FSK channel
modem = MODEM_FSK;
_radio->set_tx_config(modem, phy_tx_power, 25000, bandwidth,
phy_dr * 1000, 0, 5, false, true, 0, 0, false,
3000);
phy_dr * 1000, 0, MBED_CONF_LORA_UPLINK_PREAMBLE_LENGTH,
false, true, 0, 0, false, 3000);
} else {
modem = MODEM_LORA;
_radio->set_tx_config(modem, phy_tx_power, 0, bandwidth, phy_dr, 1,
Expand Down
98 changes: 61 additions & 37 deletions features/lorawan/lorastack/phy/LoRaPHY.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,40 +202,63 @@ class LoRaPHY : private mbed::NonCopyable<LoRaPHY> {

/** Computing Receive Windows
*
* For more details please consult the following document, chapter 3.1.2.
* http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf
* or
* http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf
*
* Downlink start: T = Tx + 1s (+/- 20 us)
* |
* TRxEarly | TRxLate
* | | |
* | | +---+---+---+---+---+---+---+---+
* | | | Latest Rx window |
* | | +---+---+---+---+---+---+---+---+
* | | |
* The algorithm tries to calculate the length of receive windows (i.e.,
* the minimum time it should remain to acquire a lock on the Preamble
* for synchronization) and the error offset which compensates for the system
* timing errors. Basic idea behind the algorithm is to optimize for the
* reception of last 'min_rx_symbols' symbols out of transmitted Premable
* symbols. The algorithm compensates for the clock drifts, tick granularity
* and system wake up time (from sleep state) by opening the window early for
* the lower SFs. For higher SFs, the symbol time is large enough that we can
* afford to open late (hence the positive offset).
* The table below shows the calculated values for SF7 to SF12 with 125 kHz
* bandwidth.
*
* +----+-----+----------+---------+-------------------------+----------------------+-------------------------+
* | SF | BW (kHz) | rx_error (ms) | wake_up (ms) | min_rx_symbols | window_timeout(symb) | window_offset(ms) |
* +----+-----+----------+---------+-------------------------+----------------------+-------------------------+
* | 7 | 125 | 5 | 5 | 5 | 18 | -7 |
* | 8 | 125 | 5 | 5 | 5 | 10 | -4 |
* | 9 | 125 | 5 | 5 | 5 | 6 | 2 |
* | 10 | 125 | 5 | 5 | 5 | 6 | 14 |
* | 11 | 125 | 5 | 5 | 5 | 6 | 39 |
* | 12 | 125 | 5 | 5 | 5 | 6 | 88 |
* +----+-----+----------+---------+-------------------------+----------------------+-------------------------+
*
* For example for SF7, the receive window will open at downlink start time
* plus the offset calculated and will remain open for the length window_timeout.
*
* Symbol time = 1.024 ms
* Downlink start: T = Tx + 1s (+/- 20 us)
* |
* |
* |
* |
* |
* +---+---+---+---+---+---+---+---+
* | 8 Preamble Symbols |
* +---+---+---+---+---+---+---+---+
* | RX Window start time = T +/- Offset
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | | | | | | | | | | | | | | | | | | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* Similarly for SF12:
*
* Symbol time = 32.768 ms
* Downlink start: T = Tx + 1s (+/- 20 us)
* |
* |
* |
* |
* |
* +---+---+---+---+---+---+---+---+
* | Earliest Rx window |
* | 8 Preamble Symbols |
* +---+---+---+---+---+---+---+---+
* |
* +---+---+---+---+---+---+---+---+
*Downlink preamble 8 symbols | | | | | | | | |
* +---+---+---+---+---+---+---+---+
*
* Worst case Rx window timings
*
* TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME
* TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME
*
* TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
*
* RxOffset = ( TRxLate + TRxEarly ) / 2
*
* RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
* RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME
*
* The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol.
* | RX Window start time = T +/- Offset
* +---+---+---+---+---+---+
* | | | | | | |
* +---+---+---+---+---+---+
*/
/*!
* Computes the RX window timeout and offset.
Expand Down Expand Up @@ -597,9 +620,10 @@ class LoRaPHY : private mbed::NonCopyable<LoRaPHY> {
/**
* Computes the RX window timeout and the RX window offset.
*/
void get_rx_window_params(double t_symbol, uint8_t min_rx_symbols,
uint32_t rx_error, uint32_t wakeup_time,
uint32_t *window_timeout, int32_t *window_offset);
void get_rx_window_params(float t_symbol, uint8_t min_rx_symbols,
float rx_error, float wakeup_time,
uint32_t *window_length, int32_t *window_offset,
uint8_t phy_dr);

/**
* Computes the txPower, based on the max EIRP and the antenna gain.
Expand Down Expand Up @@ -632,12 +656,12 @@ class LoRaPHY : private mbed::NonCopyable<LoRaPHY> {
/**
* Computes the symbol time for LoRa modulation.
*/
double compute_symb_timeout_lora(uint8_t phy_dr, uint32_t bandwidth);
float compute_symb_timeout_lora(uint8_t phy_dr, uint32_t bandwidth);

/**
* Computes the symbol time for FSK modulation.
*/
double compute_symb_timeout_fsk(uint8_t phy_dr);
float compute_symb_timeout_fsk(uint8_t phy_dr);

protected:
LoRaRadio *_radio;
Expand Down
24 changes: 14 additions & 10 deletions features/lorawan/mbed_lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,28 @@
"value": true
},
"max-sys-rx-error": {
"help": "Maximum timing error of the receiver in ms. The receiver will turn on in [-RxError : + RxError]",
"value": 10
"help": "Max. timing error fudge. The receiver will turn on in [-RxError : + RxError]",
"value": 5
},
"wakeup-time": {
"help": "Time in (ms) the platform takes to wakeup from sleep/deep sleep state. This number is platform dependent",
"value": 5
},
"downlink-preamble-length": {
"help": "Number of preamble symbols need to be captured (out of 8) for successful demodulation",
"value": 5
"help": "Number of whole preamble symbols needed to have a firm lock on the signal.",
"value": 5
},
"uplink-preamble-length": {
"help": "Number of preamble symbols to transmit. Must be <= 8",
"value": 8
"help": "Number of preamble symbols to transmit. Default: 8",
"value": 8
},
"fsb-mask": {
"help": "FSB mask for upstream [Only for US915 & AU915] Check lorawan/FSB_Usage.txt for more details",
"value": "{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x00FF}"
"help": "FSB mask for upstream [Only for US915 & AU915] Check lorawan/FSB_Usage.txt for more details",
"value": "{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x00FF}"
},
"fsb-mask-china": {
"help": "FSB mask for upstream [CN470 PHY] Check lorawan/FSB_Usage.txt for more details",
"value": "{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}"
"help": "FSB mask for upstream [CN470 PHY] Check lorawan/FSB_Usage.txt for more details",
"value": "{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}"
}
}
}
7 changes: 2 additions & 5 deletions features/lorawan/system/lorawan_data_structures.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@
typedef uint32_t lorawan_time_t;
#endif

// Radio wake-up time from sleep - unit ms.
#define RADIO_WAKEUP_TIME 1

/*!
* Sets the length of the LoRaMAC footer field.
* Mainly indicates the MIC field length.
Expand Down Expand Up @@ -1259,8 +1256,8 @@ typedef struct {

/*!
* LoRaMac reception windows delay
* \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME
* join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME
* \remark normal frame: RxWindowXDelay = ReceiveDelayX - Offset
* join frame : RxWindowXDelay = JoinAcceptDelayX - Offset
*/
uint32_t rx_window1_delay;
uint32_t rx_window2_delay;
Expand Down