Merge pull request #49 from DzikuVx/feature/frequency-hopping
Frequency hopping -> second attempt
This commit is contained in:
8
.vscode/c_cpp_properties.json
vendored
8
.vscode/c_cpp_properties.json
vendored
@@ -32,7 +32,13 @@
|
|||||||
"~/Documents/Arduino/libraries/"
|
"~/Documents/Arduino/libraries/"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"intelliSenseMode": "clang-x64"
|
"intelliSenseMode": "clang-x64",
|
||||||
|
"defines": [
|
||||||
|
"SPI_HAS_NOTUSINGINTERRUPT",
|
||||||
|
"FEATURE_TX_INPUT_SBUS",
|
||||||
|
"DEVICE_MODE_TX",
|
||||||
|
"DEVICE_MODE_RX"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version": 3
|
"version": 3
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
#define FEATURE_TX_INPUT_SBUS
|
#define FEATURE_TX_INPUT_SBUS
|
||||||
|
|
||||||
// #define DEBUG_SERIAL
|
#define DEBUG_SERIAL
|
||||||
// #define DEBUG_PING_PONG
|
// #define DEBUG_PING_PONG
|
||||||
// #define DEBUG_LED
|
// #define DEBUG_LED
|
||||||
// #define DEBUG_TX_INPUT_ON_OLED
|
// #define DEBUG_TX_INPUT_ON_OLED
|
||||||
|
|||||||
@@ -91,10 +91,48 @@ uint8_t getRadioSnr(void)
|
|||||||
return (uint8_t) constrain(LoRa.packetSnr(), 0, 255);
|
return (uint8_t) constrain(LoRa.packetSnr(), 0, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t getFrequencyForChannel(uint8_t channel) {
|
||||||
|
return RADIO_FREQUENCY_MIN + (RADIO_CHANNEL_WIDTH * channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getNextChannel(uint8_t channel) {
|
||||||
|
return (channel + RADIO_HOP_OFFSET) % RADIO_CHANNEL_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getPrevChannel(uint8_t channel) {
|
||||||
|
return (RADIO_CHANNEL_COUNT + channel - RADIO_HOP_OFFSET) % RADIO_CHANNEL_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hopFrequency(volatile RadioState_t *radioState, bool forward, uint8_t fromChannel, uint32_t timestamp) {
|
||||||
|
radioState->channelEntryMillis = timestamp;
|
||||||
|
|
||||||
|
if (forward) {
|
||||||
|
radioState->channel = getNextChannel(fromChannel);
|
||||||
|
} else {
|
||||||
|
radioState->channel = getPrevChannel(fromChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And set hardware
|
||||||
|
LoRa.sleep();
|
||||||
|
LoRa.setFrequency(
|
||||||
|
getFrequencyForChannel(radioState->channel)
|
||||||
|
);
|
||||||
|
LoRa.idle();
|
||||||
|
}
|
||||||
|
|
||||||
void onQspSuccess(QspConfiguration_t *qsp, TxDeviceState_t *txDeviceState, RxDeviceState_t *rxDeviceState, volatile RadioState_t *radioState) {
|
void onQspSuccess(QspConfiguration_t *qsp, TxDeviceState_t *txDeviceState, RxDeviceState_t *rxDeviceState, volatile RadioState_t *radioState) {
|
||||||
//If devide received a valid frame, that means it can start to talk
|
//If recide received a valid frame, that means it can start to talk
|
||||||
qsp->canTransmit = true;
|
qsp->canTransmit = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RX module hops to next channel after frame has been received
|
||||||
|
*/
|
||||||
|
#ifdef DEVICE_MODE_RX
|
||||||
|
hopFrequency(radioState, true, radioState->lastReceivedChannel, millis());
|
||||||
|
radioState->failedDwellsCount = 0; // We received a frame, so we can just reset this counter
|
||||||
|
LoRa.receive(); //Put radio back into receive mode
|
||||||
|
#endif
|
||||||
|
|
||||||
//Store the last timestamp when frame was received
|
//Store the last timestamp when frame was received
|
||||||
if (qsp->frameId < QSP_FRAME_COUNT) {
|
if (qsp->frameId < QSP_FRAME_COUNT) {
|
||||||
qsp->lastFrameReceivedAt[qsp->frameId] = millis();
|
qsp->lastFrameReceivedAt[qsp->frameId] = millis();
|
||||||
@@ -159,7 +197,7 @@ void setup(void)
|
|||||||
LORA_DI0_PIN
|
LORA_DI0_PIN
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!LoRa.begin(radioState.frequency))
|
if (!LoRa.begin(getFrequencyForChannel(radioState.channel)))
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_SERIAL
|
#ifdef DEBUG_SERIAL
|
||||||
Serial.println("LoRa init failed. Check your connections.");
|
Serial.println("LoRa init failed. Check your connections.");
|
||||||
@@ -289,6 +327,40 @@ void loop(void)
|
|||||||
uint32_t currentMillis = millis();
|
uint32_t currentMillis = millis();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* This routine handles resync of TX/RX while hoppping frequencies
|
||||||
|
*/
|
||||||
|
#ifdef DEVICE_MODE_RX
|
||||||
|
|
||||||
|
//In the beginning just keep jumping forward and try to resync over lost single frames
|
||||||
|
if (radioState.failedDwellsCount < 6 && radioState.channelEntryMillis + RX_CHANNEL_DWELL_TIME < currentMillis) {
|
||||||
|
radioState.failedDwellsCount++;
|
||||||
|
|
||||||
|
#ifdef DEBUG_SERIAL
|
||||||
|
Serial.print("Sync forward on ch ");
|
||||||
|
Serial.print(radioState.channel);
|
||||||
|
Serial.print(" number ");
|
||||||
|
Serial.println(radioState.failedDwellsCount);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
hopFrequency(&radioState, true, radioState.channel, radioState.channelEntryMillis + RX_CHANNEL_DWELL_TIME);
|
||||||
|
LoRa.receive();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are loosing more frames, start jumping in the opposite direction since probably we are completely out of sync now
|
||||||
|
if (radioState.failedDwellsCount >= 6 && radioState.channelEntryMillis + (RX_CHANNEL_DWELL_TIME * 5) < currentMillis) {
|
||||||
|
hopFrequency(&radioState, false, radioState.channel, radioState.channelEntryMillis + RX_CHANNEL_DWELL_TIME); //Start jumping in opposite direction to resync
|
||||||
|
LoRa.receive();
|
||||||
|
|
||||||
|
#ifdef DEBUG_SERIAL
|
||||||
|
Serial.println("Sync backward");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
* Detect the moment when radio module stopped transmittig and put it
|
* Detect the moment when radio module stopped transmittig and put it
|
||||||
* back in to receive state
|
* back in to receive state
|
||||||
*/
|
*/
|
||||||
@@ -297,11 +369,22 @@ void loop(void)
|
|||||||
radioState.deviceState == RADIO_STATE_TX &&
|
radioState.deviceState == RADIO_STATE_TX &&
|
||||||
!LoRa.isTransmitting()
|
!LoRa.isTransmitting()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In case of TX module, hop right now
|
||||||
|
*/
|
||||||
|
#ifdef DEVICE_MODE_TX
|
||||||
|
hopFrequency(&radioState, true, radioState.channel, millis());
|
||||||
|
#endif
|
||||||
|
|
||||||
LoRa.receive();
|
LoRa.receive();
|
||||||
radioState.deviceState = RADIO_STATE_RX;
|
radioState.deviceState = RADIO_STATE_RX;
|
||||||
radioState.nextTxCheckMillis = currentMillis + 1; //We check of TX done every 1ms
|
radioState.nextTxCheckMillis = currentMillis + 1; //We check of TX done every 1ms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is data to be read from radio!
|
||||||
|
*/
|
||||||
if (radioState.bytesToRead != NO_DATA_TO_READ) {
|
if (radioState.bytesToRead != NO_DATA_TO_READ) {
|
||||||
LoRa.read(tmpBuffer, radioState.bytesToRead);
|
LoRa.read(tmpBuffer, radioState.bytesToRead);
|
||||||
|
|
||||||
@@ -379,6 +462,8 @@ void loop(void)
|
|||||||
|
|
||||||
#ifdef DEVICE_MODE_RX
|
#ifdef DEVICE_MODE_RX
|
||||||
|
|
||||||
|
//FIXME here we are missing the whole procedure for jumping to next channel when frame was not recived
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine updates RX device state and updates one of radio channels with RSSI value
|
* This routine updates RX device state and updates one of radio channels with RSSI value
|
||||||
*/
|
*/
|
||||||
@@ -445,7 +530,7 @@ void loop(void)
|
|||||||
uint8_t size;
|
uint8_t size;
|
||||||
LoRa.beginPacket();
|
LoRa.beginPacket();
|
||||||
//Prepare packet
|
//Prepare packet
|
||||||
qspEncodeFrame(&qsp, tmpBuffer, &size);
|
qspEncodeFrame(&qsp, &radioState, tmpBuffer, &size);
|
||||||
//Sent it to radio in one SPI transaction
|
//Sent it to radio in one SPI transaction
|
||||||
LoRa.write(tmpBuffer, size);
|
LoRa.write(tmpBuffer, size);
|
||||||
LoRa.endPacketAsync();
|
LoRa.endPacketAsync();
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ void qspDecodeIncomingFrame(
|
|||||||
static uint8_t frameId;
|
static uint8_t frameId;
|
||||||
static uint8_t payloadLength;
|
static uint8_t payloadLength;
|
||||||
static uint8_t receivedPayload;
|
static uint8_t receivedPayload;
|
||||||
|
static uint8_t receivedChannel;
|
||||||
|
|
||||||
if (qsp->protocolState == QSP_STATE_IDLE)
|
if (qsp->protocolState == QSP_STATE_IDLE)
|
||||||
{
|
{
|
||||||
@@ -187,9 +188,7 @@ void qspDecodeIncomingFrame(
|
|||||||
|
|
||||||
qsp->frameId = (incomingByte >> 4) & 0x0f;
|
qsp->frameId = (incomingByte >> 4) & 0x0f;
|
||||||
payloadLength = qspFrameLengths[qsp->frameId];
|
payloadLength = qspFrameLengths[qsp->frameId];
|
||||||
//4 bytes are now free to use for something else
|
receivedChannel = incomingByte & 0x0f;
|
||||||
// payloadLength = incomingByte & 0x0f;
|
|
||||||
|
|
||||||
qsp->protocolState = QSP_STATE_FRAME_TYPE_RECEIVED;
|
qsp->protocolState = QSP_STATE_FRAME_TYPE_RECEIVED;
|
||||||
}
|
}
|
||||||
else if (qsp->protocolState == QSP_STATE_FRAME_TYPE_RECEIVED)
|
else if (qsp->protocolState == QSP_STATE_FRAME_TYPE_RECEIVED)
|
||||||
@@ -214,6 +213,7 @@ void qspDecodeIncomingFrame(
|
|||||||
{
|
{
|
||||||
if (qsp->crc == incomingByte) {
|
if (qsp->crc == incomingByte) {
|
||||||
//CRC is correct
|
//CRC is correct
|
||||||
|
radioState->lastReceivedChannel = receivedChannel;
|
||||||
qsp->onSuccessCallback(qsp, txDeviceState, rxDeviceState, radioState);
|
qsp->onSuccessCallback(qsp, txDeviceState, rxDeviceState, radioState);
|
||||||
} else {
|
} else {
|
||||||
qsp->onFailureCallback(qsp, txDeviceState, rxDeviceState, radioState);
|
qsp->onFailureCallback(qsp, txDeviceState, rxDeviceState, radioState);
|
||||||
@@ -227,7 +227,7 @@ void qspDecodeIncomingFrame(
|
|||||||
/**
|
/**
|
||||||
* Encode frame is corrent format and write to hardware
|
* Encode frame is corrent format and write to hardware
|
||||||
*/
|
*/
|
||||||
void qspEncodeFrame(QspConfiguration_t *qsp, uint8_t buffer[], uint8_t *size) {
|
void qspEncodeFrame(QspConfiguration_t *qsp, volatile RadioState_t *radioState, uint8_t buffer[], uint8_t *size) {
|
||||||
//Zero CRC
|
//Zero CRC
|
||||||
qsp->crc = 0;
|
qsp->crc = 0;
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ void qspEncodeFrame(QspConfiguration_t *qsp, uint8_t buffer[], uint8_t *size) {
|
|||||||
//Write frame type and length
|
//Write frame type and length
|
||||||
// We are no longer sending payload length, so 4 bits are now free for other usages
|
// We are no longer sending payload length, so 4 bits are now free for other usages
|
||||||
// uint8_t data = qsp->payloadLength & 0x0f;
|
// uint8_t data = qsp->payloadLength & 0x0f;
|
||||||
uint8_t data = 0;
|
uint8_t data = radioState->channel;
|
||||||
data |= (qsp->frameToSend << 4) & 0xf0;
|
data |= (qsp->frameToSend << 4) & 0xf0;
|
||||||
qspComputeCrc(qsp, data);
|
qspComputeCrc(qsp, data);
|
||||||
buffer[1] = data;
|
buffer[1] = data;
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ void qspDecodeIncomingFrame(
|
|||||||
volatile RadioState_t *radioState
|
volatile RadioState_t *radioState
|
||||||
);
|
);
|
||||||
void qspClearPayload(QspConfiguration_t *qsp);
|
void qspClearPayload(QspConfiguration_t *qsp);
|
||||||
void qspEncodeFrame(QspConfiguration_t *qsp, uint8_t buffer[], uint8_t *size);
|
void qspEncodeFrame(QspConfiguration_t *qsp, volatile RadioState_t *radioState, uint8_t buffer[], uint8_t *size);
|
||||||
|
|
||||||
void encodePingPayload(QspConfiguration_t *qsp, uint32_t currentMicros);
|
void encodePingPayload(QspConfiguration_t *qsp, uint32_t currentMicros);
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
#define RSSI_CHANNEL 11
|
#define RSSI_CHANNEL 11
|
||||||
|
|
||||||
#define TX_TRANSMIT_SLOT_RATE 67 //ms
|
#define TX_TRANSMIT_SLOT_RATE 67 //ms
|
||||||
|
#define RX_CHANNEL_DWELL_TIME (TX_TRANSMIT_SLOT_RATE + 10) //Dwell on a channel slightly longer
|
||||||
#define RX_FAILSAFE_DELAY (TX_TRANSMIT_SLOT_RATE * 8)
|
#define RX_FAILSAFE_DELAY (TX_TRANSMIT_SLOT_RATE * 8)
|
||||||
#define TX_FAILSAFE_DELAY (RX_FAILSAFE_DELAY * 4)
|
#define TX_FAILSAFE_DELAY (RX_FAILSAFE_DELAY * 4)
|
||||||
|
|
||||||
@@ -86,8 +87,14 @@ enum debugConfigFlags {
|
|||||||
#define RADIO_STATE_TX 1
|
#define RADIO_STATE_TX 1
|
||||||
#define RADIO_STATE_RX 2
|
#define RADIO_STATE_RX 2
|
||||||
|
|
||||||
|
#define RADIO_FREQUENCY_MIN 868000000
|
||||||
|
#define RADIO_FREQUENCY_MAX 870000000
|
||||||
|
#define RADIO_FREQUENCY_RANGE (RADIO_FREQUENCY_MAX-RADIO_FREQUENCY_MIN)
|
||||||
|
#define RADIO_CHANNEL_WIDTH 250000
|
||||||
|
#define RADIO_CHANNEL_COUNT 9 // 9 channels in 2MHz range (RADIO_FREQUENCY_RANGE/RADIO_CHANNEL_WIDTH) + 1
|
||||||
|
#define RADIO_HOP_OFFSET 5
|
||||||
|
|
||||||
struct RadioState_t {
|
struct RadioState_t {
|
||||||
uint32_t frequency = 867000000;
|
|
||||||
uint32_t loraBandwidth = 250000;
|
uint32_t loraBandwidth = 250000;
|
||||||
uint8_t loraSpreadingFactor = 7;
|
uint8_t loraSpreadingFactor = 7;
|
||||||
uint8_t loraCodingRate = 6;
|
uint8_t loraCodingRate = 6;
|
||||||
@@ -97,6 +104,12 @@ struct RadioState_t {
|
|||||||
uint8_t snr = 0;
|
uint8_t snr = 0;
|
||||||
uint8_t deviceState = RADIO_STATE_RX;
|
uint8_t deviceState = RADIO_STATE_RX;
|
||||||
uint32_t nextTxCheckMillis = 0;
|
uint32_t nextTxCheckMillis = 0;
|
||||||
|
|
||||||
|
const uint32_t dwellTime = TX_TRANSMIT_SLOT_RATE * 2;
|
||||||
|
uint8_t channel = 0;
|
||||||
|
uint8_t lastReceivedChannel = 0;
|
||||||
|
uint32_t channelEntryMillis = 0;
|
||||||
|
uint8_t failedDwellsCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TxDeviceState_t {
|
struct TxDeviceState_t {
|
||||||
|
|||||||
Reference in New Issue
Block a user