-
Notifications
You must be signed in to change notification settings - Fork 135
dev ARQ (retransmit) #185
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
base: main
Are you sure you want to change the base?
dev ARQ (retransmit) #185
Changes from 28 commits
c48ddb1
d07e8e3
062b73a
f43d224
67e45e5
19c5402
6d1ee37
325d3c8
197c44d
4ea70dd
d83bb71
5e0f0c7
fecd1d5
19d4fe7
432324a
f0204e1
dab1168
3a00326
b03ec59
2a42d4d
f8636d1
5890cf0
0f0754f
75f4d30
3f6b82b
6e24f98
2988cf5
e2e0d7f
1accf76
25a423a
b0a7d7d
3384f4a
609eb22
bcd6c90
5f43fc8
30fce69
9d17218
b39c98b
aa4c41f
37a4ab8
3106187
517e8d9
4b5971e
6df029a
286d991
ed4f274
15fd452
b20b9a5
d69c5b4
e74735a
40a8cbc
8199f4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,384 @@ | ||
| //******************************************************* | ||
| // Copyright (c) MLRS project | ||
| // GPL3 | ||
| // https://www.gnu.org/licenses/gpl-3.0.de.html | ||
| // OlliW @ www.olliw.eu | ||
| //******************************************************* | ||
| // ARQ Transmit/Receive | ||
| //******************************************************* | ||
| /* | ||
| TODOs: | ||
| - how does it behave with approaching link loss? | ||
| - all well with flow control? | ||
| - double check that it doesn't break TRANSMIT_FRAME_TYPE_CMD communication | ||
| - is rxLQ correct? | ||
| Notes: | ||
| - Lua script load/reload leads to few packet losses with 1/4/5 | ||
| */ | ||
| #ifndef ARQ_H | ||
| #define ARQ_H | ||
| #pragma once | ||
|
|
||
| #define USE_ARQ | ||
| #define USE_ARQ_RETRY_CNT -1 // -1: set by SetRetryCnt(), 0 = off, 255 = infinite, | ||
| //#define USE_ARQ_DBG | ||
| //#define USE_ARQ_TX_SIM_MISS 4 //9 // 0 = off | ||
| //#define USE_ARQ_RX_SIM_MISS 3 //5 //5 // 0 = off | ||
|
|
||
| #ifdef USE_ARQ | ||
|
|
||
| //------------------------------------------------------- | ||
| // Transmit | ||
| //------------------------------------------------------- | ||
|
|
||
| class tTransmitArq | ||
| { | ||
| public: | ||
| void Init(void); | ||
|
|
||
| typedef enum { | ||
| ARQ_TX_IDLE = 0, | ||
| ARQ_TX_FRAME_MISSED, | ||
| ARQ_TX_RECEIVED, | ||
| } ARQ_TX_ENUM; | ||
|
|
||
| void Disconnected(void); | ||
| void FrameMissed(void); | ||
| void Received(uint8_t ack_seq_no); | ||
|
|
||
| bool GetFreshPayload(void); | ||
| uint8_t SeqNo(void); | ||
| void SetRetryCnt(uint8_t retry_cnt); | ||
|
|
||
| void SetRetryCntAuto(int32_t _frame_cnt, uint8_t mode); | ||
|
|
||
| uint8_t status; | ||
| uint8_t received_ack_seq_no; // attention: is 0/1 only, 0 = even, 1 = odd | ||
| uint8_t payload_seq_no; // the seq_no associated to this payload | ||
| uint8_t payload_retry_cnt; // retry count for this payload | ||
| uint8_t payload_retries; // number of retries for this payload | ||
|
|
||
| bool SimulateMiss(void); | ||
| }; | ||
|
|
||
|
|
||
| void tTransmitArq::Init(void) | ||
| { | ||
| status = ARQ_TX_IDLE; | ||
| received_ack_seq_no = 0; | ||
| payload_seq_no = 0; | ||
| payload_retry_cnt = UINT8_MAX; // 0 = off, 255 = infinite | ||
| payload_retries = 0; | ||
| } | ||
|
|
||
|
|
||
| // methods called upon receive or expected receive (in doPostReceive) | ||
| // the calling sequence is: | ||
| // 1. Received(seq_no) or FrameMissed() (in handle_receive() or handle_receive_none()) | ||
| // 2. Disconnected() | ||
|
|
||
| void tTransmitArq::Disconnected(void) | ||
| { | ||
| status = ARQ_TX_IDLE; | ||
| } | ||
|
|
||
|
|
||
| void tTransmitArq::FrameMissed(void) | ||
| { | ||
| status = ARQ_TX_FRAME_MISSED; | ||
| } | ||
|
|
||
|
|
||
| void tTransmitArq::Received(uint8_t ack_seq_no) | ||
| { | ||
| received_ack_seq_no = ack_seq_no; // is 0/1 | ||
| status = ARQ_TX_RECEIVED; | ||
| } | ||
|
|
||
|
|
||
| // methods called for transmit | ||
|
|
||
| // called at begin of prepare_transmit_frame() | ||
| bool tTransmitArq::GetFreshPayload(void) | ||
| { | ||
| if (payload_retry_cnt == 0) { // ARQ disabled, new payload each time | ||
| payload_seq_no++; | ||
| payload_retries = 0; | ||
| return true; | ||
| } | ||
|
|
||
| switch (status) { | ||
| case ARQ_TX_IDLE: | ||
| // send new payload | ||
| payload_seq_no++; | ||
| payload_retries = 0; | ||
| return true; | ||
|
|
||
| case ARQ_TX_RECEIVED: | ||
|
olliw42 marked this conversation as resolved.
|
||
| // keep previous payload if received_seq_no != payload_seq_no | ||
| // attention: received_seq_no is 1 bit! | ||
| // next = (received_seq_no & 0x01) == (payload_seq_no & 0x01) | ||
| if ((received_ack_seq_no & 0x01) != (payload_seq_no & 0x01)) { | ||
|
olliw42 marked this conversation as resolved.
|
||
| if (payload_retry_cnt == UINT8_MAX) { // ARQ with infinite retries, so never next | ||
| payload_retries = 0; | ||
| return false; | ||
| } else { // ARQ with finite retries | ||
| payload_retries++; | ||
| if (payload_retries <= payload_retry_cnt) { // we still can retry | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| payload_seq_no++; // give this payload the next seq_no | ||
| payload_retries = 0; | ||
| return true; | ||
|
|
||
| case ARQ_TX_FRAME_MISSED: | ||
| if (payload_retry_cnt == UINT8_MAX) { // ARQ with infinite retries, so never next | ||
| payload_retries = 0; | ||
| return false; | ||
| } else { // ARQ with finite retries | ||
| payload_retries++; | ||
| if (payload_retries > payload_retry_cnt) { // no further retry, send new payload | ||
| payload_seq_no++; | ||
| payload_retries = 0; | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
|
|
||
| uint8_t tTransmitArq::SeqNo(void) | ||
| { | ||
| return payload_seq_no; // will be converted to 3 bit or 0...7 | ||
| } | ||
|
|
||
|
|
||
| void tTransmitArq::SetRetryCnt(uint8_t retry_cnt) | ||
| { | ||
| #if USE_ARQ_RETRY_CNT < 0 | ||
| payload_retry_cnt = retry_cnt; | ||
| #else | ||
| payload_retry_cnt = USE_ARQ_RETRY_CNT; | ||
| #endif | ||
| // payload_retries = 0; ?? I think to better not do this, allows to shorten retry cnt and the ARQ to react immediately | ||
| } | ||
|
|
||
|
|
||
| void tTransmitArq::SetRetryCntAuto(int32_t _frame_cnt, uint8_t mode) | ||
| { | ||
| switch (mode) { | ||
| case MODE_FLRC_111HZ: | ||
| if (_frame_cnt >= 800) { | ||
| SetRetryCnt(3); | ||
| } else if (_frame_cnt >= 700) { | ||
| SetRetryCnt(2); | ||
| } else { | ||
| SetRetryCnt(1); | ||
| } | ||
| break; | ||
| case MODE_FSK_50HZ: | ||
| case MODE_50HZ: | ||
| case MODE_31HZ: | ||
| case MODE_19HZ: | ||
| SetRetryCnt((_frame_cnt >= 800) ? 2 : 1); | ||
| } | ||
| SetRetryCnt(1); | ||
| } | ||
|
|
||
|
|
||
| // miscellaneous | ||
|
|
||
| bool tTransmitArq::SimulateMiss(void) | ||
| { | ||
| #if USE_ARQ_RX_SIM_MISS | ||
| static uint8_t miss_cnt = 0; | ||
| DECc(miss_cnt, USE_ARQ_RX_SIM_MISS); | ||
| if (miss_cnt == 0) return true; | ||
| #endif | ||
| return false; | ||
| } | ||
|
|
||
|
|
||
| //------------------------------------------------------- | ||
| // Receive | ||
| //------------------------------------------------------- | ||
|
|
||
| class tReceiveArq | ||
| { | ||
| public: | ||
| void Init(void); | ||
|
|
||
| typedef enum { | ||
| ARQ_RX_IDLE = 0, | ||
| ARQ_RX_FRAME_MISSED, | ||
| ARQ_RX_RECEIVED_WAS_IDLE, | ||
| ARQ_RX_RECEIVED, | ||
| } ARQ_RX_ENUM; | ||
|
|
||
| void Disconnected(void); | ||
| void FrameMissed(void); | ||
| void Received(uint8_t seq_no); | ||
|
|
||
| bool AcceptPayload(void); | ||
| bool FrameLost(void); | ||
|
|
||
| uint8_t AckSeqNo(void); | ||
|
|
||
| uint8_t status; | ||
| uint8_t received_seq_no_last; // all seq_no here are 3 bit, 0..7 | ||
| uint8_t received_seq_no; | ||
| uint8_t ack_seq_no; | ||
| bool accept_received_payload; // maybe a state? | ||
| bool frame_lost; // maybe a state? | ||
|
|
||
| void spin(void); | ||
|
|
||
| bool SimulateMiss(void); | ||
| }; | ||
|
|
||
|
|
||
| void tReceiveArq::Init(void) | ||
| { | ||
| status = ARQ_RX_IDLE; | ||
| received_seq_no = 0; | ||
| received_seq_no_last = 0; | ||
| accept_received_payload = false; | ||
| frame_lost = false; | ||
| ack_seq_no = 0; | ||
| } | ||
|
|
||
|
|
||
| // methods called upon receive or expected receive (in doPreTransmit) | ||
| // the calling sequence is: | ||
| // 1. Received(seq_no) or FrameMissed() (in handle_receive() or handle_receive_none()) | ||
| // 2. AcceptPayload() (in process_received_frame()) | ||
| // 3. FrameLost() | ||
| // 4. Disconnected() | ||
|
|
||
| void tReceiveArq::Disconnected(void) | ||
| { | ||
| status = ARQ_RX_IDLE; | ||
| } | ||
|
|
||
|
|
||
| void tReceiveArq::spin(void) | ||
| { | ||
| if (status == ARQ_RX_IDLE) { while(1); } // must not happen, should have been called after Missed,Received | ||
|
olliw42 marked this conversation as resolved.
Outdated
|
||
|
|
||
| switch (status) { | ||
| case ARQ_RX_RECEIVED_WAS_IDLE: | ||
| accept_received_payload = true; | ||
| received_seq_no_last = received_seq_no; | ||
| ack_seq_no = received_seq_no; | ||
| break; | ||
|
|
||
| case ARQ_RX_RECEIVED: | ||
| accept_received_payload = (received_seq_no != received_seq_no_last); // new seq no received, so accept it | ||
| received_seq_no_last = received_seq_no; | ||
| ack_seq_no = received_seq_no; | ||
| break; | ||
|
|
||
| case ARQ_RX_FRAME_MISSED: // is not called if handle_receive_none(), RX_STATUS_NONE ! | ||
| accept_received_payload = false; | ||
| break; | ||
|
|
||
| default: | ||
| accept_received_payload = false; | ||
| } | ||
|
|
||
| frame_lost = false; | ||
|
olliw42 marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
|
|
||
| void tReceiveArq::FrameMissed(void) | ||
| { | ||
| status = ARQ_RX_FRAME_MISSED; | ||
| spin(); | ||
| } | ||
|
|
||
|
|
||
| void tReceiveArq::Received(uint8_t seq_no) | ||
| { | ||
| received_seq_no = seq_no; | ||
| status = (status == ARQ_RX_IDLE) ? ARQ_RX_RECEIVED_WAS_IDLE : ARQ_RX_RECEIVED; | ||
| spin(); | ||
| } | ||
|
|
||
|
|
||
| bool tReceiveArq::AcceptPayload(void) | ||
| { | ||
| return accept_received_payload; | ||
| } | ||
|
|
||
|
|
||
| bool tReceiveArq::FrameLost(void) // called to check if parsers need to be reset | ||
| { | ||
| return frame_lost; | ||
| } | ||
|
|
||
|
|
||
| // methods called for transmit | ||
|
|
||
| uint8_t tReceiveArq::AckSeqNo(void) | ||
| { | ||
| return ack_seq_no; // will be converted by 1 bit to 0/1 | ||
| } | ||
|
|
||
|
|
||
| // miscellaneous | ||
|
|
||
| bool tReceiveArq::SimulateMiss(void) | ||
| { | ||
| #if USE_ARQ_TX_SIM_MISS | ||
| static uint8_t miss_cnt = 0; | ||
| DECc(miss_cnt, USE_ARQ_TX_SIM_MISS); | ||
| if (miss_cnt == 0) return true; | ||
| #endif | ||
| return false; | ||
| } | ||
|
|
||
|
|
||
| #else | ||
| // these should be nfc, i.e., result in exactly the same behavior as before | ||
|
|
||
| class tTransmitArq | ||
| { | ||
| public: | ||
| void Init(void) { seq_no = 0; } | ||
|
|
||
| void Disconnected(void) {} | ||
| void FrameMissed(void) {} | ||
|
olliw42 marked this conversation as resolved.
|
||
| void Received(uint8_t ack_seq_no) {} | ||
|
olliw42 marked this conversation as resolved.
Outdated
|
||
|
|
||
| bool GetFreshPayload(void) { return true; } | ||
| uint8_t SeqNo(void) { seq_no++; return seq_no; } | ||
| void SetRetryCnt(uint8_t retry_cnt) {} | ||
| void SetRetryCntAuto(int32_t _frame_cnt, uint8_t mode) {} | ||
|
|
||
| uint8_t seq_no; | ||
| }; | ||
|
|
||
|
|
||
| class tReceiveArq | ||
| { | ||
| public: | ||
| void Init(void) {} | ||
|
|
||
| void Disconnected(void) {} | ||
| void FrameMissed(void) {} | ||
| void Received(uint8_t _seq_no) {} | ||
| bool AcceptPayload(void) { return true; } | ||
| bool FrameLost(void) { return false; } | ||
|
olliw42 marked this conversation as resolved.
|
||
|
|
||
| uint8_t AckSeqNo(void) { return 1; } | ||
| }; | ||
|
|
||
| #undef USE_ARQ_DBG | ||
| #endif | ||
|
|
||
| #endif // ARQ_H | ||
Uh oh!
There was an error while loading. Please reload this page.